Browser Security Best Practices

Chapter 1 · Practice 3 of 4

Turn on Trusted Types to block strings at DOM sinks

From Cross-site scripting and Trusted Types

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 typeExamplesAccepts
HTMLinnerHTML, insertAdjacentHTML, document.writeTrustedHTML
Scripteval, HTMLScriptElement.textTrustedScript
Script URLHTMLScriptElement.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

TypeScript
// 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;
}