{"id":"GHSA-xcwx-r2gw-w93m","summary":"Sylius has a DQL Injection via API Order Filters","details":"### Impact\nSylius API filters `ProductPriceOrderFilter` and `TranslationOrderNameAndLocaleFilter` pass user-supplied order direction values directly to Doctrine's `orderBy()` without validation. An attacker can inject arbitrary DQL:\n\n```\nGET /api/v2/shop/products?order[price]=ASC,%20variant.code%20DESC\n```\n\n### Patches\nThe 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.\n\n### Workarounds\n\nAn `EventSubscriber` that sanitizes `order` query parameters **only on API routes** before they reach the vulnerable filters.\n\nThe subscriber accepts an `$apiRoute` constructor parameter (default `/api/v2`) and skips non-API requests entirely — so there is zero overhead on shop/admin page requests.\n\nThis follows the same pattern used by Sylius's own `KernelRequestEventSubscriber` (`src/Sylius/Bundle/ApiBundle/EventSubscriber/KernelRequestEventSubscriber.php`), which also uses `str_contains($pathInfo, $this-\u003eapiRoute)` to scope logic to API routes.\n\n---\n\n#### Step 1 — Create the EventSubscriber\n\n`src/EventSubscriber/SanitizeOrderDirectionSubscriber.php`:\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nnamespace App\\EventSubscriber;\n\nuse Symfony\\Component\\EventDispatcher\\EventSubscriberInterface;\nuse Symfony\\Component\\HttpKernel\\Event\\RequestEvent;\nuse Symfony\\Component\\HttpKernel\\KernelEvents;\n\nfinal class SanitizeOrderDirectionSubscriber implements EventSubscriberInterface\n{\n    private const ALLOWED_DIRECTIONS = ['asc', 'desc'];\n\n    public function __construct(\n        private string $apiRoute,\n    ) {\n    }\n\n    public static function getSubscribedEvents(): array\n    {\n        return [\n            KernelEvents::REQUEST =\u003e ['sanitizeOrderParameters', 64],\n        ];\n    }\n\n    public function sanitizeOrderParameters(RequestEvent $event): void\n    {\n        if (!str_contains($event-\u003egetRequest()-\u003egetPathInfo(), $this-\u003eapiRoute)) {\n            return;\n        }\n\n        $request = $event-\u003egetRequest();\n\n        /** @var mixed $order */\n        $order = $request-\u003equery-\u003eall()['order'] ?? null;\n        if (!is_array($order)) {\n            return;\n        }\n\n        $needsSanitization = false;\n        $sanitized = [];\n        foreach ($order as $field =\u003e $direction) {\n            if (is_string($direction) && in_array(strtolower($direction), self::ALLOWED_DIRECTIONS, true)) {\n                $sanitized[$field] = $direction;\n            } else {\n                $needsSanitization = true;\n            }\n        }\n\n        if (!$needsSanitization) {\n            return;\n        }\n\n        $all = $request-\u003equery-\u003eall();\n        $all['order'] = $sanitized;\n        $request-\u003equery-\u003ereplace($all);\n\n        $request-\u003eserver-\u003eset('QUERY_STRING', http_build_query($all));\n        $request-\u003eattributes-\u003eset('_api_filters', $all);\n    }\n}\n```\n\n#### Step 2 — Register the service\n\n**Option A** — If your `config/services.yaml` already has `App\\` autowiring (Symfony default):\n\n```yaml\n# Nothing to do — autoconfigure picks up EventSubscriberInterface automatically.\n# Optionally bind the API route prefix:\nservices:\n    App\\EventSubscriber\\SanitizeOrderDirectionSubscriber:\n        arguments:\n            $apiRoute: '%sylius.security.new_api_route%'\n```\n\n**Option B** — If there is no `App\\` autowiring:\n\n```yaml\nservices:\n    App\\EventSubscriber\\SanitizeOrderDirectionSubscriber:\n        arguments:\n            $apiRoute: '%sylius.security.new_api_route%'\n        tags: ['kernel.event_subscriber']\n```\n\nUsing `%sylius.security.new_api_route%` ties the subscriber to the same prefix Sylius uses (`/api/v2` by default). If the parameter is not available, hardcode `'/api/v2'` instead.\n\n#### Step 3 — Clear cache\n\n```bash\nbin/console cache:clear\n```\n\n### Reporters\n\nWe would like to extend our gratitude to the following individuals for their detailed reporting and responsible disclosure of this vulnerability:\n- Chris Alupului (@Neosprings)\n- Bartłomiej Nowiński (@bnBart)\n\n### For more information\nIf you have any questions or comments about this advisory:\n\n- Open an issue in [Sylius issues](https://github.com/Sylius/Sylius/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen)\n- Email us at [security@sylius.com](mailto:security@sylius.com)","aliases":["CVE-2026-31825"],"modified":"2026-03-11T20:50:00.468599Z","published":"2026-03-11T00:13:41Z","database_specific":{"cwe_ids":["CWE-89","CWE-943"],"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-03-11T00:13:41Z","nvd_published_at":"2026-03-10T22:16:20Z"},"references":[{"type":"WEB","url":"https://github.com/Sylius/Sylius/security/advisories/GHSA-xcwx-r2gw-w93m"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-31825"},{"type":"PACKAGE","url":"https://github.com/Sylius/Sylius"}],"affected":[{"package":{"name":"sylius/sylius","ecosystem":"Packagist","purl":"pkg:composer/sylius/sylius"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.9.12"}]}],"versions":["v0.1.0","v0.10.0","v0.11.0","v0.12.0","v0.13.0","v0.14.0","v0.15.0","v0.16.0","v0.17.0","v0.18.0","v0.19.0","v0.2.0","v0.3.0","v0.5.0","v0.6.0","v0.7.0","v0.8.0","v0.9.0","v1.0.0","v1.0.0-alpha.1","v1.0.0-alpha.2","v1.0.0-beta.1","v1.0.0-beta.2","v1.0.0-beta.3","v1.0.0-rc.1","v1.0.0-rc.2","v1.0.1","v1.0.10","v1.0.11","v1.0.12","v1.0.13","v1.0.14","v1.0.15","v1.0.16","v1.0.17","v1.0.18","v1.0.2","v1.0.3","v1.0.4","v1.0.5","v1.0.6","v1.0.7","v1.0.8","v1.0.9","v1.1.0","v1.1.0-RC","v1.1.1","v1.1.10","v1.1.11","v1.1.12","v1.1.13","v1.1.14","v1.1.15","v1.1.16","v1.1.17","v1.1.18","v1.1.2","v1.1.3","v1.1.4","v1.1.5","v1.1.6","v1.1.7","v1.1.8","v1.1.9","v1.2.0","v1.2.0-BETA","v1.2.0-RC","v1.2.1","v1.2.10","v1.2.11","v1.2.12","v1.2.13","v1.2.14","v1.2.15","v1.2.16","v1.2.17","v1.2.2","v1.2.3","v1.2.4","v1.2.5","v1.2.6","v1.2.7","v1.2.8","v1.2.9","v1.3.0","v1.3.0-BETA","v1.3.1","v1.3.10","v1.3.11","v1.3.12","v1.3.13","v1.3.14","v1.3.15","v1.3.16","v1.3.2","v1.3.3","v1.3.4","v1.3.5","v1.3.6","v1.3.7","v1.3.8","v1.3.9","v1.4.0","v1.4.0-BETA.1","v1.4.1","v1.4.10","v1.4.11","v1.4.12","v1.4.2","v1.4.3","v1.4.4","v1.4.5","v1.4.6","v1.4.7","v1.4.8","v1.4.9","v1.5.0","v1.5.0-RC.1","v1.5.1","v1.5.2","v1.5.3","v1.5.4","v1.5.5","v1.5.6","v1.5.7","v1.5.8","v1.5.9","v1.6.0","v1.6.0-ALPHA.1","v1.6.0-ALPHA.2","v1.6.0-RC.1","v1.6.1","v1.6.2","v1.6.3","v1.6.4","v1.6.5","v1.6.6","v1.6.7","v1.6.8","v1.6.9","v1.7.0","v1.7.0-ALPHA.1","v1.7.0-ALPHA.2","v1.7.0-RC.1","v1.7.1","v1.7.10","v1.7.11","v1.7.2","v1.7.3","v1.7.4","v1.7.5","v1.7.6","v1.7.7","v1.7.8","v1.7.9","v1.8.0","v1.8.0-RC.1","v1.8.1","v1.8.10","v1.8.11","v1.8.12","v1.8.2","v1.8.3","v1.8.4","v1.8.5","v1.8.6","v1.8.7","v1.8.8","v1.8.9","v1.9.0","v1.9.0-ALPHA.1","v1.9.0-ALPHA.2","v1.9.0-BETA.1","v1.9.0-BETA.2","v1.9.0-BETA.3","v1.9.0-RC.1","v1.9.0-RC.2","v1.9.1","v1.9.10","v1.9.11","v1.9.2","v1.9.3","v1.9.4","v1.9.5","v1.9.6","v1.9.7","v1.9.8","v1.9.9"],"database_specific":{"last_known_affected_version_range":"\u003c= 1.9.11","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-xcwx-r2gw-w93m/GHSA-xcwx-r2gw-w93m.json"}},{"package":{"name":"sylius/sylius","ecosystem":"Packagist","purl":"pkg:composer/sylius/sylius"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.10.0"},{"fixed":"1.10.16"}]}],"versions":["v1.10.0","v1.10.1","v1.10.10","v1.10.11","v1.10.12","v1.10.13","v1.10.14","v1.10.15","v1.10.2","v1.10.3","v1.10.4","v1.10.5","v1.10.6","v1.10.7","v1.10.8","v1.10.9"],"database_specific":{"last_known_affected_version_range":"\u003c= 1.10.15","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-xcwx-r2gw-w93m/GHSA-xcwx-r2gw-w93m.json"}},{"package":{"name":"sylius/sylius","ecosystem":"Packagist","purl":"pkg:composer/sylius/sylius"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.11.0"},{"fixed":"1.11.17"}]}],"versions":["v1.11.0","v1.11.1","v1.11.10","v1.11.11","v1.11.12","v1.11.13","v1.11.14","v1.11.15","v1.11.16","v1.11.2","v1.11.3","v1.11.4","v1.11.5","v1.11.6","v1.11.7","v1.11.8","v1.11.9"],"database_specific":{"last_known_affected_version_range":"\u003c= 1.11.16","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-xcwx-r2gw-w93m/GHSA-xcwx-r2gw-w93m.json"}},{"package":{"name":"sylius/sylius","ecosystem":"Packagist","purl":"pkg:composer/sylius/sylius"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.12.0"},{"fixed":"1.12.23"}]}],"versions":["v1.12.0","v1.12.1","v1.12.10","v1.12.11","v1.12.12","v1.12.13","v1.12.14","v1.12.15","v1.12.16","v1.12.17","v1.12.18","v1.12.19","v1.12.2","v1.12.20","v1.12.21","v1.12.22","v1.12.3","v1.12.4","v1.12.5","v1.12.6","v1.12.7","v1.12.8","v1.12.9"],"database_specific":{"last_known_affected_version_range":"\u003c= 1.12.22","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-xcwx-r2gw-w93m/GHSA-xcwx-r2gw-w93m.json"}},{"package":{"name":"sylius/sylius","ecosystem":"Packagist","purl":"pkg:composer/sylius/sylius"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.13.0"},{"fixed":"1.13.15"}]}],"versions":["v1.13.0","v1.13.1","v1.13.10","v1.13.11","v1.13.12","v1.13.13","v1.13.14","v1.13.2","v1.13.3","v1.13.4","v1.13.5","v1.13.6","v1.13.7","v1.13.8","v1.13.9"],"database_specific":{"last_known_affected_version_range":"\u003c= 1.13.14","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-xcwx-r2gw-w93m/GHSA-xcwx-r2gw-w93m.json"}},{"package":{"name":"sylius/sylius","ecosystem":"Packagist","purl":"pkg:composer/sylius/sylius"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.14.0"},{"fixed":"1.14.18"}]}],"versions":["v1.14.0","v1.14.1","v1.14.10","v1.14.11","v1.14.12","v1.14.13","v1.14.14","v1.14.15","v1.14.16","v1.14.17","v1.14.2","v1.14.3","v1.14.4","v1.14.5","v1.14.6","v1.14.7","v1.14.8","v1.14.9"],"database_specific":{"last_known_affected_version_range":"\u003c= 1.14.17","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-xcwx-r2gw-w93m/GHSA-xcwx-r2gw-w93m.json"}},{"package":{"name":"sylius/sylius","ecosystem":"Packagist","purl":"pkg:composer/sylius/sylius"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.0.0"},{"fixed":"2.0.16"}]}],"versions":["v2.0.0","v2.0.1","v2.0.10","v2.0.11","v2.0.12","v2.0.13","v2.0.14","v2.0.15","v2.0.2","v2.0.3","v2.0.4","v2.0.5","v2.0.6","v2.0.7","v2.0.8","v2.0.9"],"database_specific":{"last_known_affected_version_range":"\u003c= 2.0.15","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-xcwx-r2gw-w93m/GHSA-xcwx-r2gw-w93m.json"}},{"package":{"name":"sylius/sylius","ecosystem":"Packagist","purl":"pkg:composer/sylius/sylius"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.1.0"},{"fixed":"2.1.12"}]}],"versions":["v2.1.0","v2.1.1","v2.1.10","v2.1.11","v2.1.2","v2.1.3","v2.1.4","v2.1.5","v2.1.6","v2.1.7","v2.1.8","v2.1.9"],"database_specific":{"last_known_affected_version_range":"\u003c= 2.1.11","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-xcwx-r2gw-w93m/GHSA-xcwx-r2gw-w93m.json"}},{"package":{"name":"sylius/sylius","ecosystem":"Packagist","purl":"pkg:composer/sylius/sylius"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.2.0"},{"fixed":"2.2.3"}]}],"versions":["v2.2.0","v2.2.1","v2.2.2"],"database_specific":{"last_known_affected_version_range":"\u003c= 2.2.2","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-xcwx-r2gw-w93m/GHSA-xcwx-r2gw-w93m.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N"}]}