VYPR
High severityOSV Advisory· Published Nov 26, 2025· Updated Apr 15, 2026

CVE-2025-66035

CVE-2025-66035

Description

Angular is a development platform for building mobile and desktop web applications using TypeScript/JavaScript and other languages. Prior to versions 19.2.16, 20.3.14, and 21.0.1, there is a XSRF token leakage via protocol-relative URLs in angular HTTP clients. The vulnerability is a Credential Leak by App Logic that leads to the unauthorized disclosure of the Cross-Site Request Forgery (XSRF) token to an attacker-controlled domain. Angular's HttpClient has a built-in XSRF protection mechanism that works by checking if a request URL starts with a protocol (http:// or https://) to determine if it is cross-origin. If the URL starts with protocol-relative URL (//), it is incorrectly treated as a same-origin request, and the XSRF token is automatically added to the X-XSRF-TOKEN header. This issue has been patched in versions 19.2.16, 20.3.14, and 21.0.1. A workaround for this issue involves avoiding using protocol-relative URLs (URLs starting with //) in HttpClient requests. All backend communication URLs should be hardcoded as relative paths (starting with a single /) or fully qualified, trusted absolute URLs.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
@angular/commonnpm
>= 21.0.0-next.0, < 21.0.121.0.1
@angular/commonnpm
>= 20.0.0-next.0, < 20.3.1420.3.14
@angular/commonnpm
< 19.2.1619.2.16

Affected products

1
  • Range: 10.0.0-next.0, 10.0.0-next.1, 10.0.0-next.2, …

Patches

3
0276479e7d0e

fix(http): prevent XSRF token leakage to protocol-relative URLs

