Severity by source
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
AC:H because exploitation requires both non-default permissive slash-accepting route constraints and an RFC 3986-conformant downstream consumer; S:C because normalized URLs can reach resources outside the originating route's intended scope.
Primary rating from Vendor (https://github.com/symfony/symfony).
CVSS VectorVendor: https://github.com/symfony/symfony
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Lifecycle Timeline
3DescriptionCVE.org
Description
Symfony\Component\Routing\Generator\UrlGenerator::doGenerate() percent-encodes . and .. path segments so that the generated URL still resolves to the originating route after RFC 3986 §5.2.4 dot-segment removal (which strict RFC-3986 consumers - routers, reverse proxies, HTTP clients - perform *before* percent-decoding).
The encoding was implemented as strtr($url, ['/../' => '/%2E%2E/', '/./' => '/%2E/']) plus a trailing-segment fixup. strtr advances past the trailing / of each match, so the next dot-segment in a chained sequence was left unescaped:
| Input | Output (before fix) | Expected |
|---|---|---|
/../../../ | /%2E%2E/../%2E%2E/ | /%2E%2E/%2E%2E/%2E%2E/ |
/foo/../../../bar | /foo/%2E%2E/../%2E%2E/bar | /foo/%2E%2E/%2E%2E/%2E%2E/bar |
When a route exposes a parameter constrained by a permissive requirement (.+, .*, or similar) that accepts dots and slashes, attacker-controlled chained .. or . segments produce a generated URL that, under strict RFC 3986 normalization, collapses to a different path than the originating route. The Twig path() / url() helpers and any server-side use of UrlGenerator are affected. Same class of route round-trip integrity issue as CVE-2026-45065.
Note: WHATWG-conformant browsers treat %2E/%2E%2E as dot-segments during URL parsing, so the encoding never protected browser-side traversal. The defense exists for RFC-3986-conformant consumers; restoring it for chained segments closes the gap there.
Resolution
UrlGenerator now matches every /. or /.. dot-segment in a single left-to-right preg_replace_callback pass using a lookahead that does not consume the trailing /, so adjacent dot-segments are encoded correctly.
The patches for this issue are available here for branch 5.4 (and forward-ported to 6.4, 7.4, 8.0 and 8.1).
Credits
Symfony would like to thank Alex Pott for reporting the issue and Nicolas Grekas for providing the fix.
AnalysisAI
Symfony's UrlGenerator encodes only alternating dot-segments in chained ../ or ./ sequences due to a strtr() advancement bug, producing URLs that RFC 3986-conformant reverse proxies, API gateways, and HTTP clients collapse to unintended paths. Routes exposing parameters with permissive requirements (.+, .*) that permit slashes are the exploitable surface when attacker-controlled values contain multiple consecutive dot-segments. No active exploitation is confirmed (not in CISA KEV, no EPSS score assigned), but vendor-confirmed patches are available across all maintained Symfony branches, and the vulnerability shares the same route round-trip integrity class as the previously disclosed CVE-2026-45065.
Technical ContextAI
The defect resides in Symfony\Component\Routing\Generator\UrlGenerator::doGenerate() within the symfony/routing and symfony/symfony Composer packages (CPE: pkg:composer/symfony_routing, pkg:composer/symfony_symfony). The method is responsible for percent-encoding . and .. path segments so that generated URLs survive RFC 3986 §5.2.4 dot-segment normalization intact - a normalization step that strict consumers perform before percent-decoding. The original implementation used strtr($url, ['/../' => '/%2E%2E/', '/./' => '/%2E/']), but PHP's strtr advances its scan position past the trailing / of each consumed match; in a chained sequence like /../../../, this causes every second dot-segment to be left as a raw ... The root cause is classified as CWE-172 (Encoding Error). The fix replaces the strtr call with an explode('/', $url)/foreach/implode pass that evaluates each segment individually and is immune to the overlap problem. WHATWG-conformant browsers already interpret %2E/%2E%2E as dot-segments and were never protected by this encoding; the defense applies exclusively to RFC 3986-conformant server-to-server or server-to-proxy communication.
RemediationAI
Upgrade symfony/routing or symfony/symfony to a patched release: 5.4.53, 6.4.41, 7.4.13, or 8.0.13, as documented in the advisory at https://github.com/symfony/symfony/security/advisories/GHSA-h5x3-xfc9-m39h. The single authoritative fix commit is https://github.com/symfony/symfony/commit/4b63c3a3f7af04ecd79c89a594b0b02a01990b1d. If immediate patching is not feasible, audit all route definitions for parameters using permissive requirements such as .+ or .* that allow slashes, and tighten those constraints to explicitly reject dot-containing values (e.g., [^./]+); this eliminates the attack surface without upgrading but requires application-level review and may break legitimate use cases where slash-delimited path parameters are intentional. A secondary compensating control is to validate route parameter values at the application boundary before passing them to UrlGenerator, rejecting inputs containing consecutive dot-segment patterns. No generic network-level controls (WAF rules, port blocking) reliably address this issue because the flaw is in URL generation logic rather than inbound request parsing.
Same weakness CWE-172 – Encoding Error
View allSame technique Information Disclosure
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-44371
GHSA-h5x3-xfc9-m39h