Build1 publisher2 min readPublished
A CSP directive turns every string passed to innerHTML into a TypeError
MDN now lists the Trusted Types API as Baseline 2026, newly available. The JavaScript half only lets you sanitize; require-trusted-types-for is the part that makes an unsanitized assignment throw, and the sanitizer stays yours to write.
The Engineer · Build desk
What happened
- MDN's Trusted Types API page now carries a Baseline 2026, newly available label for the API it describes as a way to force input through a developer-supplied transformation function before execution.
- The API sorts injection sinks into three kinds: HTML sinks such as Element.innerHTML and document.write(), JavaScript sinks such as eval(), and JavaScript URL sinks such as HTMLScriptElement.src.
- Policies produce three trusted types, TrustedHTML for sinks that render HTML, TrustedScript for sinks that execute JavaScript, and TrustedScriptURL for sinks that parse a script URL.
- Application code creates a TrustedHTML object from a string through the policy and assigns that object to innerHTML, instead of assigning the string directly.
- Adding require-trusted-types-for to a page's CSP makes any string passed into an injection sink raise a TypeError exception, which is what enforces the policy at runtime.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision DOM-XSS work becomes a scheduled change with two parts, a policy module and a response header, and the sequencing question is whether every sink write goes through the policy before the directive is switched on.
- exposure The browser only guarantees that data went through your createHTML function, so a DOMPurify bypass or a case you did not handle still reaches the sink with the directive fully enforced.
- cost Violations show up as runtime exceptions in users' browsers, so the cost falls on whoever owns the rarely-exercised code path and the third-party tag that trips it.
- constraint A policy can refuse all input for JavaScript and script-URL sinks, and dependencies that expect eval or dynamic script src to work will stop working when it does.
Under `require-trusted-types-for`, the failure happens at the assignment. MDN says that with the directive set, passing strings into injection sinks results in a `TypeError` exception [9]. The browser does not sanitize on your behalf at that moment; the call throws.
MDN's documentation is explicit that the JavaScript API on its own does not get you there: "The API described above enables you to sanitize data, but it doesn't ensure that your code never passes input directly to an injection sink: that is, it doesn't stop you passing a string into innerHTML" [8]. MDN describes the API as two parts, a JavaScript API for sanitizing data and two CSP directives that enforce and control its usage [10].
The header enforces a narrow guarantee. The doc's example policy is `trustedTypes.createPolicy("my-policy", { createHTML: (input) => DOMPurify.sanitize(input) })` [7]. A note on the page says "The Trusted Types API does not itself supply a policy or any transformation functions: the developer defines their own policy, which contains the transformations that they wish to apply" [6]. Enforcement proves that the string went through `createHTML`; whether the output is safe depends on the function you wrote. The audit surface shrinks from every sink call site to one function.
For JavaScript and JavaScript URL sinks, MDN says the policy may turn off the sinks entirely or allow certain predefined inputs, such as specific URLs [11]. If your app never calls `eval`, refusing everything is the cheap configuration.
The adoption cost is finding the writes. MDN names five specific sink APIs across the three categories: `Element.innerHTML`, `document.write()`, `eval()`, `HTMLScriptElement.text` and `HTMLScriptElement.src` [13]. The doc offers those five as illustrations, and the sink surface a browser exposes runs wider. Because the directive sits in the document's CSP and the check fires at the sink call, a vendor tag that assigns a string raises the same `TypeError` as your own code does [17].
The Baseline 2026 badge is MDN's, and it reads "Newly available" [1]. The page as supplied leaves the browser versions behind that badge unlisted, and names one of the two CSP directives it says the API has [15]. Enforcement is performed by the browser, so a browser that does not implement the directive raises nothing and the string reaches the sink as before [14].
What to watch
- A per-browser version table behind the Baseline 2026 label would tell a team whether the directive is actually enforced for its own traffic.
- The name and behaviour of the second CSP directive MDN says the API has, unnamed in the current page text.
- Any documented report-only path for collecting sink violations before enforcement starts throwing in production.