Security: Cross-Company Customer IDOR in CustomerPolicy (CWE-639)
Summary
CustomerPolicy is missing the $user->hasCompany($customer->company_id) check that other resource policies consistently use. This allows users to access, modify, and delete customers belonging to other companies.
Details
Vulnerable: app/Policies/CustomerPolicy.php
All permission methods (view, update, delete, restore, forceDelete) check only Bouncer ability scope:
public function view(User $user, Customer $customer)
{
if (BouncerFacade::can('view-customer', $customer)) {
return true;
}
return false;
}
Compare with secure policies:
InvoicePolicy::view():
if (BouncerFacade::can('view-invoice', $invoice) && $user->hasCompany($invoice->company_id)) {
return true;
}
Similarly, PaymentMethodPolicy, ReportPolicy, EstimatePolicy, and other resource policies all include $user->hasCompany($resource->company_id).
Bouncer's scope restricts abilities to their assigned scope, but if abilities are assigned at the class level (e.g., view-customer on Customer::class), the scope check passes for ANY customer instance regardless of company.
Impact
A user in Company A can view, edit, and delete customers belonging to Company B by supplying their customer IDs directly.
Suggested Fix
Add $user->hasCompany($customer->company_id) to all per-object permission methods in CustomerPolicy:
public function view(User $user, Customer $customer)
{
if (BouncerFacade::can('view-customer', $customer) && $user->hasCompany($customer->company_id)) {
return true;
}
return false;
}
Affected Methods
view()
update()
delete()
restore()
forceDelete()
Classification
CWE-639: Authorization Bypass Through User-Controlled Key
Security: Cross-Company Customer IDOR in CustomerPolicy (CWE-639)
Summary
CustomerPolicyis missing the$user->hasCompany($customer->company_id)check that other resource policies consistently use. This allows users to access, modify, and delete customers belonging to other companies.Details
Vulnerable:
app/Policies/CustomerPolicy.phpAll permission methods (view, update, delete, restore, forceDelete) check only Bouncer ability scope:
Compare with secure policies:
InvoicePolicy::view():Similarly,
PaymentMethodPolicy,ReportPolicy,EstimatePolicy, and other resource policies all include$user->hasCompany($resource->company_id).Bouncer's scope restricts abilities to their assigned scope, but if abilities are assigned at the class level (e.g.,
view-customeronCustomer::class), the scope check passes for ANY customer instance regardless of company.Impact
A user in Company A can view, edit, and delete customers belonging to Company B by supplying their customer IDs directly.
Suggested Fix
Add
$user->hasCompany($customer->company_id)to all per-object permission methods in CustomerPolicy:Affected Methods
view()update()delete()restore()forceDelete()Classification
CWE-639: Authorization Bypass Through User-Controlled Key