chore(deps): update dependency shell-quote to v1.9.0 [security]#986
Open
renovate[bot] wants to merge 1 commit into
Open
chore(deps): update dependency shell-quote to v1.9.0 [security]#986renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
renovate
Bot
requested review from
JamesRobert20,
edelauna,
hannesrudolph,
navedmerchant and
taltas
as code owners
July 22, 2026 00:22
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
1.8.4→1.9.0shell-quote: Quadratic-complexity Denial of Service in
parse()(CWE-407)CVE-2026-13311 / GHSA-395f-4hp3-45gv
More information
Details
Summary
shell-quote'sparse()finalizes its token list with areducethat usesArray.prototype.concatas the accumulator. Eachprev.concat(arg)copies the entire growingarray, so
parse()runs in O(n²) in the number of tokens. An unauthenticated attacker whocan submit a string to any code path that calls
parse()on it can block the single-threadedNode.js event loop for tens of seconds with a small input — a denial of service. The trigger
needs no shell metacharacters (plain space-separated words suffice), so input filters that
only screen for
;,|,$, or backticks do not help.Root cause
parse.js(lines 200–203), inparseInternal— this path runs on everyparse()call:prev.concat(arg)allocates a new array and copies all ofprevon every iteration, soproducing an N-token result costs
1 + 2 + … + N = O(N²)copies. A secondacc.concat(s)reduce in the
module.exportswrapper (lines 211–224, reached only whenenvis a function)has the same shape. The maintainer's own
// TODO: replace this whole reduce with a concatalready flags the construct.
Proof of Concept
Measured on
shell-quote@1.8.4, Node v24:parse()Time grows ~×4 per 2× input → confirmed O(n²). A ~128 KB input blocks the event loop ~15 s;

~256 KB → ~57 s; a few hundred KB more → minutes.
poc.js
Impact
parse()is synchronous on the main thread; while it copies arrays quadratically the entireevent loop is blocked and the process serves no other requests. Any service that calls
parse()on attacker-influenced input (command parsers, chat-ops / bot command handlers, REPLs,
build-script / arg-string splitters) can be driven to a sustained DoS with a single small
request. No code execution and no data disclosure — availability only.
End-to-end confirmation: a minimal HTTP server that calls
parse()on the request body, hitwith one
POSTof'x '.repeat(32000)(~63 KB), froze for ~4.5 s. An out-of-process probeclient issuing harmless
GET /pingrequests (normally ~1 ms) observed 27 consecutive pingsstalled by up to 4374 ms during that single request — i.e. 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). It is distinct from the known
shell-quotecommand-injection issues (CVE-2021-42740, CVE-2016-10541, CVE-2026-9277), which are all in
quote(), notparse().Suggested remediation
Replace the O(n²) concat-in-reduce with a linear flatten that pushes into the
accumulator instead of reallocating and copying it on every iteration. Apply the
same shape to the wrapper's
acc.concat(s)reduce. A defensive input-length cap onparse()is a cheap additional stop-gap.Disclosure
Found by source audit + wall-clock confirmation against 1.8.4 (and verified the same code is
present on
main). Reported privately here; no public disclosure until a fix is available.Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
ljharb/shell-quote (shell-quote)
v1.9.0Compare Source
Commits
dca6e21eslint9aa9e8fparse: finalize tokens in linear time (GHSA-395f-4hp3-45gv)7ff548875e8497@types/esrecurse3fb739dnpm installon Windows to survive npm 2/3 staging-rename flakeabe0163b4bafa2quote: escape leading~to prevent shell tilde-expansion7a76c1aauto-changelog,tape7184b44jackspeakis no longer in the graph9ba368aConfiguration
📅 Schedule: (in timezone America/Los_Angeles)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.