Shescape escape() leaves bracket glob expansion active on Bash, BusyBox, and Dash
Description
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.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
shescapenpm | < 2.1.10 | 2.1.10 |
Affected products
1- Range: < 2.1.10
Patches
16add105c6f6bEscape bracket glob expansion for Bash, BusyBox, and Dash (#2410)
6 files changed · +45 −43
CHANGELOG.md+3 −2 modified@@ -9,7 +9,7 @@ Versioning]. ## [Unreleased] -- _No changes yet_ +- Correct escaping of `[` and `]` for Bash, BusyBox, and Dash. ([#2410]) ## [2.1.9] - 2026-03-06 @@ -47,7 +47,7 @@ Versioning]. ## [2.1.2] - 2025-03-25 -- Correct escaping of `%` escaping for CMD. ([#1916]) +- Correct escaping of `%` for CMD. ([#1916]) ## [2.1.1] - 2024-05-01 @@ -395,6 +395,7 @@ Versioning]. [#2383]: https://github.com/ericcornelissen/shescape/pull/2383 [#2387]: https://github.com/ericcornelissen/shescape/pull/2387 [#2388]: https://github.com/ericcornelissen/shescape/pull/2388 +[#2410]: https://github.com/ericcornelissen/shescape/pull/2410 [552e8ea]: https://github.com/ericcornelissen/shescape/commit/552e8eab56861720b1d4e5474fb65741643358f9 [keep a changelog]: https://keepachangelog.com/en/1.0.0/ [semantic versioning]: https://semver.org/spec/v2.0.0.html
src/internal/unix/bash.js+1 −1 modified@@ -17,7 +17,7 @@ export function getEscapeFunction() { const backslashes = new RegExp("\\\\", "g"); const comments = new RegExp("(^|\\s)#", "g"); const home = new RegExp("(^|[\\s:=])~", "g"); - const specials = new RegExp("([\"$&'()*;<>?`{|])", "g"); + const specials = new RegExp("([\"$&'()*;<>?[\\]`{|])", "g"); const whitespace = new RegExp("([\t ])", "g"); return (arg) => arg
src/internal/unix/busybox.js+1 −1 modified@@ -17,7 +17,7 @@ export function getEscapeFunction() { const backslashes = new RegExp("\\\\", "g"); const comments = new RegExp("(^|\\s)#", "g"); const home = new RegExp("(^|\\s)~", "g"); - const specials = new RegExp("([\"$&'()*;<>?`|])", "g"); + const specials = new RegExp("([\"$&'()*;<>?[\\]`|])", "g"); const whitespace = new RegExp("([\t ])", "g"); return (arg) => arg
src/internal/unix/dash.js+1 −1 modified@@ -15,7 +15,7 @@ function escapeArg(arg) { .replace(/\n/gu, " ") .replace(/\\/gu, "\\\\") .replace(/(?<=^|\s)([#~])/gu, "\\$1") - .replace(/(["$&'()*;<>?`|])/gu, "\\$1") + .replace(/(["$&'()*;<>?[\]`|])/gu, "\\$1") .replace(/([\t ])/gu, "\\$1"); }
test/fixtures/unix.js+38 −38 modified@@ -2236,53 +2236,53 @@ export const escape = { "square brackets ('[', ']')": [ { input: "a[b", - expected: "a[b", + expected: "a\\[b", }, { input: "a[b[c", - expected: "a[b[c", + expected: "a\\[b\\[c", }, { input: "a[", - expected: "a[", + expected: "a\\[", }, { input: "[a", - expected: "[a", + expected: "\\[a", }, { input: "a]b", - expected: "a]b", + expected: "a\\]b", }, { input: "a]b]c", - expected: "a]b]c", + expected: "a\\]b\\]c", }, { input: "a]", - expected: "a]", + expected: "a\\]", }, { input: "]a", - expected: "]a", + expected: "\\]a", }, { input: "a[b]c", - expected: "a[b]c", + expected: "a\\[b\\]c", }, ], "square brackets ('[', ']') + commas (',')": [ { input: "a[b,c]d", - expected: "a[b,c]d", + expected: "a\\[b,c\\]d", }, { input: "a[,b]c", - expected: "a[,b]c", + expected: "a\\[,b\\]c", }, { input: "a[b,]c", - expected: "a[b,]c", + expected: "a\\[b,\\]c", }, ], "curly brackets ('{', '}')": [ @@ -3496,53 +3496,53 @@ export const escape = { "square brackets ('[', ']')": [ { input: "a[b", - expected: "a[b", + expected: "a\\[b", }, { input: "a[b[c", - expected: "a[b[c", + expected: "a\\[b\\[c", }, { input: "a[", - expected: "a[", + expected: "a\\[", }, { input: "[a", - expected: "[a", + expected: "\\[a", }, { input: "a]b", - expected: "a]b", + expected: "a\\]b", }, { input: "a]b]c", - expected: "a]b]c", + expected: "a\\]b\\]c", }, { input: "a]", - expected: "a]", + expected: "a\\]", }, { input: "]a", - expected: "]a", + expected: "\\]a", }, { input: "a[b]c", - expected: "a[b]c", + expected: "a\\[b\\]c", }, ], "square brackets ('[', ']') + commas (',')": [ { input: "a[b,c]d", - expected: "a[b,c]d", + expected: "a\\[b,c\\]d", }, { - input: "a[,b]c", - expected: "a[,b]c", + input: "a[b,]c", + expected: "a\\[b,\\]c", }, { - input: "a[b,]c", - expected: "a[b,]c", + input: "a[,b]c", + expected: "a\\[,b\\]c", }, ], "curly brackets ('{', '}')": [ @@ -5762,53 +5762,53 @@ export const escape = { "square brackets ('[', ']')": [ { input: "a[b", - expected: "a[b", + expected: "a\\[b", }, { input: "a[b[c", - expected: "a[b[c", + expected: "a\\[b\\[c", }, { input: "a[", - expected: "a[", + expected: "a\\[", }, { input: "[a", - expected: "[a", + expected: "\\[a", }, { input: "a]b", - expected: "a]b", + expected: "a\\]b", }, { input: "a]b]c", - expected: "a]b]c", + expected: "a\\]b\\]c", }, { input: "a]", - expected: "a]", + expected: "a\\]", }, { input: "]a", - expected: "]a", + expected: "\\]a", }, { input: "a[b]c", - expected: "a[b]c", + expected: "a\\[b\\]c", }, ], "square brackets ('[', ']') + commas (',')": [ { input: "a[b,c]d", - expected: "a[b,c]d", + expected: "a\\[b,c\\]d", }, { input: "a[b,]c", - expected: "a[b,]c", + expected: "a\\[b,\\]c", }, { input: "a[,b]c", - expected: "a[,b]c", + expected: "a\\[,b\\]c", }, ], "curly brackets ('{', '}')": [
test/fuzz/corpus/e00161899be97ad52705bea9b53f7428734db7270d26032ea6af0c6b953df045+1 −0 added@@ -0,0 +1 @@ +pa[c]kage.json \ No newline at end of file
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-9jfh-9xrq-4vwmghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-32094ghsaADVISORY
- github.com/ericcornelissen/shescape/commit/6add105c6f6b508662bb5ae3b3bdd4c9bcebf37aghsax_refsource_MISCWEB
- github.com/ericcornelissen/shescape/pull/2410ghsaWEB
- github.com/ericcornelissen/shescape/releases/tag/v2.1.10ghsaWEB
- github.com/ericcornelissen/shescape/security/advisories/GHSA-9jfh-9xrq-4vwmghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.