Which @angular/* package(s) are the source of the bug?
core, platform-server
Is this a regression?
No known unaffected version; I did not identify an introduction commit.
Description
An Angular SSR application that displays user-submitted rich text through a normal [innerHTML] binding can spend several seconds synchronously parsing a small, deeply nested HTML article. The server-side sanitizer creates a Domino template, assigns the untrusted HTML to template.innerHTML, and reads it back during its stability check. With 8,000 well-nested <div> levels, the pinned Domino parser overflows the stack in recursivelySetOwner().
The reproduction accepts an unauthenticated Markdown submission (184 KB, below its 1 MiB limit). Marked 18.0.11 preserves the raw HTML, and an ordinary AngularNodeAppEngine request renders the stored article. It does not use bypassSecurityTrustHtml, a private parser API, modified Angular packages, or direct server state changes. optimization.styles.inlineCritical is set to false to isolate this sanitizer path from critical-CSS processing.
Expected: sanitization either renders or rejects an over-deep article without a long synchronous worker stall.
Actual: the article GET returns HTTP 200 with an empty article after a stack error; another request arriving at the same single-worker server during parsing waits behind it. The stored article triggers the same error on later GETs. The Node process stays alive.
In a fresh Linux arm64 container with Node 24.21.0 and released Angular 22.2.0 packages:
| Input (fresh worker each) |
Article GET |
Unrelated GET started 2 s later |
Outcome |
| 184 KB shallow, same byte count |
644 ms |
14 ms |
Article rendered; no stack error |
| 184 KB, 8,000 levels plus inner children, run 1 |
10,025 ms |
8,017 ms |
Empty article; RangeError |
| Same nested input, run 2 |
10,097 ms |
8,091 ms |
Empty article; RangeError |
This demonstrates a repeatable single-worker availability problem and article render failure. It does not establish a process crash or multiworker service outage. The application must accept raw HTML in lower-trust content and render it on SSR; applications that strip raw HTML, limit depth, or do not SSR that content are outside this path.
Please provide a link to a minimal reproduction of the bug
https://github.com/BlueIridium-Security/angular-ssr-nested-html-worker-stall-repro
The repository has a pinned package lock and a one-command Docker validator:
docker build -t angular-ssr-nested-html-repro .
docker run --rm angular-ssr-nested-html-repro
The validator starts a new server for each case, sends the article through HTTP, measures a concurrent unrelated request, checks the empty-vs-rendered article, and requires the Domino stack error in both nested runs.
Please provide the exception or error you saw
RangeError: Maximum call stack size exceeded
The generated server log maps this stack to Domino's recursive owner assignment. The request is caught by the SSR renderer, which returns a 200 page with an empty article.
Please provide the environment you discovered this bug in
Angular core / platform-server / SSR: 22.2.0
Angular CLI / build: 22.2.0
Marked: 18.0.11
Node: 24.21.0
npm: 11.19.0
OS: Linux arm64 (node:24.21.0-bookworm-slim container)
Anything else?
The relevant source path is _sanitizeHtml() reading inert innerHTML, InertDocumentHelper assigning the template, and Domino's recursive recursivelySetOwner(). The framework source path remains present on the current official main commit 7d96a37af4f7b7dd9dc9a2b9b90cc043c7da2a31; the reproduction uses released packages so it can be installed independently.
This is separate from #70926, whose mis-nested formatting input grows through active-formatting reconstruction and produces a heap OOM. This input uses only well-nested div elements and overflows Domino's owner-assignment recursion. Critical-CSS inlining is disabled, so the critical-CSS scanner is not involved.
Which @angular/* package(s) are the source of the bug?
core,platform-serverIs this a regression?
No known unaffected version; I did not identify an introduction commit.
Description
An Angular SSR application that displays user-submitted rich text through a normal
[innerHTML]binding can spend several seconds synchronously parsing a small, deeply nested HTML article. The server-side sanitizer creates a Dominotemplate, assigns the untrusted HTML totemplate.innerHTML, and reads it back during its stability check. With 8,000 well-nested<div>levels, the pinned Domino parser overflows the stack inrecursivelySetOwner().The reproduction accepts an unauthenticated Markdown submission (184 KB, below its 1 MiB limit). Marked 18.0.11 preserves the raw HTML, and an ordinary
AngularNodeAppEnginerequest renders the stored article. It does not usebypassSecurityTrustHtml, a private parser API, modified Angular packages, or direct server state changes.optimization.styles.inlineCriticalis set tofalseto isolate this sanitizer path from critical-CSS processing.Expected: sanitization either renders or rejects an over-deep article without a long synchronous worker stall.
Actual: the article GET returns HTTP 200 with an empty article after a stack error; another request arriving at the same single-worker server during parsing waits behind it. The stored article triggers the same error on later GETs. The Node process stays alive.
In a fresh Linux arm64 container with Node 24.21.0 and released Angular 22.2.0 packages:
RangeErrorRangeErrorThis demonstrates a repeatable single-worker availability problem and article render failure. It does not establish a process crash or multiworker service outage. The application must accept raw HTML in lower-trust content and render it on SSR; applications that strip raw HTML, limit depth, or do not SSR that content are outside this path.
Please provide a link to a minimal reproduction of the bug
https://github.com/BlueIridium-Security/angular-ssr-nested-html-worker-stall-repro
The repository has a pinned package lock and a one-command Docker validator:
docker build -t angular-ssr-nested-html-repro . docker run --rm angular-ssr-nested-html-reproThe validator starts a new server for each case, sends the article through HTTP, measures a concurrent unrelated request, checks the empty-vs-rendered article, and requires the Domino stack error in both nested runs.
Please provide the exception or error you saw
The generated server log maps this stack to Domino's recursive owner assignment. The request is caught by the SSR renderer, which returns a 200 page with an empty article.
Please provide the environment you discovered this bug in
Anything else?
The relevant source path is
_sanitizeHtml()reading inertinnerHTML,InertDocumentHelperassigning the template, and Domino's recursiverecursivelySetOwner(). The framework source path remains present on the current officialmaincommit7d96a37af4f7b7dd9dc9a2b9b90cc043c7da2a31; the reproduction uses released packages so it can be installed independently.This is separate from #70926, whose mis-nested formatting input grows through active-formatting reconstruction and produces a heap OOM. This input uses only well-nested
divelements and overflows Domino's owner-assignment recursion. Critical-CSS inlining is disabled, so the critical-CSS scanner is not involved.