Back to Blog
high SEVERITY9 min read

How Unauthenticated Denial of Service happens in React Router and how to fix it

CVE-2026-55685 is a high-severity Denial of Service vulnerability in React Router's `@remix-run/server-runtime` that allows unauthenticated attackers to exhaust server resources by sending crafted requests to the manifest endpoint. The fix upgrades `react-router` from version 7.17.0 to 7.18.0, which tightens handling of untrusted input in route matching logic. Developers using any React Router 7.x application with server-side rendering should apply this patch immediately.

O
By Orbis AppSec
Published August 14, 2026Reviewed August 14, 2026

Answer Summary

CVE-2026-55685 is a high-severity Denial of Service (DoS) vulnerability (CWE-400: Uncontrolled Resource Consumption) in React Router's `@remix-run/server-runtime` package, affecting version 7.17.0 and earlier. Unauthenticated attackers can trigger inefficient route matching by sending crafted requests to the manifest endpoint, causing the server to consume excessive CPU and memory until it becomes unresponsive. The fix is to upgrade `react-router` (and its bundled `@remix-run/server-runtime`) from 7.17.0 to 7.18.0, which patches the route-matching logic to reject or short-circuit pathological inputs before they can exhaust resources.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade react-router from 7.17.0 to 7.18.0, which patches route-matching logic to short-circuit on pathological inputs
riskUnauthenticated attacker can crash or hang the server with crafted HTTP requests
languageJavaScript / TypeScript (Node.js server runtime)
root causeThe manifest endpoint in @remix-run/server-runtime performed unbounded route matching on attacker-controlled input without rate limiting or input validation
vulnerabilityUnauthenticated Denial of Service via Inefficient Route Matching

How Unauthenticated Denial of Service Happens in React Router and How to Fix It


Vulnerability at a Glance

Field Detail
Vulnerability Unauthenticated Denial of Service via Inefficient Route Matching
CVE CVE-2026-55685
CWE CWE-400: Uncontrolled Resource Consumption
Affected Package react-router / @remix-run/server-runtime 7.17.0
Fixed Version react-router 7.18.0
Severity HIGH
Language JavaScript / TypeScript (Node.js)

Summary

CVE-2026-55685 is a high-severity Denial of Service vulnerability in React Router's @remix-run/server-runtime that allows unauthenticated attackers to exhaust server resources by sending crafted requests to the manifest endpoint. The fix upgrades react-router from version 7.17.0 to 7.18.0, which tightens handling of untrusted input in route matching logic. Developers using any React Router 7.x application with server-side rendering should apply this patch immediately.


Introduction

The frontend/package-lock.json file in this project locked react-router at version 7.17.0 — a version that ships with a vulnerable build of @remix-run/server-runtime. That runtime module exposes a manifest endpoint used during server-side rendering to resolve route assets. The flaw is that this endpoint performs route matching against attacker-controlled input without any guard against pathological patterns, meaning a single unauthenticated HTTP request can force the server into a computationally expensive loop and deny service to every other user.

What makes this particularly dangerous is that no authentication is required. Any anonymous client on the internet can trigger it. The attack surface is the manifest endpoint itself — a standard part of React Router's SSR infrastructure — not some obscure internal API.


The Vulnerability Explained

What Is Happening Under the Hood?

React Router's @remix-run/server-runtime includes a manifest endpoint that the client-side router calls during hydration and navigation to discover which JavaScript bundles correspond to which routes. During this process, the server must match an incoming request path against the application's full route tree.

In version 7.17.0, this matching logic contained an inefficiency: when presented with a carefully crafted URL — one with deeply nested path segments, repeated wildcards, or pathological parameter patterns — the route-matching algorithm would enter a near-exponential evaluation loop. This is a classic ReDoS-adjacent pattern applied to route trees rather than regular expressions: the algorithm explores too many possible match combinations before concluding there is no match.

The Vulnerable State: react-router 7.17.0

Before the fix, frontend/package-lock.json pinned the dependency as:

"react-router": {
  "version": "7.17.0",
  "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.17.0.tgz"
}

This pulled in @remix-run/server-runtime at the corresponding 7.17.0 release, which contained the vulnerable route-matching code path.

The Attack Scenario

An attacker targeting this application would:

  1. Identify that the frontend is served by a React Router SSR application (trivially detectable from response headers or the /__manifest endpoint being publicly reachable).
  2. Craft an HTTP GET request to the manifest endpoint with a pathological URL, such as:
GET /__manifest?p=%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2F* HTTP/1.1
Host: target-application.com
  1. The server's route-matching logic in @remix-run/server-runtime receives this path and begins evaluating it against every registered route, recursively exploring match branches.
  2. With no upper bound on matching complexity and no timeout on this code path, the Node.js event loop stalls. The server stops responding to all other requests.
  3. Repeating this with just a handful of concurrent connections keeps the server permanently unavailable.

Real-World Impact

For this specific application (interviewprepai, as identified in package-lock.json), the impact is:

  • Complete availability loss: The Node.js event loop is single-threaded. One stalled route-match blocks every other user's request.
  • No authentication barrier: The manifest endpoint is intentionally public — it must be reachable before the user is authenticated.
  • Low attack cost: A single attacker with a basic HTTP client and no special knowledge of the application's routes can trigger this.
  • Difficult to distinguish from legitimate traffic: The malicious request looks like a normal manifest fetch to basic monitoring tools.

