Perfect types with `setHTML()`

Sat 07 March 2026

TLDR: Use require-trusted-types-for 'script'; trusted-types 'none'; in your CSP and nothing besides setHTML() works, essentially removing all DOM-XSS risks.

Background: Sanitizer API

I was guest at the ShopTalkShow Podcast to talk about setHTML() and the HTML Sanitizer API. Feel free to listen to the whole episode, if you want to take it all in. It's a great introduction.

Meanwhile, MDN has a good explanation of the Sanitizer constructor to create a custom configuration and Element.setHTML() as the main entry point of the API. People who do not want to directly insert into the document can also create a new document with Document.parseHTML().

Trusted Types

Trusted Types (TT) is a feature in Content-Security-Policy (CSP), that can help prevent DOM-based XSS. By enabling "trusted types" in your CSP, with a policy like so require-trusted-types-for 'script'; trusted-types 'mypolicy', scripts will not be able to start HTML parsing/insertion from normal strings (e.g., through document.write(), innerHTML= and so on). These so-called HTML parsing sinks will now only accept TrustedHTML objects - hence the name trusted. Creating these types is ideally controlled with a so-called TrustedTypePolicy that is also allowed per the header (mypolicy in the example above).

I would argue that the creation and maintenance of policy code will require constant vigilance and maintenance by security-minded people. It is possible, but not something I would require of every web developer.

Perfect Types

This leads us to Perfect Types. The idea of so-called Perfect Types is that no policy is allowed. The following header both requires that the legacy HTML parsing APIs all require Trusted Types but also that no policy exists to ever create them:

require-trusted-types-for 'script'; trusted-types 'none';.

With this policy, your only way to safe HTML modification is by using the new safe methods:

  1. Use setHTML() to directly insert into your document
  2. Use Document.parseHTML() to insert into a temporary document, select your desired piece of context (e.g., with querySelector) and then use cloneNode or other Node APIs to move the elements into your current document.

Acknowledgements

This idea is heavily inspired by a great idea from Jun Kokatsu and his blog post about "Perfect Types". He didn't suggest using setHTML(), because it didn't exist yet. Instead, he described using Perfect Types to disallow the DOM XSS sinks and then relied on React to take care of the safe HTML modification.

If you find a mistake in this article, you can submit a pull request on GitHub.

Home - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.0. UTC+08:00, 2026-03-24 16:16
浙ICP备14020137号-1 $Map of visitor$