v157 · miscellaneous · sanitizer api
Add javascriptURLs option to SanitizerConfig and Sanitizer
The Sanitizer API has always removed javascript: URLs from navigation attributes — but only as a hidden side effect of which method you called: setHTML() stripped them, setHTMLUnsafe() kept them, and no configuration could say otherwise. HTML PR 12898 regularises this into an explicit, developer-selectable knob: a javascriptURLs boolean on SanitizerConfig and a setJavascriptURLs(allow) method on Sanitizer. Defaults are unchanged — safe methods still always strip (removeUnsafe() forces the option off), unsafe methods still default to keeping — but an unsafe-path sanitization can now opt in to stripping javascript: URLs.
concepts
-
javascript: URL playground
Paste markup with
javascript:hrefs and run it through the four real call shapes —setHTML(),setHTML()withjavascriptURLs: true(spoiler:removeUnsafe()wins),setHTMLUnsafe()with the newjavascriptURLs: false, and the permissive unsafe default. Serialized output, a per-attribute kept/removed diff, and a navigation-disabled rendered preview, all from genuine API calls. -
Config inspector
The option's lifecycle, probed step by step: what
new Sanitizer().get()canonicalisesjavascriptURLsto, the exact booleansetJavascriptURLs()returns (true = changed, false = no-op — twice in a row proves it), andremoveUnsafe()forcing the option back tofalse. Every step prints the real return value or the real exception. -
Escape artists
Edge-case matrix: obfuscated spellings (
JaVaScRiPt:, leading whitespace, HTML-entity-encoded, tab-split scheme) crossed with every navigation attribute the spec names —a/areahref,form action,input/buttonformaction, SVG and MathMLhref. Which cells the sanitizer actually strips, computed live, including the ones it deliberately leaves alone. -
Untrusted comment feed
The product use case: a comment thread that wants links, so
hrefis on the allowlist — andjavascript:hrefs still must not survive. Render hostile comments through safesetHTML(), then throughsetHTMLUnsafe()+javascriptURLs: false(the new unsafe-but-no-script-URLs middle ground), and inspect every link that comes out.
why it shipped
Before this change, whether javascript: URLs were sanitized was a side effect of the method: any "safe" method stripped them, any "unsafe" method did not, and the SanitizerConfig had no say. That made one real configuration impossible to express — an unsafe-path sanitization (needed when you must keep comments, data-* attributes, or other XSS-irrelevant markup the safe path removes) that still strips javascript: navigation URLs. The HTML Standard regularised the behaviour into the config itself: javascriptURLs defaults to the old behaviour (canonicalised to false for safe calls — and removeUnsafe() always forces it to false — and to true for unsafe calls), so nothing breaks, but the knob is now yours. Behaviour when the option is not used remains exactly the same.
the shipped surface
dictionary SanitizerConfig {
// ...
boolean comments;
boolean dataAttributes;
boolean javascriptURLs; // NEW — preserve attributes containing javascript: URLs?
};
partial interface Sanitizer {
boolean setJavascriptURLs(boolean allow); // NEW — returns true if the config changed
};
// The new expressible configuration: unsafe path, javascript: URLs still stripped.
el.setHTMLUnsafe(untrusted, { sanitizer: { javascriptURLs: false } });