Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
📦 npm

GHSA-w7jw-789q-3m8p

HIGH

shell-quote quote() does not escape newlines in object .op values

Also known asCVE-2026-9277
Published
Jun 9, 2026
Updated
Jun 10, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed

EPSS Exploitation Probability

via FIRST.org ↗
0.8%probability of exploitation in next 30 days
Lower Risk54th percentile0.00%
0.00%0.45%0.90%1.35%0.1%0.8%0.8%Jun 26Jul 26Jul 26

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

Blast Radius

1 pkg affected

Weekly download volume for affected packages — a proxy for how broadly this vulnerability is deployed.

shell-quotenpm
70.0Mdownloads / week

Description

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:

  1. Direct construction. A caller builds { op: '...\n...' } from external input (e.g. a deserialized argument array) and passes it to quote().
  2. envFn return. parse(cmd, envFn) is documented to splice the return value of envFn into the result array when it is an object. An attacker-influenced data source consulted by envFn can introduce an object token whose .op reaches quote().

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 }.op must be a string from the same allowlist the parser emits (||, &&, ;;, |&, <(, <<<, >>, >&, <&, &, ;, (, ), |, <, >). Anything else throws TypeError. This is the direct fix for the reported issue and removes the entire class of .op injection.
  • { op: 'glob', pattern }.pattern must 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\b was emitted — a latent bug, not security-relevant.)
  • { comment }.comment must be a string with no line terminators (line terminators would end the shell comment and resume command parsing — same injection shape).
  • Any other object shapeTypeError.

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

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
📦npmshell-quote1.1.0&&< 1.8.41.8.4

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for shell-quote. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update shell-quote to 1.8.4 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-w7jw-789q-3m8p is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-w7jw-789q-3m8p is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-w7jw-789q-3m8p. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### 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 emit
O3 Security · Impact-Aware SCA

Is GHSA-w7jw-789q-3m8p in your dependencies?

O3 detects GHSA-w7jw-789q-3m8p across npm dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.