skip to content

When you enforce Trusted Types on an Angular app, which Angular policy names must the trusted-types directive allow, and what breaks if one is missing?

level: seniorimportance: nice to knowfreq 24%

answer

  1. one required, four conditional
  2. sanitized values versus bypassed values
  3. lazy chunks from the bundler
  4. policy creation fails quietly
  5. the sink assignment then throws

basics

~10 s

The angular policy is always required; angular#unsafe-bypass is needed for bypassSecurityTrust methods, angular#bundler for CLI lazy chunks, angular#unsafe-jit for JIT and angular#unsafe-upgrade for AngularJS hybrids. Without one, that feature's DOM writes fail as violations.

solid answer

~40 s

Trusted Types enforcement, `require-trusted-types-for 'script'`, makes the browser reject plain strings at sinks such as `innerHTML` and script URLs, and the `trusted-types` directive lists which named policies may create trusted values. Angular uses five names. `angular` wraps values Angular has already sanitized and trusted template constants, and is always required. `angular#unsafe-bypass` wraps values from `DomSanitizer.bypassSecurityTrust*`. `angular#bundler` is used by the CLI bundler for lazy chunk files, `angular#unsafe-jit` by the JIT compiler, and `angular#unsafe-upgrade` by `@angular/upgrade`. When a name is not allowed, Angular catches the policy-creation error and falls back to plain strings, so nothing fails at startup; the failure comes later, when that feature writes to a sink and the browser throws. The `unsafe` names mark features worth removing rather than allowing.

go deeper

for a junior

Recall that Angular ships named Trusted Types policies and that the angular policy is always required under enforcement.

for a middle

Map each policy to its feature: bypass methods, CLI lazy chunks, JIT and AngularJS upgrade.

for a senior

Diagnose a violation back to the missing policy, knowing that failure appears at the sink rather than at bootstrap, and prefer removing unsafe features over allowing their policies.

for a principal

Decide whether the organisation enforces Trusted Types, how third-party libraries are handled, and who approves any unsafe policy in the header.

## What enforcement changes **Trusted Types** is a browser feature switched on through CSP. Two directives matter: - `require-trusted-types-for 'script'` makes injection sinks, such as `innerHTML`, `srcdoc`, script `src` and `eval`, **reject plain strings**. They accept only typed objects such as `TrustedHTML`. - `trusted-types <names>` lists the **policies** allowed to create those objects. A policy is a named factory created with `trustedTypes.createPolicy(name, rules)`. Angular's own DOM writes therefore have to pass typed values, and it creates them through a small set of named policies. The browser API itself is a platform subject; what an Angular interview asks is **which names Angular uses and what each one covers**. ## Angular's policies | Policy | Used for | Allow it when | | :--- | :--- | :--- | | `angular` | values Angular has sanitized, and constant values from templates | always: Angular needs it to function under enforcement | | `angular#unsafe-bypass` | values wrapped by `bypassSecurityTrustHtml`, `bypassSecurityTrustScript` and `bypassSecurityTrustResourceUrl` | the app calls any `DomSanitizer` bypass method | | `angular#bundler` | the Angular CLI bundler when it creates lazy chunk files | the app lazy-loads code | | `angular#unsafe-jit` | the JIT compiler turning generated source into functions | the app runs in JIT mode or uses the runtime compiler | | `angular#unsafe-upgrade` | the `@angular/upgrade` package | the app is an AngularJS hybrid | A typical header for an AOT app with lazy routes and no bypasses: ```txt Content-Security-Policy: trusted-types angular angular#bundler; require-trusted-types-for 'script'; ``` The `#unsafe` suffix is a review signal. Values created through `angular#unsafe-bypass` are exactly the ones Angular did **not** sanitize, so listing that policy is an admission that some part of the app trusts unchecked strings. The same goes for JIT, which turns strings into code. ## What a missing policy looks like Angular creates each policy lazily, the first time a feature needs it. If the name is not in `trusted-types`, `createPolicy` throws, and Angular **catches** the error and falls back to plain strings. Nothing breaks at bootstrap. The breakage appears later: 1. A component binds a `SafeHtml` created by `bypassSecurityTrustHtml` to `[innerHTML]`. 2. Angular tries to wrap it through `angular#unsafe-bypass`, cannot, and passes a plain string. 3. The browser rejects the string at the `innerHTML` sink and reports a Trusted Types violation; that content never renders. Sanitized `[innerHTML]` values in the same app keep working, because they go through `angular`. That split, where only the bypassed content fails, is the usual clue to which policy is missing. ## What enforcement buys an Angular app - **Direct DOM writes fail loudly.** `nativeElement.innerHTML = value` or a third-party widget assigning a plain string now throws instead of silently creating markup. This is the protection the sanitizer cannot give, because it lives only in compiled bindings. - **An audit trail of trust.** Every trusted value in the app comes from a named policy, so reviewing `angular#unsafe-bypass` usage is the same job as reviewing every bypass call. - **Graceful degradation.** Browsers without Trusted Types ignore the directives; the guide notes the app keeps working and is still protected by Angular's sanitizer. ## Reading a violation A Trusted Types violation names the **sink** that rejected a plain string, such as an `innerHTML` assignment or a script `src`, and the stack trace points at the code that wrote it. Three patterns cover most Angular cases: - the stack goes through Angular's binding code and the value came from a bypass method: allow `angular#unsafe-bypass` or, better, stop bypassing; - the stack goes through the lazy-loading path when navigating to a lazy route: allow `angular#bundler`; - the stack goes through your own directive or a third-party widget: a direct DOM write that Angular never sanitized, which needs a code fix. ## Rolling it out - Configure the same header for production serving, for `ng serve` (the guide points to the `headers` option in `angular.json`) and for unit and end-to-end test runs, so violations surface before production. - Start by allowing only `angular`; every violation you see names a feature that needs a policy or a fix. - Prefer **removing** the feature over allowing an `unsafe` policy: replace a bypass with sanitized content, and replace JIT with AOT. - Third-party libraries that write to sinks need their own policies or replacement; Angular's names cover only Angular's code.

  • Why does a missing Angular policy not fail at bootstrap?
    Angular creates each policy lazily and wraps `createPolicy` in a try/catch; on failure it falls back to plain strings so the app still boots. The error only surfaces when a feature relying on that policy writes to a sink, and the browser rejects the plain string.
  • Under enforcement, does a sanitized [innerHTML] binding need angular#unsafe-bypass?
    No. Angular's HTML sanitizer returns its output through the `angular` policy, so ordinary sanitized bindings work with `trusted-types angular` alone. Only values produced by `DomSanitizer` bypass methods go through `angular#unsafe-bypass`.

The trusted-types directive is a guest list at a door that only admits wristbanded values; Angular hands out wristbands from five named booths, and if a booth is not on the list its guests are turned away at the door, not at the booth.

saying these in an interview costs you the question

  • Allowing only angular#unsafe-bypass is enough for any Angular app.
  • A missing Angular policy stops the app from bootstrapping at all.
  • Trusted Types enforcement disables Angular's own HTML sanitizer.
  • Browsers without Trusted Types support refuse to run the app.
  • Angular's policies also cover third-party libraries that write innerHTML.