To optimize client-side bootstrap in Server-Side Rendered (SSR) environments, Angular supports **Hydration** via `provideClientHydration()`. During SSR, Angular serializes the application's runtime state (such as cached `HttpClient` responses) and outputs it into the HTML stream as a `<script>` tag with a predictable identifier: ```html <script type="application/json" id="ng-state"> {"some-api-url": {"body": ...}} </script> ```` During client bootstrap, Angular recovers this state by looking up the element via `document.getElementById('ng-state')` and parsing its text content. Because the DOM element lookup for the state container is predictable and relies solely on the ID selector (`ng-state`), it is susceptible to **DOM Clobbering**. If the application binds untrusted user input or CMS content to element properties such as `id` (e.g., `<div [id]="userInput">` or `<a id="ng-state">`) *before* the genuine `<script>` tag is parsed by the browser, the attacker-controlled element takes precedence in the DOM lookup. During hydration, when Angular calls `document.getElementById('ng-state')`, the browser returns the attacker's clobbered element. Angular then attempts to parse the text content or attributes of this clobbered element as JSON. ### Impact By clobbering the state element, the attacker can inject a custom JSON payload into Angular's `TransferState` cache. The most critical exploitation vector is poisoning the **HTTP Transfer Cache**. 1. The attacker injects a clobbered `ng-state` element containing custom JSON. 2. The JSON maps a key (representing a target API endpoint URL) to a malicious payload of the attacker's choice. 3. During client-side initialization, Angular's `HttpClient` checks `TransferState` before making requests. Finding the poisoned key, `HttpClient` returns the forged response instantly instead of requesting the genuine backend API. Depending on how the application processes and renders the affected API response, this can lead to: * **DOM-based Cross-Site Scripting (XSS)** if poisoned fields are rendered using unsafe bindings. * **Privilege Escalation** by spoofing user info or session details retrieved from poisoned API payloads. * **UI Hijacking** and redirection by spoofing configuration endpoints. ### Patched Versions * 22.0.1 * 21.2.17 * 20.3.25 ### Workarounds If you cannot immediately update to a patched Angular version, apply the following workarounds: #### A. Avoid Dynamic/User-Controlled IDs Avoid binding raw user-supplied values or dynamic CMS IDs directly to element attributes. If dynamic IDs are required, sanitize them or prepend a static safe prefix: ```html <!-- Vulnerable Pattern --> <div [id]="userControlledInput">...</div> <!-- Mitigated Pattern --> <div [id]="'safe-prefix-' + userControlledInput">...</div> ``` #### B. Configure a Custom Application ID Declaring a unique, non-predictable `APP_ID` changes the ID suffix of the state element, making it harder for attackers to predict and target: ```ts // app.config.ts import { APP_ID } from '@angular/core'; import { provideClientHydration } from '@angular/platform-browser'; export const appConfig = { providers: [ { provide: APP_ID, useValue: 'unique-obfuscated-app-id' }, provideClientHydration() ] }; ``` This changes the state element lookup ID from `ng-state` to `unique-obfuscated-app-id-state`.
To optimize client-side bootstrap in Server-Side Rendered (SSR) environments, Angular supports **Hydration** via `provideClientHydration()`. During SSR, Angular serializes the application's runtime state (such as cached `HttpClient` responses) and outputs it into the HTML stream as a `<script>` tag with a predictable identifier: ```html <script type="application/json" id="ng-state"> {"some-api-url": {"body": ...}} </script> ```` During client bootstrap, Angular recovers this state by looking up the element via `document.getElementById('ng-state')` and parsing its text content. Because the DOM element lookup for the state container is predictable and relies solely on the ID selector (`ng-state`), it is susceptible to **DOM Clobbering**. If the application binds untrusted user input or CMS content to element properties such as `id` (e.g., `<div [id]="userInput">` or `<a id="ng-state">`) *before* the genuine `<script>` tag is parsed by the browser, the attacker-controlled element takes precedence in the DOM lookup. During hydration, when Angular calls `document.getElementById('ng-state')`, the browser returns the attacker's clobbered element. Angular then attempts to parse the text content or attributes of this clobbered element as JSON. ### Impact By clobbering the state element, the attacker can inject a custom JSON payload into Angular's `TransferState` cache. The most critical exploitation vector is poisoning the **HTTP Transfer Cache**. 1. The attacker injects a clobbered `ng-state` element containing custom JSON. 2. The JSON maps a key (representing a target API endpoint URL) to a malicious payload of the attacker's choice. 3. During client-side initialization, Angular's `HttpClient` checks `TransferState` before making requests. Finding the poisoned key, `HttpClient` returns the forged response instantly instead of requesting the genuine backend API. Depending on how the application processes and renders the affected API response, this can lead to: * **DOM-based Cross-Site Scripting (XSS)** if poisoned fields are rendered using unsafe bindings. * **Privilege Escalation** by spoofing user info or session details retrieved from poisoned API payloads. * **UI Hijacking** and redirection by spoofing configuration endpoints. ### Patched Versions * 22.0.1 * 21.2.17 * 20.3.25 ### Workarounds If you cannot immediately update to a patched Angular version, apply the following workarounds: #### A. Avoid Dynamic/User-Controlled IDs Avoid binding raw user-supplied values or dynamic CMS IDs directly to element attributes. If dynamic IDs are required, sanitize them or prepend a static safe prefix: ```html <!-- Vulnerable Pattern --> <div [id]="userControlledInput">...</div> <!-- Mitigated Pattern --> <div [id]="'safe-prefix-' + userControlledInput">...</div> ``` #### B. Configure a Custom Application ID Declaring a unique, non-predictable `APP_ID` changes the ID suffix of the state element, making it harder for attackers to predict and target: ```ts // app.config.ts import { APP_ID } from '@angular/core'; import { provideClientHydration } from '@angular/platform-browser'; export const appConfig = { providers: [ { provide: APP_ID, useValue: 'unique-obfuscated-app-id' }, provideClientHydration() ] }; ``` This changes the state element lookup ID from `ng-state` to `unique-obfuscated-app-id-state`.
Update @angular/core to 22.0.1; @angular/core to 21.2.17; @angular/core to 20.3.25 if you use the affected versions. Test the change in a non-production environment first.
Local check
hol-guard supply-chain scanAngular Client Hydration DOM Clobbering & Response-Cache Poisoning affects @angular/core (npm), @angular/core (npm), @angular/core (npm), @angular/core (npm). Severity is high. To optimize client-side bootstrap in Server-Side Rendered (SSR) environments, Angular supports **Hydration** via `provideClientHydration()`. During SSR, Angular serializes the application's runtime state (such as cached `HttpClient` responses) and outputs it into the HTML stream as a `<script>` tag with a predictable identifier: ```html <script type="application/json" id="ng-state"> {"some-api-url": {"body": ...}} </script> ```` During client bootstrap, Angular recovers this state by looking up the element via `document.getElementById('ng-state')` and parsing its text content. Because the DOM element lookup for the state container is predictable and relies solely on the ID selector (`ng-state`), it is susceptible to **DOM Clobbering**. If the application binds untrusted user input or CMS content to element properties such as `id` (e.g., `<div [id]="userInput">` or `<a id="ng-state">`) *before* the genuine `<script>` tag is parsed by the browser, the attacker-controlled element takes precedence in the DOM lookup. During hydration, when Angular calls `document.getElementById('ng-state')`, the browser returns the attacker's clobbered element. Angular then attempts to parse the text content or attributes of this clobbered element as JSON. ### Impact By clobbering the state element, the attacker can inject a custom JSON payload into Angular's `TransferState` cache. The most critical exploitation vector is poisoning the **HTTP Transfer Cache**. 1. The attacker injects a clobbered `ng-state` element containing custom JSON. 2. The JSON maps a key (representing a target API endpoint URL) to a malicious payload of the attacker's choice. 3. During client-side initialization, Angular's `HttpClient` checks `TransferState` before making requests. Finding the poisoned key, `HttpClient` returns the forged response instantly instead of requesting the genuine backend API. Depending on how the application processes and renders the affected API response, this can lead to: * **DOM-based Cross-Site Scripting (XSS)** if poisoned fields are rendered using unsafe bindings. * **Privilege Escalation** by spoofing user info or session details retrieved from poisoned API payloads. * **UI Hijacking** and redirection by spoofing configuration endpoints. ### Patched Versions * 22.0.1 * 21.2.17 * 20.3.25 ### Workarounds If you cannot immediately update to a patched Angular version, apply the following workarounds: #### A. Avoid Dynamic/User-Controlled IDs Avoid binding raw user-supplied values or dynamic CMS IDs directly to element attributes. If dynamic IDs are required, sanitize them or prepend a static safe prefix: ```html <!-- Vulnerable Pattern --> <div [id]="userControlledInput">...</div> <!-- Mitigated Pattern --> <div [id]="'safe-prefix-' + userControlledInput">...</div> ``` #### B. Configure a Custom Application ID Declaring a unique, non-predictable `APP_ID` changes the ID suffix of the state element, making it harder for attackers to predict and target: ```ts // app.config.ts import { APP_ID } from '@angular/core'; import { provideClientHydration } from '@angular/platform-browser'; export const appConfig = { providers: [ { provide: APP_ID, useValue: 'unique-obfuscated-app-id' }, provideClientHydration() ] }; ``` This changes the state element lookup ID from `ng-state` to `unique-obfuscated-app-id-state`.
AI coding agents often install or upgrade packages automatically in npm. A high vulnerability in a dependency can be pulled into a project through a normal install or update without a human reviewing the change, expanding the blast radius from a single package to every agent workspace that depends on it.
| Package | Affected range | Fixed version |
|---|---|---|
| @angular/corenpm | >=22.0.0-next.0,<22.0.1 | 22.0.1 |
| @angular/corenpm | >=21.0.0-next.0,<21.2.17 | 21.2.17 |
| @angular/corenpm | >=20.0.0-next.0,<20.3.25 | 20.3.25 |
| @angular/corenpm | <=19.2.25 | Not reported |
Fixed versions are reported by the source feed; confirm compatibility before updating.
Reported by GitHub Security Advisories (ghsa).
HOL Guard can help your team review package activity against supported protection paths.
Explore HOL GuardUpdate @angular/core to 22.0.1; @angular/core to 21.2.17; @angular/core to 20.3.25 if you use the affected versions. Test the change in a non-production environment first.
Local check
hol-guard supply-chain scanAngular Client Hydration DOM Clobbering & Response-Cache Poisoning affects @angular/core (npm), @angular/core (npm), @angular/core (npm), @angular/core (npm). Severity is high. To optimize client-side bootstrap in Server-Side Rendered (SSR) environments, Angular supports **Hydration** via `provideClientHydration()`. During SSR, Angular serializes the application's runtime state (such as cached `HttpClient` responses) and outputs it into the HTML stream as a `<script>` tag with a predictable identifier: ```html <script type="application/json" id="ng-state"> {"some-api-url": {"body": ...}} </script> ```` During client bootstrap, Angular recovers this state by looking up the element via `document.getElementById('ng-state')` and parsing its text content. Because the DOM element lookup for the state container is predictable and relies solely on the ID selector (`ng-state`), it is susceptible to **DOM Clobbering**. If the application binds untrusted user input or CMS content to element properties such as `id` (e.g., `<div [id]="userInput">` or `<a id="ng-state">`) *before* the genuine `<script>` tag is parsed by the browser, the attacker-controlled element takes precedence in the DOM lookup. During hydration, when Angular calls `document.getElementById('ng-state')`, the browser returns the attacker's clobbered element. Angular then attempts to parse the text content or attributes of this clobbered element as JSON. ### Impact By clobbering the state element, the attacker can inject a custom JSON payload into Angular's `TransferState` cache. The most critical exploitation vector is poisoning the **HTTP Transfer Cache**. 1. The attacker injects a clobbered `ng-state` element containing custom JSON. 2. The JSON maps a key (representing a target API endpoint URL) to a malicious payload of the attacker's choice. 3. During client-side initialization, Angular's `HttpClient` checks `TransferState` before making requests. Finding the poisoned key, `HttpClient` returns the forged response instantly instead of requesting the genuine backend API. Depending on how the application processes and renders the affected API response, this can lead to: * **DOM-based Cross-Site Scripting (XSS)** if poisoned fields are rendered using unsafe bindings. * **Privilege Escalation** by spoofing user info or session details retrieved from poisoned API payloads. * **UI Hijacking** and redirection by spoofing configuration endpoints. ### Patched Versions * 22.0.1 * 21.2.17 * 20.3.25 ### Workarounds If you cannot immediately update to a patched Angular version, apply the following workarounds: #### A. Avoid Dynamic/User-Controlled IDs Avoid binding raw user-supplied values or dynamic CMS IDs directly to element attributes. If dynamic IDs are required, sanitize them or prepend a static safe prefix: ```html <!-- Vulnerable Pattern --> <div [id]="userControlledInput">...</div> <!-- Mitigated Pattern --> <div [id]="'safe-prefix-' + userControlledInput">...</div> ``` #### B. Configure a Custom Application ID Declaring a unique, non-predictable `APP_ID` changes the ID suffix of the state element, making it harder for attackers to predict and target: ```ts // app.config.ts import { APP_ID } from '@angular/core'; import { provideClientHydration } from '@angular/platform-browser'; export const appConfig = { providers: [ { provide: APP_ID, useValue: 'unique-obfuscated-app-id' }, provideClientHydration() ] }; ``` This changes the state element lookup ID from `ng-state` to `unique-obfuscated-app-id-state`.
AI coding agents often install or upgrade packages automatically in npm. A high vulnerability in a dependency can be pulled into a project through a normal install or update without a human reviewing the change, expanding the blast radius from a single package to every agent workspace that depends on it.
| Package | Affected range | Fixed version |
|---|---|---|
| @angular/corenpm | >=22.0.0-next.0,<22.0.1 | 22.0.1 |
| @angular/corenpm | >=21.0.0-next.0,<21.2.17 | 21.2.17 |
| @angular/corenpm | >=20.0.0-next.0,<20.3.25 | 20.3.25 |
| @angular/corenpm | <=19.2.25 | Not reported |
Fixed versions are reported by the source feed; confirm compatibility before updating.
Reported by GitHub Security Advisories (ghsa).
HOL Guard can help your team review package activity against supported protection paths.
Explore HOL Guard