CVE-2026-48760Medium▾ SunlitSymfony: HtmlSanitizer URL Parser Deny Gates Underinclusive: Percent-Encoded BiDi Marks and Unicode Whitespace Bypass Visual-Spoofing Defense
▾ Sunlit zone — Low / medium · no exploitation signal
impact 27.5 · likelihood 0.1 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
Exploit-prediction probability, daily snapshots since Jul 15.
Disclosure to exploitation, from the record and what we observed since indexing it.
Disclosed via GHSA
0.4%
Symfony\Component\HtmlSanitizer\TextSanitizer\UrlSanitizer::parse() rejects URLs containing raw Unicode explicit-direction BiDi formatting characters (U+202A–U+202E, U+2066–U+2069) as a defense against visual-spoofing of the rendered href. The check covers only the raw UTF-8 forms of those code points: the percent-encoded forms (%E2%80%AE for U+202E, %E2%81%A6 for U+2066, etc.) are not matched by the deny regex, survive league/uri's parse/build cycle, and are re-emitted unchanged in the sanitized URL. Any downstream consumer that decodes the link before display — phishing-detection filters that compare urldecode($href) against a domain allow-list, audit-log dashboards that show a decoded form for readability, hover-tooltip previews, federated/syndicated content where the decoder lives on the consuming side — restores the BiDi character and the visual spoof that the original defense was filed to prevent.
The same UrlSanitizer::parse() carries an ASCII-only /\s/ whitespace check (no /u modifier) intended as a backstop against malformed URLs. Without the /u modifier, PCRE's \s matches only ASCII whitespace, so Unicode whitespace characters — NBSP (U+00A0), the zero-width no-break space / BOM (U+FEFF), line/paragraph separators (U+2028, U+2029), ogham space (U+1680), the U+2000–U+200A en/em quad family, narrow / medium / ideographic spaces (U+202F, U+205F, U+3000) and NEL (U+0085) — pass through unchanged in both raw and percent-encoded forms. In hostname positions they enable lookalike spoofs (example<NBSP>.com); in path/query/fragment they enable allow-list drift when a downstream consumer strips whitespace before comparison.
UrlSanitizer::parse() now denies BiDi formatting marks together with Unicode whitespace and the zero-width no-break space, in both the raw input and the percent-decoded form of each parsed URL component (user, pass, host, path, query, fragment). ASCII space remains tolerated in path/query/fragment via the existing percent-encoding step.
The patches for this issue are available here for branch 6.4 (and forward-ported to 7.4, 8.0 and 8.1).
Symfony would like to thank Scott Arciszewski (Trail of Bits) for reporting the issue and Nicolas Grekas for providing the fix.
symfony/html-sanitizer >= 6.1.0, < 6.4.41symfony/html-sanitizer >= 7.0.0, < 7.4.13symfony/html-sanitizer >= 8.0.0, < 8.0.13symfony/symfony >= 6.1.0, < 6.4.41symfony/symfony >= 7.0.0, < 7.4.13symfony/symfony >= 8.0.0, < 8.0.13Upgrade to a patched release:
symfony/html-sanitizer 6.4.41symfony/html-sanitizer 7.4.13symfony/html-sanitizer 8.0.13symfony/symfony 6.4.41symfony/symfony 7.4.13symfony/symfony 8.0.13Connected by shared product, vendor, weakness, or advisory.
CVE-2026-48761MediumSymfony: HtmlSanitizer UrlAttributeSanitizer Misses URL Attributes
CVE-2026-47767MediumSymfonyRuntime CVE-2024-50340 Patch Bypass: Web Requests Can Still Set APP_ENV/APP_DEBUG via parse_str/SAPI Argv Mismatch
CVE-2026-48489HighSymfony: Security Firewall Bypass via failure_forward Subrequest: Unauthenticated Access to access_control-Protected GET Routes
CVE-2026-48736MediumSymfony: IpUtils::PRIVATE_SUBNETS Omits IPv6 Transition Forms (6to4, NAT64, Teredo, IPv4-compatible): SSRF Bypass in NoPrivateNetworkHttpClient
CVE-2026-48747MediumSymfony: Mailomat Mailer Webhook Parser Reads the HMAC Algorithm from the Request: Signature Algorithm Downgrade
CVE-2026-48784MediumSymfony: UrlGenerator Dot-Segment Encoding Skips Every Other Chained `../` or `./` → Generated URL Collapses Off-Route Under RFC 3986 Normalization