{"id":"CVE-2026-13311","aliases":["CLSA-2026-1784683086","CLSA-2026-1791201398","CLSA-2026-1791584881","CLSA-2026-1792163710","DEBIAN-CVE-2026-13311","EUVD-2026-39180","GHSA-395f-4hp3-45gv","ROOT-APP-NPM-CVE-2026-13311","UBUNTU-CVE-2026-13311","openSUSE-SU-2026:11145-1","openSUSE-SU-2026:11149-1","openSUSE-SU-2026:11472-1","openSUSE-SU-2026:21176-1","openSUSE-SU-2026:21448-1"],"status":"published","title":"shell-quote parse() is quadratic in token count, enabling denial of service","description":"shell-quote prior to 1.8.5 finalizes parsed tokens in parse() using Array.prototype.concat as a reduce accumulator, which reallocates and copies the entire growing array on every iteration. As a result parse() runs in O(n^2) time relative to the number of input tokens. An attacker who can supply an attacker-controlled string to any code path that calls parse() (no shell metacharacters are required; plain space-separated words suffice) can block the single-threaded Node.js event loop for an extended period with a small input, resulting in a denial of service. There is no code execution or data disclosure; impact is to availability only. Fixed in 1.8.5.","severity":{"version":"4.0","score":8.7,"vector":"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","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.0036,"percentile":0.30196,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-407"],"published":"2026-06-25T04:48:36Z","modified":"2026-10-28T02:30:47Z","fixes":[{"ecosystem":"npm","package":"shell-quote","fixed_in":["1.9.0"],"sources":["ghsa","osv"]},{"ecosystem":"TuxCare:npm","package":"shell-quote","fixed_in":["1.8.3-tuxcare.2"],"sources":["osv"]},{"ecosystem":"Debian:13","package":"node-shell-quote","fixed_in":["1.7.4+~1.7.1-1+deb13u2"],"sources":["osv"]},{"ecosystem":"Debian:14","package":"node-shell-quote","fixed_in":["1.9.0+~1.7.5-1"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"shell-quote","fixed_in":["1.7.2-aikido.2","1.8.0-aikido.2","1.8.3-aikido.3","1.7.3-aikido.2","1.8.2-aikido.2"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"@rootio/shell-quote","fixed_in":["1.7.2-root.io.2","1.8.0-root.io.2","1.8.3-root.io.3","1.7.3-root.io.2","1.8.2-root.io.2"],"sources":["osv"]},{"ecosystem":"openSUSE:Tumbleweed","package":"agama-web-ui","fixed_in":["23+75.1a877fb50-50.1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"1.8.4"},{"fixed":"1.9.0"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.7.3-tuxcare.0"},{"fixed":"1.7.3-tuxcare.1"}]}],"versions":["1.7.3"],"bundled":2},{"source":"osv","ecosystem":"TuxCare:npm","package":"shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.8.1-tuxcare.0"},{"fixed":"1.8.1-tuxcare.2"}]}],"versions":["1.8.1","1.8.1-tuxcare.1"],"bundled":2},{"source":"osv","ecosystem":"TuxCare:npm","package":"shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.8.3-tuxcare.0"},{"fixed":"1.8.3-tuxcare.2"}]}],"versions":["1.8.3","1.8.3-tuxcare.1"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.8.2-tuxcare.0"},{"fixed":"1.8.2-tuxcare.1"}]}],"versions":["1.8.2"],"bundled":2},{"source":"osv","ecosystem":"Debian:12","package":"node-shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.10.0-1","1.12.0-1","1.7.4+~1.7.1-1","1.7.4+~1.7.1-1+deb12u1","1.7.4+~1.7.1-2","1.8.3+~1.7.5-1","1.8.4+~1.7.5-1","1.9.0+~1.7.5-1"]},{"source":"osv","ecosystem":"Debian:13","package":"node-shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.7.4+~1.7.1-1+deb13u2"}]}],"versions":["1.7.4+~1.7.1-1","1.7.4+~1.7.1-1+deb13u1"]},{"source":"osv","ecosystem":"Debian:14","package":"node-shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.9.0+~1.7.5-1"}]}],"versions":["1.7.4+~1.7.1-1","1.7.4+~1.7.1-2","1.8.3+~1.7.5-1","1.8.4+~1.7.5-1"]},{"source":"osv","ecosystem":"npm","package":"shell-quote","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"1.9.0"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.7.2-aikido.2"},{"fixed":"1.8.0-aikido.2"},{"fixed":"1.8.3-aikido.3"},{"fixed":"1.7.3-aikido.2"},{"fixed":"1.8.2-aikido.2"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"@rootio/shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.7.2-root.io.2"},{"fixed":"1.8.0-root.io.2"},{"fixed":"1.8.3-root.io.3"},{"fixed":"1.7.3-root.io.2"},{"fixed":"1.8.2-root.io.2"}]}]},{"source":"osv","ecosystem":"Ubuntu:Pro:18.04:LTS","package":"node-shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.6.1+20160617-git72fb5a8ce29b-1","1.6.1+20160617-git72fb5a8ce29b-1ubuntu0.1~esm1"]},{"source":"osv","ecosystem":"Ubuntu:Pro:22.04:LTS","package":"node-shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.7.3+~1.7.1-1","1.7.3+~1.7.1-1ubuntu0.1~esm1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"node-shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.7.4+~1.7.1-1","1.7.4+~1.7.1-1ubuntu0.24.04.1"]},{"source":"osv","ecosystem":"Ubuntu:25.10","package":"node-shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.7.4+~1.7.1-1","1.7.4+~1.7.1-1ubuntu0.25.10.1"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"node-shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.8.3+~1.7.5-1","1.9.0+~1.7.5-1","1.10.0-1"]},{"source":"osv","ecosystem":"Ubuntu:Pro:26.04:LTS","package":"node-shell-quote","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.7.4+~1.7.1-1","1.8.3+~1.7.5-1","1.8.3+~1.7.5-1ubuntu0.1~esm1"]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"heroic-games-launcher","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.22.0-3.1"}]}],"bundled":2},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"python-pytest-html","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.2.0-3.1"}]}],"bundled":2},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"agama-web-ui","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"23+75.1a877fb50-50.1"}]}]},{"source":"osv","ecosystem":"openSUSE:Leap 16.0","package":"python-pytest-html","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.1.1-bp160.3.1"}]}],"bundled":2},{"source":"osv","ecosystem":"openSUSE:Leap 16.0","package":"agama-web-ui","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"17+673.b97ba64d6-160000.12.1"}]}],"bundled":13}],"products":[{"vendor":"ljharb","product":"shell-quote"},{"vendor":"shell-quote_project","product":"shell-quote"}],"references":[{"url":"https://github.com/ljharb/shell-quote/security/advisories/GHSA-395f-4hp3-45gv","tags":["vendor-advisory","exploit","Vendor Advisory","advisory","web","report"]},{"url":"https://www.npmjs.com/package/shell-quote","tags":["product","Product","web","report"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-13311","tags":["advisory"]},{"url":"https://github.com/ljharb/shell-quote/commit/7ff5488599d01c323514f02f5efb74088dd134ec","tags":["web"]},{"url":"https://github.com/ljharb/shell-quote/releases/tag/v1.9.0","tags":["web"]},{"url":"https://github.com/advisories/GHSA-395f-4hp3-45gv"},{"url":"https://www.npmjs.com/package","tags":["web"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/13xxx/CVE-2026-13311.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-13311","tags":["advisory"]},{"url":"https://github.com/ljharb/shell-quote","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-13311","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-13311","tags":["report"]},{"url":"","tags":["advisory"]},{"url":"https://www.suse.com/security/cve/CVE-2026-13311","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-48779","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-53550","tags":["web"]},{"url":"https://bugzilla.suse.com/1268818","tags":["report"]},{"url":"https://bugzilla.suse.com/1269361","tags":["report"]},{"url":"https://bugzilla.suse.com/1246822","tags":["report"]},{"url":"https://bugzilla.suse.com/1259169","tags":["report"]},{"url":"https://bugzilla.suse.com/1268851","tags":["report"]},{"url":"https://bugzilla.suse.com/1269359","tags":["report"]},{"url":"https://bugzilla.suse.com/1269514","tags":["report"]},{"url":"https://bugzilla.suse.com/1269595","tags":["report"]},{"url":"https://bugzilla.suse.com/1269927","tags":["report"]},{"url":"https://bugzilla.suse.com/1272310","tags":["report"]},{"url":"https://bugzilla.suse.com/1272311","tags":["report"]},{"url":"https://bugzilla.suse.com/1272312","tags":["report"]},{"url":"https://bugzilla.suse.com/1272313","tags":["report"]},{"url":"https://bugzilla.suse.com/1272317","tags":["report"]},{"url":"https://bugzilla.suse.com/1272318","tags":["report"]},{"url":"https://bugzilla.suse.com/1272319","tags":["report"]},{"url":"https://www.suse.com/security/cve/CVE-2025-7783","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-12143","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-13149","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-13676","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-27601","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-40181","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-49356","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-53632","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-54466","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-54490","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-55602","tags":["web"]}],"sources":{"cve":[{"id":"CVE-2026-13311","title":"shell-quote parse() is quadratic in token count, enabling denial of service","description":"shell-quote prior to 1.8.5 finalizes parsed tokens in parse() using Array.prototype.concat as a reduce accumulator, which reallocates and copies the entire growing array on every iteration. As a result parse() runs in O(n^2) time relative to the number of input tokens. An attacker who can supply an attacker-controlled string to any code path that calls parse() (no shell metacharacters are required; plain space-separated words suffice) can block the single-threaded Node.js event loop for an extended period with a small input, resulting in a denial of service. There is no code execution or data disclosure; impact is to availability only. Fixed in 1.8.5.","status":"published","published":"2026-06-25T04:48:36.351Z","modified":"2026-06-25T12:49:41.978Z","cvss":[{"version":"4.0","score":8.7,"vector":"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","level":"high","assessor":"cna"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"cna"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T14:19:49.885652Z"}],"euvd":[{"id":"EUVD-2026-39180","description":"shell-quote prior to 1.8.5 finalizes parsed tokens in parse() using Array.prototype.concat as a reduce accumulator, which reallocates and copies the entire growing array on every iteration. As a result parse() runs in O(n^2) time relative to the number of input tokens. An attacker who can supply an attacker-controlled string to any code path that calls parse() (no shell metacharacters are required; plain space-separated words suffice) can block the single-threaded Node.js event loop for an extended period with a small input, resulting in a denial of service. There is no code execution or data disclosure; impact is to availability only. Fixed in 1.8.5.","status":"published","published":"2026-06-25T04:48:36Z","modified":"2026-06-25T12:49:41Z","cvss":[{"version":"4.0","score":8.7,"vector":"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","level":"high"}],"updated_at":"2026-11-02T18:20:29.213001Z"}],"ghsa":[{"id":"GHSA-395f-4hp3-45gv","title":"shell-quote: Quadratic-complexity Denial of Service in `parse()` (CWE-407)","description":"### 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 =\u003e { 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 -\u003e', ms(() =\u003e 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\u003cimg width=\"656\" height=\"214\" alt=\"image\" src=\"https://github.com/user-attachments/assets/e8955b0e-0527-45ca-94b7-c3a2d8c0c82e\" /\u003e\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\u003e **Maintainer note (edit):** the originally-suggested `Array.prototype.flat()` is\n\u003e ES2019 / Node 11+, but `shell-quote` declares `engines: node \u003e= 0.4`, so `.flat()`\n\u003e would silently drop support for older runtimes. The fix instead flattens one-level\n\u003e array tokens with `forEach`/`push` — and deliberately not `push.apply(...)`, since\n\u003e spreading a large array into function arguments can exceed the engine's argument\n\u003e count limit. Output is byte-identical to the current code across strings, `undefined`\n\u003e holes, one-level array tokens, and `{op}`/`{comment}`/`{op:'glob'}` objects, and\n\u003e finalizing is now linear (1,024,000 tokens in ~150 ms vs ~57 s for 128,000 before).\n\u003e 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.","status":"published","published":"2026-07-20T21:49:34Z","modified":"2026-07-20T21:49:34Z","cvss":[{"version":"4.0","score":8.7,"vector":"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","level":"high"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T14:40:48.410776Z"}],"nvd":[{"id":"CVE-2026-13311","description":"shell-quote prior to 1.8.5 finalizes parsed tokens in parse() using Array.prototype.concat as a reduce accumulator, which reallocates and copies the entire growing array on every iteration. As a result parse() runs in O(n^2) time relative to the number of input tokens. An attacker who can supply an attacker-controlled string to any code path that calls parse() (no shell metacharacters are required; plain space-separated words suffice) can block the single-threaded Node.js event loop for an extended period with a small input, resulting in a denial of service. There is no code execution or data disclosure; impact is to availability only. Fixed in 1.8.5.","status":"published","published":"2026-06-25T05:16:52.763Z","modified":"2026-10-25T23:10:00.167Z","cvss":[{"version":"4.0","score":8.7,"vector":"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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high","assessor":"secondary"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T14:12:25.67873Z"}],"osv":[{"id":"CVE-2026-13311","title":"shell-quote parse() is quadratic in token count, enabling denial of service","description":"shell-quote prior to 1.8.5 finalizes parsed tokens in parse() using Array.prototype.concat as a reduce accumulator, which reallocates and copies the entire growing array on every iteration. As a result parse() runs in O(n^2) time relative to the number of input tokens. An attacker who can supply an attacker-controlled string to any code path that calls parse() (no shell metacharacters are required; plain space-separated words suffice) can block the single-threaded Node.js event loop for an extended period with a small input, resulting in a denial of service. There is no code execution or data disclosure; impact is to availability only. Fixed in 1.8.5.","status":"published","published":"2026-06-25T04:48:36.351Z","modified":"2026-10-28T02:30:47.286704241Z","cvss":[{"version":"4.0","score":8.7,"vector":"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","level":"high"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T15:44:57.354194Z"},{"id":"DEBIAN-CVE-2026-13311","description":"shell-quote prior to 1.8.5 finalizes parsed tokens in parse() using Array.prototype.concat as a reduce accumulator, which reallocates and copies the entire growing array on every iteration. As a result parse() runs in O(n^2) time relative to the number of input tokens. An attacker who can supply an attacker-controlled string to any code path that calls parse() (no shell metacharacters are required; plain space-separated words suffice) can block the single-threaded Node.js event loop for an extended period with a small input, resulting in a denial of service. There is no code execution or data disclosure; impact is to availability only. Fixed in 1.8.5.","status":"published","published":"2026-06-25T05:16:52.763Z","modified":"2026-10-26T11:47:31.822175005Z","cvss":[{"version":"4.0","score":8.7,"vector":"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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high"}],"updated_at":"2026-11-02T15:45:51.374409Z"},{"id":"GHSA-395f-4hp3-45gv","title":"shell-quote: Quadratic-complexity Denial of Service in `parse()` (CWE-407)","description":"### 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 =\u003e { 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 -\u003e', ms(() =\u003e 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\u003cimg width=\"656\" height=\"214\" alt=\"image\" src=\"https://github.com/user-attachments/assets/e8955b0e-0527-45ca-94b7-c3a2d8c0c82e\" /\u003e\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\u003e **Maintainer note (edit):** the originally-suggested `Array.prototype.flat()` is\n\u003e ES2019 / Node 11+, but `shell-quote` declares `engines: node \u003e= 0.4`, so `.flat()`\n\u003e would silently drop support for older runtimes. The fix instead flattens one-level\n\u003e array tokens with `forEach`/`push` — and deliberately not `push.apply(...)`, since\n\u003e spreading a large array into function arguments can exceed the engine's argument\n\u003e count limit. Output is byte-identical to the current code across strings, `undefined`\n\u003e holes, one-level array tokens, and `{op}`/`{comment}`/`{op:'glob'}` objects, and\n\u003e finalizing is now linear (1,024,000 tokens in ~150 ms vs ~57 s for 128,000 before).\n\u003e 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.","status":"published","published":"2026-07-20T21:49:34Z","modified":"2026-09-10T03:51:10.090834688Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"},{"version":"4.0","score":8.7,"vector":"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","level":"high"}],"cwes":["CWE-407"],"updated_at":"2026-11-02T15:46:35.406455Z"},{"id":"UBUNTU-CVE-2026-13311","description":"shell-quote prior to 1.8.5 finalizes parsed tokens in parse() using Array.prototype.concat as a reduce accumulator, which reallocates and copies the entire growing array on every iteration. As a result parse() runs in O(n^2) time relative to the number of input tokens. An attacker who can supply an attacker-controlled string to any code path that calls parse() (no shell metacharacters are required; plain space-separated words suffice) can block the single-threaded Node.js event loop for an extended period with a small input, resulting in a denial of service. There is no code execution or data disclosure; impact is to availability only. Fixed in 1.8.5.","status":"published","published":"2026-06-25T05:16:00Z","modified":"2026-10-17T12:58:45.48475198Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"},{"version":"4.0","score":8.7,"vector":"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","level":"high"}],"updated_at":"2026-11-02T16:56:00.592716Z"},{"id":"CLSA-2026-1784683086","title":"TuxCare security update for shell-quote (2 CVEs)","description":"TuxCare rebuilt shell-quote to address 2 CVEs. Fixed in 1.7.3-tuxcare.1.","status":"published","published":"2026-08-04T14:52:47Z","modified":"2026-10-15T09:47:54.242122463Z","updated_at":"2026-11-02T15:43:46.482572Z"},{"id":"CLSA-2026-1791201398","title":"TuxCare security update for shell-quote (2 CVEs)","description":"TuxCare rebuilt shell-quote to address 2 CVEs. Fixed in 1.8.1-tuxcare.2.","status":"published","published":"2026-10-05T11:56:38Z","modified":"2026-10-15T09:47:58.197677644Z","updated_at":"2026-11-02T15:43:49.016976Z"},{"id":"CLSA-2026-1791584881","title":"TuxCare security update for shell-quote (1 CVE)","description":"TuxCare rebuilt shell-quote to address 1 CVE. Fixed in 1.8.3-tuxcare.2.","status":"published","published":"2026-10-09T22:28:01Z","modified":"2026-10-15T09:48:04.617965831Z","updated_at":"2026-11-02T15:43:50.599064Z"},{"id":"CLSA-2026-1792163710","title":"TuxCare security update for shell-quote (2 CVEs)","description":"TuxCare rebuilt shell-quote to address 2 CVEs. Fixed in 1.8.2-tuxcare.1.","status":"published","published":"2026-10-16T15:15:09Z","modified":"2026-10-16T16:30:10.808889362Z","updated_at":"2026-11-02T15:43:51.466048Z"},{"id":"openSUSE-SU-2026:11145-1","title":"heroic-games-launcher-2.22.0-3.1 on GA media","description":"These are all security issues fixed in the heroic-games-launcher-2.22.0-3.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-06-29T00:00:00Z","modified":"2026-06-30T09:15:06.413898514Z","updated_at":"2026-11-02T16:58:38.95195Z"},{"id":"openSUSE-SU-2026:11149-1","title":"python311-pytest-html-4.2.0-3.1 on GA media","description":"These are all security issues fixed in the python311-pytest-html-4.2.0-3.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-06-29T00:00:00Z","modified":"2026-06-30T09:15:07.418659662Z","updated_at":"2026-11-02T16:58:38.95195Z"},{"id":"openSUSE-SU-2026:11472-1","title":"agama-web-ui-23+75.1a877fb50-50.1 on GA media","description":"These are all security issues fixed in the agama-web-ui-23+75.1a877fb50-50.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-08-09T00:00:00Z","modified":"2026-08-10T08:15:09.373486884Z","updated_at":"2026-11-02T16:58:39.516824Z"},{"id":"openSUSE-SU-2026:21176-1","title":"Security update for python-pytest-html","description":"This update for python-pytest-html fixes the following issues:\n\nChanges in python-pytest-html:\n\n- Revendor updating shell-quote and js-yaml deps:\n  - CVE-2026-13311: shell-quote: inefficient input parsing can lead to a\n    denial of service (bsc#1269361)\n  - CVE-2026-53550: js-yaml: quadratic complexity when processing a\n    crafted YAML document can lead to CPU exhaustion (bsc#1268818)\n","status":"published","published":"2026-06-30T07:49:32Z","modified":"2026-07-02T18:24:18.559035843Z","updated_at":"2026-11-02T16:58:41.885092Z"},{"id":"openSUSE-SU-2026:21448-1","title":"Security update for agama-web-ui","description":"This update for agama-web-ui fixes the following issues:\n\n- CVE-2025-7783: form-data: unsafe `Math.random()` function is used to select a boundary value for multipart\n  form-encoded data (bsc#1246822).\n- CVE-2026-12143: form-data: CRLF injection via unescaped multipart field names and filenames (bsc#1272310).\n- CVE-2026-13149: brace-expansion: `expand()` function exhibits exponential-time complexity when processing\n  non-expanding `{}` brace groups (bsc#1269927).\n- CVE-2026-13311: shell-quote: quadratic complexity in `parse()` function when processing specially crafted strings\n  (bsc#1269359).\n- CVE-2026-13676: fast-uri: host-based policy bypass due to failure to canonicalize Unicode/IDN hostnames for\n  HTTP-family URLs (bsc#1269595).\n- CVE-2026-27601: underscore: DoS via stack overflow due to missing depth limits in `_.flatten` and `_.isEqual`\n  functions (bsc#1259169).\n- CVE-2026-40181: react-router: open redirect to an external domain due to path values starting with `//` being\n  reinterpreted as protocol-relative URLs (bsc#1272311).\n- CVE-2026-49356: @babel/core: arbitrary file read via `sourceMappingURL` comment (bsc#1272317).\n- CVE-2026-53550: js-yaml: quadratic complexity in merge-key processing when processing a crafted YAML document\n  (bsc#1268851).\n- CVE-2026-53632: launch-editor: NTLMv2 hash disclosure via UNC path handling on Windows (bsc#1272319).\n- CVE-2026-54466: websocket-driver: message corruption via abuse of protocol length headers (bsc#1272312).\n- CVE-2026-54490: websocket-driver: resource limit bypass via message compression (bsc#1272313).\n- CVE-2026-55602: http-proxy-middleware: Host-header-driven backend routing bypass via `router` host+path substring\n  matching (bsc#1272318).\n","status":"published","published":"2026-07-27T15:55:38Z","modified":"2026-07-29T18:24:28.648555524Z","updated_at":"2026-11-02T16:58:41.885092Z"},{"id":"ROOT-APP-NPM-CVE-2026-13311","title":"CVE-2026-13311 in shell-quote - Patched by Root","description":"Root has patched CVE-2026-13311 in the shell-quote package for Root:npm. Multiple fixed versions available.","status":"published","published":"2026-09-10T06:37:31Z","modified":"2026-09-10T07:15:06.990747959Z","updated_at":"2026-11-02T15:53:27.362922Z"}]}}
