fix(platform-server): avoid stripping unicode whitespace during url resolution

Avoid trimming urlStr with String.prototype.trim() in resolveUrl to ensure URL parsing and resolution align with the WHATWG URL standard.
This commit is contained in:
Alan Agius
2026-08-25 11:27:58 +00:00
committed by Leon Senft
parent 7596548e9b
commit 3e924cc8db
3 changed files with 28 additions and 2 deletions
-2
View File
@@ -57,8 +57,6 @@ export function resolveUrl(
return originUrl || null;
}
urlStr = urlStr.trim();
// Fast-path: if the URL is a valid, standard absolute URL, parse and return it immediately.
let resolved: URL | undefined;
try {
@@ -1582,6 +1582,17 @@ class HiddenModule {}
});
});
it('should resolve non-breaking space prefixed URLs as relative paths on the same origin', async () => {
ref.injector.get(NgZone).run(() => {
http.get('\u00A0//attacker.example/collect').subscribe((body) => {
expect(body).toEqual('success!');
});
mock
.expectOne('http://localhost:4000/%C2%A0//attacker.example/collect')
.flush('success!');
});
});
it('should reject backslash bypass SSRF attempts in relative requests and throw a suspicious origin error', async () => {
const badUrls = [
'/\\attacker.com',
+17
View File
@@ -68,6 +68,22 @@ describe('resolveUrl', () => {
const url = 'ht\ntp://evil.com/path';
expect(() => resolveUrl(url, 'http://test.com')).toThrowError(/NG05703/);
});
it('should not trim unicode whitespace into protocol-relative URLs', () => {
const urls = ['\u00A0//attacker.example/collect', '\uFEFF//attacker.example/collect'];
for (const urlStr of urls) {
const urlWithProtocolRelative = resolveUrl(urlStr, 'http://test.com', {
allowProtocolRelative: true,
});
expect(urlWithProtocolRelative.origin).toBe('http://test.com');
expect(urlWithProtocolRelative.pathname).toContain('//attacker.example/collect');
const urlWithoutProtocolRelative = resolveUrl(urlStr, 'http://test.com');
expect(urlWithoutProtocolRelative.origin).toBe('http://test.com');
expect(urlWithoutProtocolRelative.pathname).toContain('//attacker.example/collect');
}
});
});
describe('without origin', () => {
@@ -76,6 +92,7 @@ describe('resolveUrl', () => {
expect(resolveUrl('deep/path')).toBeNull();
expect(resolveUrl('/\\attacker.com/deep/path')).toBeNull();
expect(resolveUrl('\\\\attacker.com/deep/path')).toBeNull();
expect(resolveUrl('\u00A0//attacker.com/deep/path')).toBeNull();
});
it('should parse valid absolute URLs', () => {