Skip to content

chore(deps): update dependency @angular/router to v22.2.1 [security] - autoclosed - #275

Closed
renovate[bot] wants to merge 1 commit into
masterfrom
renovate/npm-angular-router-vulnerability
Closed

renovate[bot] wants to merge 1 commit into
masterfrom
renovate/npm-angular-router-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
@angular/router (source) 22.2.0 → 22.2.1 age confidence

@​angular/router: Denial of Service via Unmatched Empty-Path Outlet Route Matching in Server-Side Rendering (SSR)

CVE-2026-111752 / GHSA-62vg-58rm-qff7

More information

Details

A denial of service (DoS) vulnerability was identified in @angular/router when Server-Side Rendering (SSR) is enabled on Node.js (V8).

During route recognition, @angular/router fails to validate whether an auxiliary outlet segment in the URL matches a route configured for that specific outlet name. Because unconfigured empty-path outlet groups (such as a:/(), x:/(...), or ///(...)) are not rejected during matching, the router repeatedly re-evaluates the route configuration against each arbitrary outlet segment provided in the URL.

When a request URL contains multiple arbitrary empty-path outlet segments, this improper matching forces the router to instantiate duplicate ActivatedRouteSnapshot trees, repeatedly execute canMatch guards and route resolvers, and allocate separate copies of route parameters and queryParams for each unconfigured outlet.

An unauthenticated remote attacker can exploit this behavior by sending requests with crafted outlet segments to exhaust the Node.js memory heap or overwhelm CPU resources, leading to SSR worker termination and service disruption.

Impact

Successful exploitation allows an unauthenticated remote attacker to exhaust the Node.js heap with a single crafted request or a small number of concurrent requests, terminating the SSR worker process with a fatal JavaScript heap out-of-memory crash. When the matched routes declare canMatch guards or resolvers, a single short request can also trigger repeated guard or resolver executions, increasing load on backend services and holding worker connections open.

  • Transient Allocation: Memory is released once route recognition completes; there is no persistent memory leak. The primary impact is process termination.
  • Client-side SPAs Unaffected: Client-side Angular applications running in the browser are not vulnerable, as browser memory consumption does not cross a security boundary.
  • Prerendering (SSG) Unaffected: Build-time prerendering does not process attacker-supplied request URLs.
Attack Preconditions & Vulnerable Configurations

An application is affected only if all of the following conditions are met:

  1. SSR Enabled: The application renders on demand on a Node.js / V8 server.
  2. Susceptible Route Configuration: At least one level of the route configuration contains either:
    • an empty-path (path: "") route (e.g., {path: "", component: ShopPage}), or
    • a route assigned to a named outlet (e.g., {path: "dashboard", children: [{path: ":id", outlet: "detail", component: DetailPanel}]}).
  3. Upstream Request Line Forwarding: Request lines reach the Node.js process without being rejected upstream by edge proxies. Node.js accepts request headers up to 16 KiB by default. Guard/resolver amplification requires only a short URL.
  4. Constrained Heap Size: Worker heaps are sized small enough relative to the request payload and concurrency (e.g., containers or serverless instances configured with 128 MiB–1 GiB).
Exploit Payload Examples

Against a configuration with empty-path (path: "") routes and query parameters:

GET /shop/(a:/()//b:/()//c:/()//...)?param1=value&param2=value... HTTP/1.1
Host: example.com

Against a configuration with named-outlet or sibling routes:

GET /dashboard/(detail:1//x:/(detail:1//x:/(...)//y:/(...))//y:/(...)) HTTP/1.1
Host: example.com
Patches

The issue is resolved in angular/angular#71055. The router now requires every child outlet segment group to match a route configured for its target outlet before unwrapping child segments or completing a match, and prevents unconfigured empty-path outlet groups from matching routes declared for other outlets.

Patched versions:

  • 22.2.1
  • 21.2.25
  • 20.3.33

Versions prior to v20 are also affected but are no longer supported and will not receive a patch.

Workarounds & Mitigations

If you cannot immediately upgrade to a patched version, apply one or more of the following mitigations:

  1. Reject Parenthesized or Empty-Path Outlet URLs in Middleware or WAF (Recommended):
    In @angular/router, auxiliary outlet groups in a URL always use parentheses (...), and empty-path outlet groups in a URL always take the form :/( , (/ , or ///( .

    • If your application does not use named (auxiliary) outlets in URLs, reject requests whose path contains ( or ) before Angular SSR handles the request.
    • If your application does use named outlets, reject requests whose path contains empty-path outlet sequences (:/( , (/ , or ///( ).

    Unlike URL length limits, this completely blocks both heap exhaustion and short-URL guard/resolver amplification:

    // server.ts (placed before Angular SSR handler)
    app.use((req, res, next) => {
      let pathname: string;
      try {
        pathname = decodeURI(req.url.split(/[?#]/, 1)[0]);
      } catch {
        res.status(400).send("Bad Request");
        return;
      }
    
      // Option A: If the app does not use named outlets in URLs, block all parenthesized groups:
      if (/[()]/.test(pathname)) {
        res.status(400).send("Bad Request");
        return;
      }
    
      // Option B: If the app uses named outlets, block only empty-path outlet groups:
      if (/:\/\(|\(\//\(|\/\/\/\(/.test(pathname)) {
        res.status(400).send("Bad Request");
        return;
      }
    
      next();
    });
  2. Limit URL Length at Reverse Proxy or Middleware:
    Configure your edge proxy, web application firewall (WAF), or reverse proxy to reject requests with excessively long URLs (e.g., exceeding 2,048 bytes). The outlet portion of the payload is in the path, so limit the full request target, not only the query string. This reduces, but does not eliminate, guard/resolver amplification, which requires only a short URL.

  3. Increase Node.js Old Space:
    Increasing --max-old-space-size raises the request size and concurrency required to exhaust memory, though it does not eliminate the issue.

Severity

  • CVSS Score: 8.2 / 10 (High)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


@​angular/router: Denial of Service via RouterLink Query Parameter Retention in Server-Side Rendering (SSR)

CVE-2026-111751 / GHSA-57xq-rjx2-v5xh

More information

Details

A denial of service (DoS) vulnerability was identified in @angular/router when Server-Side Rendering (SSR) is enabled on Node.js (V8) and RouterLink directives are rendered with queryParamsHandling: 'merge' or 'preserve', configured either per link or globally via defaultQueryParamsHandling.

Starting in v21.2.0 (angular/angular#66181), RouterLink internals were refactored to use signals, introducing a computed signal (_urlTree) that cached the returned UrlTree instance on the directive. Because each rendered link maintains an active RouterLink instance for the entire duration of the request lifecycle, the computed signal permanently pinned the UrlTree and its associated query parameter dictionary in the heap until the SSR response completed.

When query parameter handling is set to 'merge' or 'preserve', each link copies the incoming request's query parameters into its own UrlTree. During SSR, memory consumption scales multiplicatively:

$$\text{Heap Retention} \approx (\text{Number of affected links}) \times (\text{Size of parsed query parameter map})$$

Because parsed JavaScript objects and property dictionaries require substantially more heap space than the raw query string—particularly when property names trigger dense array allocations under V8 property-storage heuristics—an attacker can supply oversized query strings to amplify heap consumption by orders of magnitude per in-flight request.

Impact

Successful exploitation allows an unauthenticated remote attacker to exhaust the Node.js old-space heap with modest concurrent request volume, terminating the SSR worker with an unrecoverable JavaScript heap out of memory fatal crash.

  • Numeric Names Not Required: Numeric query parameter names trigger larger V8 property-storage allocations (~558 KB per parsed dictionary in tests vs. ~197 KB for non-numeric equivalents), lowering the concurrency threshold needed for a crash. However, non-numeric query parameters also produce significant heap amplification and can exhaust worker memory with proportionally higher concurrency.
  • Client-side SPAs Unaffected: Pure client-side Angular applications (Single Page Applications without SSR) are not vulnerable, as local browser memory consumption does not cross a security boundary.
Attack Preconditions & Vulnerable Configurations

An application is affected only if all of the following conditions are met:

  1. SSR Enabled: The application runs in a Server-Side Rendering environment powered by Node.js / V8.
  2. Merged or Preserved Query Parameters: RouterLink elements rendered during SSR use queryParamsHandling: 'merge' or 'preserve', either bound explicitly on links or configured application-wide via withRouterConfig({ defaultQueryParamsHandling: 'merge' }).
  3. Upstream Proxy Forwards Long Request Lines: Reverse proxies or load balancers (e.g., Nginx, AWS ALB, Cloudflare) forward long query strings to the Node.js application rather than rejecting them at the edge. (For example, Nginx's default 8 KB large_client_header_buffers limit rejects 16 KB request lines unless explicitly increased).
  4. Overlapping Renders: Because retained memory is freed once each request completes, multiple concurrent requests must be processed simultaneously in the same Node.js worker instance (such as pages awaiting asynchronous data fetching, route resolvers, or external API calls during SSR).
Exploit Payload Example

An attacker issues concurrent HTTP requests containing dense query parameters to an SSR page that renders multiple merged links:

GET /products?0&17&34&51&68&85&...&48042 HTTP/1.1
Host: example.com
Patches

The issue is resolved by removing persistent UrlTree retention from RouterLink (angular/angular#70953). RouterLink now tracks route dependencies directly in reactiveHref and discards intermediate UrlTree instances immediately after computing the serialized href string. The urlTree getter and click handlers compute UrlTree instances on demand.

As a result, intermediate query parameter maps are short-lived allocations collected during young-generation GC scavenges rather than remaining pinned in the heap for the lifetime of the request, restoring the memory profile of Angular 21.1 and earlier.

Patched versions:

  • 22.2.1
  • 21.2.25

Versions prior to 21.2.0 (including Angular 20.x LTS and 21.1.x) do not retain UrlTree instances in RouterLink and are not vulnerable to this issue.

Workarounds & Mitigations

If you cannot immediately upgrade to a patched version, apply one or more of the following mitigations:

  1. Avoid 'merge' and 'preserve' on Server-Rendered Links:
    Remove defaultQueryParamsHandling: 'merge' (or 'preserve') from global configuration. Explicitly bind only the specific query parameters each link requires (e.g., [queryParams]="{ filter: filter() }"). Even on patched versions, this practice prevents unnecessary request-derived parameter propagation into generated DOM markup.

  2. Limit Query String Length at Reverse Proxy or Middleware:
    Configure your edge proxy, web application firewall (WAF), or reverse proxy to reject requests with excessively long query strings (e.g., exceeding 2,048 bytes).

    Alternatively, reject oversized query strings in Node.js before Angular SSR handles the request:

    // server.ts (before Angular SSR handler)
    app.use((req, res, next) => {
      const queryIndex = req.url.indexOf('?');
      if (queryIndex !== -1 && req.url.length - queryIndex > 2048) {
        res.status(414).send('URI Too Long');
        return;
      }
      next();
    });
  3. Increase Node.js Old Space:
    Increasing --max-old-space-size (e.g., to 2048 MB or 4096 MB) increases the memory headroom and the concurrency threshold required to exhaust memory, though it does not fully eliminate vulnerability under sustained traffic.

Severity

  • CVSS Score: 8.2 / 10 (High)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

angular/angular (@​angular/router)

v22.2.1

Compare Source

common
Commit Type Description
ad2f291ba0 fix let NgOptimizedImage pass a [srcset] binding through when srcset optimization is disabled
compiler
Commit Type Description
0ecaf8e7ab fix add explicit return types to generated functions
c451a3b98d fix avoid incorrectly stripping parentheses in template pipeline
bbc556daa9 fix avoid stripping parentheses from some expressions
compiler-cli
Commit Type Description
fe9ebf5bf5 fix support extendedConfigCache in readConfiguration
core
Commit Type Description
21851fc3a3 fix add debugName on afterRenderEffect
7c634ecec4 fix block dangerous data: and vbscript: URLs in URL sanitizer
9aeef8df6f fix extend data: URL allowlist for non-executable MIME types
f5e8edc5a6 fix include signal and component name in NG0600 error message
88614a5407 fix render error boundary fallbacks after HMR
http
Commit Type Description
6dee7fe91c fix forward reportProgress of httpResource as reportDownloadProgress
platform-server
Commit Type Description
928dcfd258 fix reject protocol-relative paths in resolveUrl
router
Commit Type Description
568d6a83a2 fix do not copy URL-sized objects into every route snapshot
a6e6097b3d fix fall back when serializing protocol-relative URLs
8b6dd5e947 fix limit absolute redirects from redirect functions
3b6796f1ab fix reject duplicate outlets in production builds
e9bf52aa03 fix require outlets to match a route before processing child segments
d23932f2fd fix set initial blocking resource input synchronously on activation
14793bfe4b perf do not retain UrlTree instances in RouterLink

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate Bot added the dependencies Pull requests that update a dependency file label Oct 20, 2026
@renovate renovate Bot changed the title chore(deps): update dependency @angular/router to v22.2.1 [security] chore(deps): update dependency @angular/router to v22.2.1 [security] - autoclosed Oct 21, 2026
@renovate renovate Bot closed this Oct 21, 2026
@renovate
renovate Bot deleted the renovate/npm-angular-router-vulnerability branch October 21, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants