symfony/html-sanitizer lets applications sanitise untrusted HTML. The configuration methods allowLinkHosts([...]) and allowLinkSchemes([...]) are intended to restrict <a href> targets to an allowlist of hosts/schemes; allowMediaHosts() / allowMediaSchemes() do the same for <img src> etc.
Three distinct bypasses allow a content author to smuggle off-allowlist URLs past these checks. First, UrlSanitizer::parse() parses the input following RFC-3986, while browsers follow the WHATWG URL Standard which normalises \ to / before parsing the authority of "special" schemes; so an input like https://evil\@trusted.com/ parses with host trusted.com server-side but navigates to https://evil/ in the browser. Second, WHATWG collapses any run of / after the scheme into //, while RFC-3986 does not; so https:/evil.com/ and https:///evil.com/ parse as host-less (skipping the host allowlist) but resolve to evil.com in the browser. Third, UrlAttributeSanitizer checks 'a' === $element to route to the link policy and falls through to the media policy otherwise, but <area> is a navigable hyperlink equivalent to <a>; so <area href> was sanitised against the media policy (which typically allows data: and may have no host allowlist), bypassing allowLinkHosts() / allowLinkSchemes() entirely.
UrlSanitizer::sanitize() now rejects URLs that contain a backslash or that use a special scheme (http, https, ftp, ws, wss) followed by a single slash or three slashes before parsing, eliminating the parser-differential bypasses. UrlAttributeSanitizer now applies the link policy to both <a> and <area> elements.
The patch for this issue is available here for branch 5.4.
Symfony would like to thank Claude Mythos Preview (via Project Glasswing) for reporting the issue and providing the fix.
{
"cwe_ids": [
"CWE-184",
"CWE-436"
],
"github_reviewed": true,
"nvd_published_at": null,
"github_reviewed_at": "2026-05-27T20:13:04Z",
"severity": "MODERATE"
}