-
Notifications
You must be signed in to change notification settings - Fork 1
Security Goals and Threat Model
Retrofit Trusted Types protection onto code you cannot easily change. The goal is to neutralize DOM-based XSS that flows through Trusted Types HTML sinks, by sanitizing at the sink rather than at every call site, and to do it honestly: if it cannot protect you, it tells you, and it never trades a real guarantee for a comforting-but-false one.
It is a hardening layer, not a license to stop fixing XSS at the source. Treat it as defense in depth.
- DOM-XSS through Trusted Types HTML sinks:
innerHTML,outerHTML,insertAdjacentHTML,document.write,range.createContextualFragment, and the rest. Attacker-controlled markup reaching these is sanitized by thedefaultpolicy before it lands. - Execution through script sinks:
eval,new Function,script.src,javascript:URLs. These are refused outright unless you explicitly opt a value in. - mutation XSS, to the extent your sanitizer handles it. DOMFortify routes the string to the sanitizer; DOMPurify's mXSS defenses then apply.
- DOMFortify runs first and owns the
defaultpolicy. The policy is winner-takes-all; if something else claims it first, DOMFortify steps aside and reports that it did not install. - Enforcement is on. Trusted Types has to be enforced for any of this to engage. That enforcement can come from a response header, a parse-time
<meta>, or DOMFortify's ownINJECT_META, but it has to be present. With enforcement off, DOMFortify is inert and says so viastatus(). - The sanitizer is sound. DOMFortify is only as good as the sanitizer behind it. A current DOMPurify is the default for a reason; a permissive
SANITIZER_CONFIGcan re-open holes. - The page is not already compromised before DOMFortify loads. If attacker code runs before
init(), it can claim the policy or otherwise get ahead of you.
- Sinks that are not Trusted Types sinks. Inline event handler attributes (
onclick=), style sinks, and plain URL navigation (a.href = "javascript:...") are not Trusted Types sinks, so DOMFortify cannot see or stop them. Close those with ascript-srcthat drops'unsafe-inline'. - Server-side and stored XSS that does not pass through a DOM sink. DOMFortify is a browser-side sink guard, not an input filter.
- The sanitizer's own bugs. A bypass in DOMPurify is a bypass through DOMFortify.
- Anything when enforcement is off, when DOMFortify did not win the
defaultpolicy, or on a page matched byEXCLUDE. - Side channels, timing, and the broader platform. DOMFortify addresses one specific class of bug well, not everything.
DOMFortify treats its configuration and the sanitizer as trusted, and the page content as untrusted. Within that boundary it is built to resist being quietly disabled by hostile content:
- Native references (
trustedTypes,document,location,hasOwnProperty) are captured at load, so later DOM clobbering cannot swap them out. - Configuration is read own-key only, so a polluted
Object.prototypecannot inject a rule, point the sanitizer elsewhere, or open a script sink. - The internal shallow copy of your sanitizer config drops
__proto__,constructor, andprototype. - The violation reporter (
ON_VIOLATION) is wrapped so a throwing or hostile reporter cannot abort installation or turn a fail-closed sink into a thrown exception.
These reduce the chance that a partially-compromised page can neutralize DOMFortify without tripping enforcement, but they are not a substitute for loading first and keeping the sanitizer current.