demo · v138
The mXSS round-trip
A real mutation-XSS pattern. A “safe” user input is put into an attribute. The DOM is then re-serialised via outerHTML — into an iframe, a server round-trip, a cache, anywhere. Pre-138, the round-trip silently became different markup. 138 closes the loop.
This is a security fix. Several real-world XSS bugs followed the “put attacker input in an attribute, then assume the DOM’s textual form is faithful” pattern. Sanitisers running on the textual form saw clean attributes; the actual reconstructed DOM had a tag boundary inside the attribute value.
1. attribute set with user input
2. outerHTML serialisation
3. browser re-parses that string
4. did the DOM round-trip safely?
awaiting…
scenario
A wiki accepts a footnote whose content is dropped into img.title — an attribute. The HTML is then cached for cross-region replicas by reading document.documentElement.outerHTML. The replicas inject that HTML into other iframes via iframe.contentDocument.write(...). Without 138’s escaping, the replica iframe ends up with different DOM than the origin — including a runnable <img onerror>.
the fix in one line
// Pre-138: attribute serialisation escaped " and & only.
// 138: also escapes < and >, killing the round-trip mutation vector.
el.title = '<img src=x onerror=alert(1)>';
el.outerHTML;
// pre-138: <span title="<img src=x onerror=alert(1)>"></span> // reparsed: TWO elements!
// 138+: <span title="<img src=x onerror=alert(1)>"></span> // reparsed: same span, same attribute