JavaScript Utility Library Can Be Crashed by Malicious Input (CVE-2026-27601)

Your application uses a JavaScript helper library called Underscore.js that has a flaw in two of its functions. Under specific conditions, an attacker could send specially crafted deeply-nested data to your server, causing it to crash and become temporarily unavailable. Think of it like sending a letter with 4,500 envelopes nested inside each other — the library tries to open every one and runs out of room.

Business Impact And Actions

high urgency

Business Impact

If exploited, your application could be knocked offline temporarily, affecting paying customers and potentially triggering SLA or uptime obligations. This is a targeted availability attack, not a data breach — no customer data is exposed. The risk is highest for server-side Node.js applications that accept and process untrusted user input. The fix is a simple library upgrade with no breaking changes expected.

What To Do

  1. Ask your developer to upgrade the Underscore.js library to version 1.13.8 or later — this is a straightforward package update that should take under an hour.
  2. If an immediate upgrade isn't possible, ask your developer to add a depth limit (e.g., 1000 levels) to any code that parses user-submitted data before it's processed by the library.
  3. After the upgrade is deployed, ask your developer to confirm the new version is running in production using the verification steps in the technical guide.
  4. If your application is customer-facing and processes user-submitted structured data (e.g., JSON from API requests), treat this as a priority fix for this week.

Underscore.js < 1.13.8 — Unbounded Recursion DoS via _.flatten / _.isEqual (CVE-2026-27601)

high severity CVSS 8.2

Vulnerability Explanation

The _.flatten and _.isEqual functions in Underscore.js perform recursive traversal of data structures without any depth limit. An attacker who can supply deeply-nested input (e.g., a JSON object or array nested ~4500 levels deep) can trigger a JavaScript call stack overflow (RangeError: Maximum call stack size exceeded) when either function processes that input. The crash propagates unless the calling code wraps the invocation in a try/catch. In a Node.js server context, an uncaught RangeError will terminate the process or the current request handler, causing a denial of service.

Root Cause

CWE-770: Allocation of Resources Without Limits or Throttling. The recursive implementations of _.flatten and _.isEqual do not enforce a maximum recursion depth, making them susceptible to stack exhaustion when processing pathologically deep data structures sourced from untrusted input.

Technical Impact

Denial of service: an unauthenticated remote attacker can crash or hang the Node.js process by submitting a deeply-nested JSON payload that is subsequently passed to _.flatten or _.isEqual without a depth guard. No data confidentiality or integrity impact. Exploitability requires multiple specific conditions to be met simultaneously (see affected conditions below).

Severity Justification

CVSS 4.0 score of 8.2 (AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H) assigned by GitHub Security. Network-reachable, no authentication required, but attack requirements (AT:P) are present — multiple specific application-level conditions must hold for exploitation.

Affected Components

  • underscore < 1.13.8

Remediation Steps

  1. Upgrade underscore to 1.13.8 or later: run `npm install underscore@latest` (or `yarn upgrade underscore@latest`). This is the complete fix — the patched version adds internal depth limits to both _.flatten and _.isEqual.
  2. If you cannot upgrade immediately, add an input depth guard before passing untrusted data to either function. Enforce a maximum nesting depth of 1000 levels on any JSON.parse output that flows into _.flatten or _.isEqual.
  3. For _.flatten specifically, pass a finite depth limit as the second argument (e.g., _.flatten(arr, 1000)) to prevent unbounded recursion on untrusted arrays.
  4. Wrap any call to _.flatten or _.isEqual that processes untrusted input in a try/catch block so that a RangeError does not crash the process — this is a defence-in-depth measure, not a substitute for upgrading.
  5. After deploying the upgrade, verify the installed version in production (see verification steps).

Verification Steps

  1. Run `npm list underscore` (or `yarn list --pattern underscore`) in your project directory and confirm the installed version is 1.13.8 or higher.
  2. Run `node -e "const _ = require('underscore'); let n=[]; for(let i=0;i<4500;i++) n=[n]; console.log(_.flatten(n).length);"` — on 1.13.8 this should complete without throwing a RangeError.
  3. Check your package-lock.json or yarn.lock to ensure no transitive dependency is pinning an older version of underscore.

Code Examples (javascript)

Vulnerable
// Vulnerable: no depth limit on user-supplied input
app.post('/compare', (req, res) => {
  const a = JSON.parse(req.body.dataA); // untrusted, potentially 4500 levels deep
  const b = JSON.parse(req.body.dataB);
  const equal = _.isEqual(a, b);        // RangeError if deeply nested
  res.json({ equal });
});
Fixed
// Fixed option 1: upgrade to underscore 1.13.8 (depth limit built in)
// No code change needed — just upgrade the package.

// Fixed option 2 (if upgrade is delayed): guard input depth
function limitDepth(obj, maxDepth, depth = 0) {
  if (depth >= maxDepth || typeof obj !== 'object' || obj === null) return obj;
  if (Array.isArray(obj)) return obj.map(v => limitDepth(v, maxDepth, depth + 1));
  return Object.fromEntries(
    Object.entries(obj).map(([k, v]) => [k, limitDepth(v, maxDepth, depth + 1)])
  );
}

app.post('/compare', (req, res) => {
  const a = limitDepth(JSON.parse(req.body.dataA), 1000);
  const b = limitDepth(JSON.parse(req.body.dataB), 1000);
  const equal = _.isEqual(a, b); // safe
  res.json({ equal });
});

Best Practices

  • Validate and limit the nesting depth of all JSON input at the API boundary before passing it to any recursive utility function.
  • Pin direct dependencies to a minimum safe version in package.json (e.g., `"underscore": ">=1.13.8"`) and run `npm audit` in CI to catch regressions.
  • Wrap calls to third-party recursive functions that process untrusted input in try/catch blocks as a defence-in-depth measure.
  • Regularly audit transitive dependencies — a direct dependency may re-introduce an older version of underscore as a nested dependency.

Found this in your infrastructure?

VulWall scans for this and dozens of other issues automatically.

Scan Your Domain Free