Every CVE Search/ Get API key

CVE-2026-31824

Sylius has a Promotion Usage Limit Bypass via Race Condition

8.2 HighFix available

Sylius is an Open Source eCommerce Framework on Symfony. A Time-of-Check To Time-of-Use (TOCTOU) race condition was discovered in the promotion usage limit enforcement. The same class of vulnerability affects the promotion usage limit (the global used counter on Promotion entities), coupon usage limit (the global used counter on PromotionCoupon entities), and coupon per-customer usage limit (the per-customer redemption count on PromotionCoupon entities). In all three cases, the eligibility check reads the used counter (or order count) from an in-memory Doctrine entity during validation, while the actual usage increment in OrderPromotionsUsageModifier happens later during order completion — with no database-level locking or atomic operations between the two phases. Because Doctrine flushes an absolute value (SET used = 1) rather than an atomic increment (SET used = used + 1), and because the affected entities lack optimistic locking, concurrent requests all read the same stale usage counts and pass the eligibility checks simultaneously. An attacker can exploit this by preparing multiple carts with the same limited-use promotion or coupon and firing simultaneous PATCH /api/v2/shop/orders/{token}/complete requests. All requests pass the usage limit checks and complete successfully, allowing a single-use promotion or coupon to be redeemed an arbitrary number of times. The per-customer limit can be bypassed in the same way by a single customer completing multiple orders concurrently. No authentication is required to exploit this vulnerability. This may lead to direct financial loss through unlimited redemption of limited-use promotions and discount coupons. The issue is fixed in versions: 1.9.12, 1.10.16, 1.11.17, 1.12.23, 1.13.15, 1.14.18, 2.0.16, 2.1.12, 2.2.3 and above.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
High
Availability
Low

Exploit likelihood

0.21%Up 0.2 points since Mar 11, 2026
90%50%10%1%

What each source reports

CVE List Score used 8.2

Sylius has a Promotion Usage Limit Bypass via Race Condition

Sylius is an Open Source eCommerce Framework on Symfony. A Time-of-Check To Time-of-Use (TOCTOU) race condition was discovered in the promotion usage limit enforcement. The same class of vulnerability affects the promotion usage limit (the global used counter on Promotion entities), coupon usage limit (the global used counter on PromotionCoupon entities), and coupon per-customer usage limit (the per-customer redemption count on PromotionCoupon entities). In all three cases, the eligibility check reads the used counter (or order count) from an in-memory Doctrine entity during validation, while the actual usage increment in OrderPromotionsUsageModifier happens later during order completion — with no database-level locking or atomic operations between the two phases. Because Doctrine flushes an absolute value (SET used = 1) rather than an atomic increment (SET used = used + 1), and because the affected entities lack optimistic locking, concurrent requests all read the same stale usage counts and pass the eligibility checks simultaneously. An attacker can exploit this by preparing multiple carts with the same limited-use promotion or coupon and firing simultaneous PATCH /api/v2/shop/orders/{token}/complete requests. All requests pass the usage limit checks and complete successfully, allowing a single-use promotion or coupon to be redeemed an arbitrary number of times. The per-customer limit can be bypassed in the same way by a single customer completing multiple orders concurrently. No authentication is required to exploit this vulnerability. This may lead to direct financial loss through unlimited redemption of limited-use promotions and discount coupons. The issue is fixed in versions: 1.9.12, 1.10.16, 1.11.17, 1.12.23, 1.13.15, 1.14.18, 2.0.16, 2.1.12, 2.2.3 and above.

Record
CVE-2026-31824
CVSS 3.1
8.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
Weakness
CWE-362, CWE-367
Updated
2026-11-02
EUVDEUVD-2026-10920 8.2

