Summary
Multiple Livewire components bypass route-level Spatie permission enforcement, allowing read-only CRM users to delete records, change deal statuses, and even delete system users across teams.
Findings
1. Livewire action methods bypass Spatie permission checks (HIGH)
15+ Livewire Index/Show components have action methods (delete(), won(), lost(), accept(), reject()) with no authorization. Routes enforce can: middleware, but Livewire POST to /livewire/message bypasses it entirely.
Affected: DealIndex, LeadIndex, PersonIndex, OrganizationIndex, QuoteIndex, OrderIndex, InvoiceIndex, PurchaseOrderIndex, DeliveryIndex, ProductShow, PersonShow, LeadShow, OrganizationShow, UserIndex, TeamIndex
2. Cross-team user deletion without authorization (HIGH)
Livewire/Users/UserIndex.php — delete($id) uses User::find($id) which does NOT have BelongsToTeams scope. Any authenticated user can delete ANY system user including cross-team users or admins.
3. Cross-team role assignment (HIGH)
UserController.php — Role::find($request->role) fetches roles from any team. Admin in Team A can assign Team B's 'Owner' role to users.
4. Activities routes missing permission middleware (MEDIUM)
routes.php lines 512-533 — Activity routes have only auth middleware, no can: checks, unlike all other resource routes.
5. Sub-resource routes missing permission middleware (MEDIUM)
Deal/Quote/Order product routes lack can: middleware. View-only users can create product lines.
Root Cause
Route-level can: middleware correctly enforces Spatie permissions for page loads, but Livewire action methods are called via /livewire/message POST which bypasses route middleware. None of the Livewire action methods call $this->authorize() or check permissions server-side.
Recommended Fix
Add $this->authorize() to all Livewire action methods:
public function delete($id)
{
if ($deal = Deal::find($id)) {
$this->authorize('delete', $deal); // ADD THIS
$deal->delete();
}
}
For UserIndex::delete(), also scope to current team. For role assignment, filter: Role::where('team_id', $user->currentTeam->id)->find($request->role).
Summary
Multiple Livewire components bypass route-level Spatie permission enforcement, allowing read-only CRM users to delete records, change deal statuses, and even delete system users across teams.
Findings
1. Livewire action methods bypass Spatie permission checks (HIGH)
15+ Livewire Index/Show components have action methods (
delete(),won(),lost(),accept(),reject()) with no authorization. Routes enforcecan:middleware, but Livewire POST to/livewire/messagebypasses it entirely.Affected:
DealIndex,LeadIndex,PersonIndex,OrganizationIndex,QuoteIndex,OrderIndex,InvoiceIndex,PurchaseOrderIndex,DeliveryIndex,ProductShow,PersonShow,LeadShow,OrganizationShow,UserIndex,TeamIndex2. Cross-team user deletion without authorization (HIGH)
Livewire/Users/UserIndex.php—delete($id)usesUser::find($id)which does NOT haveBelongsToTeamsscope. Any authenticated user can delete ANY system user including cross-team users or admins.3. Cross-team role assignment (HIGH)
UserController.php—Role::find($request->role)fetches roles from any team. Admin in Team A can assign Team B's 'Owner' role to users.4. Activities routes missing permission middleware (MEDIUM)
routes.phplines 512-533 — Activity routes have onlyauthmiddleware, nocan:checks, unlike all other resource routes.5. Sub-resource routes missing permission middleware (MEDIUM)
Deal/Quote/Order product routes lack
can:middleware. View-only users can create product lines.Root Cause
Route-level
can:middleware correctly enforces Spatie permissions for page loads, but Livewire action methods are called via/livewire/messagePOST which bypasses route middleware. None of the Livewire action methods call$this->authorize()or check permissions server-side.Recommended Fix
Add
$this->authorize()to all Livewire action methods:For
UserIndex::delete(), also scope to current team. For role assignment, filter:Role::where('team_id', $user->currentTeam->id)->find($request->role).