
This release round has a common thread: how much Sylius trusts what a request tells it. Six reports reached us in October, and each pointed at a place where that trust was misplaced. Rather than closing six symptoms, we followed each report to the code that caused it and asked what else rested on the same assumption. One of them turned out to reach far beyond what was reported, which is why the most consequential fix of this round touches four classes instead of one.
The advisories cover, in order of severity: an authorization bypass in the Shop API that exposed other customers’ data on Sylius 2.x, a way to reach an API that had been switched off, a customer password-reset e-mail that could be made to point outside the store, product reviews that were never approved becoming readable, the return in 2.3.0 of a race condition on promotion and coupon limits that we had fixed in March, and a payment endpoint that did not respect the channel configuration of payment methods.
The fixes are in Sylius 1.14.21, 2.1.17, 2.2.12 and 2.3.2. If your store runs anything older on one of those lines, update now. Be aware that 2.2.11 and 2.3.1, published on 14 October, do not contain these fixes. Sylius 2.0.x and the 1.x lines below 1.14 are affected wherever the vulnerable code exists in them and are outside security support, so on those versions the choice is between upgrading and applying the workarounds from the advisories.
Two of the fixes are visible to customers, and both deserve a look before you deploy. On a store whose channel has no hostname and which does not configure framework.trusted_hosts, the links in e-mails become relative paths after the upgrade; the advisory explains how to keep them absolute. And a product review is no longer readable through the Shop API until an administrator has accepted it, which includes the customer who just wrote it.
Each advisory below gives the impact, the patched versions and a short pointer to the workaround. The full workarounds, with code, live in the advisories themselves.
The original security advisory has been published on GitHub at Sylius/Sylius repository.
Setting sylius_api.enabled: false was supposed to make the API unreachable. The check behind that setting compared the raw, undecoded request path against the API prefix, so percent-encoding a single character in the prefix, for example /api/v%32/shop/orders instead of /api/v2/shop/orders, bypassed it. Symfony decodes the path before matching the route, so the request was then routed and served normally. An operator who turned the API off, and sized their exposure accordingly, was still serving the entire API to anyone who asked for it with an encoded prefix. This is the default production state of a Sylius-Standard installation, so it covers every store that simply never enabled the API. The firewall and access control rules were not affected, so administrator endpoints still required administrator credentials. On Sylius 2.0 and newer the same encoding additionally disables the shop API ownership filters, which is the separate and more severe issue described next.
The issue is fixed in versions: 1.14.21, 2.1.17, 2.2.12, 2.3.2 and above. Every release since the setting was introduced, in 1.9.6 and 1.10.1, is affected; the 1.x lines below 1.14 are covered by upgrading within the 1.x line to 1.14.21.
If an immediate upgrade is not possible, do not rely on sylius_api.enabled in the meantime. Block the API prefix, raw and encoded, in the web server or reverse proxy, or register a copy of the kernel request subscriber that decodes the path before applying the check. See the full workaround in the advisory.
The original security advisory has been published on GitHub at Sylius/Sylius repository.
When the channel handling a request has no hostname configured and framework.trusted_hosts is not set, which is the default on a single-channel installation whose administrator never filled the Hostname field in, the links in every e-mail Sylius sends were built from the request’s Host header. An unauthenticated attacker who knew a customer’s e-mail address could therefore request a password reset for that customer and make the link in the resulting e-mail point at a domain of the attacker’s choosing. When the customer clicked it, the valid reset token was delivered to the attacker, who could then set a new password on the real store. The same fallback was used by the registration, e-mail verification and order confirmation e-mails and by the shared e-mail layout. Sending e-mails through an asynchronous Messenger transport did not help, because the e-mail is rendered inside the request that triggers it. The administrator password reset e-mail, fixed in an earlier release, was not affected.
The issue is fixed in versions: 1.14.21, 2.1.17, 2.2.12, 2.3.2 and above. Sylius 1.9 through 1.13 are affected and are covered by upgrading within the 1.x line to 1.14.21.
After upgrading, a store whose channel has no hostname and which does not configure framework.trusted_hosts will receive relative links in its e-mails. Set the hostname on each channel, or configure framework.trusted_hosts with a strict pattern for your domain, to keep the links absolute.
If an immediate upgrade is not possible, configure a hostname for every channel in Configuration, Channels, or restrict the accepted hosts with framework.trusted_hosts using a strict pattern for your own domain. Either closes the vulnerability on its own. Note that trusted_proxies is not a substitute, because it does not validate the host. See the full workaround in the advisory.
The original security advisory has been published on GitHub at Sylius/Sylius repository.
The GET /api/v2/shop/product-reviews/{id} endpoint returned product reviews regardless of their moderation status. A review awaiting moderation or already rejected was served with HTTP 200 to anyone who supplied its numeric identifier, while the collection endpoint correctly listed accepted reviews only. Since review identifiers are sequential integers, all pending and rejected reviews of a store could be enumerated, exposing the full review content and the author’s first name. No account, token or session was required. No write or privilege escalation impact was identified.
The issue is fixed in versions: 1.14.21, 2.1.17, 2.2.12, 2.3.2 and above. Sylius 1.9 through 1.13 are affected and are covered by upgrading within the 1.x line to 1.14.21.
After upgrading, a review is not readable through the shop API until it has been accepted, including by its author and including under the identifier returned when the review was created.
If an immediate upgrade is not possible, register your own item query extension that adds the status = accepted condition to shop API item queries for product reviews. See the full workaround in the advisory.
The original security advisory has been published on GitHub at Sylius/Sylius repository.
A registered customer could read, modify and delete other customers’ data through the shop API by percent-encoding a character in the API path prefix, for example requesting /api/v%32/shop/orders instead of /api/v2/shop/orders. Symfony decodes the path before matching the route and the firewall, so the request authenticated and dispatched normally, but Sylius resolved the application section from the same path without decoding it, concluded the request was not a shop API request, and skipped every ownership filter. An authenticated customer could read every customer’s orders, addresses and order items, read any customer record by id, and overwrite or delete another customer’s address or profile. Only a valid customer account was required. This also affected stores that never enabled the API, because the kill switch described above was bypassable in the same way. Account takeover was not possible and administrator endpoints were not affected. Sylius 1.x is not affected by the data exposure.
The issue is fixed in versions: 2.1.17, 2.2.12, 2.3.2 and above. Sylius 2.0.x is affected and does not receive a patch, so upgrade to 2.1.17 or later, or apply the workaround.
If an immediate upgrade is not possible, replace the section provider and the kernel request subscriber with copies that decode the request path before matching it, so that an encoded path is served exactly like the plain one, with the ownership filters on. Disabling the API is not a workaround. See the full workaround in the advisory.
The original security advisory has been published on GitHub at Sylius/Sylius repository
The POST /api/v2/shop/orders/{tokenValue}/payment-requests endpoint, used to create a payment request for a placed order, did not validate that the chosen payment method is available for the order’s channel, and also accepted payment methods that are disabled. A shop customer, or a guest holding the order token, could create a payment request for their own order with any payment method that exists in the store, including methods the store operator has disabled or explicitly excluded from that channel, and the payment request was then processed through the gateway configured for that method. The equivalent endpoints for choosing and changing a payment method correctly reject such methods with HTTP 422. This is the same class of issue as CVE-2026-53638, on the one remaining endpoint that accepts a payment method code.
The issue is fixed in versions: 2.1.17, 2.2.12, 2.3.2 and above. Sylius 2.0.x is affected and does not receive a patch, so upgrade to 2.1.17 or later, or apply the workaround.
If an immediate upgrade is not possible, decorate the sylius_api.command_handler.payment.add_payment_request service to validate the chosen payment method against the methods supported for the order before the payment request is created. See the full workaround in the advisory.
The original security advisory has been published on GitHub at Sylius/Sylius repository.
Sylius 2.3.0 reintroduced the time-of-check to time-of-use race in promotion usage limit enforcement that was fixed as CVE-2026-31824. The atomic conditional updates from that fix were replaced with an optimistic lock that could never fail, and the limits were not checked again when the usage counter was incremented. An attacker can prepare multiple carts with the same limited-use promotion or coupon and complete them concurrently; the requests pass the usage limit checks and complete successfully, allowing a single-use promotion or coupon to be redeemed multiple times, with the usage counter ending above the limit. The per-customer coupon limit can be bypassed in the same way. No authentication is required. This may lead to direct financial loss through repeated redemption of limited promotions and discount coupons. Sylius 2.2 and earlier are not affected.
The issue is fixed in version: 2.3.2 and above. Both 2.3.0 and 2.3.1 are affected.
If an immediate upgrade is not possible, replace the sylius.modifier.promotion.order_usage.atomic service with an implementation that re-checks the limits in the database at the moment the usage counter is incremented. See the full workaround in the advisory.
We would like to extend our gratitude to the researchers who responsibly disclosed these vulnerabilities:
| # | Advisory | Affected | Severity |
|---|---|---|---|
| 1 | A Disabled API Stays Reachable Through a Percent-Encoded Path | 1.9.6 to 2.3 | Moderate |
| 2 | Host Header Injection in the Shop Password Reset E-mail Link | 1.9 to 2.3 | High |
| 3 | Unpublished Product Reviews Disclosed Through the Shop API Item Endpoint | 1.9 to 2.3 | Moderate |
| 4 | Percent-Encoded API Path Bypasses Shop API Ownership Filters | 2.0 to 2.3 | High |
| 5 | Channel-based Payment Method Restriction Bypass on Shop Payment Request API Endpoint | 2.0 to 2.3 | Moderate |
| 6 | Promotion and Coupon Usage Limit Bypass via Race Condition | 2.3.0 and 2.3.1 | High |
The encoded-path issues leave a clear trace. Search your access logs for requests whose path contains a percent sign inside the /api/v2 prefix itself, for example %32, %76, %61 or %73 before /shop/ or /admin/, rather than further down the path where encoding is legitimate. On a store with the API disabled, any such request that received a 200 reached the API. On Sylius 2.0 and newer, any such request carrying a customer token should be treated as a potential read of other customers’ data, and a PUT or DELETE among them as a potential modification; the affected customer records are the ones named in those requests. Keep in mind that many proxies and log pipelines normalise the path before writing it, so check what your logs actually store before concluding that there is nothing there.
For the password-reset issue, look for requests to the shop reset endpoints whose Host header, or X-Forwarded-Host behind a trusted proxy, does not match a canonical shop domain, and treat any customer whose password was reset through a link they did not request as compromised. Stores that already had a hostname on every channel, or framework.trusted_hosts configured, were not reachable.
For the product reviews, a run of sequential requests to /api/v2/shop/product-reviews/{id} from one client in quick succession is the enumeration pattern to look for; the reviews that were pending or rejected at that time are what was exposed.
For the promotion race, which concerns Sylius 2.3 only, reconcile the counters against the limits: a promotion or coupon whose used value exceeds its usage limit, or a customer with more completed orders on a coupon than its per-customer limit allows, was redeemed past the limit. For the payment method issue, list payment requests whose payment method is disabled or not assigned to the channel of the order they belong to.
These fixes concern areas that directly affect the customer journey and store operations, including customer data access through the API, account recovery, payments, promotions and content moderation.
Keeping your store secure is crucial; please update your Sylius installation and inform your peers in the community about these fixes to ensure a safer environment for all.
We would like to thank everyone who participated in responsible disclosure of these vulnerabilities. If you believe you have found a security vulnerability in Sylius, please do not post it publicly. Instead, report it to [email protected].