https://github.com/angular/angularAlan AgiusNov 25, 2025via ghsa
2 files changed · +23 3
  • packages/common/http/src/xsrf.ts+6 3 modified
    @@ -82,11 +82,15 @@ export class HttpXsrfCookieExtractor implements HttpXsrfTokenExtractor {
       }
     }
     
    +/**
    + * Regex to match absolute URLs, including protocol-relative URLs.
    + */
    +const ABSOLUTE_URL_REGEX = /^(?:https?:)?\/\//i;
    +
     export function xsrfInterceptorFn(
       req: HttpRequest<unknown>,
       next: HttpHandlerFn,
     ): Observable<HttpEvent<unknown>> {
    -  const lcUrl = req.url.toLowerCase();
       // Skip both non-mutating requests and absolute URLs.
       // Non-mutating requests don't require a token, and absolute URLs require special handling
       // anyway as the cookie set
    @@ -95,8 +99,7 @@ export function xsrfInterceptorFn(
         !inject(XSRF_ENABLED) ||
         req.method === 'GET' ||
         req.method === 'HEAD' ||
    -    lcUrl.startsWith('http://') ||
    -    lcUrl.startsWith('https://')
    +    ABSOLUTE_URL_REGEX.test(req.url)
       ) {
         return next(req);
       }
    
  • packages/common/http/test/xsrf_spec.ts+17 0 modified
    @@ -70,6 +70,23 @@ describe('HttpXsrfInterceptor', () => {
         expect(req.request.headers.has('X-XSRF-TOKEN')).toEqual(false);
         req.flush({});
       });
    +
    +  it('does not apply XSRF protection when request is absolute', () => {
    +    interceptor
    +      .intercept(new HttpRequest('POST', 'https://example.com/test', {}), backend)
    +      .subscribe();
    +    const req = backend.expectOne('https://example.com/test');
    +    expect(req.request.headers.has('X-XSRF-TOKEN')).toBeFalse();
    +    req.flush({});
    +  });
    +
    +  it('does not apply XSRF protection when request is protocol relative', () => {
    +    interceptor.intercept(new HttpRequest('POST', '//example.com/test', {}), backend).subscribe();
    +    const req = backend.expectOne('//example.com/test');
    +    expect(req.request.headers.has('X-XSRF-TOKEN')).toBeFalse();
    +    req.flush({});
    +  });
    +
       it('does not overwrite existing header', () => {
         interceptor
           .intercept(
    
05fe6686a97f

fix(http): prevent XSRF token leakage to protocol-relative URLs

https://github.com/angular/angularAlan AgiusNov 25, 2025via ghsa
2 files changed · +23 3
  • packages/common/http/src/xsrf.ts+6 3 modified
    @@ -82,11 +82,15 @@ export class HttpXsrfCookieExtractor implements HttpXsrfTokenExtractor {
       }
     }
     
    +/**
    + * Regex to match absolute URLs, including protocol-relative URLs.
    + */
    +const ABSOLUTE_URL_REGEX = /^(?:https?:)?\/\//i;
    +
     export function xsrfInterceptorFn(
       req: HttpRequest<unknown>,
       next: HttpHandlerFn,
     ): Observable<HttpEvent<unknown>> {
    -  const lcUrl = req.url.toLowerCase();
       // Skip both non-mutating requests and absolute URLs.
       // Non-mutating requests don't require a token, and absolute URLs require special handling
       // anyway as the cookie set
    @@ -95,8 +99,7 @@ export function xsrfInterceptorFn(
         !inject(XSRF_ENABLED) ||
         req.method === 'GET' ||
         req.method === 'HEAD' ||
    -    lcUrl.startsWith('http://') ||
    -    lcUrl.startsWith('https://')
    +    ABSOLUTE_URL_REGEX.test(req.url)
       ) {
         return next(req);
       }
    
  • packages/common/http/test/xsrf_spec.ts+17 0 modified
    @@ -70,6 +70,23 @@ describe('HttpXsrfInterceptor', () => {
         expect(req.request.headers.has('X-XSRF-TOKEN')).toEqual(false);
         req.flush({});
       });
    +
    +  it('does not apply XSRF protection when request is absolute', () => {
    +    interceptor
    +      .intercept(new HttpRequest('POST', 'https://example.com/test', {}), backend)
    +      .subscribe();
    +    const req = backend.expectOne('https://example.com/test');
    +    expect(req.request.headers.has('X-XSRF-TOKEN')).toBeFalse();
    +    req.flush({});
    +  });
    +
    +  it('does not apply XSRF protection when request is protocol relative', () => {
    +    interceptor.intercept(new HttpRequest('POST', '//example.com/test', {}), backend).subscribe();
    +    const req = backend.expectOne('//example.com/test');
    +    expect(req.request.headers.has('X-XSRF-TOKEN')).toBeFalse();
    +    req.flush({});
    +  });
    +
       it('does not overwrite existing header', () => {
         interceptor
           .intercept(
    
3240d856d942

fix(http): prevent XSRF token leakage to protocol-relative URLs

https://github.com/angular/angularAlan AgiusNov 25, 2025via ghsa
2 files changed · +23 3
  • packages/common/http/src/xsrf.ts+6 3 modified
    @@ -91,11 +91,15 @@ export abstract class HttpXsrfTokenExtractor {
       abstract getToken(): string | null;
     }
     
    +/**
    + * Regex to match absolute URLs, including protocol-relative URLs.
    + */
    +const ABSOLUTE_URL_REGEX = /^(?:https?:)?\/\//i;
    +
     export function xsrfInterceptorFn(
       req: HttpRequest<unknown>,
       next: HttpHandlerFn,
     ): Observable<HttpEvent<unknown>> {
    -  const lcUrl = req.url.toLowerCase();
       // Skip both non-mutating requests and absolute URLs.
       // Non-mutating requests don't require a token, and absolute URLs require special handling
       // anyway as the cookie set
    @@ -104,8 +108,7 @@ export function xsrfInterceptorFn(
         !inject(XSRF_ENABLED) ||
         req.method === 'GET' ||
         req.method === 'HEAD' ||
    -    lcUrl.startsWith('http://') ||
    -    lcUrl.startsWith('https://')
    +    ABSOLUTE_URL_REGEX.test(req.url)
       ) {
         return next(req);
       }
    
  • packages/common/http/test/xsrf_spec.ts+17 0 modified
    @@ -71,6 +71,23 @@ describe('HttpXsrfInterceptor', () => {
         expect(req.request.headers.has('X-XSRF-TOKEN')).toEqual(false);
         req.flush({});
       });
    +
    +  it('does not apply XSRF protection when request is absolute', () => {
    +    interceptor
    +      .intercept(new HttpRequest('POST', 'https://example.com/test', {}), backend)
    +      .subscribe();
    +    const req = backend.expectOne('https://example.com/test');
    +    expect(req.request.headers.has('X-XSRF-TOKEN')).toBeFalse();
    +    req.flush({});
    +  });
    +
    +  it('does not apply XSRF protection when request is protocol relative', () => {
    +    interceptor.intercept(new HttpRequest('POST', '//example.com/test', {}), backend).subscribe();
    +    const req = backend.expectOne('//example.com/test');
    +    expect(req.request.headers.has('X-XSRF-TOKEN')).toBeFalse();
    +    req.flush({});
    +  });
    +
       it('does not overwrite existing header', () => {
         interceptor
           .intercept(
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

9

News mentions

0

No linked articles in our index yet.