Skip to content

Commit 05fe668

Browse files
alan-agius4kirjs
authored andcommitted
fix(http): prevent XSRF token leakage to protocol-relative URLs
The XSRF interceptor previously failed to detect protocol-relative URLs (starting with `//`) as absolute URLs. This allowed requests to such URLs to include the XSRF token, potentially leaking it to external domains. This change updates the interceptor to correctly identify protocol-relative URLs as absolute and exclude them from receiving the XSRF token.
1 parent 6409044 commit 05fe668

File tree

2 files changed

+23
-3
lines changed

2 files changed

+23
-3
lines changed

packages/common/http/src/xsrf.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,15 @@ export class HttpXsrfCookieExtractor implements HttpXsrfTokenExtractor {
8282
}
8383
}
8484

85+
/**
86+
* Regex to match absolute URLs, including protocol-relative URLs.
87+
*/
88+
const ABSOLUTE_URL_REGEX = /^(?:https?:)?\/\//i;
89+
8590
export function xsrfInterceptorFn(
8691
req: HttpRequest<unknown>,
8792
next: HttpHandlerFn,
8893
): Observable<HttpEvent<unknown>> {
89-
const lcUrl = req.url.toLowerCase();
9094
// Skip both non-mutating requests and absolute URLs.
9195
// Non-mutating requests don't require a token, and absolute URLs require special handling
9296
// anyway as the cookie set
@@ -95,8 +99,7 @@ export function xsrfInterceptorFn(
9599
!inject(XSRF_ENABLED) ||
96100
req.method === 'GET' ||
97101
req.method === 'HEAD' ||
98-
lcUrl.startsWith('http://') ||
99-
lcUrl.startsWith('https://')
102+
ABSOLUTE_URL_REGEX.test(req.url)
100103
) {
101104
return next(req);
102105
}

packages/common/http/test/xsrf_spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,23 @@ describe('HttpXsrfInterceptor', () => {
7070
expect(req.request.headers.has('X-XSRF-TOKEN')).toEqual(false);
7171
req.flush({});
7272
});
73+
74+
it('does not apply XSRF protection when request is absolute', () => {
75+
interceptor
76+
.intercept(new HttpRequest('POST', 'https://example.com/test', {}), backend)
77+
.subscribe();
78+
const req = backend.expectOne('https://example.com/test');
79+
expect(req.request.headers.has('X-XSRF-TOKEN')).toBeFalse();
80+
req.flush({});
81+
});
82+
83+
it('does not apply XSRF protection when request is protocol relative', () => {
84+
interceptor.intercept(new HttpRequest('POST', '//example.com/test', {}), backend).subscribe();
85+
const req = backend.expectOne('//example.com/test');
86+
expect(req.request.headers.has('X-XSRF-TOKEN')).toBeFalse();
87+
req.flush({});
88+
});
89+
7390
it('does not overwrite existing header', () => {
7491
interceptor
7592
.intercept(

0 commit comments

Comments
 (0)