Sep 29, 2026·9 min read·3 visits
An unauthenticated attacker can achieve arbitrary JavaScript execution in a victim's browser by injecting closing tags into ProcessingInstruction nodes processed during server-side rendering, exploiting an escaping discrepancy in Angular's DOM emulator.
A high-severity Cross-Site Scripting (XSS) vulnerability in Angular server-side rendering (SSR) component allows unauthenticated attackers to execute arbitrary client-side JavaScript. The flaw is caused by a parsing discrepancy between the server-side DOM emulator, Domino, and standard client-side browser HTML5 parsers. When serializing ProcessingInstruction nodes inside raw-content fallback elements, Domino fails to escape matching ancestor closing tags, causing the client-side parser to transition out of raw-text mode prematurely and execute subsequent sibling elements as active HTML.
Angular Server-Side Rendering (SSR) utilizes the @angular/platform-server package to bootstrap and execute applications on a server-side environment before delivering the finalized HTML to the client browser. To emulate standard browser Document Object Model (DOM) behaviors on the server, the framework integrates Domino, a lightweight server-side DOM implementation. During the lifecycle of an SSR request, Domino manages the virtual DOM and eventually serializes the fully constructed tree into a single raw HTML output string. This output string is then delivered over the network to the victim browser, which parses it as standard HTML5 markup.
The critical vulnerability identified as CVE-2026-88058 arises from a context-dependent parsing discrepancy between the server-side Domino serializer and the client-side browser tokenizer. The flaw manifests when processing fallback raw-content elements, including <noscript>, <iframe>, <noembed>, and <noframes>. Specifically, Domino's HTML serialization engine fails to properly escape or sanitize ProcessingInstruction DOM nodes (nodeType === 7) when they are nested inside these raw-content containers. If an attacker controls the data payload of these nodes, they can craft an injection that forces the client browser to prematurely terminate the raw-content container and interpret subsequent text as active HTML.
While standard Angular template compilers restrict the creation of arbitrary processing instructions, application-specific code or third-party libraries may bypass these constraints. Developers often utilize DOM-manipulation techniques, such as injecting raw-content fragments or programmatically creating nodes via inject(DOCUMENT).createProcessingInstruction(). If the data supplied to these APIs is derived from untrusted user input without thorough server-side sanitization, an attacker can manipulate the generated DOM tree structure. Consequently, the resulting serialization output allows for full Cross-Site Scripting (XSS) vulnerabilities inside the browser environment.
To understand the technical cause of CVE-2026-88058, it is necessary to examine the HTML5 specification tokenizer states for raw-content container tags. When a standard browser parser encounters raw-content container tags such as <noscript>, it transitions its internal tokenizer into the RAWTEXT state. In this specialized state, the parser does not recognize standard HTML tags, delimiters, or elements like comments and processing instructions. Instead, it reads all characters as literal text until it encounters a case-insensitive matching end tag sequence, such as </noscript>. This design ensures that raw code block contents are not inadvertently interpreted as active HTML markup.
Conversely, when Domino acts as the server-side DOM emulator, it processes processing instruction nodes as distinct DOM components, irrespective of their parent elements. During serialization, Domino converts these nodes into processing instruction strings bounded by <? and ?> characters. Because Domino's original implementation only escaped the greater-than character (>) to > inside the data field of these instructions, it left less-than characters (<) unmodified. This incomplete escaping mechanism becomes problematic when a processing instruction containing a sequence like </noscript is nested inside a <noscript> container.
When the client-side browser receives the serialized HTML, it starts tokenizing the container in the RAWTEXT state. Because it is in RAWTEXT mode, the browser ignores the starting boundary of the processing instruction (<?) and continues consuming data until it parses the nested </noscript sequence. According to the HTML5 specification, the trailing space in </noscript satisfies the end tag token requirement, prompting the tokenizer to exit RAWTEXT mode and return to the default Data state. The subsequent characters, including any attacker-controlled element like a malicious image tag, are then interpreted as live HTML elements, resulting in arbitrary code execution.
The vulnerability in Domino is located within the node serialization logic, specifically in the serializeOne() function of the lib/NodeUtils.js module. In older versions of the library, the serializer handled processing instruction nodes (case 7) by applying a basic escaping function to the node data before outputting the serialized tag. The function escapeProcessingInstructionContent() only converted > to >, ignoring < characters. Because the serializer did not examine the tag's ancestors, it could not determine whether the node was located within a raw-content container that would trigger RAWTEXT parsing in the browser.
Furthermore, the vulnerability was compounded by an architectural limitation in how Domino parsed nested document fragments. When Angular compiled elements within a DOCUMENT_FRAGMENT_NODE (such as inside <template> contexts), the fragments did not maintain a functional parentNode relationship linking back to their parent elements. This caused Domino's upward tree-climbing function, fallbackRawContentTags(), to fail when attempting to resolve ancestors past the fragment boundaries. As a result, processing instructions nested inside template fragments escaped detection, preventing any context-aware serialization from occurring.
To resolve this issue, the maintainers modified several core modules in Domino. In lib/htmlelts.js, the HTMLTemplateElement constructor was updated to define a hidden _host property that references the template parent element, allowing fragments to preserve a path to their host. In lib/NodeUtils.js, the fallbackRawContentTags() function was rewritten to detect the _host property on DOCUMENT_FRAGMENT_NODE instances and traverse upward past fragment boundaries. Finally, the serializer in serializeOne() was patched to check if processing instruction content contains the </ sequence; if present, the engine dynamically resolves all raw-content ancestors and escapes matching closing tags using the escapeMatchingClosingTag() function.
// Fragment of the patched serializeOne function inside Domino:
case 7: // PROCESSING_INSTRUCTION_NODE
let content = escapeProcessingInstructionContent(kid.data);
if (content.includes('</')) {
// Resolve all active raw content fallback tags up the DOM chain
const fallbackTags = fallbackRawContentTags(parent);
for (const fallbackTag of fallbackTags) {
// Escape any occurrences of matching closing sequences
content = escapeMatchingClosingTag(content, fallbackTag);
}
}
s += '<?' + kid.target + ' ' + content + '?>';
break;Exploitation of CVE-2026-88058 requires an environment where an attacker can supply input to a programmatically generated ProcessingInstruction node. The vulnerability cannot be triggered via standard static Angular template definitions, which do not natively support processing instruction compilation. Instead, the attacker targets custom application components, dynamic page-rendering engines, or third-party libraries that use the Renderer2 API or DOCUMENT token to construct DOM nodes dynamically. The attack flow begins when the server accepts un-sanitized input, such as a query parameter or header, and assigns it to a newly created processing instruction.
The attacker crafts a payload that includes a container breakout sequence followed by a malicious HTML element, such as </noscript><img src=x onerror=alert(1)>. On the server, the application executes standard SSR rendering routines, creating a <noscript> container and nesting the processing instruction inside it. When Domino serializes the virtual DOM tree, it outputs the sequence <noscript><?x </noscript ?><img src="x" onerror="alert(1)"></noscript>. Note that because the payload contains no > characters before the breakout tag, the legacy serializer emits the sequence unmodified, failing to neutralize the <noscript closing sequence.
When the victim loads the resulting web page, their browser begins parsing the server-rendered HTML document. The browser encounters the <noscript> tag and enters RAWTEXT parsing mode, treating the subsequent content as literal character data. However, as soon as the tokenizer parses the </noscript sequence, it recognizes a valid container-ending token and exits RAWTEXT mode. The browser then treats the trailing ?><img src="x" onerror="alert(1)"> as standard HTML markup, executing the JavaScript payload within the security context of the victim's session.
The impact of CVE-2026-88058 is classified as high because successful exploitation leads to full Cross-Site Scripting (XSS) in the victim's browser context. An attacker who successfully exploits this vulnerability can execute arbitrary JavaScript code within the session of any user who visits the rendered page. This capability permits the unauthorized extraction of session identifiers, authentication tokens, and sensitive data stored in local storage or cookies. Additionally, the attacker can perform unauthorized actions on behalf of the victim, such as initiating transactions or modifying account settings.
Because the vulnerability occurs during Server-Side Rendering, the malicious payload is delivered directly within the initial HTML payload sent by the server. This transmission mechanism can bypass some basic client-side detection vectors, as the payload is parsed and executed immediately during the initial page-render lifecycle. According to the CVSS v4.0 metrics, this flaw has been assigned a base score of 8.6, reflecting the high confidentiality and integrity impact on the client-side system. The threat landscape is further influenced by the low attack complexity and the lack of authentication requirements needed to deliver the exploit payload.
While the EPSS score indicates a relatively low probability of active exploitation in the wild, the vulnerability remains a significant threat to applications utilizing Angular SSR. Since Angular applications often process highly sensitive business logic, an XSS exploit can compromise administrative interfaces and orchestrate broader lateral movement attacks. Security teams must treat the issue with high priority, especially in applications that render untrusted user data within custom server-side layout managers.
The primary remediation for CVE-2026-88058 is to upgrade @angular/platform-server and the underlying domino package to patched versions. The vulnerability has been resolved in the Angular release tracks 20.x, 21.x, and 22.x, with specific secure releases designated as 20.3.30, 21.2.22, and 22.1.4 respectively. Organizations must update their package manifests and rebuild their applications to apply the patches. Additionally, developers should audit their package lockfiles to verify that the transitive domino dependency resolves to a version incorporating the fix committed in the upstream repository.
If immediate upgrading is not feasible due to legacy constraints or deployment cycles, development teams should implement strict input validation on all data passed to DOM-generation APIs. Specifically, any string supplied to createProcessingInstruction() or custom DOM manipulation routines must be validated to prevent the inclusion of HTML delimiter characters. Implementing a robust Content Security Policy (CSP) provides an additional layer of defense by restricting inline script execution and limiting the impact of any successful injection. A restrictive CSP can block unauthorized script execution even if an attacker successfully injects elements into the page markup.
Finally, development teams must conduct systematic code reviews to identify patterns where raw DOM APIs are used in server-side contexts. Security auditors should pay special attention to sections of code where the DOCUMENT injection token or Renderer2 are used to construct processing instructions or fallback raw-content elements. Ensuring that all dynamic content is bound using standard Angular data-binding patterns, which automatically apply contextual escaping, significantly reduces the application's attack surface.
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| Product | Affected Versions | Fixed Version |
|---|---|---|
@angular/platform-server Angular | >= 20.0.0, < 20.3.30 | 20.3.30 |
@angular/platform-server Angular | >= 21.0.0, < 21.2.22 | 21.2.22 |
@angular/platform-server Angular | >= 22.0.0, < 22.1.4 | 22.1.4 |
domino Angular | < 2.1.7 | 2.1.7 (Commit 04f987dc08ff3736b427f50941adf1722458528f) |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS Base Score | 8.6 |
| EPSS Score | 0.00875 |
| Impact | Arbitrary Client-Side Code Execution (XSS) |
| Exploit Status | poc |
| KEV Status | Not Listed |
| Target Component | Domino HTML Serializer inside @angular/platform-server |
The application does not neutralize or incorrectly neutralizes user-controlled input before it is placed in output that is used as a web page that is served to other users.
ZohoCorp ManageEngine EventLog Analyzer and Log360 before build 13071 were vulnerable to a denial-of-service (DoS) vulnerability that allowed unauthenticated remote attackers to crash the log collector service using malformed syslog packets.
A critical HTTP request/response smuggling vulnerability (CWE-444) exists in Citrix NetScaler ADC and Citrix NetScaler Gateway. This flaw arises from inconsistent request boundary parsing between NetScaler appliances and backend web servers, allowing remote, unauthenticated attackers to bypass security boundaries, access restricted resources, or hijack active user sessions on multiplexed TCP connections.
A validation bypass vulnerability exists in the npm package `ip-address` from version 10.2.0 to 10.5.1. The library's `Address6.isPrivate()` classifier fails to recognize the NAT64 local-use prefix range 64:ff9b:1::/48 as a restricted, private subnet. In networks implementing NAT64 routing configurations, an attacker can exploit this flaw to execute Server-Side Request Forgery (SSRF) and bypass local trust-boundary validations.
A validation bypass exists in the ip-address library prior to version 10.5.1. The Address6.isLinkLocal() method inaccurately restricted link-local classifications to the fe80::/64 subnet, failing to cover the complete RFC 4291 fe80::/10 allocation. This allows attackers to bypass SSRF filters relying on this library to safeguard local network boundaries.
An interpretation conflict in the fast-uri library allows unauthenticated remote attackers to bypass Server-Side Request Forgery filters due to inconsistent handling of malformed bracket notation in hostnames.
An authority injection vulnerability exists in the serialization components of fast-uri (versions before 2.4.6, 3.1.7, and 4.1.4) where unvalidated port components can contain authority delimiters (such as '@'). This results in host demotion to userinfo, redirection of traffic to an arbitrary attacker-controlled host, and downstream Server-Side Request Forgery (SSRF) without causing parser errors in standard clients.