{"schema":"threatline.record/2","record":{"id":"CVE-2026-101896","aliases":["GHSA-ff3f-86qr-9cv3"],"summary":"Angular Server-Side Rendering (SSR): Denial of Service via Numeric URL Matrix Parameters","details":"A denial of service (DoS) vulnerability was identified in `@angular/router` when Server-Side Rendering (SSR) is enabled on Node.js (V8).\n\nWhen `@angular/router` parses incoming request URLs, it extracts path segments, matrix parameters, and child outlets into plain JavaScript objects (`Record<string, string>`). When matrix parameter names or outlet names are numeric strings (such as `/a;990;2522`), the V8 JavaScript engine interprets them as array-indexed properties rather than named properties.\n\nUnder V8's internal property-storage heuristics, setting numeric keys on an initially empty object causes V8 to allocate a dense array backing store (`HOLEY_ELEMENTS`) sized to the maximum index rather than falling back to sparse dictionary storage. Specifically, assigning sequential or moderately large numeric keys (like `990` followed by `2522`) causes V8 to allocate a contiguous backing store of ~2,522 pointers (~20 KB to 25 KB of heap) for a single 11-byte segment.\n\nBecause each segment in a URL path allocates its own independent `parameters` object, an attacker can craft URLs with repeated numeric matrix parameters to achieve an asymmetric memory amplification factor of approximately **~350x**.\n\n### Impact\n\nSuccessful exploitation allows an unauthenticated remote attacker to exhaust the Node.js old-space heap with modest request volume, terminating the SSR worker with an unrecoverable `JavaScript heap out of memory` fatal error and causing a Denial of Service.\n\n* **High Amplification:** A single 11-byte segment (`/a;990;2522`) consumes ~20 KB–25 KB of V8 heap.\n* **Low Concurrency Required:**\n  * With 8 KB request paths (~740 segments, within default Nginx 8 KB buffer limits), as few as **12–22 concurrent requests** crash a 256 MiB–512 MiB Node.js SSR worker.\n  * With smaller 1 KB–2 KB request paths (~90–180 segments), a burst of ~50–100 concurrent requests achieves the same heap exhaustion.\n* **Client-side SPAs Unaffected:** Pure client-side Angular applications (Single Page Applications without SSR) are **not** vulnerable, as local browser memory consumption does not cross a security boundary.\n\n### Attack Preconditions & Vulnerable Configurations\n\nAn application is affected only if **all** of the following conditions are met:\n\n* **SSR Enabled:** The application runs in a Server-Side Rendering environment powered by Node.js / V8.\n* **Direct Router Parsing:** User-controlled request URLs are parsed by `@angular/router` during SSR.\n* **No Reverse-Proxy Semicolon/Segment Filtering:** Upstream reverse proxies (Nginx, Cloudflare, ALB) forward URLs containing semicolons (`;`) and multiple path segments without stripping or rejecting them.\n\n#### Exploit Payload Example\n\nAn attacker sends concurrent HTTP requests with repeated numeric matrix parameters:\n\n```http\nGET /a;990;2522/a;990;2522/a;990;2522/... HTTP/1.1\nHost: example.com\n```\n\nEven with paths under 2 KB, overlapping requests during SSR will rapidly consume the V8 heap until the process crashes.\n\n### Patches\n\nThe issue is resolved by updating `@angular/router` to enforce V8 dictionary elements storage (`setUrlDerivedKey`) for numeric URL-derived keys (index >= 32). This prevents V8 from allocating oversized contiguous array backing stores while preserving route matching, parameter values, and component input bindings.\n\n* `22.2.0`\n* `21.2.24`\n* `20.3.32`\n\n### Workarounds & Mitigations\n\nIf you cannot immediately upgrade to a patched version, apply one of the following mitigations at your edge or reverse proxy:\n\n1. **Block or Sanitize Matrix Parameters at the Reverse Proxy:**  \n   Configure your reverse proxy (e.g., Nginx, Cloudflare, or AWS WAF) to reject or strip semicolons (`;`) in request paths before forwarding requests to the Angular SSR service:\n   ```nginx\n   # Nginx example: reject requests containing matrix parameters\n   if ($uri ~* \";\") {\n       return 400;\n   }\n   ```\n2. **Enforce Strict Path Segment Limits:**  \n   Reject requests with excessive path depth (e.g., more than 20–30 segments).\n3. **Increase Node.js Old Space:**  \n   Increase `--max-old-space-size` (e.g., to 2048 or 4096 MB) to increase the concurrency threshold required to exhaust memory, though this does not fully eliminate the vulnerability under sustained traffic.","categories":["Frameworks"],"product":"@angular/router","vendor":"angular","ecosystem":"npm","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T15:40:48Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/router"},"range":">= 22.0.0, < 22.2.0"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T15:40:48Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/router"},"range":">= 21.0.0, < 21.2.24"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T15:40:48Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/router"},"range":">= 20.0.0, < 20.3.32"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T15:40:48Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/router"},"range":"<= 19.2.25"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-101896","source_modified_at":"2026-10-20T01:32:46.385Z","fetched_at":"2026-10-23T18:45:31.882637Z","product":"angular","vendor":"angular","versions":[{"status":"affected","version":"<= 19.2.25"},{"status":"affected","version":">= 20.0.0, < 20.3.32"},{"status":"affected","version":">= 21.0.0, < 21.2.24"},{"status":"affected","version":">= 22.0.0, < 22.2.0"}],"defaultStatus":null},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-101896","source_modified_at":"2026-10-20T01:32:46.385Z","fetched_at":"2026-10-23T18:45:31.882637Z","product":"router","vendor":"@angular","versions":[{"status":"affected","version":"<= 19.2.25"},{"status":"affected","version":">= 20.0.0, < 20.3.32"},{"status":"affected","version":">= 21.0.0, < 21.2.24"},{"status":"affected","version":">= 22.0.0, < 22.2.0"}],"defaultStatus":null},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T16:00:28.500151110Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"@angular/router","ecosystem":"npm","purl":"pkg:npm/%40angular/router"},"ranges":[{"type":"SEMVER","events":[{"introduced":"22.0.0"},{"fixed":"22.2.0"}]}],"versions":[]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T16:00:28.500151110Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"@angular/router","ecosystem":"npm","purl":"pkg:npm/%40angular/router"},"ranges":[{"type":"SEMVER","events":[{"introduced":"21.0.0"},{"fixed":"21.2.24"}]}],"versions":[]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T16:00:28.500151110Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"@angular/router","ecosystem":"npm","purl":"pkg:npm/%40angular/router"},"ranges":[{"type":"SEMVER","events":[{"introduced":"20.0.0"},{"fixed":"20.3.32"}]}],"versions":[]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T16:00:28.500151110Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"@angular/router","ecosystem":"npm","purl":"pkg:npm/%40angular/router"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"last_affected":"19.2.25"}]}],"versions":[]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T15:40:48Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/router"},"version":"22.2.0"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T15:40:48Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/router"},"version":"21.2.24"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T15:40:48Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/router"},"version":"20.3.32"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T16:00:28.500151110Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"@angular/router","ecosystem":"npm","purl":"pkg:npm/%40angular/router"},"version":"22.2.0"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T16:00:28.500151110Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"@angular/router","ecosystem":"npm","purl":"pkg:npm/%40angular/router"},"version":"21.2.24"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-ff3f-86qr-9cv3","source_modified_at":"2026-09-30T16:00:28.500151110Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"@angular/router","ecosystem":"npm","purl":"pkg:npm/%40angular/router"},"version":"20.3.32"}],"cvss":8.2,"severity":"High","kev":{"listed":false},"epss":{"probability":0.00686,"percentile":0.52109,"score_date":"2026-10-23"},"published_at":"2026-10-14T15:05:42.934Z","modified_at":"2026-10-20T01:32:46.385Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-ff3f-86qr-9cv3","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-09-30T15:40:48Z"},{"source":"CVE List","url":"https://www.cve.org/CVERecord?id=CVE-2026-101896","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-10-20T01:32:46.385Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-ff3f-86qr-9cv3","fetched_at":"2026-10-23T18:48:36.087640Z","source_modified_at":"2026-09-30T16:00:28.500151110Z"},{"source":"EPSS","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-101896","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23"}],"sources":["GitHub Advisory Database","CVE List","OSV","EPSS"],"references":[{"source":"CVE List","url":"https://github.com/angular/angular/security/advisories/GHSA-ff3f-86qr-9cv3","tags":["x_refsource_CONFIRM"]},{"source":"CVE List","url":"https://github.com/angular/angular/issues/70716","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/angular/pull/70717","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/angular/commit/03872a80bcf1c89b2b04cdd3f444b2ee954da583","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/angular/commit/5af61216eab8bf4a6697a1d79bda3d857c06f89d","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/angular/commit/ddfe21072ba32ca4cd9d7d3c6b7df66af81d58c4","tags":["x_refsource_MISC"]},{"url":"https://github.com/advisories/GHSA-ff3f-86qr-9cv3","source":"GitHub Advisory Database"},{"source":"CVE List","url":"https://github.com/angular/angular/releases/tag/v20.3.32","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/angular/releases/tag/v21.2.24","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/angular/angular/releases/tag/v22.2.0","tags":["x_refsource_MISC"]}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"cve_state":"PUBLISHED","ssvc":{"exploitation":"none","automatable":"yes","technical_impact":"partial","assessed_at":"2026-10-20T01:32:32.758636Z"},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"product","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-10-20T01:32:46.385Z","content_hash":"381902c01444f6c7a979c2e361ee43ad96d8be05ebc9b9c6c28222c2b53238c1"}}
