Fix guide · info · missing_trusted_types
CSP does not opt in to Trusted Types
Your CSP doesn't include require-trusted-types-for 'script'. Adding it instructs Chrome / Edge to refuse innerHTML / document.write / eval-shaped sink writes unless the value passes through a vetted policy — a real defense-in-depth XSS mitigation.
Why it matters
Trusted Types is a browser-enforced policy that turns dangerous JavaScript sinks (innerHTML, outerHTML, document.write, document.writeln, Element.insertAdjacentHTML, eval, new Function, setTimeout/setInterval with string args, srcdoc) into typed APIs. The browser refuses to accept a raw string for any of these — only values produced by a registered TrustedTypePolicy are allowed.
The effect: even if XSS injection succeeds at landing reflected user input on the page, the attacker can't actually trigger any of the standard XSS payloads because every dangerous sink rejects them.
Real-world impact:
- DOM XSS becomes near-impossible to exploit through standard sinks. The attacker has to find an unvetted custom-element bypass.
- Build pipeline gains type-safety on HTML construction. Frameworks (React, Vue, Lit, Svelte) all have Trusted Types integration; turning it on flushes out unsafe innerHTML usage at build time.
- Reports on policy violations before they're enforced (run in report-only mode first).
This is info-severity because it's optional, not a vulnerability per se. We surface it as a positive-signal upgrade opportunity. Adoption is growing — Google.com, GitHub, Microsoft 365 all use Trusted Types in production.
Why not just turn it on?
- React 18+ supports it but needs
react-dom/serverTrusted Types policy registration - Many third-party libraries (jQuery older versions, some analytics) still call innerHTML directly; you'll need to migrate or accept the policy violation reports
- Report-only mode is safe to deploy immediately; enforcement is a separate decision
How to fix it
Step 1 — report-only mode (safe to deploy):
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; trusted-types default 'allow-duplicates'; report-uri /api/csp-violation
Deploy, watch your /api/csp-violation endpoint, identify which library / code paths are calling unvetted innerHTML.
Step 2 — register policies for the legitimate sinks:
// Once at app boot:
if (window.trustedTypes && trustedTypes.createPolicy) {
trustedTypes.createPolicy("default", {
createHTML: (s) => {
// Sanitize via DOMPurify (or a trusted sanitizer).
return DOMPurify.sanitize(s, { RETURN_TRUSTED_TYPE: true });
},
createScriptURL: (s) => {
// Allowlist trusted script-URL prefixes.
if (s.startsWith("https://js.stripe.com/")) return s;
throw new Error("Trusted Types refused: " + s);
},
});
}
Step 3 — flip to enforcement:
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types default
Browser support: Chrome 83+, Edge 83+. Firefox + Safari don't enforce yet (they ignore the directive). Adoption today still meaningfully protects Chromium-engine users, which is the majority.
Reference: https://web.dev/articles/trusted-types
Did vibecheck flag this on your app?
If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.
Run another inspection