Skip to main content

Shopper CVE-2026-47741

| EUVDEUVD-2026-33409 MEDIUM
Race Condition (CWE-362)
2026-05-29 GitHub_M
5.9
CVSS 3.1 · Vendor: GitHub_M
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Identify active coupon code with usage_limit configured
  2. Delivery
    Submit concurrent checkout requests applying coupon code
  3. Exploit
    Multiple requests simultaneously read pre-limit total_use value
  4. Execution
    All pass validation before any counter increment is written
  5. Persist
    Orders committed to database with full discount applied to price_amount
  6. Impact
    Counter reaches usage_limit after over-commitment - merchant absorbs unintended financial loss with no alert

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A06:2025 - Insecure Design
ASVS 5.0
V15 - Secure Coding and Architecture Concurrency control, locking
MITRE ATT&CK

1 technique across 1 tactic: Privilege Escalation

The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up.

Exploitation Conditions

Exploitation of the global limit bypass requires at least one active discount code configured with a non-zero usage_limit in the target Shopper installation, and the ability to submit multiple simultaneous checkout requests applying that code - achievable with a scripted HTTP client, coordinated browser tabs, or simply by sharing a coupon publicly during a high-traffic event. No authentication is required per CVSS PR:N, as the checkout flow is customer-facing. The race window is narrow per request pair, making reliable single-attacker exploitation difficult at low request volume, but the concurrency threshold is inherently met during promotional events the merchant deliberately drives. Exploitation of the per-user limit bypass (usage_limit_per_user) requires only that the discount be configured with a per-user cap - no concurrency is needed, as the underlying DiscountDetail.total_use counter was never incremented in any code path, making every redemption appear as the customer's first regardless of history. For discounts with eligibility=Everyone, the per-user check cannot fire at all because no DiscountDetail row exists for that eligibility mode. Limiting factors for the global race: attack complexity is genuinely high at low traffic volumes; exploitation becomes reliable only when merchant-driven promotional load creates natural concurrency.

Impact

CVSS 5.9 (AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N) accurately classifies the network-accessible, unauthenticated attack surface with an Integrity:High impact reflecting unauthorized financial manipulation. The AC:H designation acknowledges the race window that must be won, but this rating can be misleading in practice: during promotional events that generate natural high-concurrency checkout traffic, the race becomes statistically reliable without any attacker-controlled infrastructure. No EPSS score was provided in the input data. The CVE is not in CISA KEV, and no public POC exploit code has been identified, indicating no confirmed active exploitation at time of analysis. Real-world financial risk is elevated for merchants running time-limited promotional campaigns - the exact traffic conditions that make exploitation reliable are also the conditions merchants deliberately create. The secondary per-user limit bug means that merchants who believed they had per-user caps as a safety net were entirely unprotected, compounding the integrity impact beyond what the global limit alone suggests. CVSS Confidentiality:N is confirmed - no data exfiltration vector is present. The 'Information Disclosure' tag in the source metadata appears to be a classification error inconsistent with both the description and the CVSS vector.

Exploit ScenarioDerived

During a flash sale, an attacker distributes a limited-use coupon code or identifies one through normal shopping activity, then scripts dozens of simultaneous POST requests to the checkout endpoint - all applying the same coupon at the moment total_use approaches usage_limit. Because pre-2.8.0 Shopper reads total_use, validates, inserts the Order row, and only then increments the counter without database locking, multiple concurrent requests observe the same below-limit counter value, pass validation independently, and are all committed with the full discount applied. The merchant's usage_limit counter eventually reaches its ceiling, but significantly more orders than intended have already been stored with discounted price_amount values, representing direct and silent financial loss with no alert generated. No public POC exploit code has been identified at time of analysis.

This Month Plan within 30 days. Why: MEDIUM severity (CVSS 4.0-6.9).
Patch available 0.0% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – May 29, 2026

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.

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.

Affected VersionsENISA

Shopper < 2.8.0

Affected Packages

composer shopper/cart fix: 2.8.0
euvd shopper fix: 2.8.0

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 2 packages in composer, euvd

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

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.

Know when Shopper gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (GitHub_M) PRIMARY 5.9 MEDIUM
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N

Primary rating from Vendor (GitHub_M) · only source for this CVE.

CVSS VectorVendor: GitHub_M

Attack Vector
Network
Attack Complexity
High
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
High
Availability
None

Lifecycle Timeline

1
Patch available
May 29, 2026 - 20:02 EUVD

DescriptionCVE.org

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.

Threat Intelligence Overview

EPSS Score
0.0%
Top 93% exploitable
Data Quality
6 sources
Confidence: High

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity MEDIUM
CISA KEV No
Public POC No
EPSS 0.0%
Patch available Yes
Active threat No
Deciding rule
MEDIUM severity (CVSS 4.0-6.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation
Poc
System Exposure· derived
Open
Automatable
No
Human Impact· derived
Medium
Technical Impact
Partial

Priority Score

30
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +30
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Information disclosure occurs when an application unintentionally exposes sensitive data that aids attackers in reconnaissance or directly compromises security.

Race conditions occur when multiple concurrent requests exploit the brief time gap between checking a condition and acting upon it (TOCTOU - Time of Check, Time of Use).

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy