{"schema":"threatline.record/2","record":{"id":"CVE-2026-111754","aliases":["GHSA-w739-gvwx-grc3"],"summary":"@angular/platform-server: Open Redirect via Protocol-Relative URLs in Server-Side Rendering (SSR)","details":"An open redirect vulnerability was identified in `@angular/platform-server` when Server-Side Rendering (SSR) is enabled.\n\nWhen Angular SSR renders an incoming request, URL resolution and platform navigation are managed by `ServerPlatformLocation` using `resolveUrl` (or `parseUrl` in v20). While `resolveUrl` rejected protocol-relative URLs whose raw string directly started with `//`, this check ran *prior* to WHATWG URL normalization.\n\nWhen a request URL includes a popping dot-segment followed by multiple slashes (such as `/.//evil.test` or serialized from router route shapes like `/.;/(//evil.test)`), the input bypassed the initial `urlStr.startsWith('//')` check. Subsequent WHATWG URL parsing resolved the dot-segments and resulted in a `pathname` starting with `//` (`//evil.test`).\n\nConsequently, `ServerPlatformLocation` stored `pathname = '//evil.test'`. When `@angular/ssr` detected a mismatch between the rendered URL and the router's final location state, it emitted the unnormalized pathname into the HTTP `Location` response header, producing an HTTP 302 redirect with `Location: //evil.test`, which browsers resolve as a protocol-relative navigation off-origin.\n\n### Impact\n\nSuccessful exploitation allows an unauthenticated remote attacker to craft URLs that trick Angular SSR into redirecting users to an arbitrary external domain.\n\n* **Bypasses Application-Level Guards:** Common application-level validation routines (such as checking `!returnUrl.startsWith('//')` before invoking `router.navigateByUrl`) accept `/.//evil.test` or `/.;/(//evil.test)` as a safe relative path, allowing attackers to bypass redirect allowlists.\n* **Client-side SPAs Unaffected:** Pure client-side Angular applications (Single Page Applications without SSR) are **not** vulnerable, as `ServerPlatformLocation` is only utilized in server-side execution environments.\n\n### Attack Preconditions & Vulnerable Configurations\n\nAn application is affected if the following conditions are met:\n\n* **SSR Enabled:** The application runs in a Server-Side Rendering environment using `@angular/platform-server` and `@angular/ssr`.\n* **Parameter Route Shapes:** The router contains parameter routes with wildcard or child segments (e.g. `/:tenant` matching `.` and serializing secondary outlet groups `(//evil.test)` to `/.//evil.test`), or passes user-provided navigation targets (e.g. `returnUrl`).\n* **Reverse Proxy Passthrough:** Upstream reverse proxies (Nginx, Cloudflare, ALB) pass semicolons (`;`) or dot-segments without stripping or normalizing them.\n\n#### Exploit Payload Example\n\nAn attacker sends a request targeting a route with parameters and child segments:\n\n```http\nGET /.;/(//evil.test) HTTP/1.1\nHost: example.com\n```\n\n**Response:**\n```http\nHTTP/1.1 302 Found\nLocation: //evil.test\n```\n\nThe client browser resolves `//evil.test` protocol-relatively and redirects the user away from `example.com` to `evil.test`.\n\n### Patches\n\nThe issue is resolved by updating `resolveUrl` (and `parseUrl` in v20) in `@angular/platform-server` to verify that the normalized URL pathname does not start with `//` when `allowProtocolRelative` is `false`.\n\n* `22.2.1`\n* `21.2.25`\n* `20.3.33`\n\n### Workarounds & Mitigations\n\nIf you cannot immediately upgrade to a patched version, apply one of the following mitigations at your edge reverse proxy or WAF:\n\n1. **Reject Paths with Dot-Segments Followed by Slashes:**\n   Configure your reverse proxy (e.g., Nginx, Cloudflare, AWS WAF) to block requests containing `/.//`, `/..//`, or `/.;/`:\n   ```nginx\n   # Nginx example: reject suspicious path normalization sequences\n   if ($request_uri ~* \"(\\n/\\.\\/|\\/\\.\\;)\") {\n       return 400;\n   }\n   ```\n2. **Sanitize Redirect Headers:**\n   Ensure edge proxies or API gateways rewrite or disallow protocol-relative (`//`) `Location` response headers originating from the SSR upstream.","categories":["Frameworks"],"product":"@angular/platform-server","vendor":"","ecosystem":"npm","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:35:51Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"range":">= 22.0.0, < 22.2.1"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:35:51Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"range":">= 21.0.0, < 21.2.25"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:35:51Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"range":">= 20.0.0, < 20.3.33"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:35:51Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"range":"<= 19.2.25"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:45:03.771349618Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"ranges":[{"type":"SEMVER","events":[{"introduced":"22.0.0"},{"fixed":"22.2.1"}]}],"versions":[]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:45:03.771349618Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"ranges":[{"type":"SEMVER","events":[{"introduced":"21.0.0"},{"fixed":"21.2.25"}]}],"versions":[]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:45:03.771349618Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"ranges":[{"type":"SEMVER","events":[{"introduced":"20.0.0"},{"fixed":"20.3.33"}]}],"versions":[]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:45:03.771349618Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"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-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:35:51Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"version":"22.2.1"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:35:51Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"version":"21.2.25"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:35:51Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@angular/platform-server"},"version":"20.3.33"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:45:03.771349618Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"version":"22.2.1"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:45:03.771349618Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"version":"21.2.25"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-w739-gvwx-grc3","source_modified_at":"2026-10-20T14:45:03.771349618Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"name":"@angular/platform-server","ecosystem":"npm","purl":"pkg:npm/%40angular/platform-server"},"version":"20.3.33"}],"cvss":5.1,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-20T14:35:49Z","modified_at":"2026-10-20T14:45:03.771349618Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-w739-gvwx-grc3","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-20T14:35:51Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-w739-gvwx-grc3","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-20T14:45:03.771349618Z"}],"sources":["GitHub Advisory Database","OSV"],"references":[{"url":"https://github.com/angular/angular/security/advisories/GHSA-w739-gvwx-grc3","source":"GitHub Advisory Database"},{"url":"https://github.com/angular/angular/pull/71077","source":"GitHub Advisory Database"},{"url":"https://github.com/angular/angular/pull/71078","source":"GitHub Advisory Database"},{"url":"https://github.com/angular/angular/pull/71079","source":"GitHub Advisory Database"},{"url":"https://github.com/angular/angular/commit/450af9c813c7660b9bd91e5635f705ee3ed7b32a","source":"GitHub Advisory Database"},{"url":"https://github.com/angular/angular/commit/83944fc8c50e86be90e8ec4184cd15002b5af262","source":"GitHub Advisory Database"},{"url":"https://github.com/angular/angular/commit/928dcfd2582152655b5559509d057bf6a5a19521","source":"GitHub Advisory Database"},{"url":"https://github.com/angular/angular/commit/d7d81e189d227108c3b9d39178dbb7389f6f970d","source":"GitHub Advisory Database"},{"url":"https://github.com/angular/angular/releases/tag/v20.3.33","source":"GitHub Advisory Database"},{"url":"https://github.com/angular/angular/releases/tag/v21.2.25","source":"GitHub Advisory Database"},{"url":"https://github.com/angular/angular/releases/tag/v22.2.1","source":"GitHub Advisory Database"},{"url":"https://github.com/advisories/GHSA-w739-gvwx-grc3","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"product","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-10-20T14:45:03.771349618Z","content_hash":"edac968555e69c1daa08e9fcc4dd301c258fdd649e19c6cdc70ba502c94dde23"}}
