Severity by source
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N
Lifecycle Timeline
3DescriptionGitHub Advisory
Shopper is a Headless e-commerce Admin Panel. Prior to 2.8.0, CreateOrderFromCartAction::execute previously created the Order row before checking and incrementing the discount's total_use counter. Under concurrent checkout pressure (Black Friday, flash sale, viral coupon), the global usage_limit was silently exceeded: orders were committed with the discount fully applied to price_amount while the counter blocked at usage_limit. The merchant had no signal that an over-redemption had occurred. This vulnerability is fixed in 2.8.0.
AnalysisAI
Discount over-redemption via race condition in Shopper (shopperlabs/shopper prior to v2.8.0) allows concurrent unauthenticated checkout requests to bypass a coupon's global usage_limit, resulting in committed orders carrying unauthorized discounts the merchant never intended to grant. The CreateOrderFromCartAction::execute method created the Order database row before atomically reserving the discount slot, opening a classic TOCTOU window that becomes reliably exploitable under the high-concurrency traffic of flash sales or Black Friday events. A compounding bug rendered usage_limit_per_user entirely non-functional in all pre-2.8.0 versions - the underlying counter was never incremented anywhere in the codebase - meaning per-user caps provided zero protection regardless of a customer's redemption history. No public exploit identified at time of analysis; this CVE is not listed in CISA KEV.
Technical ContextAI
The vulnerability exists in the shopperlabs/shopper PHP Composer packages (shopper/cart and shopper/core), a Laravel-based headless e-commerce admin panel (CPE: cpe:2.3:a:shopperlabs:shopper:*:*:*:*:*:*:*:*). The root cause maps precisely to CWE-362 (Concurrent Execution using Shared Resource with Improper Synchronization). The CreateOrderFromCartAction::execute method performed a non-atomic check-then-act sequence: it read the discount's total_use value, validated it against usage_limit, committed the Order row to the database, and only then incremented total_use - without holding a database lock across these operations. This creates a TOCTOU window where multiple concurrent HTTP requests can simultaneously read the same pre-limit total_use value, all pass validation independently, and all commit orders with the discount applied before any counter increment is written. The fix in v2.8.0 wraps the entire sequence inside a DB::transaction using lockForUpdate with a compare-and-swap on total_use, making the reservation atomic and rolling back with DiscountLimitReachedException if the limit was exhausted between cart validation and commit. The secondary defect - DiscountDetail.total_use never being incremented - meant the per-user check perpetually observed 0 prior uses; for eligibility=Everyone discounts, the requisite DiscountDetail row did not even exist, making the per-user path entirely unreachable.
RemediationAI
Upgrade both shopper/cart and shopper/core to version 2.8.0 by running 'composer require shopper/cart:^2.8 shopper/core:^2.8' followed by 'php artisan migrate' - the migration step is required to apply schema changes that snapshot discount metadata (discount_id, discount_code, discount_type, discount_value_at_apply, discount_currency_code) onto the orders table for audit resilience. The fix is confirmed in PR #511 (commit fcd0c5920588702df5b874f432b1042abd77a50b) and detailed in the vendor advisory at https://github.com/shopperlabs/shopper/security/advisories/GHSA-9rh9-hf3w-9fgg. The vendor explicitly states no workarounds exist. As an emergency compensating control prior to upgrading, operators can disable all discount codes with usage limits entirely, eliminating the financial exposure at the cost of all promotional functionality. Setting artificially low usage_limit values does not prevent the race condition but limits the magnitude of over-redemption per campaign. Neither compensating control addresses the per-user limit bug, which requires the v2.8.0 code fix.
Privilege escalation in Shopper headless e-commerce admin panel prior to version 2.8.0 allows any low-privilege authenti
Authorization bypass in Shopperlabs Shopper headless e-commerce admin panel prior to 2.8.0 allows authenticated low-priv
Privilege escalation in shopperlabs/shopper prior to 2.8.0 allows any authenticated admin panel user - regardless of ass
Missing authorization enforcement in the Shopper headless e-commerce admin panel (all versions prior to 2.8.0) allows an
Same weakness CWE-362 – Race Condition
View allSame technique Information Disclosure
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-33409