Improper Handling of URL Encoding (Hex Encoding) in Sylius - #VU159502
Published: November 5, 2026
Vulnerability details
The vulnerability allows a registered customer to bypass Sylius Shop API ownership, channel, and state filters to read, modify, and delete other customers' data. Exposed information includes all customers' addresses across channels and customer records containing email, first name, last name, default address, gender, birthday, phone number, and newsletter subscription status. An authenticated customer can also overwrite or delete another customer's address and overwrite another customer's profile. Only a valid customer account is required.
The vulnerability exists due to inconsistent decoding of request paths between Symfony and the Sylius application section resolver. Symfony applies rawurldecode() before route and firewall matching, while SyliusBundleCoreBundleSectionResolverUriBasedSectionProvider matches prefixes against the undecoded path. A remote attacker can authenticate with a valid customer account and percent-encode a character in the API prefix, requesting /api/v%32/shop/addresses instead of /api/v2/shop/addresses, to reach the same endpoint while causing section resolution to fall back to ShopSection rather than ShopApiSection. Every Shop API Doctrine query extension returns without applying its filters when the section is not ShopApiSection, causing authorization to fail open. These extensions are the only ownership enforcement on affected endpoints: is_granted('SYLIUS_SHOP_USER') checks the authenticated token rather than the requested record. The extensions apply to both collections and item operations, so the bypass also affects writes.
Encoding other characters in the prefix produces the same behavior, including /api/%76%32/shop/addresses, /%61pi/v2/shop/addresses, and /api/v2/%73hop/addresses. Affected operations include PUT and DELETE on /shop/addresses/{id} and PUT on /shop/customers/{id}. Stores with sylius_api.enabled set to false are also exposed because the API-disable check reads the raw path and can be bypassed by the same encoding, leaving the Shop API accessible without ownership filters. This does not enable account takeover through /shop/customers/{id}/password, which takes its subject from the authenticated token, or escalation to administrator endpoints, whose firewall and access-control rules decode the path and still require administrator credentials.