Angular SSR: XSS via Unescaped Processing Instruction (<?...?>) Nodes in Fallback Raw-Content Elements
Summary
An XSS vulnerability exists in @angular/platform-server during server-side rendering (SSR) HTML serialization of ProcessingInstruction DOM nodes (<?target data?>, nodeType === 7) when nested inside fallback raw-content elements (<noscript>, <iframe>, <noembed>, <noframes>). While processing instruction data escaped > to >, it did not check for or escape matching closing tags of ancestor fallback elements (e.g., </noscript>). When rendered in a browser with scripting enabled, an unescaped closing tag sequence in a processing instruction prematurely closes the fallback raw-content tag and causes subsequent sibling elements to execute as live HTML.
Technical Description
In HTML5 parsing, fallback raw-content elements (<noscript>, <iframe>, <noembed>, <noframes>) place the browser's HTML tokenizer into RAWTEXT mode. In RAWTEXT mode, processing instruction tokens (<?...?>) are treated as literal raw text rather than bogus comments, and the parser ignores > or ?>. The only token sequence that terminates the container is an end tag matching the container tag name (</noscript, </iframe, etc.).
During server-side HTML serialization, processing instruction nodes previously only replaced > with > (preventing bogus comment breakouts in normal HTML data states) but left < untouched. Crucially, processing instruction serialization never inspected ancestor fallback raw-content tags. As a result, if a ProcessingInstruction node inside <noscript> contained </noscript in its data payload, it was emitted unescaped as <?x </noscript ?>.
Impact & Reachability
- Reachability: Processing instruction nodes cannot be authored directly through standard Angular template syntax (which parses
<?...>into comment nodes in DOM position). Reaching this vulnerability requires application or library code callinginject(DOCUMENT).createProcessingInstruction(target, data)orRenderer2DOM insertion methods with untrusted user input passed todatainside a fallback raw-content container. - Impact: In applications that programmatically construct processing instruction nodes inside fallback elements during server-side rendering, an attacker controlling the processing instruction data can break out of the container and execute arbitrary JavaScript in victims' browsers.
Proof of Concept (Minimal Reproduction)
import { Component, ElementRef, Renderer2, inject, DOCUMENT, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
template: `<noscript id="host"></noscript>`
})
export class AppComponent implements AfterViewInit {
private r = inject(Renderer2);
private el = inject(ElementRef);
private doc = inject(DOCUMENT);
ngAfterViewInit() {
const host = this.el.nativeElement.querySelector('#host');
// Attacker-controlled input passed as Processing Instruction data
const pi = this.doc.createProcessingInstruction('x', '</noscript ');
this.r.appendChild(host, pi);
// Sibling markup that should remain inert inside <noscript>
const img = this.r.createElement('img');
this.r.setAttribute(img, 'src', 'x');
this.r.setAttribute(img, 'onerror', 'alert("SSR_PI_XSS")');
this.r.appendChild(host, img);
}
}
Vulnerable SSR Output:
<noscript><?x </noscript ?><img src="x" onerror="alert('SSR_PI_XSS')"></noscript>
Workarounds
- Avoid passing untrusted user input into
document.createProcessingInstruction(target, data)when the node is inserted into<noscript>,<iframe>,<noembed>, or<noframes>during server-side rendering. - Manually sanitize or replace
<with<in any untrusted data passed to processing instruction nodes on the server.
Software afectado
Haz clic en una tecnología para ver todas sus vulnerabilidades.
| Plataforma | Paquete | Versiones vulnerables | Corregida en |
|---|---|---|---|
| Paquete de npm | @angular/platform-server | >= 22.0.0, < 22.1.4, >= 21.0.0, < 21.2.22, >= 20.0.0, < 20.3.30, <= 19.2.25 | 22.1.4, 21.2.22, 20.3.30 |
Referencias
- https://github.com/angular/angular/security/advisories/GHSA-j3r3-mxqp-r2p4
- https://nvd.nist.gov/vuln/detail/CVE-2026-88058
- https://github.com/angular/angular/issues/70146
- https://github.com/angular/angular/commit/73d8bbd27cb46495426d4132975a1355b47ad915
- https://github.com/angular/angular/commit/89b20568dfaee1ec8e0b3bcf1872acdddd2f4fef
- https://github.com/angular/angular/commit/ba3bc47b20b3d12f5eb141ec9c651373ae4d15e8
- https://github.com/angular/domino/commit/04f987dc08ff3736b427f50941adf1722458528f
- https://github.com/angular/angular
- https://github.com/angular/angular/releases/tag/v20.3.30
- https://github.com/angular/angular/releases/tag/v21.2.22
- https://github.com/angular/angular/releases/tag/v22.1.4
- Vector CVSS
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N- Debilidad (CWE)
CWE-116, CWE-79- Plataforma
- npm
- Paquete
@angular/platform-server- Publicada
- 28.09.2026
- Actualizado
- 11.10.2026 10:22
Fuentes y licencias
OSV / GitHub Advisory Database - GHSA-j3r3-mxqp-r2p4 - CC BY 4.0
Historial de vulnerabilidades: @angular/platform-server
Todas las vulnerabilidades publicadas de este paquete (Paquete de npm), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.
- Entradas en total
- 12
- Primera → última
- 09.2025 10.2026
- Sin corrección
- 0
- Explotada en ataques
- 0
Distribución por gravedad
- Alta 11
- Media 1
Divulgaciones por año
- 2025: 1
- 2026: 11
-
Angular SSR: XSS via Unescaped Processing Instruction (<?...?>) Nodes in Fallback Raw-Content Elements esta entrada
Identifica las vulnerabilidades de tu aplicación
Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.