Sylius is an Open Source eCommerce Framework on Symfony. A Time-of-Check To Time-of-Use (TOCTOU) race condition was discovered in the promotion usage limit enforcement. The same class of vulnerability affects the promotion usage limit (the global used counter on Promotion entities), coupon usage limit (the global used counter on PromotionCoupon entities), and coupon per-customer usage limit (the per-customer redemption count on PromotionCoupon entities). In all three cases, the eligibility check reads the used counter (or order count) from an in-memory Doctrine entity during validation, while the actual usage increment in OrderPromotionsUsageModifier happens later during order completion — with no database-level locking or atomic operations between the two phases. Because Doctrine flushes an absolute value (SET used = 1) rather than an atomic increment (SET used = used + 1), and because the affected entities lack optimistic locking, concurrent requests all read the same stale usage counts and pass the eligibility checks simultaneously. An attacker can exploit this by preparing multiple carts with the same limited-use promotion or coupon and firing simultaneous PATCH /api/v2/shop/orders/{token}/complete requests. All requests pass the usage limit checks and complete successfully, allowing a single-use promotion or coupon to be redeemed an arbitrary number of times. The per-customer limit can be bypassed in the same way by a single customer completing multiple orders concurrently. No authentication is required to exploit this vulnerability. This may lead to direct financial loss through unlimited redemption of limited-use promotions and discount coupons. The issue is fixed in versions: 1.9.12, 1.10.16, 1.11.17, 1.12.23, 1.13.15, 1.14.18, 2.0.16, 2.1.12, 2.2.3 and above.

Record
EUVD-2026-10920
CVSS 3.1
8.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
Updated
2026-11-02
GitHub AdvisoriesGHSA-7mp4-25j8-hp5q 8.2

Sylius has a Promotion Usage Limit Bypass via Race Condition

Impact

A Time-of-Check To Time-of-Use (TOCTOU) race condition was discovered in the promotion usage limit enforcement. The same class of vulnerability affects three independent limits:

  1. Promotion usage limit - the global used counter on Promotion entities
  2. Coupon usage limit - the global used counter on PromotionCoupon entities
  3. Coupon per-customer usage limit - the per-customer redemption count on PromotionCoupon entities

In all three cases, the eligibility check reads the used counter (or order count) from an in-memory Doctrine entity during validation, while the actual usage increment in OrderPromotionsUsageModifier happens later during order completion — with no database-level locking or atomic operations between the two phases.

Because Doctrine flushes an absolute value (SET used = 1) rather than an atomic increment (SET used = used + 1), and because the affected entities lack optimistic locking, concurrent requests all read the same stale usage counts and pass the eligibility checks simultaneously.

An attacker can exploit this by preparing multiple carts with the same limited-use promotion or coupon and firing simultaneous PATCH /api/v2/shop/orders/{token}/complete requests. All requests pass the usage limit checks and complete successfully, allowing a single-use promotion or coupon to be redeemed an arbitrary number of times. The per-customer limit can be bypassed in the same way by a single customer completing multiple orders concurrently. No authentication is required to exploit this vulnerability.

This may lead to direct financial loss through unlimited redemption of limited-use promotions and discount coupons.

Patches

The issue is fixed in versions: 1.9.12, 1.10.16, 1.11.17, 1.12.23, 1.13.15, 1.14.18, 2.0.16, 2.1.12, 2.2.3 and above.

Workarounds

Decoration of the OrderPromotionsUsageModifier service to use atomic operations based on actual database-synchronized values.

The decorated service id in Sylius >=2.0 is sylius.modifier.promotion.order_usage, while <2.0 it's sylius.promotion_usage_modifier; The following instruction uses the latter, but it needs to be changed depending on the Sylius version.

Step 1. Create the decorator service

src/Modifier/AtomicOrderPromotionsUsageModifier.php:

<?php

declare(strict_types=1);

namespace App\Modifier;

use Doctrine\DBAL\Connection;
use Doctrine\ORM\OptimisticLockException;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PromotionCouponInterface;
use Sylius\Component\Core\Promotion\Modifier\OrderPromotionsUsageModifierInterface;
use Sylius\Component\Promotion\Model\PromotionInterface;
// use Symfony\Component\DependencyInjection\Attribute\AsDecorator;

