Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐘 Packagist
Not in CISA KEV

GHSA-v3wm-qf9p-c549 symfony/html-sanitizer

Fix: symfony/symfony@b21a626

GHSA-v3wm-qf9p-c549 is a CWE-451 vulnerability in symfony/html-sanitizer. A fix is available for symfony/html-sanitizer — see the affected versions and patch details below.

Symfony: HtmlSanitizer URL Parser Deny Gates Underinclusive: Percent-Encoded BiDi Marks and Unicode Whitespace Bypass Visual-Spoofing Defense

Also known asCVE-2026-48760
Published
Jun 15, 2026
Updated
Sep 10, 2026
Affected
6 pkgs
Patched
6 / 6
Exploits
None indexed
Exploitation data as of Sep 18, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

No confirmed exploitation observed yet

  • CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.

Exploitation and automatability from CISA’s SSVC triage for GHSA-v3wm-qf9p-c549.

EPSS Exploitation Probability

via FIRST.org ↗
0.3%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs27th percentile — riskier than 27% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

Real-World Exposure

6 pkgs affected
🐘symfony/html-sanitizer🐘symfony/html-sanitizer🐘symfony/html-sanitizer🐘symfony/symfony🐘symfony/symfony🐘symfony/symfony

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.

Description

Description

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.

Resolution

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).

Credits

Symfony would like to thank Scott Arciszewski (Trail of Bits) for reporting the issue and Nicolas Grekas for providing the fix.

Affected Packages

6 total 6 fixed
EcosystemPackageVulnerable rangeFix
🐘Packagistsymfony/html-sanitizer6.1.0&&< 6.4.416.4.41composer require symfony/html-sanitizer:^6.4.41
🐘Packagistsymfony/html-sanitizer7.0.0&&< 7.4.137.4.13composer require symfony/html-sanitizer:^7.4.13
🐘Packagistsymfony/html-sanitizer8.0.0&&< 8.0.138.0.13composer require symfony/html-sanitizer:^8.0.13
🐘Packagistsymfony/symfony6.1.0&&< 6.4.416.4.41composer require symfony/symfony:^6.4.41
🐘Packagistsymfony/symfony7.0.0&&< 7.4.137.4.13composer require symfony/symfony:^7.4.13
🐘Packagistsymfony/symfony8.0.0&&< 8.0.138.0.13composer require symfony/symfony:^8.0.13

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for symfony/html-sanitizer, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update symfony/html-sanitizer to 6.4.41 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-v3wm-qf9p-c549 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-v3wm-qf9p-c549 can be triaged on real exposure rather than presence alone.

Tailored to GHSA-v3wm-qf9p-c549. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### Description `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 — phi
O3 Security · Impact-Aware SCA

Is GHSA-v3wm-qf9p-c549 in your dependencies?

O3 Security finds GHSA-v3wm-qf9p-c549 across Packagist dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

GHSA-v3wm-qf9p-c549: symfony/html | O3 Security