GHSA-8V8V-G73J-492J
Vulnerability from github – Published: 2026-05-28 17:34 – Updated: 2026-05-28 17:34Description
The JsonPath component's match() and search() filter functions compile a caller-supplied pattern straight into preg_match():
'match' => @preg_match(\sprintf('/^%s$/u', $this->transformJsonPathRegex($argList[1])), $value),
'search' => @preg_match("/{$this->transformJsonPathRegex($argList[1])}/u", $value),
transformJsonPathRegex() only performs cosmetic escaping: there is no length cap, no restriction to the RFC 9485 i-regexp subset, and no bound on backtracking. An application that evaluates an attacker-influenced JSONPath expression server-side (e.g. one taken from a query parameter or API field and passed to JsonCrawler) can therefore be made to run a catastrophic-backtracking pattern such as $[?search(@, "(a+)+$")]. Evaluated against a moderately sized document, this pins a CPU core for seconds per request, so a handful of concurrent requests exhausts the worker pool: a denial of service. Because the preg_match() calls are prefixed with @, the PCRE backtrack-limit errors that would otherwise surface are suppressed, leaving no log trace.
Conditions for exploitation
An application that evaluates an attacker-influenced JSONPath expression containing a match() / search() filter against any non-trivial JSON input.
Resolution
JsonCrawler runs the preg_match() calls through a helper that lowers pcre.backtrack_limit to 10000 for the duration of the call (restoring the previous value afterwards), so a pathological pattern fails fast instead of stalling the worker.
The patch for this issue is available here for branch 7.4.
Credits
Symfony would like to thank Himanshu Anand for reporting the issue and Alexandre Daubois for providing the fix.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "symfony/json-path"
},
"ranges": [
{
"events": [
{
"introduced": "7.3.0"
},
{
"fixed": "7.4.12"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "symfony/json-path"
},
"ranges": [
{
"events": [
{
"introduced": "8.0.0"
},
{
"fixed": "8.0.12"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "symfony/symfony"
},
"ranges": [
{
"events": [
{
"introduced": "7.3.0"
},
{
"fixed": "7.4.12"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "symfony/symfony"
},
"ranges": [
{
"events": [
{
"introduced": "8.0.0"
},
{
"fixed": "8.0.12"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-45756"
],
"database_specific": {
"cwe_ids": [
"CWE-1333",
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-28T17:34:55Z",
"nvd_published_at": null,
"severity": "LOW"
},
"details": "### Description\n\nThe `JsonPath` component\u0027s `match()` and `search()` filter functions compile a caller-supplied pattern straight into `preg_match()`:\n\n```php\n\u0027match\u0027 =\u003e @preg_match(\\sprintf(\u0027/^%s$/u\u0027, $this-\u003etransformJsonPathRegex($argList[1])), $value),\n\u0027search\u0027 =\u003e @preg_match(\"/{$this-\u003etransformJsonPathRegex($argList[1])}/u\", $value),\n```\n\n`transformJsonPathRegex()` only performs cosmetic escaping: there is no length cap, no restriction to the RFC 9485 i-regexp subset, and no bound on backtracking. An application that evaluates an attacker-influenced JSONPath expression server-side (e.g. one taken from a query parameter or API field and passed to `JsonCrawler`) can therefore be made to run a catastrophic-backtracking pattern such as `$[?search(@, \"(a+)+$\")]`. Evaluated against a moderately sized document, this pins a CPU core for seconds per request, so a handful of concurrent requests exhausts the worker pool: a denial of service. Because the `preg_match()` calls are prefixed with `@`, the PCRE backtrack-limit errors that would otherwise surface are suppressed, leaving no log trace.\n\n### Conditions for exploitation\n\nAn application that evaluates an attacker-influenced JSONPath expression containing a `match()` / `search()` filter against any non-trivial JSON input.\n\n### Resolution\n\n`JsonCrawler` runs the `preg_match()` calls through a helper that lowers `pcre.backtrack_limit` to 10000 for the duration of the call (restoring the previous value afterwards), so a pathological pattern fails fast instead of stalling the worker.\n\nThe patch for this issue is available [here](https://github.com/symfony/symfony/commit/1ac2d47418ec23066112db1e6ca35be6fe123d14) for branch 7.4.\n\n### Credits\n\nSymfony would like to thank Himanshu Anand for reporting the issue and Alexandre Daubois for providing the fix.",
"id": "GHSA-8v8v-g73j-492j",
"modified": "2026-05-28T17:34:55Z",
"published": "2026-05-28T17:34:55Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/symfony/symfony/security/advisories/GHSA-8v8v-g73j-492j"
},
{
"type": "WEB",
"url": "https://github.com/symfony/symfony/commit/1ac2d47418ec23066112db1e6ca35be6fe123d14"
},
{
"type": "WEB",
"url": "https://github.com/FriendsOfPHP/security-advisories/blob/master/symfony/json-path/CVE-2026-45756.yaml"
},
{
"type": "WEB",
"url": "https://github.com/FriendsOfPHP/security-advisories/blob/master/symfony/symfony/CVE-2026-45756.yaml"
},
{
"type": "PACKAGE",
"url": "https://github.com/symfony/symfony"
},
{
"type": "WEB",
"url": "https://symfony.com/cve-2026-45756"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:U",
"type": "CVSS_V4"
}
],
"summary": "Symfony\u0027s JsonPath Evaluates Attacker-Controlled Regular Expressions in match()/search() Without Limits \u2014 ReDoS"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.