{"id":"GHSA-w7jw-789q-3m8p","summary":"shell-quote quote() does not escape newlines in object .op values","details":"### Summary\n\n`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.\n\nThe 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:\n\n1. **Direct construction.** A caller builds `{ op: '...\\n...' }` from external input (e.g. a deserialized argument array) and passes it to `quote()`.\n2. **`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()`.\n\n### Impact\n\nShell 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.\n\n### PoC\n\n```js\nconst { parse, quote } = require('shell-quote');\n\n// Direct construction\nquote([{ op: ';\\nid' }]);\n// → \"\\;\\n\\\\i\\\\d\"  ← literal newline; second line executes as a command\n\n// Via parse() with an envFn returning attacker-shaped objects\nconst tokens = parse('echo $X', () =\u003e ({ op: ';\\nid' }));\nrequire('child_process').execSync(quote(tokens), { shell: true });\n// Executes `id` after `echo \\;`.\n```\n\nConfirmed under `sh`, `bash`, `dash`, and `zsh`.\n\n### Patch\n\nFixed by replacing the per-character escape with strict shape validation in `quote()`. The object-token branch now:\n\n- **`{ op }`** — `.op` must be a string from the same allowlist the parser emits (`||`, `&&`, `;;`, `|&`, `\u003c(`, `\u003c\u003c\u003c`, `\u003e\u003e`, `\u003e&`, `\u003c&`, `&`, `;`, `(`, `)`, `|`, `\u003c`, `\u003e`). Anything else throws `TypeError`. This is the direct fix for the reported issue and removes the entire class of `.op` injection.\n- **`{ 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.)\n- **`{ comment }`** — `.comment` must be a string with no line terminators (line terminators would end the shell comment and resume command parsing — same injection shape).\n- **Any other object shape** — `TypeError`.\n\nThe 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`).\n\n### Workarounds\n\nPrior 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.\n\n### Credits\n\nReported by Akshat Sinha","aliases":["CVE-2026-9277"],"modified":"2026-07-17T21:07:51.013995740Z","published":"2026-06-09T14:27:15Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-06-09T14:27:15Z","nvd_published_at":"2026-05-22T14:16:30Z","cwe_ids":["CWE-77","CWE-78"],"severity":"CRITICAL"},"references":[{"type":"WEB","url":"https://github.com/ljharb/shell-quote/security/advisories/GHSA-w7jw-789q-3m8p"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-9277"},{"type":"WEB","url":"https://github.com/ljharb/shell-quote/commit/1518179"},{"type":"PACKAGE","url":"https://github.com/ljharb/shell-quote"},{"type":"WEB","url":"https://www.npmjs.com/package/shell-quote"},{"type":"WEB","url":"http://www.openwall.com/lists/oss-security/2026/05/23/2"}],"affected":[{"package":{"name":"shell-quote","ecosystem":"npm","purl":"pkg:npm/shell-quote"},"ranges":[{"type":"SEMVER","events":[{"introduced":"1.1.0"},{"fixed":"1.8.4"}]}],"database_specific":{"last_known_affected_version_range":"\u003c= 1.8.3","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/06/GHSA-w7jw-789q-3m8p/GHSA-w7jw-789q-3m8p.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"},{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"}]}