Chapter 1 · Practice 3 of 4
Turn on Trusted Types to block strings at DOM sinks
Why it matters
Code review catches some sink misuse; Trusted Types makes the browser catch all of it. With enforcement on, MDN explains, passing a plain string to an injection sink throws a TypeError. Sinks only accept typed objects created by a policy you define:
| Sink type | Examples | Accepts |
|---|---|---|
| HTML | innerHTML, insertAdjacentHTML, document.write | TrustedHTML |
| Script | eval, HTMLScriptElement.text | TrustedScript |
| Script URL | HTMLScriptElement.src, new Worker() | TrustedScriptURL |
That turns XSS review into reviewing a few policy functions instead of every DOM write in the app and its dependencies.
Two CSP directives control it:
require-trusted-types-for 'script'turns on enforcement.trusted-types <names>lists which policy names may be created; creating any other throws.
MDN marks Trusted Types as Baseline 2026, newly available across current browsers since February 2026; older browsers ignore the directives, so treat it as defense in depth on top of safe code. A policy named default receives every string sent to a sink, which MDN recommends only as a migration aid.
Roll out in report-only mode first (see the CSP chapter): violations show you every remaining sink in your code and libraries.
Example
// CSP header:
// require-trusted-types-for 'script'; trusted-types sanitize-html
import DOMPurify from 'dompurify';
const htmlPolicy = window.trustedTypes?.createPolicy('sanitize-html', {
createHTML: (input: string) => DOMPurify.sanitize(input),
});
export function setHtml(el: Element, html: string) {
// a plain string here would throw under enforcement
el.innerHTML = (htmlPolicy ? htmlPolicy.createHTML(html) : DOMPurify.sanitize(html)) as string;
}