Packages / @angular/platform-server / Security
Angular SSR: Denial of Service (DoS) via Infinite Loop on Malformed DOCTYPE
Affected releases · @angular/platform-server
- 22.x22.0.0 – 22.1.5Fixed in 22.1.6
- 21.x21.0.0 – 21.2.22Fixed in 21.2.23
- 20.x20.0.0 – 20.3.30Fixed in 20.3.31
- 19.x19.0.0 – 19.2.25No fix on this line yet
- 18.x and older2.0.0 – 18.2.14No fix, no longer updated
On 22.x, the newest line:
npm install @angular/platform-server@22.1.6Affected
22.0.0 – 22.1.5
15 releases
Fixed
22.1.6 – 22.2.3
7 releases, latest
Affected
21.0.0 – 21.2.22
40 releases
Fixed
21.2.23 – 21.2.25
3 releases
Affected
20.0.0 – 20.3.30
52 releases
Fixed
20.3.31 – 20.3.34
4 releases
Affected
19.0.0 – 19.2.25
43 releases
Affected
18.0.0 – 18.2.14
29 releases
Affected
17.0.0 – 17.3.12
32 releases
Affected
16.0.0 – 16.2.12
30 releases
Affected
15.0.0 – 15.2.10
22 releases
Affected
14.0.0 – 14.3.0
26 releases
Affected
13.0.0 – 13.4.0
30 releases
Affected
12.0.0 – 12.2.17
30 releases
Affected
11.0.0 – 11.2.14
28 releases
Affected
10.0.0 – 10.2.5
28 releases
Affected
9.0.0 – 9.1.13
22 releases
Affected
8.0.0 – 8.2.14
23 releases
Affected
7.0.0 – 7.2.16
27 releases
Affected
6.0.0 – 6.1.10
20 releases
Affected
5.0.0 – 5.2.11
22 releases
Affected
4.0.0 – 4.4.7
29 releases
Affected
2.0.0 – 2.4.10
24 releases
Is your project affected?
Paste your package.json, or type the range you declare for @angular/platform-server. It is read in your browser and sent nowhere.
What happens
A Denial of Service (DoS) vulnerability exists in @angular/platform-server's DOM emulation parser (domino). When processing untrusted user input containing an incomplete DOCTYPE declaration ending with whitespace before EOF (such as <!DOCTYPE html ), the HTML parser enters an infinite synchronous loop, pegging CPU utilization at 100% and completely freezing the Node.js server process.
Technical Description
In Angular Server-Side Rendering (SSR), @angular/platform-server uses domino to parse and sanitize HTML bound through template bindings (such as [innerHTML]) or manipulated via DOM APIs.
In Domino's HTML parser (lib/HTMLParser.js), tokenizer states that specify fixed lookahead—such as after_doctype_name_state (lookahead = 6)—rely on the state handler function to explicitly advance the character index pointer (nextchar). While branches for whitespace, >, and keyword matching advance nextchar, the EOF branch (case -1: // EOF) emitted doctype and EOF tokens without advancing nextchar or transitioning out of the state:
case -1: // EOF forcequirks(); emitDoctype(); emitEOF(); break;
Because nextchar remained unchanged pointing to the EOF marker character (\uFFFF), the scanner loop (while (nextchar < numchars)) repeatedly re-invoked after_doctype_name_state with codepoint = EOF indefinitely. In Node.js's single-threaded runtime, this synchronous loop starves the event loop entirely.
Impact & Reachability
Reachability: The vulnerability is reachable in any Angular SSR application where untrusted user input is bound to
[innerHTML], interpolated into markup, or sanitized on the server.Impact: Successful exploitation allows an unauthenticated remote attacker to cause an immediate Denial of Service (DoS) by sending a payload containing an incomplete DOCTYPE (e.g.,
<!DOCTYPE html). The Node.js SSR process locks up at 100% CPU and ceases responding to all concurrent and subsequent HTTP requests.
Proof of Concept:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
template: `<div [innerHTML]="payload"></div>`,
})
export class AppComponent {
// Attacker-controlled input containing an incomplete DOCTYPE ending with whitespace
payload = '<!DOCTYPE html ';
}Workarounds
Avoid binding untrusted user input directly to
[innerHTML]in server-rendered templates; use standard text interpolation ({{ userInput }}) or[textContent]when raw HTML rendering is not required.Validate or sanitize user input before passing it to
[innerHTML]on the server by stripping or rejecting strings matching/^<!DOCTYPE/i.