The Angular compiler is a security control, not just a build tool — it decides which template bindings get sanitized and which get treated as trusted, so a defect in it can quietly turn a normally safe binding into a stored XSS sink. Most teams think of @angular/compiler-cli as the thing that turns their .html templates and decorators into JavaScript during ng build. That is true, but the compiler also bakes in Angular's security context model: at compile time it classifies each binding — is this an HTML context, a URL, a resource URL, a style? — and wires up the runtime sanitizer accordingly. When that classification is wrong, the sanitizer never runs, and untrusted data reaches the DOM verbatim.
What the Angular Compiler Actually Does at Build Time
The Angular compiler (via the ngc binary shipped in @angular/compiler-cli) performs ahead-of-time (AOT) compilation. It parses templates, resolves component metadata, checks types across template expressions, and emits Ivy instructions. Two of those steps matter for security.
First, template type checking catches a class of bugs before runtime — passing the wrong type into a binding, referencing a property that does not exist, or misusing an async pipe. Enabling strictTemplates in tsconfig.json under angularCompilerOptions turns these from silent runtime surprises into build failures.
Second, and more importantly, the compiler assigns a security context to every bound attribute and property. Angular's built-in DomSanitizer then strips dangerous content from interpolated values based on that context. Text interpolation like {{userComment}} is escaped. A binding to [innerHTML] is sanitized as HTML. A binding to [href] is sanitized as a URL. This is why plain Angular apps resist XSS by default — but the default only holds if the compiler's internal schema correctly recognizes the attribute.
The CVE-2025-66412 Stored XSS: When the Schema Was Incomplete
In late 2025, CVE-2025-66412 demonstrated exactly what happens when that internal schema has a gap. The vulnerability, rated CVSS v4 7.6 (High), was a stored cross-site scripting flaw in Angular's template compiler. The root cause: certain URL-bearing SVG and MathML attributes were not classified as needing strict sanitization, so JavaScript URLs bound to them bypassed Angular's sanitizer.
A related part of the same issue affected SVG animation elements — animate, set, animateMotion, and animateTransform. The attributeName property on these elements was not validated, letting an attacker target sensitive attributes like href or xlink:href on other elements. When untrusted data was bound into those attributes, the compiler could fall back to a non-sanitizing context, opening the door to arbitrary script injection.
The affected ranges were broad: all versions up through 18.2.14, the 19.x line before 19.2.17, the 20.x line before 20.3.15, and the 21.x line before 21.0.2. The fix shipped in the patched @angular/compiler releases. If your package.json pins an older Angular, this is a concrete reason to upgrade — not a theoretical one.
Until you can upgrade, the practical mitigation is to avoid dynamic bindings on SVG animation and script elements entirely, and to validate any dynamic URL server-side before it ever reaches a template. Do not lean on bypassSecurityTrustUrl or its siblings as a workaround; those methods exist to disable sanitization and are the last thing you want near attacker-controlled data.
Keeping @angular/compiler-cli Patched
Because the compiler defines your sanitization contract, its version is a security-relevant fact. Angular ships @angular/compiler, @angular/compiler-cli, and the rest of the framework packages as a coordinated set — they should all sit on the same minor version. A common mistake is bumping @angular/core while a resolution or an override holds angular/compiler-cli back, leaving you on a compiler that predates a security patch.
Check what you actually resolved, not what you asked for:
npm ls @angular/compiler @angular/compiler-cli
Then upgrade the whole framework together with the official tooling rather than editing versions by hand:
ng update @angular/core @angular/cli
ng update runs migration schematics and keeps the packages in lockstep, which is safer than a manual npm install @angular/core@latest that can leave the compiler stranded. Commit the resulting package-lock.json so CI installs the same tree you tested.
Where Software Composition Analysis Fits
A vulnerable compiler is a dependency problem, and dependency problems are exactly what software composition analysis exists to catch. The Angular framework packages are almost always transitive relative to your build output, and a version buried in the lockfile is easy to miss during a manual review. An SCA tool such as Safeguard can flag a compiler version inside a known-vulnerable range against advisory data, so CVE-2025-66412 becomes a one-line finding in your pipeline instead of something you discover after a pentest.
Pair that with lockfile discipline. Use npm ci in CI so the exact resolved tree is installed, and treat a new advisory against @angular/compiler-cli the same way you would treat one against your web server — as a patch to schedule, not a notification to dismiss.
Compiler Options That Harden Your Templates
Beyond staying patched, a few angularCompilerOptions flags reduce the blast radius of template mistakes:
{
"angularCompilerOptions": {
"strictTemplates": true,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true
}
}
strictTemplates gives you full type checking inside templates, which catches many binding errors that would otherwise surface at runtime. It will not stop a sanitizer-bypass CVE — that lives inside the compiler itself — but it narrows the space of accidental unsafe bindings your own team can introduce.
The broader habit worth building: treat every point where you write raw HTML or a raw URL into the DOM as a decision that needs justification. If you find yourself reaching for DomSanitizer.bypassSecurityTrust*, stop and ask whether the input is truly under your control. Most XSS in Angular apps comes not from framework bugs but from developers opting out of the protection the compiler set up for them.
FAQ
Is @angular/compiler-cli a production dependency?
No — it is a build-time tool and normally lives in devDependencies. It compiles your templates during ng build and does not ship to the browser. That said, its version still matters for security, because the sanitization behavior it emits is what runs in production.
How do I know if my Angular app is affected by CVE-2025-66412?
Run npm ls @angular/compiler and compare the resolved version against the affected ranges: up to 18.2.14, 19.x before 19.2.17, 20.x before 20.3.15, and 21.x before 21.0.2. If you are below the patched release for your line, upgrade with ng update. Apps that never bind dynamic data into SVG animation or URL attributes were at lower practical risk, but patching is the correct fix.
Does strictTemplates prevent XSS?
Not directly. strictTemplates is a type-safety feature that catches binding and type errors at build time. XSS protection in Angular comes from the compiler's security context classification and the runtime DomSanitizer. Both matter, but they solve different problems.
Should I ever use bypassSecurityTrustHtml?
Only for content you fully control and have already sanitized yourself, and even then sparingly. These methods disable Angular's sanitizer on purpose. Passing attacker-influenced data through them is the most common way teams reintroduce XSS into an otherwise-safe Angular app. When in doubt, don't.