Angular sanitizes bindings per security context; what does it do with an untrusted string bound to [innerHTML], [style], an anchor's [href] and an iframe's [src]?
answer
- chosen from element plus property
- decided at compile time
- HTML and URL get sanitized
- style passes through
- resource URL cannot be sanitized
basics
~20 sEach binding gets a security context at compile time. HTML is sanitized, style passes through unchanged, a javascript: URL gets an unsafe: prefix while other URLs pass, and a plain string bound to an iframe src throws.
solid answer
~40 sThe template compiler looks up each element-and-property pair in Angular's security schema and attaches the matching sanitizer to the binding. `[innerHTML]` is the **HTML** context: the value is parsed inertly and reduced to allow-listed elements and attributes. `[style]` is the **style** context, but current Angular passes the string through unchanged; its guide says only HTML and URLs are sanitized. `<a [href]>` is the **URL** context: the current URL sanitizer lets any scheme except `javascript:` through and prefixes a rejected value with `unsafe:`. `<iframe [src]>` is the **resource URL** context, meaning a URL loaded as code or as a document, which cannot be made safe by inspection, so a plain string throws `unsafe value used in a resource URL context` and only a `SafeResourceUrl` is accepted.
code
ts · 17 linesimport {Component} from '@angular/core';
@Component({
selector: 'app-context-demo',
template: `
<div [innerHTML]="html"></div> <!-- HTML: script and onclick stripped -->
<div [style]="css"></div> <!-- style: string applied unchanged -->
<a [href]="link">open</a> <!-- URL: becomes unsafe:javascript:... -->
<iframe [src]="frameUrl"></iframe> <!-- resource URL: throws for a plain string -->
`,
})
export class ContextDemo {
html = '<p onclick="steal()">Hi</p><script>steal()</script>';
css = 'color: crimson';
link = 'javascript:steal()';
frameUrl = 'https://example.com/embed/42';
}go deeper
Recall the four contexts Angular documents, HTML, style, URL and resource URL, and that iframe src is the strict one.
Explain what each context does to a plain string: allow-list HTML, pass style through, prefix unsafe: on a javascript: URL, throw for a resource URL, all chosen at compile time.
Show you can read an error such as 'unsafe value used in a resource URL context' or 'Required a safe URL, got a HTML' and trace it to the binding and the wrapper that caused it.
Weigh relying on the built-in URL rule, which only blocks javascript:, against an app-level allow-list of schemes and origins for user-supplied links and embeds.
## Why Angular needs more than one context **Sanitization** means inspecting an untrusted value and turning it into something safe to put in the DOM. What counts as safe depends on where the value lands: a string that is harmless as text can be dangerous as a link target, and a URL that is fine for an image is not fine as the source of a frame. Angular therefore defines **security contexts** and applies a different rule in each. The `SecurityContext` enum in `@angular/core` lists `NONE`, `HTML`, `STYLE`, `SCRIPT`, `URL`, `RESOURCE_URL` and an internal `ATTRIBUTE_NO_BINDING`. ## How a binding gets its context The choice is made **at compile time**, not by inspecting the value: 1. The template compiler sees a property or attribute binding, for example `[href]` or `[attr.href]` on an `<a>`. 2. It looks the element and property up in the **security schema**, a hand-maintained table that ships with Angular. 3. It emits the binding instruction together with the matching sanitizer function; a binding in the `NONE` context gets no sanitizer at all. Because the decision is structural, the same string can be safe in one binding and rejected in the next: `'https://example.com/page'` passes untouched through `<a [href]>` yet throws in `<iframe [src]>`. It also means the context cannot be fooled by the value's content or by how it is spelled; what matters is the element and the property it is written to. Host bindings declared on directives and components go through the same lookup. The schema is short and deliberately conservative: | Context | Example bindings in the schema | | :--- | :--- | | HTML | `innerHTML`, `outerHTML` on any element; `srcdoc` on `iframe` | | Style | `style` on any element | | URL | `a[href]`, `area[href]`, `form[action]`, `formAction`, `img[src]`, `video[src]` | | Resource URL | `iframe[src]`, `embed[src]`, `frame[src]`, `object[data]`, `link[href]`, `base[href]` | ## What happens in each context - **HTML**: the value is parsed into an inert document and re-serialized keeping only allow-listed elements and attributes. Scripts, event-handler attributes and `style` attributes disappear, and URLs inside kept attributes are run through the URL rule. - **Style**: the context still exists and still checks the type of a trusted wrapper, but a plain string is **passed through unchanged**. The security guide states that Angular sanitizes untrusted values for HTML and URLs, and the style context is not among them. - **URL**: the current sanitizer accepts any scheme except `javascript:`, and relative URLs. A rejected value is not dropped; it is rewritten with an `unsafe:` prefix, so `javascript:alert(1)` becomes `unsafe:javascript:alert(1)` and the browser treats it as an unknown scheme. In development mode Angular logs `WARNING: sanitizing unsafe URL value`. - **Resource URL**: this is a URL whose target is **loaded and executed or rendered** as code or a document. No inspection of the string can prove the target is harmless, so Angular does not try: a plain string **throws** a runtime error, `unsafe value used in a resource URL context`. Only a value created with `DomSanitizer.bypassSecurityTrustResourceUrl` is accepted. - **Script**: exists in the enum, but no template binding lands in it, because the template parser removes `<script>` elements from templates. ## Trusted wrappers and the context check A value produced by a `bypassSecurityTrust*` method carries its context. When it reaches a binding, Angular checks that the wrapper matches: a `SafeHtml` bound to `[href]` throws `Required a safe URL, got a HTML`. The one allowed crossover is a `SafeResourceUrl` in a URL context, because a resource URL is strictly more trusted. ## A related rule: attributes that cannot be bound Some iframe attributes that change its security posture, such as `sandbox`, `allow` and `referrerPolicy`, are in the `ATTRIBUTE_NO_BINDING` category: Angular throws if you bind one, neutralizing that iframe by clearing its `src` and removing it, and the error tells you to set the attribute statically instead. ## Interview traps - Saying Angular **sanitizes CSS** in `[style]`: current Angular does not; it only type-checks trusted wrappers there. - Saying a bad `[href]` is **removed**: it is kept with an `unsafe:` prefix. - Saying an iframe `[src]` is **sanitized like `href`**: it is not sanitized at all; a plain string fails loudly. - Saying the context is chosen by **looking at the value**: it is chosen from the element and property when the template is compiled.
- Why can Angular sanitize an href but not an iframe src?An `href` is dangerous mainly through its scheme, so rejecting `javascript:` removes the executable case. An iframe `src` loads a whole document from wherever it points, and nothing in the string tells you whether that document is hostile. Since no inspection can make it safe, Angular requires an explicit `SafeResourceUrl` instead of guessing.
- Does a data: URL bound to <a [href]> pass Angular's current URL sanitizer?Yes. The current rule only rejects `javascript:` and otherwise accepts any scheme, so `data:` and custom schemes pass unchanged. If an app must restrict links to `https:` or to its own origin, that is an application-level check before binding, not something the sanitizer does for you.
saying these in an interview costs you the question
- Angular inspects each value at runtime to decide its security context.
- Current Angular strips dangerous CSS from values bound to [style].
- A javascript: URL bound to [href] is removed from the element.
- An iframe [src] is sanitized the same way as an anchor [href].
- Any SafeValue is accepted in any context once it has been trusted.