NodeJS/shell-quote/1.4.2
quote and parse shell commands
https://www.npmjs.com/package/shell-quote
MIT
3 Security Vulnerabilities
Potential Command Injection in shell-quote
Affected versions of shell-quote do not properly escape command line arguments, which may result in command injection if the library is used to escape user input destined for use as command line arguments.
Proof of Concept:
The following characters are not escaped properly: >,;,{,}
Bash has a neat but not well known feature known as Bash Brace Expansion
, wherein a sub-command can be executed without spaces by running it between a set of {} and using the , instead of to seperate arguments. Because of this, full command injection is possible even though it was initially thought to be impossible.
const quote = require('shell-quote').quote;
console.log(quote(['a;{echo,test,123,234}']));
// Actual "a;{echo,test,123,234}"
// Expected "a\;\{echo,test,123,234\}"
// Functional Equivalent "a; echo 'test' '123' '1234'"
Recommendation
Update to version 1.6.1 or later.
shell-quote quote() does not escape newlines in object .op values
- https://github.com/ljharb/shell-quote/security/advisories/GHSA-w7jw-789q-3m8p
- https://nvd.nist.gov/vuln/detail/CVE-2026-9277
- https://github.com/ljharb/shell-quote/commit/1518179
- https://github.com/ljharb/shell-quote
- https://www.npmjs.com/package/shell-quote
- http://www.openwall.com/lists/oss-security/2026/05/23/2
- https://github.com/advisories/GHSA-w7jw-789q-3m8p
Summary
shell-quote's quote() function did not validate object-token inputs against the operator model used by parse(). The .op field was backslash-escaped character by character using /(.)/g, which in JavaScript does not match line terminators (\n, \r, U+2028, U+2029). A line terminator in .op therefore passed through unescaped into the output; POSIX shells treat a literal \n as a command separator, so any content after it would execute as a second command.
The vulnerable code path is reachable in two ways. Neither requires the parser to misbehave — parse() only emits ops from a fixed control set — but both are documented API surface:
- Direct construction. A caller builds
{ op: '...\n...' }from external input (e.g. a deserialized argument array) and passes it toquote(). envFnreturn.parse(cmd, envFn)is documented to splice the return value ofenvFninto the result array when it is an object. An attacker-influenced data source consulted byenvFncan introduce an object token whose.opreachesquote().
Impact
Shell command injection in callers that pass object tokens with attacker-influenced .op values to quote() and then hand the result to a shell. The preconditions are narrower than ordinary string injection — they require the caller to feed object tokens into quote() — but object tokens are a public, documented part of the API surface, and quote() is intended to be a shell-safety boundary.
PoC
const { parse, quote } = require('shell-quote');
// Direct construction
quote([{ op: ';\nid' }]);
// → "\;\n\\i\\d" ← literal newline; second line executes as a command
// Via parse() with an envFn returning attacker-shaped objects
const tokens = parse('echo $X', () => ({ op: ';\nid' }));
require('child_process').execSync(quote(tokens), { shell: true });
// Executes `id` after `echo \;`.
Confirmed under sh, bash, dash, and zsh.
Patch
Fixed by replacing the per-character escape with strict shape validation in quote(). The object-token branch now:
{ op }—.opmust be a string from the same allowlist the parser emits (||,&&,;;,|&,<(,<<<,>>,>&,<&,&,;,(,),|,<,>). Anything else throwsTypeError. This is the direct fix for the reported issue and removes the entire class of.opinjection.{ op: 'glob', pattern }—.patternmust be a string with no line terminators. Glob metacharacters (*,?,[,],{,},,) pass through; all other shell-special characters are backslash-escaped. (Previously the pattern field was discarded entirely and the literal string\g\l\o\bwas emitted — a latent bug, not security-relevant.){ comment }—.commentmust be a string with no line terminators (line terminators would end the shell comment and resume command parsing — same injection shape).- Any other object shape —
TypeError.
The fix is allowlist-based rather than a targeted regex tweak, so it closes the reported vector and forecloses adjacent ones (U+2028 / U+2029 line separators in .op, line terminators in comments, unknown-shape objects coerced through .replace).
Workarounds
Prior to upgrading, callers that build object tokens from untrusted input should validate .op against the parser's operator set themselves, and never construct { op } from attacker-controlled strings.
Credits
Reported by Akshat Sinha
Potential Command Injection
The npm module shell-quote
cannot correctly escape >
and <
operator used for redirection in shell. I'm wondering if this might be possible vulnerability for many application which depends on shell-quote.
For example:
const quote = require('shell-quote').quote; console.log(quote(['foo>bar']));
will print foo>bar
, where foo\>bar
is desirable.
This module is downloaded more than 1M times per month and many other modules are depending on this. If an application is escaping command-line args with this module, they might be vulnerable from malicious user input.
For example: ``` var sq = require('../tests/get/shell-quote-1.6.0'); var exec = require('child_process').exec;
var pattern = process.argv[2];
command = sq.quote(['grep', pattern])); exec('cat file | ' + command, function ( err, stdout, stderr) { console.log(command, stdout);
}); ``` will be vulnerable when user input something like pattern = ':</etc/passwd', which causes the content of /etc/passwd to be leaked.
Internally, (Jon Lamendola, Nick Starke, Jacob Waddell) found that the ;, {, and } characters weren't escaped properly either. This allows for full command injection. A malicious user could input 'a;{echo,test,123,234}' to execute echo fully.
31 Other Versions
| Version | License | Security | Released | |
|---|---|---|---|---|
| 1.9.0 | MIT | 2026-06-25 - 04:47 | 9 days | |
| 1.8.4 | MIT | 2026-05-22 - 13:13 | about 1 month | |
| 1.8.3 | MIT | 1 | 2025-06-02 - 05:03 | about 1 year |
| 1.8.2 | MIT | 1 | 2024-11-27 - 21:33 | over 1 year |
| 1.8.1 | MIT | 1 | 2023-04-07 - 20:56 | about 3 years |
| 1.8.0 | MIT | 1 | 2023-01-31 - 03:27 | over 3 years |
| 1.7.4 | MIT | 1 | 2022-10-13 - 16:52 | over 3 years |
| 1.7.3 | MIT | 1 | 2021-10-21 - 06:34 | over 4 years |
| 1.7.2 | MIT | 2 | 2019-09-01 - 07:46 | almost 7 years |
| 1.7.1 | MIT | 2 | 2019-08-13 - 13:35 | almost 7 years |
| 1.7.0 | MIT | 2 | 2019-08-13 - 07:52 | almost 7 years |
| 1.6.3 | MIT | 2 | 2019-08-13 - 07:41 | almost 7 years |
| 1.6.2 | MIT | 1 | 2019-08-13 - 07:15 | almost 7 years |
| 1.6.1 | MIT | 1 | 2016-06-17 - 20:43 | about 10 years |
| 1.6.0 | MIT | 3 | 2016-04-24 - 05:53 | about 10 years |
| 1.5.0 | MIT | 3 | 2016-03-16 - 17:58 | over 10 years |
| 1.4.3 | MIT | 3 | 2015-03-08 - 03:47 | over 11 years |
| 1.4.2 | MIT | 3 | 2014-07-20 - 21:27 | almost 12 years |
| 1.4.1 | MIT | 3 | 2013-12-25 - 01:00 | over 12 years |
| 1.4.0 | MIT | 3 | 2013-10-18 - 01:40 | over 12 years |
| 1.3.3 | MIT | 3 | 2013-06-24 - 12:01 | about 13 years |
| 1.3.2 | MIT | 3 | 2013-06-24 - 11:50 | about 13 years |
| 1.3.1 | MIT | 3 | 2013-05-13 - 13:48 | about 13 years |
| 1.3.0 | MIT | 3 | 2013-05-13 - 13:42 | about 13 years |
| 1.2.0 | MIT | 3 | 2013-05-13 - 12:10 | about 13 years |
| 1.1.0 | MIT | 3 | 2013-05-13 - 10:35 | about 13 years |
| 1.0.0 | MIT | 2 | 2013-05-13 - 10:27 | about 13 years |
| 0.1.1 | MIT | 2 | 2013-04-17 - 08:06 | about 13 years |
| 0.1.0 | MIT | 2 | 2013-04-15 - 04:36 | about 13 years |
| 0.0.1 | MIT | 2 | 2012-05-18 - 18:25 | about 14 years |
| 0.0.0 | MIT | 2 | 2012-05-18 - 10:42 | about 14 years |
