ESC

Type to search the knowledge base.

Trusted Types Intro

Trusted Types lock dangerous DOM sinks to typed values — policy design, CSP require-trusted-types-for, and DOM XSS.

advanced3 min read
  • browser
  • trusted-types
  • xss
  • csp
  • security

Trusted Types redesign dangerous DOM sinks so you can’t assign raw strings to innerHTML, script.src, and friends without going through a policy that returns typed objects. Paired with CSP, it turns many DOM XSS bugs into hard failures at the assignment site.

Docs: MDN Trusted Types, web.dev Trusted Types, W3C Trusted Types.

The problem

el.innerHTML = userInput; // string → HTML parser → XSS

Frameworks reduce accidents; plugins and dangerouslySetInnerHTML bring them back. Trusted Types make the sink type-safe at runtime:

el.innerHTML = userInput; // TypeError under enforced Trusted Types

CSP enablement

Content-Security-Policy: require-trusted-types-for 'script'; trusted-types myPolicy default
  • require-trusted-types-for 'script' — enforce for script-related sinks (and related).
  • trusted-types … — allowlist policy names; default policy optional.

Roll out with report-only / violation reports first — same discipline as CSP.

Creating a policy

const policy = trustedTypes.createPolicy('myPolicy', {
  createHTML: (input) => DOMPurify.sanitize(input),
  createScript: () => {
    throw new Error('inline scripts blocked');
  },
  createScriptURL: (url) => {
    const u = new URL(url, location.origin);
    if (u.origin !== location.origin) throw new Error('bad script url');
    return u.toString();
  },
});

el.innerHTML = policy.createHTML(userHtml);
script.src = policy.createScriptURL('/app.js');

Policies are the only place sanitization/allowlisting should live — keep them few and reviewed.

Sinks covered (illustrative)

  • Element.innerHTML / outerHTML
  • Document.write
  • setTimeout(string) / setInterval(string)
  • script.src, iframe.srcdoc (as specified)
  • Various DOM APIs listed in the spec

Exact sink list evolves — test target browsers.

Framework notes

React 19+ and other libraries increasingly produce Trusted Types-compatible values when policies exist. You may still need a default policy for third-party widgets that assign strings.

trustedTypes.createPolicy('default', {
  createHTML: (s) => DOMPurify.sanitize(s),
});

Default policies are powerful and dangerous — treat like a global sanitizer.

Limitations

Trusted Types don’t fix URL-based XSS alone (javascript: hrefs), CSS injection, or backend HTML injection into initial documents. Stack with sanitization, CSP nonces, and cookie flags — XSS, CSP.

Interview out-loud

“Trusted Types make dangerous sinks reject raw strings unless a named policy creates HTML/script/URL types. I enable require-trusted-types-for via CSP, centralize sanitization in createHTML, and lock createScriptURL to known origins.”

Rollout sequence

  1. Inventory sinks (innerHTML, etc.).
  2. Add report-only CSP for Trusted Types.
  3. Introduce policies; fix violations.
  4. Enforce on a canary.
  5. Remove default-policy crutches that reintroduce string sinks globally.

Track violation reports like CSP — volume spikes mean a new feature bypassed the policy.

Further depth

Teams often under-invest in this topic until an incident or CWV regression. Schedule a one-hour drill: reproduce the failure mode in DevTools, list the top three mitigations for your stack, and file tickets with owners. Revisit after the next major feature that touches networking, rendering, auth, or third parties — those are the moments regressions land. Keep primary documentation links in the runbook so on-call is not searching chat history at 2am.

Concrete artifacts to leave behind: a short architecture note, a CI assertion or header snapshot, and a dashboard panel (lab or field) that would have caught the last bug. Teaching the rest of the team the mental model matters as much as the one-line fix.

Further reading

Related guides