CVE-2026-32094: Shescape escape() leaves bracket glob expansion active on Bash, BusyBox, and Dash
Summary
Shescape#escape() does not escape square-bracket glob syntax for Bash, BusyBox sh, and Dash. Applications that interpolate the return value directly into a shell command string can cause an attacker-controlled value like secret[12] to expand into multiple filesystem matches instead of a single literal argument, turning one argument into multiple trusted-pathname matches.
Details
The unquoted Unix escape helpers never add [ or ] to their “special characters” regexes:
- src/internal/unix/bash.js:14-30 - src/internal/unix/busybox.js:14-30 - src/internal/unix/dash.js:12-19
They escape /? but not brackets, so new Shescape({ shell: "/usr/bin/bash" }).escape("secret[12]") still produces secret[12]. The fixtures (test/fixtures/unix.js:2236-2265, 3496-3525, 5762-5792) are currently written to expect literal brackets for these shells, confirming the behavior. The documentation recommends Shescape#escape() as the fallback for exec when quoting isn’t possible (docs/recipes.md:154-183).
Proof of Concept
Use the published npm tarball without modifications:
shell tmp=$(mktemp -d) cd "$tmp" npm pack shescape@2.1.9 >/dev/null mkdir pkg tar -xzf shescape-2.1.9.tgz -C pkg cd pkg/package npm install --omit=dev
node --input-type=module - <<'NODE' import { mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { execSync } from "node:childprocess"; import { Shescape } from "./src/index.js";
const dir = mkdtempSync(path.join(tmpdir(), "shescape-ghsa-poc-")); writeFileSync(path.join(dir, "secret1"), ""); writeFileSync(path.join(dir, "secret2"), "");
for (const shell of ["/usr/bin/bash", "/usr/bin/dash"]) { const shescape = new Shescape({ shell }); const escaped = shescape.escape("secret[12]"); console.log(${shell} escaped=${escaped}); const out = execSync(printf '<%s>\\n' ${escaped}, { cwd: dir, shell }).toString(); process.stdout.write(out); } NODE
Output:
text /usr/bin/bash escaped=secret[12] <secret1> <secret2> /usr/bin/dash escaped=secret[12] <secret1> <secret2>
Expected: the shell receives secret\[12\], so only one literal argument runs.
Impact
Argument injection: a single untrusted argument expands into multiple pathname matches from the trusted filesystem. This can change command behavior, target unintended files, or leak filenames. Any application calling Shescape#escape() with Bash/BusyBox/Dash shells and interpolating the result into a shell command string is affected.
Other sources
Shescape is a simple shell escape library for JavaScript. Prior to 2.1.10, Shescape#escape() does not escape square-bracket glob syntax for Bash, BusyBox sh, and Dash. Applications that interpolate the return value directly into a shell command string can cause an attacker-controlled value like secret[12] to expand into multiple filesystem matches instead of a single literal argument, turning one argument into multiple trusted-pathname matches. This vulnerability is fixed in 2.1.10.
— MITRE
Affected Software
Remediation
Event History
Frequently Asked Questions
What is the severity of CVE-2026-32094?
CVE-2026-32094 is classified as a high-severity vulnerability due to its potential for command injection attacks.
How do I fix CVE-2026-32094?
To fix CVE-2026-32094, upgrade to version 2.1.10 or later of the shescape package.
What systems are affected by CVE-2026-32094?
CVE-2026-32094 affects systems using Bash, BusyBox sh, and Dash that utilize the shescape#escape() function.
What kind of vulnerabilities does CVE-2026-32094 expose?
CVE-2026-32094 exposes applications to command injection vulnerabilities through improper handling of square-bracket glob syntax.
Can CVE-2026-32094 be exploited remotely?
Yes, CVE-2026-32094 can potentially be exploited remotely if the vulnerable software is exposed to user-controlled input.