Monitor vulnerabilities like this one.
Sign up free to get alerted when software you use is affected.
8.1
Sylius API Allows Unauthorized Cart Item Addition
CVE-2026-31821
GHSA-wjmg-4cq5-m8hg
GHSA-wjmg-4cq5-m8hg
Summary
Unauthenticated attackers can add items to other customers' carts if they know the cart token value. This can reveal sensitive information like email addresses, cart contents, and payment and shipment IDs. Affected versions of Sylius are fixed in 2.0.16, 2.1.12, and 2.2.3, so update to one of these versions or add an ownership check in the AddItemToCartHandler to fix the issue.
What to do
- Update sylius sylius to version 2.0.16.
- Update sylius sylius to version 2.1.12.
- Update sylius sylius to version 2.2.3.
- Update sylius sylius/sylius to version 2.0.16.
- Update sylius sylius/sylius to version 2.1.12.
- Update sylius sylius/sylius to version 2.2.3.
Affected software
| Vendor | Product | Affected versions | Fix available |
|---|---|---|---|
| sylius | sylius | > 2.0.0 , <= 2.0.15 | 2.0.16 |
| sylius | sylius | > 2.1.0 , <= 2.1.11 | 2.1.12 |
| sylius | sylius | > 2.2.0 , <= 2.2.2 | 2.2.3 |
| sylius | sylius | > 2.0.0 , <= 2.0.16 | – |
| sylius | sylius | > 2.1.0 , <= 2.1.12 | – |
| sylius | sylius | > 2.2.0 , <= 2.2.3 | – |
| sylius | sylius/sylius | > 2.0.0 , <= 2.0.16 | 2.0.16 |
| sylius | sylius/sylius | > 2.1.0 , <= 2.1.12 | 2.1.12 |
| sylius | sylius/sylius | > 2.2.0 , <= 2.2.3 | 2.2.3 |
Original title
Sylius is Missing Authorization in API v2 Add Item Endpoint
Original description
### Impact
The `POST /api/v2/shop/orders/{tokenValue}/items` endpoint does not verify cart ownership. An unauthenticated attacker can add items to other registered customers' carts by knowing the cart `tokenValue`.
```
POST /api/v2/shop/orders/{tokenValue}/items
```
Other mutation endpoints (PUT, PATCH, DELETE) are **not affected**. API Platform loads the Order entity through the state provider for these operations, which triggers `VisitorBasedExtension` and returns 404 for unauthorized users.
An attacker who obtains a cart `tokenValue` can add arbitrary items to another customer's cart. The endpoint returns the full cart representation in the response (HTTP 201), potentially leaking:
- Customer email address
- Cart contents (products, quantities, prices)
- Address data (billing and shipping if set)
- Payment and shipment IDs
- Order totals and tax breakdown
- Checkout state
### Patches
The issue is fixed in versions: 2.0.16, 2.1.12, 2.2.3, and above.
### Workarounds
Add an ownership check in `AddItemToCartHandler` by injecting `UserContextInterface` and verifying the current user matches the cart owner before adding items.
#### Step 1. Patch the handler
Create new `src/CommandHandler/Cart/AddItemToCartHandler.php`:
```php
<?php
declare(strict_types=1);
namespace App\CommandHandler\Cart;
use Sylius\Bundle\ApiBundle\Command\Cart\AddItemToCart;
use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
use Sylius\Component\Core\Factory\CartItemFactoryInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\OrderItemInterface;
use Sylius\Component\Core\Model\ProductVariantInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Core\Repository\ProductVariantRepositoryInterface;
use Sylius\Component\Order\Modifier\OrderItemQuantityModifierInterface;
use Sylius\Component\Order\Modifier\OrderModifierInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
#[AsMessageHandler]
final readonly class AddItemToCartHandler
{
public function __construct(
private OrderRepositoryInterface $orderRepository,
private ProductVariantRepositoryInterface $productVariantRepository,
private OrderModifierInterface $orderModifier,
private CartItemFactoryInterface $cartItemFactory,
private OrderItemQuantityModifierInterface $orderItemQuantityModifier,
private UserContextInterface $userContext,
) {
}
public function __invoke(AddItemToCart $addItemToCart): OrderInterface
{
/** @var ProductVariantInterface|null $productVariant */
$productVariant = $this->productVariantRepository->findOneBy(['code' => $addItemToCart->productVariantCode]);
if ($productVariant === null) {
throw new \InvalidArgumentException('Product variant with given code has not been found.');
}
/** @var OrderInterface|null $cart */
$cart = $this->orderRepository->findCartByTokenValue($addItemToCart->orderTokenValue);
if ($cart === null) {
throw new \InvalidArgumentException('Cart with given token has not been found.');
}
$this->assertCartAccessible($cart);
/** @var OrderItemInterface $cartItem */
$cartItem = $this->cartItemFactory->createNew();
$cartItem->setVariant($productVariant);
$this->orderItemQuantityModifier->modify($cartItem, $addItemToCart->quantity);
$this->orderModifier->addToOrder($cart, $cartItem);
return $cart;
}
private function assertCartAccessible(OrderInterface $cart): void
{
if ($cart->isCreatedByGuest()) {
return;
}
$cartCustomer = $cart->getCustomer();
if (null === $cartCustomer || null === $cartCustomer->getUser()) {
return;
}
$currentUser = $this->userContext->getUser();
if (
$currentUser instanceof ShopUserInterface
&& $currentUser->getCustomer()?->getId() === $cartCustomer->getId()
) {
return;
}
throw new NotFoundHttpException('Cart not found.');
}
}
```
#### Step 2. Override the service
```diff
# config/services.yaml
services:
App\:
resource: '../src/*'
- exclude: '../src/{Entity,Kernel.php}'
+ exclude: '../src/{Entity,Kernel.php,CommandHandler}'
sylius_api.command_handler.cart.add_item_to_cart:
class: App\CommandHandler\Cart\AddItemToCartHandler
arguments:
$orderRepository: '@sylius.repository.order'
$productVariantRepository: '@sylius.repository.product_variant'
$orderModifier: '@sylius.modifier.order'
$cartItemFactory: '@sylius.factory.order_item'
$orderItemQuantityModifier: '@sylius.modifier.order_item_quantity'
$userContext: '@Sylius\Bundle\ApiBundle\Context\UserContextInterface'
tags:
- { name: messenger.message_handler, bus: sylius.command_bus }
```
#### Step 3. Clear cache
```bash
bin/console cache:clear
```
### Reporters
We would like to extend our gratitude to the following individuals for their detailed reporting and responsible disclosure of this vulnerability:
- @rokorolov
### For more information
If you have any questions or comments about this advisory:
- Open an issue in [Sylius issues](https://github.com/Sylius/Sylius/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen)
- Email us at [[email protected]](mailto:[email protected])
The `POST /api/v2/shop/orders/{tokenValue}/items` endpoint does not verify cart ownership. An unauthenticated attacker can add items to other registered customers' carts by knowing the cart `tokenValue`.
```
POST /api/v2/shop/orders/{tokenValue}/items
```
Other mutation endpoints (PUT, PATCH, DELETE) are **not affected**. API Platform loads the Order entity through the state provider for these operations, which triggers `VisitorBasedExtension` and returns 404 for unauthorized users.
An attacker who obtains a cart `tokenValue` can add arbitrary items to another customer's cart. The endpoint returns the full cart representation in the response (HTTP 201), potentially leaking:
- Customer email address
- Cart contents (products, quantities, prices)
- Address data (billing and shipping if set)
- Payment and shipment IDs
- Order totals and tax breakdown
- Checkout state
### Patches
The issue is fixed in versions: 2.0.16, 2.1.12, 2.2.3, and above.
### Workarounds
Add an ownership check in `AddItemToCartHandler` by injecting `UserContextInterface` and verifying the current user matches the cart owner before adding items.
#### Step 1. Patch the handler
Create new `src/CommandHandler/Cart/AddItemToCartHandler.php`:
```php
<?php
declare(strict_types=1);
namespace App\CommandHandler\Cart;
use Sylius\Bundle\ApiBundle\Command\Cart\AddItemToCart;
use Sylius\Bundle\ApiBundle\Context\UserContextInterface;
use Sylius\Component\Core\Factory\CartItemFactoryInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\OrderItemInterface;
use Sylius\Component\Core\Model\ProductVariantInterface;
use Sylius\Component\Core\Model\ShopUserInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Core\Repository\ProductVariantRepositoryInterface;
use Sylius\Component\Order\Modifier\OrderItemQuantityModifierInterface;
use Sylius\Component\Order\Modifier\OrderModifierInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
#[AsMessageHandler]
final readonly class AddItemToCartHandler
{
public function __construct(
private OrderRepositoryInterface $orderRepository,
private ProductVariantRepositoryInterface $productVariantRepository,
private OrderModifierInterface $orderModifier,
private CartItemFactoryInterface $cartItemFactory,
private OrderItemQuantityModifierInterface $orderItemQuantityModifier,
private UserContextInterface $userContext,
) {
}
public function __invoke(AddItemToCart $addItemToCart): OrderInterface
{
/** @var ProductVariantInterface|null $productVariant */
$productVariant = $this->productVariantRepository->findOneBy(['code' => $addItemToCart->productVariantCode]);
if ($productVariant === null) {
throw new \InvalidArgumentException('Product variant with given code has not been found.');
}
/** @var OrderInterface|null $cart */
$cart = $this->orderRepository->findCartByTokenValue($addItemToCart->orderTokenValue);
if ($cart === null) {
throw new \InvalidArgumentException('Cart with given token has not been found.');
}
$this->assertCartAccessible($cart);
/** @var OrderItemInterface $cartItem */
$cartItem = $this->cartItemFactory->createNew();
$cartItem->setVariant($productVariant);
$this->orderItemQuantityModifier->modify($cartItem, $addItemToCart->quantity);
$this->orderModifier->addToOrder($cart, $cartItem);
return $cart;
}
private function assertCartAccessible(OrderInterface $cart): void
{
if ($cart->isCreatedByGuest()) {
return;
}
$cartCustomer = $cart->getCustomer();
if (null === $cartCustomer || null === $cartCustomer->getUser()) {
return;
}
$currentUser = $this->userContext->getUser();
if (
$currentUser instanceof ShopUserInterface
&& $currentUser->getCustomer()?->getId() === $cartCustomer->getId()
) {
return;
}
throw new NotFoundHttpException('Cart not found.');
}
}
```
#### Step 2. Override the service
```diff
# config/services.yaml
services:
App\:
resource: '../src/*'
- exclude: '../src/{Entity,Kernel.php}'
+ exclude: '../src/{Entity,Kernel.php,CommandHandler}'
sylius_api.command_handler.cart.add_item_to_cart:
class: App\CommandHandler\Cart\AddItemToCartHandler
arguments:
$orderRepository: '@sylius.repository.order'
$productVariantRepository: '@sylius.repository.product_variant'
$orderModifier: '@sylius.modifier.order'
$cartItemFactory: '@sylius.factory.order_item'
$orderItemQuantityModifier: '@sylius.modifier.order_item_quantity'
$userContext: '@Sylius\Bundle\ApiBundle\Context\UserContextInterface'
tags:
- { name: messenger.message_handler, bus: sylius.command_bus }
```
#### Step 3. Clear cache
```bash
bin/console cache:clear
```
### Reporters
We would like to extend our gratitude to the following individuals for their detailed reporting and responsible disclosure of this vulnerability:
- @rokorolov
### For more information
If you have any questions or comments about this advisory:
- Open an issue in [Sylius issues](https://github.com/Sylius/Sylius/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen)
- Email us at [[email protected]](mailto:[email protected])
nvd CVSS4.0
6.9
Vulnerability type
CWE-862
Missing Authorization
Published: 11 Mar 2026 · Updated: 13 Mar 2026 · First seen: 10 Mar 2026