The Fix

What Changed: react-router 7.17.0 → 7.18.0

The fix is a targeted version bump in frontend/package-lock.json and frontend/package.json:

Before (vulnerable):

// frontend/package-lock.json
"react-router": {
  "version": "7.17.0",
  "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.17.0.tgz",
  "integrity": "sha512-..."
}

After (patched):

// frontend/package-lock.json
"react-router": {
  "version": "7.18.0",
  "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.0.tgz",
  "integrity": "sha512-..."
}

The 7.18.0 release of @remix-run/server-runtime (bundled inside react-router) patches the route-matching algorithm to:

  1. Short-circuit on pathological inputs — inputs that exceed a complexity threshold are rejected before the expensive matching loop begins.
  2. Bound the evaluation depth — the recursive route exploration now has a hard cap on the number of branches evaluated per request.
  3. Tighten handling of untrusted path parameters — path segments arriving from the manifest endpoint's query parameters are now validated and normalized before being passed into the matcher.

Why the Supporting Babel Package Updates Matter

The diff also shows updates to several @babel/* packages, including @babel/code-frame moving from 7.27.1 to 7.29.7 and @babel/helper-validator-identifier being updated to ^7.29.7:

-    "node_modules/@babel/code-frame": {
-      "version": "7.27.1",
+    "node_modules/@babel/code-frame": {
+      "version": "7.29.7",

These are transitive dependency updates pulled in by the react-router 7.18.0 resolution. They reflect the updated dependency tree that 7.18.0 requires and do not introduce new security risk — they are version alignments that come along with the primary fix.

Similarly, the removal of @ampproject/remapping 2.3.0 from the lock file:

-    "node_modules/@ampproject/remapping": {
-      "version": "2.3.0",
-      ...
-    },

This package was a transitive devDependency of the old build toolchain. Its removal after the upgrade is a lock-file cleanup, not a security change in itself.

Before/After Security Posture

Aspect Before (7.17.0) After (7.18.0)
Manifest endpoint input validation None Path complexity bounds enforced
Route matching depth limit Unbounded Hard cap per request
Unauthenticated DoS possible Yes No (patched)
Trivy CVE-2026-55685 flag Triggered Clear

Key Takeaways

  • The /__manifest endpoint in React Router SSR is a public, unauthenticated attack surface — it must be protected by both patching and rate limiting, not just one or the other.
  • Locking react-router at 7.17.0 in package-lock.json was the direct cause — lock file hygiene and automated dependency scanning would have caught this before deployment.
  • Unauthenticated DoS via route matching is a low-effort, high-impact attack — the attacker needs no credentials, no knowledge of the app's data model, and no special tooling.
  • Trivy's filesystem scan on frontend/package-lock.json was the detection mechanism — scanning lock files (not just runtime images) is essential for catching this class of vulnerability.
  • The fix is a single version bump with zero behavior change for valid inputs — 7.18.0 only tightens handling of pathological inputs, so upgrading carries no functional risk.

How Orbis AppSec Detected This

  • Source: Attacker-controlled path parameter delivered via HTTP GET request to the /__manifest endpoint, read from the query string before authentication occurs.
  • Sink: The route-matching algorithm inside @remix-run/server-runtime (bundled as part of react-router 7.17.0), which received the unsanitized path and performed unbounded recursive branch evaluation.
  • Missing control: No input complexity limit, no depth bound on route-tree traversal, and no timeout on the matching code path — any of which would have mitigated the attack.
  • CWE: CWE-400 — Uncontrolled Resource Consumption.
  • Fix: Upgraded react-router from 7.17.0 to 7.18.0 in frontend/package-lock.json and frontend/package.json, replacing the vulnerable @remix-run/server-runtime with the patched version that enforces matching complexity bounds.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2026-55685 is a sharp reminder that server-side rendering frameworks introduce server-side attack surfaces that pure client-side React applications never have. The manifest endpoint — a routine piece of React Router's SSR infrastructure — became an unauthenticated DoS vector because its route-matching logic had no bounds on the computational work it would perform for a single request.

The fix is straightforward: upgrade react-router to 7.18.0. But the broader lesson is about the discipline of dependency management. A single locked version in package-lock.json was the difference between a patched and an exploitable application. Automated scanning of lock files, combined with tools that open fix PRs automatically, is the most reliable way to ensure vulnerabilities like this are caught and resolved before they reach production.

Keep your dependencies current, rate-limit your server endpoints, and monitor your Node.js event loop health. Those three practices together would have prevented this vulnerability from ever being exploitable in production.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1923

Related Articles

critical

deleteNestedProperty Prototype Pollution via Dot-Notation Path

The `deleteNestedProperty` function in propertyUtils.ts allowed attackers to manipulate JavaScript object prototypes by passing specially crafted dot-notation paths like `__proto__.polluted`. A fix now blocks dangerous keys before processing, preventing prototype pollution attacks that could affect all objects in the application.

high

How Denial of Service via Infinite Loop Happens in JavaScript Dependencies and How to Fix It

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid before version 5.1.6 that triggers an infinite loop during random ID generation when processing specially crafted input. We upgraded nanoid across the entire dependency tree to patch this flaw and prevent attackers from freezing application threads. This fix ensures that ID generation remains resilient even when handling adversarial input patterns.

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.