shell-quote (Nodejs), Denial of Service (Quadratic Complexity), CVE-2026-13311 (High) -DC-Jul2026-1110

Listen to this Post

The `parse()` function in `shell-quote` prior to version 1.8.5 uses an inefficient algorithm that causes a denial-of-service vulnerability. The root cause lies in the `parseInternal` function (parse.js lines 200–203), where the token list is finalized using a `reduce` that employs `Array.prototype.concat` as the accumulator. On each iteration, `prev.concat(arg)` allocates a new array and copies the entire growing `prev` array, making the cost of producing an N-token result equal to 1 + 2 + … + N = O(N²) copies. A second `acc.concat(s)` reduce in the `module.exports` wrapper (lines 211–224, reached only when `env` is a function) has the same shape. The maintainer’s own `// TODO: replace this whole reduce with a concat` comment already flags this construct.
An unauthenticated attacker who can submit a string to any code path that calls `parse()` can block the single-threaded Node.js event loop for tens of seconds with a relatively small input. The trigger requires no shell metacharacters — plain space-separated words suffice — so input filters that only screen for ;, |, $, or backticks do not help. The vulnerability is distinct from the known `shell-quote` command-injection issues (CVE-2021-42740, CVE-2016-10541, CVE-2026-9277), which are all in quote(), not parse().
Proof of concept code demonstrates the quadratic growth: on `[email protected]` with Node v24, an input of 128,000 tokens (~256 KB) blocks the event loop for ~57 seconds, while 1,024,000 tokens would take minutes. A minimal HTTP server that calls `parse()` on the request body, hit with a single POST of `’x ‘.repeat(32000)` (~63 KB), froze for ~4.5 seconds. An out-of-process probe client issuing harmless `GET /ping` requests observed 27 consecutive pings stalled by up to 4374 ms during that single request — every concurrent client was denied service for the whole parse(). Scaling the body to a few hundred KB extends the outage to minutes. This is the same class as several accepted 2026 advisories for quadratic-parser DoS on untrusted input (e.g., markdown-it CVE-2026-48988, js-yaml CVE-2026-53550, python-multipart CVE-2026-53539).
The fix replaces the O(n²) concat-in-reduce with a linear flatten that pushes into the accumulator instead of reallocating and copying it on every iteration. The same shape is applied to the wrapper’s `acc.concat(s)` reduce. The fix uses forEach/push — not `push.apply(…)` — to avoid exceeding the engine’s argument count limit when spreading large arrays. Output is byte-identical to the current code, and finalizing is now linear (1,024,000 tokens in ~150 ms vs ~57 s for 128,000 before). A defensive input-length cap on `parse()` is a cheap additional stop-gap.

DailyCVE Form:

Platform: Node.js / shell-quote
Version: <= 1.8.4
Vulnerability: Quadratic parsing DoS
Severity: High
date: 2026-06-25

Prediction: 2026-06-25 (fixed in 1.8.5)

What Undercode Say:

Analytics: Time grows ~×4 per 2× input → confirmed O(n²)
PoC timing on [email protected], Node v24:
| input (N tokens) | bytes | parse() | ratio vs prev |
| 16,000 | 32 KB | 678 ms | — |
| 32,000 | 64 KB | 4,169 ms | ×6.2 |
| 64,000 | 128 KB | 14,914 ms| ×3.6 |
| 128,000 | 256 KB | 57,319 ms| ×3.8 |
// poc.js
const { parse } = require('shell-quote');
const ms = fn => { const t = process.hrtime.bigint(); fn(); return Number(process.hrtime.bigint()-t)/1e6; };
for (const N of [16000, 32000, 64000, 128000]) {
console.log(N, 'tokens ->', ms(() => parse('x '.repeat(N))).toFixed(0), 'ms');
}
End-to-end HTTP server confirmation
POST 'x '.repeat(32000) (~63 KB) → froze for ~4.5 s
27 consecutive GET /ping requests stalled by up to 4374 ms

Exploit:

An attacker can send a single HTTP request with a large body consisting of space-separated words (e.g., 'x '.repeat(32000)) to any endpoint that calls `parse()` on user input. No shell metacharacters are required, so common input filters are ineffective. The synchronous `parse()` call blocks the entire Node.js event loop, denying service to all concurrent clients for the duration of the parsing.

Protection:

  • Upgrade to `[email protected]` or later, which contains the linear-time fix.
  • As a temporary stop-gap, enforce a strict input-length cap on any data passed to `parse()` (e.g., reject strings longer than 64 KB).
  • Avoid calling `parse()` on untrusted input in critical request paths; offload parsing to a worker thread if absolutely necessary.

Impact:

  • Availability only — no code execution, no data disclosure.
  • A single small request (a few hundred KB) can cause a sustained denial of service, blocking the event loop for minutes.
  • Any service that calls `parse()` on attacker-influenced input (command parsers, chat-ops/bot command handlers, REPLs, build-script/arg-string splitters) is vulnerable.
  • All concurrent clients are denied service while the `parse()` call is running, as demonstrated by the stalled `GET /ping` requests.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top