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

Prevention & Best Practices

1. Pin and Audit Your Lock Files

The vulnerability existed because package-lock.json froze react-router at 7.17.0. Regularly audit lock files with:

# Using npm audit
npm audit

# Using Trivy for container/filesystem scanning
trivy fs ./frontend --scanners vuln

# Using Snyk
snyk test --file=frontend/package-lock.json

2. Apply Input Validation at the Framework Boundary

Even when using a framework's built-in routing, validate and sanitize path inputs before they reach matching logic. For Express-based SSR wrappers around React Router:

// Middleware to reject obviously malicious paths before they reach React Router
app.use((req, res, next) => {
  const MAX_PATH_DEPTH = 20;
  const segments = req.path.split('/').filter(Boolean);
  if (segments.length > MAX_PATH_DEPTH) {
    return res.status(400).json({ error: 'Path too deep' });
  }
  next();
});

3. Rate Limit the Manifest Endpoint

If you are running React Router in SSR mode, apply rate limiting specifically to the /__manifest endpoint:

import rateLimit from 'express-rate-limit';

const manifestLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 100,            // 100 requests per IP per minute
  message: 'Too many manifest requests',
});

app.use('/__manifest', manifestLimiter);

4. Monitor Event Loop Lag

DoS attacks against Node.js applications often manifest as event loop lag before CPU or memory alarms trigger. Instrument your application:

import { monitorEventLoopDelay } from 'perf_hooks';

const histogram = monitorEventLoopDelay({ resolution: 10 });
histogram.enable();

setInterval(() => {
  const lagMs = histogram.mean / 1e6;
  if (lagMs > 100) {
    console.warn(`Event loop lag: ${lagMs.toFixed(1)}ms — possible DoS`);
  }
}, 5000);

5. Keep React Router Current

React Router 7.x is an actively maintained major version. Subscribe to its security advisories:

  • Watch the react-router GitHub repository for security advisories
  • Enable Dependabot or Renovate Bot to auto-PR patch updates
  • Use npm audit in your CI pipeline as a required check

Security Standards Reference

  • OWASP: Denial of Service Cheat Sheet
  • CWE-400: Uncontrolled Resource Consumption — the root class for this vulnerability
  • OWASP Top 10 2021 — A05: Security Misconfiguration (leaving vulnerable dependencies in production)

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.


References

Frequently Asked Questions

What is a Denial of Service vulnerability in React Router?

It is a flaw where an attacker sends specially crafted requests to a React Router server endpoint—specifically the manifest endpoint—that forces the server to perform extremely expensive route-matching computations, consuming all available CPU or memory and making the application unavailable to legitimate users.

How do you prevent Denial of Service in React Router applications?

Keep react-router and @remix-run/server-runtime up to date (7.18.0+), apply input validation and size limits on all server endpoints, use rate limiting middleware, and monitor server resource consumption for anomalies.

What CWE is this Denial of Service vulnerability?

CWE-400: Uncontrolled Resource Consumption. The server runtime consumed unbounded resources in response to attacker-controlled route-matching input.

Is rate limiting alone enough to prevent this React Router DoS?

Rate limiting reduces exposure but is not sufficient by itself. The root cause is in the route-matching algorithm processing pathological input; the correct fix is patching the library so the algorithm cannot be abused, combined with rate limiting as defense-in-depth.

Can static analysis detect this type of Denial of Service vulnerability?

Yes — tools like Trivy (which flagged this exact CVE) and Semgrep can detect known-vulnerable package versions in dependency lock files. Trivy identified react-router 7.17.0 in `frontend/package-lock.json` as matching CVE-2026-55685.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1923

Related Articles

high

How Quadratic CPU Consumption in YAML Parsing happens in JavaScript and how to fix it

A high-severity vulnerability in js-yaml versions 3.x and 4.x allowed attackers to cause quadratic CPU consumption through specially crafted YAML documents using the `!!omap` type. This denial-of-service vulnerability (GHSA-5p4m-2wfm-xmqj) was fixed by upgrading from js-yaml 4.3.0 to 4.3.1, protecting applications from algorithmic complexity attacks during YAML parsing.

high

How Infinite Loop Denial of Service happens in nanoid custom alphabet generation and how to fix it

A high-severity infinite loop vulnerability (CVE-2026-67213) was discovered in nanoid versions before 5.1.6 and 3.3.17, affecting the custom alphabet generation feature. When processing certain malformed alphabet configurations, nanoid would enter an infinite loop, causing a complete denial of service. This vulnerability was fixed by upgrading from nanoid 3.3.16 to 3.3.17 and implementing dependency overrides to ensure the patched version is used throughout the dependency tree.

high

How Quadratic CPU Consumption in !!omap Resolution Happens in js-yaml and How to Fix It

A high-severity denial-of-service vulnerability in js-yaml (GHSA-5p4m-2wfm-xmqj) caused quadratic CPU consumption when resolving `!!omap` (ordered map) YAML tags, affecting both the 3.x and 4.x release lines. Upgrading to js-yaml 4.3.1 or 3.15.1 closes the gap by fixing the algorithmic inefficiency in `!!omap` duplicate-key detection. Any application that parses untrusted YAML input is at risk of resource exhaustion leading to service unavailability.

high

How Denial of Service via Infinite Loop happens in Node.js dependencies and how to fix it

A high-severity vulnerability in the nanoid package (CVE-2026-67213) allowed attackers to trigger infinite loops through the customAlphabet function, potentially causing complete denial of service. This fix upgrades nanoid from version 3.3.16 to 3.3.17 in the app_store dependency tree, eliminating the DoS risk through a simple version override.

high

How Denial of Service via Infinite Loop happens in Go and how to fix it

CVE-2026-56852 is a high-severity Denial of Service vulnerability in the `golang.org/x/text` package where a `norm.Iter` iterator can enter an infinite loop when processing certain invalid UTF-8 input sequences. Applications using `golang.org/x/text` v0.37.0 or earlier that accept untrusted text input are at risk of complete service disruption. The fix is a one-line dependency bump in `go.mod` from v0.37.0 to v0.39.0.

high

How package_managers.pnpm.pnpm-missing-minimum-release-age.pnpm-minimum-release-age happens in pnpm and how to fix it

A pnpm workspace configuration in `site-astro/pnpm-workspace.yaml` was missing critical supply chain security settings including `minimumReleaseAge`, `trustPolicy`, and `blockExoticSubdeps`. Without these protections, the project could install freshly published malicious packages within minutes of their release. The fix adds a 7-day quarantine period, downgrade protection, and exotic subdependency blocking.