CVE-2026-13311: shell-quote Denial of Service Vulnerability
HIGHCVE-2026-13311 is a HIGH (CVSS 7.5) denial of service vulnerability (CWE-407: Inefficient Algorithmic Complexity) affecting shell-quote libraries in the npm ecosystem. The issue involves quadratic-complexity denial of service in `parse()` (cwe-407). Remediation is available by upgrading affected packages to version 1.9.0.
CVE-2026-13311 at a Glance
Security Impact
CVSS: 7.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H).
How to Fix CVE-2026-13311
To remediate this vulnerability, upgrade all affected shell-quote dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install shell-quote@1.9.0
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls shell-quote
How to Check if You Are Affected
To determine whether your Node.js application is affected by CVE-2026-13311, inspect your project dependencies using npm:
npm ls shell-quote
Analyze the terminal output to locate any version of shell-quote in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.9.0), your application is affected.
Note: The npm ls shell-quote command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-407: Inefficient Algorithmic Complexity, where an algorithm in a product has an inefficient worst-case computational complexity that may be detrimental to system performance and can be triggered by an attacker, typically using crafted manipulations that ensure that the worst case is being reached. In shell-quote, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-13311 Affected shell-quote Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
shell-quote
|
npm |
< 1.9.0
|
1.9.0
|
References
Frequently Asked Questions
What is CVE-2026-13311?
Which shell-quote versions are affected?
What version fixes CVE-2026-13311?
Is shell-quote affected?
What is the CVSS score of CVE-2026-13311?
What CWE is associated with CVE-2026-13311?
How can I check npm dependencies for CVE-2026-13311?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-395f-4hp3-45gv",
"modified" : "2026-07-20T21:49:34Z",
"published" : "2026-07-20T21:49:34Z",
"aliases" : [ "CVE-2026-13311" ],
"summary" : "shell-quote: Quadratic-complexity Denial of Service in `parse()` (CWE-407)",
"details" : "### Summary\n`shell-quote`'s `parse()` finalizes its token list with a `reduce` that uses\n`Array.prototype.concat` as the accumulator. Each `prev.concat(arg)` copies the entire growing\narray, so `parse()` runs in **O(n²)** in the number of tokens. An unauthenticated attacker who\ncan submit a string to any code path that calls `parse()` on it can block the single-threaded\nNode.js event loop for tens of seconds with a small input — a denial of service. The trigger\nneeds **no shell metacharacters** (plain space-separated words suffice), so input filters that\nonly screen for `;`, `|`, `$`, or backticks do not help.\n\n### Root cause\n`parse.js` (lines 200–203), in `parseInternal` — this path runs on **every** `parse()` call:\n\n```js\n}).reduce(function (prev, arg) { // finalize parsed arguments\n // TODO: replace this whole reduce with a concat\n return typeof arg === 'undefined' ? prev : prev.concat(arg);\n}, []);\n```\n\n`prev.concat(arg)` allocates a new array and copies all of `prev` on every iteration, so\nproducing an N-token result costs `1 + 2 + … + N = O(N²)` copies. A second `acc.concat(s)`\nreduce in the `module.exports` wrapper (lines 211–224, reached only when `env` is a function)\nhas the same shape. The maintainer's own `// TODO: replace this whole reduce with a concat`\nalready flags the construct.\n\n### Proof of Concept\n```js\nconst { parse } = require('shell-quote');\nconst ms = fn => { const t = process.hrtime.bigint(); fn(); return Number(process.hrtime.bigint()-t)/1e6; };\nfor (const N of [16000, 32000, 64000, 128000]) {\n console.log(N, 'tokens ->', ms(() => parse('x '.repeat(N))).toFixed(0), 'ms');\n}\n```\n\nMeasured on `shell-quote@1.8.4`, Node v24:\n\n| input (N tokens) | bytes | `parse()` | ratio vs prev (2× input) |\n|-----------------:|-------:|----------:|:------------------------:|\n| 16 000 | 32 KB | 678 ms | — |\n| 32 000 | 64 KB | 4 169 ms | ×6.2 |\n| 64 000 | 128 KB | 14 914 ms | ×3.6 |\n| 128 000 | 256 KB | **57 319 ms** | ×3.8 |\n\nTime grows ~×4 per 2× input → confirmed O(n²). A ~128 KB input blocks the event loop ~15 s;\n~256 KB → ~57 s; a few hundred KB more → minutes.\n<img width=\"656\" height=\"214\" alt=\"image\" src=\"https://github.com/user-attachments/assets/e8955b0e-0527-45ca-94b7-c3a2d8c0c82e\" />\n[poc.js](https://github.com/user-attachments/files/29255995/poc.js)\n\n### Impact\n`parse()` is synchronous on the main thread; while it copies arrays quadratically the entire\nevent loop is blocked and the process serves no other requests. Any service that calls `parse()`\non attacker-influenced input (command parsers, chat-ops / bot command handlers, REPLs,\nbuild-script / arg-string splitters) can be driven to a sustained DoS with a single small\nrequest. No code execution and no data disclosure — availability only.\n\nEnd-to-end confirmation: a minimal HTTP server that calls `parse()` on the request body, hit\nwith **one** `POST` of `'x '.repeat(32000)` (~63 KB), froze for ~4.5 s. An out-of-process probe\nclient issuing harmless `GET /ping` requests (normally ~1 ms) observed **27 consecutive pings\nstalled by up to 4374 ms** during that single request — i.e. every concurrent client was denied\nservice for the whole parse. Scaling the body to a few hundred KB extends the outage to minutes.\n\nThis is the same class as several accepted 2026 advisories for quadratic-parser DoS on\nuntrusted input (e.g. markdown-it CVE-2026-48988, js-yaml CVE-2026-53550,\npython-multipart CVE-2026-53539). It is **distinct** from the known `shell-quote`\ncommand-injection issues (CVE-2021-42740, CVE-2016-10541, CVE-2026-9277), which are all in\n`quote()`, not `parse()`.\n\n### Suggested remediation\nReplace the O(n²) concat-in-reduce with a linear flatten that **pushes into the\naccumulator** instead of reallocating and copying it on every iteration. Apply the\nsame shape to the wrapper's `acc.concat(s)` reduce. A defensive input-length cap on\n`parse()` is a cheap additional stop-gap.\n\n> **Maintainer note (edit):** the originally-suggested `Array.prototype.flat()` is\n> ES2019 / Node 11+, but `shell-quote` declares `engines: node >= 0.4`, so `.flat()`\n> would silently drop support for older runtimes. The fix instead flattens one-level\n> array tokens with `forEach`/`push` — and deliberately not `push.apply(...)`, since\n> spreading a large array into function arguments can exceed the engine's argument\n> count limit. Output is byte-identical to the current code across strings, `undefined`\n> holes, one-level array tokens, and `{op}`/`{comment}`/`{op:'glob'}` objects, and\n> finalizing is now linear (1,024,000 tokens in ~150 ms vs ~57 s for 128,000 before).\n> Thanks for the clear report and PoC — the analysis and reproduction were spot on.\n\n### Disclosure\nFound by source audit + wall-clock confirmation against 1.8.4 (and verified the same code is\npresent on `main`). Reported privately here; no public disclosure until a fix is available.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
}, {
"type" : "CVSS_V4",
"score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "shell-quote"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "1.9.0"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 1.8.4"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/ljharb/shell-quote/security/advisories/GHSA-395f-4hp3-45gv"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-13311"
}, {
"type" : "WEB",
"url" : "https://github.com/ljharb/shell-quote/commit/7ff5488599d01c323514f02f5efb74088dd134ec"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/ljharb/shell-quote"
}, {
"type" : "WEB",
"url" : "https://github.com/ljharb/shell-quote/releases/tag/v1.9.0"
}, {
"type" : "WEB",
"url" : "https://www.npmjs.com/package/shell-quote"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-407" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-07-20T21:49:34Z",
"nvd_published_at" : "2026-06-25T05:16:52Z"
}
}