// #[AsDecorator(decorates: 'sylius.promotion_usage_modifier')]
final class AtomicOrderPromotionsUsageModifier implements OrderPromotionsUsageModifierInterface
{
    /** @var Connection */
    private $connection;

    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }

    public function increment(OrderInterface $order): void
    {
        foreach ($order->getPromotions() as $promotion) {
            $this->incrementPromotionUsage($promotion);
        }

        /** @var PromotionCouponInterface|null $coupon */
        $coupon = $order->getPromotionCoupon();
        if (null === $coupon) {
            return;
        }

        $this->incrementCouponUsage($coupon, $order);
    }

    public function decrement(OrderInterface $order): void
    {
        foreach ($order->getPromotions() as $promotion) {
            $this->decrementPromotionUsage($promotion);
        }

        /** @var PromotionCouponInterface|null $coupon */
        $coupon = $order->getPromotionCoupon();
        if (null === $coupon) {
            return;
        }

        if (OrderInterface::STATE_CANCELLED === $order->getState() && !$coupon->isReusableFromCancelledOrders()) {
           …
Record
GHSA-7mp4-25j8-hp5q
CVSS 3.1
8.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
Weakness
CWE-362, CWE-367
Updated
2026-11-02
NVD 8.2

Sylius is an Open Source eCommerce Framework on Symfony. A Time-of-Check To Time-of-Use (TOCTOU) race condition was discovered in the promotion usage limit enforcement. The same class of vulnerability affects the promotion usage limit (the global used counter on Promotion entities), coupon usage limit (the global used counter on PromotionCoupon entities), and coupon per-customer usage limit (the per-customer redemption count on PromotionCoupon entities). In all three cases, the eligibility check reads the used counter (or order count) from an in-memory Doctrine entity during validation, while the actual usage increment in OrderPromotionsUsageModifier happens later during order completion — with no database-level locking or atomic operations between the two phases. Because Doctrine flushes an absolute value (SET used = 1) rather than an atomic increment (SET used = used + 1), and because the affected entities lack optimistic locking, concurrent requests all read the same stale usage counts and pass the eligibility checks simultaneously. An attacker can exploit this by preparing multiple carts with the same limited-use promotion or coupon and firing simultaneous PATCH /api/v2/shop/orders/{token}/complete requests. All requests pass the usage limit checks and complete successfully, allowing a single-use promotion or coupon to be redeemed an arbitrary number of times. The per-customer limit can be bypassed in the same way by a single customer completing multiple orders concurrently. No authentication is required to exploit this vulnerability. This may lead to direct financial loss through unlimited redemption of limited-use promotions and discount coupons. The issue is fixed in versions: 1.9.12, 1.10.16, 1.11.17, 1.12.23, 1.13.15, 1.14.18, 2.0.16, 2.1.12, 2.2.3 and above.

Record
CVE-2026-31824
CVSS 3.1
8.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
CVSS 3.1
5.9 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N
Weakness
CWE-362, CWE-367
Updated
2026-11-02
OSV 8.2

Sylius has a Promotion Usage Limit Bypass via Race Condition

Sylius is an Open Source eCommerce Framework on Symfony. A Time-of-Check To Time-of-Use (TOCTOU) race condition was discovered in the promotion usage limit enforcement. The same class of vulnerability affects the promotion usage limit (the global used counter on Promotion entities), coupon usage limit (the global used counter on PromotionCoupon entities), and coupon per-customer usage limit (the per-customer redemption count on PromotionCoupon entities). In all three cases, the eligibility check reads the used counter (or order count) from an in-memory Doctrine entity during validation, while the actual usage increment in OrderPromotionsUsageModifier happens later during order completion — with no database-level locking or atomic operations between the two phases. Because Doctrine flushes an absolute value (SET used = 1) rather than an atomic increment (SET used = used + 1), and because the affected entities lack optimistic locking, concurrent requests all read the same stale usage counts and pass the eligibility checks simultaneously. An attacker can exploit this by preparing multiple carts with the same limited-use promotion or coupon and firing simultaneous PATCH /api/v2/shop/orders/{token}/complete requests. All requests pass the usage limit checks and complete successfully, allowing a single-use promotion or coupon to be redeemed an arbitrary number of times. The per-customer limit can be bypassed in the same way by a single customer completing multiple orders concurrently. No authentication is required to exploit this vulnerability. This may lead to direct financial loss through unlimited redemption of limited-use promotions and discount coupons. The issue is fixed in versions: 1.9.12, 1.10.16, 1.11.17, 1.12.23, 1.13.15, 1.14.18, 2.0.16, 2.1.12, 2.2.3 and above.

Record
CVE-2026-31824
CVSS 3.1
8.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
Weakness
CWE-362, CWE-367
Updated
2026-11-02
OSVGHSA-7mp4-25j8-hp5q 8.2

Sylius has a Promotion Usage Limit Bypass via Race Condition

Impact

A Time-of-Check To Time-of-Use (TOCTOU) race condition was discovered in the promotion usage limit enforcement. The same class of vulnerability affects three independent limits:

  1. Promotion usage limit - the global used counter on Promotion entities
  2. Coupon usage limit - the global used counter on PromotionCoupon entities
  3. Coupon per-customer usage limit - the per-customer redemption count on PromotionCoupon entities

In all three cases, the eligibility check reads the used counter (or order count) from an in-memory Doctrine entity during validation, while the actual usage increment in OrderPromotionsUsageModifier happens later during order completion — with no database-level locking or atomic operations between the two phases.

Because Doctrine flushes an absolute value (SET used = 1) rather than an atomic increment (SET used = used + 1), and because the affected entities lack optimistic locking, concurrent requests all read the same stale usage counts and pass the eligibility checks simultaneously.

An attacker can exploit this by preparing multiple carts with the same limited-use promotion or coupon and firing simultaneous PATCH /api/v2/shop/orders/{token}/complete requests. All requests pass the usage limit checks and complete successfully, allowing a single-use promotion or coupon to be redeemed an arbitrary number of times. The per-customer limit can be bypassed in the same way by a single customer completing multiple orders concurrently. No authentication is required to exploit this vulnerability.

This may lead to direct financial loss through unlimited redemption of limited-use promotions and discount coupons.

Patches

The issue is fixed in versions: 1.9.12, 1.10.16, 1.11.17, 1.12.23, 1.13.15, 1.14.18, 2.0.16, 2.1.12, 2.2.3 and above.

Workarounds

Decoration of the OrderPromotionsUsageModifier service to use atomic operations based on actual database-synchronized values.

The decorated service id in Sylius >=2.0 is sylius.modifier.promotion.order_usage, while <2.0 it's sylius.promotion_usage_modifier; The following instruction uses the latter, but it needs to be changed depending on the Sylius version.

Step 1. Create the decorator service

src/Modifier/AtomicOrderPromotionsUsageModifier.php:

<?php

declare(strict_types=1);

namespace App\Modifier;

use Doctrine\DBAL\Connection;
use Doctrine\ORM\OptimisticLockException;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PromotionCouponInterface;
use Sylius\Component\Core\Promotion\Modifier\OrderPromotionsUsageModifierInterface;
use Sylius\Component\Promotion\Model\PromotionInterface;
// use Symfony\Component\DependencyInjection\Attribute\AsDecorator;

// #[AsDecorator(decorates: 'sylius.promotion_usage_modifier')]
final class AtomicOrderPromotionsUsageModifier implements OrderPromotionsUsageModifierInterface
{
    /** @var Connection */
    private $connection;

    public function __construct(Connection $connection)
    {
        $this->connection = $connection;
    }

    public function increment(OrderInterface $order): void
    {
        foreach ($order->getPromotions() as $promotion) {
            $this->incrementPromotionUsage($promotion);
        }

        /** @var PromotionCouponInterface|null $coupon */
        $coupon = $order->getPromotionCoupon();
        if (null === $coupon) {
            return;
        }

        $this->incrementCouponUsage($coupon, $order);
    }

    public function decrement(OrderInterface $order): void
    {
        foreach ($order->getPromotions() as $promotion) {
            $this->decrementPromotionUsage($promotion);
        }

        /** @var PromotionCouponInterface|null $coupon */
        $coupon = $order->getPromotionCoupon();
        if (null === $coupon) {
            return;
        }

        if (OrderInterface::STATE_CANCELLED === $order->getState() && !$coupon->isReusableFromCancelledOrders()) {
           …
Record
GHSA-7mp4-25j8-hp5q
CVSS 3.1
8.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L
Weakness
CWE-362, CWE-367
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
sylius/sylius
Packagist
through 1.9.11
1.9.12
sylius/sylius
Packagist
1.10.0 to 1.10.15
1.10.16
sylius/sylius
Packagist
1.11.0 to 1.11.16
1.11.17
sylius/sylius
Packagist
1.12.0 to 1.12.22
1.12.23
sylius/sylius
Packagist
1.13.0 to 1.13.14
1.13.15
sylius/sylius
Packagist
1.14.0 to 1.14.17
1.14.18
sylius/sylius
Packagist
2.0.0 to 2.0.15
2.0.16
sylius/sylius
Packagist
2.1.0 to 2.1.11
2.1.12
sylius/sylius
Packagist
2.2.0 to 2.2.2
2.2.3
sylius/sylius
Packagist
before 1.9.12, plus listed versions
v0.1.0v0.10.0v0.11.0v0.12.0v0.13.0
Show 45 moreShow less
v0.14.0v0.15.0v0.16.0v0.17.0v0.18.0v0.19.0v0.2.0v0.3.0v0.5.0v0.6.0v0.7.0v0.8.0v0.9.0v1.0.0v1.0.0-alpha.1v1.0.0-alpha.2v1.0.0-beta.1v1.0.0-beta.2v1.0.0-beta.3v1.0.0-rc.1v1.0.0-rc.2v1.0.1v1.0.10v1.0.11v1.0.12v1.0.13v1.0.14v1.0.15v1.0.16v1.0.17v1.0.18v1.0.2v1.0.3v1.0.4v1.0.5v1.0.6v1.0.7v1.0.8v1.0.9v1.1.0v1.1.0-RCv1.1.1v1.1.10v1.1.11v1.1.12
1.9.12
Show 8 moreShow less
Affected packages
PackageAffectedFixed
sylius/sylius
Packagist
1.10.0 up to 1.10.16, plus listed versions
v1.10.0v1.10.1v1.10.10v1.10.11v1.10.12
Show 11 moreShow less
v1.10.13v1.10.14v1.10.15v1.10.2v1.10.3v1.10.4v1.10.5v1.10.6v1.10.7v1.10.8v1.10.9
1.10.16
sylius/sylius
Packagist
1.11.0 up to 1.11.17, plus listed versions
v1.11.0v1.11.1v1.11.10v1.11.11v1.11.12
Show 12 moreShow less
v1.11.13v1.11.14v1.11.15v1.11.16v1.11.2v1.11.3v1.11.4v1.11.5v1.11.6v1.11.7v1.11.8v1.11.9
1.11.17
sylius/sylius
Packagist
1.12.0 up to 1.12.23, plus listed versions
v1.12.0v1.12.1v1.12.10v1.12.11v1.12.12
Show 18 moreShow less
v1.12.13v1.12.14v1.12.15v1.12.16v1.12.17v1.12.18v1.12.19v1.12.2v1.12.20v1.12.21v1.12.22v1.12.3v1.12.4v1.12.5v1.12.6v1.12.7v1.12.8v1.12.9
1.12.23
sylius/sylius
Packagist
1.13.0 up to 1.13.15, plus listed versions
v1.13.0v1.13.1v1.13.10v1.13.11v1.13.12
Show 10 moreShow less
v1.13.13v1.13.14v1.13.2v1.13.3v1.13.4v1.13.5v1.13.6v1.13.7v1.13.8v1.13.9
1.13.15
sylius/sylius
Packagist
1.14.0 up to 1.14.18, plus listed versions
v1.14.0v1.14.1v1.14.10v1.14.11v1.14.12
Show 13 moreShow less
v1.14.13v1.14.14v1.14.15v1.14.16v1.14.17v1.14.2v1.14.3v1.14.4v1.14.5v1.14.6v1.14.7v1.14.8v1.14.9
1.14.18
sylius/sylius
Packagist
2.0.0 up to 2.0.16, plus listed versions
v2.0.0v2.0.1v2.0.10v2.0.11v2.0.12
Show 11 moreShow less
v2.0.13v2.0.14v2.0.15v2.0.2v2.0.3v2.0.4v2.0.5v2.0.6v2.0.7v2.0.8v2.0.9
2.0.16
sylius/sylius
Packagist
2.1.0 up to 2.1.12, plus listed versions
v2.1.0v2.1.1v2.1.10v2.1.11v2.1.2
Show 7 moreShow less
v2.1.3v2.1.4v2.1.5v2.1.6v2.1.7v2.1.8v2.1.9
2.1.12
sylius/sylius
Packagist
2.2.0 up to 2.2.3, plus listed versions
v2.2.0v2.2.1v2.2.2
2.2.3

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-31824 is a high vulnerability (CVSS 8.2) in Sylius, published March 10, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.21%, the 10th percentile. Fixed in sylius/sylius 1.9.12.

References

This page doesn't list everything

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

MoveEnter Open