#1321·crater

Security: Cross-company customer IDOR in CustomerPolicy (CWE-639)

Author: lighthousekeeper1212Created Feb 20, 2026Updated Feb 20, 2026

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:

php
public function view(User $user, Customer $customer)
{
    if (BouncerFacade::can('view-customer', $customer)) {
        return true;
    }
    return false;
}

Compare with secure policies:

InvoicePolicy::view():

php
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:

php
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

Source: crater-invoice-inc/crater