Severity by source
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/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
Network-accessible stored XSS with scope change; PR:N since the sanitizer layer imposes no auth requirement; UI:R as victim page load is needed to trigger execution.
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:P/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\HtmlSanitizer\Visitor\AttributeSanitizer\UrlAttributeSanitizer::getSupportedAttributes() enumerates the attribute names whose values are scrubbed through UrlSanitizer::sanitize() (scheme and host allow-lists, javascript: rejection, BiDi check, etc.). The list is ['src', 'href', 'lowsrc', 'background', 'ping', 'action', 'formaction', 'poster', 'cite']. Other URL-bearing attributes are absent: <object data=…>, <applet codebase=…>, <applet archive=…> and <object archive=…>, <iframe longdesc=…> and <img longdesc=…>. When an integrator opts these elements/attributes in via allowElement('object', ['data']), allowElement('applet', ['codebase']), etc., or via allowAttribute(), no URL sanitization runs: data="javascript:alert(1)" and similar payloads ship through unchanged into the output, enabling stored XSS.
<meta http-equiv="refresh" content="0; url=…"> is the same class of bug routed differently: the URL is embedded inside a multi-field content attribute that the per-attribute sanitizer cannot detect from the attribute name alone. Integrators who enable <meta> with the content attribute (e.g. via allowStaticElements()) see content="0; url=javascript:alert(1)" pass through, producing a refresh-driven navigation to a javascript: URL.
Default configurations are not affected: <object>, <applet> and <iframe> are not in W3CReference::BODY_ELEMENTS and <meta> requires an explicit opt-in to <head> context. The vulnerability surface is integrators who explicitly allow any of those elements together with the listed URL-bearing attributes.
Resolution
UrlAttributeSanitizer now also routes data, codebase, archive and longdesc through UrlSanitizer::sanitize(). A new MetaRefreshAttributeSanitizer registered as a default attribute sanitizer detects the <delay>; url=<url> syntax inside <meta content>, sanitizes the embedded URL, and drops the attribute if the URL is rejected; non-refresh meta content values are passed through unchanged.
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.
AnalysisAI
Stored XSS in the Symfony HtmlSanitizer component (symfony/html-sanitizer) allows injection of javascript: scheme URLs through HTML attributes not covered by the library's URL sanitization allow-list. The UrlAttributeSanitizer hardcodes a fixed set of URL-bearing attributes to sanitize, omitting data (on object), codebase and archive (on applet), and longdesc (on iframe/img); a related gap allows javascript: URLs embedded in meta http-equiv=refresh content values to pass through unchanged. Only non-default configurations that explicitly opt in to those element-attribute combinations via allowElement() or allowAttribute() are affected - default Symfony configurations are confirmed safe. No public exploit code has been identified at time of analysis, though the vendor advisory includes functional proof-of-concept payloads; the issue was reported by Scott Arciszewski of Trail of Bits.
Technical ContextAI
The Symfony HtmlSanitizer component (composer packages symfony/html-sanitizer and symfony/symfony, CWE-79) implements an allow-list-driven HTML sanitizer. Its UrlAttributeSanitizer class maintains a hardcoded list of attributes whose values are passed through UrlSanitizer::sanitize() for scheme validation (javascript: rejection), host allow-listing, and BiDi checks. That list - src, href, lowsrc, background, ping, action, formaction, poster, cite - omits several HTML attributes that also carry navigable or executable URLs: data on object elements, codebase and archive on applet and object elements, and longdesc on iframe and img elements. A second class of gap affects meta http-equiv=refresh: that element encodes its URL inside the composite content attribute (e.g., 0; url=javascript:alert(1)), making it undetectable by a per-attribute-name sanitizer. The root cause (CWE-79) is an incomplete output filter - the sanitizer correctly handles known URL sinks but its enumeration of those sinks is incomplete. The fix adds data, codebase, archive, and longdesc to UrlAttributeSanitizer and introduces a new MetaRefreshAttributeSanitizer that parses the delay;url= syntax and subjects the embedded URL to the same UrlSanitizer pipeline.
RemediationAI
Upgrade symfony/html-sanitizer (or symfony/symfony) to the patched releases: 6.4.41 for the 6.x series, 7.4.13 for the 7.x series, or 8.0.13 for the 8.0.x series, as documented in the security advisory at https://github.com/symfony/symfony/security/advisories/GHSA-x5qj-865h-mgvm; the patch commit is https://github.com/symfony/symfony/commit/069a70f9f26e61e9de3b7f9a864a86ed24b36bd0. If an immediate upgrade is not possible, audit every HtmlSanitizerConfig instance in the application and remove any explicit allowElement() or allowAttribute() calls that permit object with data, applet with codebase or archive, object with codebase, iframe or img with longdesc, or meta with content - removing these allowances eliminates the attack surface entirely but may break intentional rich-content features. Applications that rely on meta refresh functionality should delay that workaround until the patch can be applied, as removing the attribute may disrupt redirect-driven flows. No generic compensating controls (WAF rules, CSP) reliably substitute for the library-level fix because the payload reaches stored state before any response-phase control acts on it.
Same weakness CWE-79 – Cross-site Scripting (XSS)
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-44389
GHSA-x5qj-865h-mgvm