Insufficient escaping of line feeds for CMD in shescape
Description
Shescape is a simple shell escape package for JavaScript. Versions prior to 1.5.8 were found to be subject to code injection on windows. This impacts users that use Shescape (any API function) to escape arguments for cmd.exe on Windows An attacker can omit all arguments following their input by including a line feed character ('\n') in the payload. This bug has been patched in [v1.5.8] which you can upgrade to now. No further changes are required. Alternatively, line feed characters ('\n') can be stripped out manually or the user input can be made the last argument (this only limits the impact).
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
shescapenpm | < 1.5.8 | 1.5.8 |
Affected products
1- Range: < 1.5.8
Patches
1aceea7358f72Improve testing and escaping of newlines (#332)
15 files changed · +1101 −27
CHANGELOG.md+7 −1 modified@@ -7,7 +7,12 @@ Versioning]. ## [Unreleased] -- _No changes yet_ +- Fix escaping of line feed characters for Bash, Dash, and Zsh on Unix + systems. ([#332]) +- Fix escaping of line feed and carriage return characters for PowerShell and + CMD on Windows systems. ([#332]) +- Fix escaping of `~` and `{` for Bash on Unix systems with input strings + containing line terminating characters. ([#332]) ## [1.5.7] - 2022-07-06 @@ -151,5 +156,6 @@ Versioning]. [#310]: https://github.com/ericcornelissen/shescape/pull/310 [#322]: https://github.com/ericcornelissen/shescape/pull/322 [#324]: https://github.com/ericcornelissen/shescape/pull/324 +[#332]: https://github.com/ericcornelissen/shescape/pull/332 [keep a changelog]: https://keepachangelog.com/en/1.0.0/ [semantic versioning]: https://semver.org/spec/v2.0.0.html
src/unix.js+6 −3 modified@@ -46,13 +46,14 @@ function escapeArgBash(arg, interpolation, quoted) { if (interpolation) { result = result .replace(/\\/g, "\\\\") + .replace(/\n/g, " ") .replace(/(^|\s)(~|#)/g, "$1\\$2") .replace(/(\*|\?)/g, "\\$1") .replace(/(\$|\;|\&|\|)/g, "\\$1") .replace(/(\(|\)|\<|\>)/g, "\\$1") .replace(/("|'|`)/g, "\\$1") - .replace(/\{(?=(.*?(?:\,|\.).*?)\})/g, "\\{") - .replace(/(?<=\=(?:.*?:)?)(~)(?=\:|\=|\-|\+|\/|0|\s|$)/g, "\\$1"); + .replace(/\{(?=([^]*?(?:\,|\.)[^]*?)\})/g, "\\{") + .replace(/(?<=\=(?:[^]*?:)?)(~)(?=\:|\=|\-|\+|\/|0|\s|$)/g, "\\$1"); } else if (quoted) { result = result.replace(/'/g, `'\\''`); } @@ -74,12 +75,13 @@ function escapeArgDash(arg, interpolation, quoted) { if (interpolation) { result = result .replace(/\\/g, "\\\\") + .replace(/\n/g, " ") .replace(/(^|\s)(~|#)/g, "$1\\$2") .replace(/(\*|\?)/g, "\\$1") .replace(/(\$|\;|\&|\|)/g, "\\$1") .replace(/(\(|\)|\<|\>)/g, "\\$1") .replace(/("|'|`)/g, "\\$1") - .replace(/\{(?=(.*?(?:\,|\.).*?)\})/g, "\\{"); + .replace(/\{(?=([^]*?(?:\,|\.)[^]*?)\})/g, "\\{"); } else if (quoted) { result = result.replace(/'/g, `'\\''`); } @@ -101,6 +103,7 @@ function escapeArgZsh(arg, interpolation, quoted) { if (interpolation) { result = result .replace(/\\/g, "\\\\") + .replace(/\n/g, " ") .replace(/(^|\s)(~|#|=)/g, "$1\\$2") .replace(/(\*|\?)/g, "\\$1") .replace(/(\$|\;|\&|\|)/g, "\\$1")
src/win.js+2 −1 modified@@ -33,7 +33,7 @@ const binPowerShell = "powershell.exe"; * @returns {string} The escaped argument. */ function escapeArgCmd(arg, interpolation, quoted) { - let result = arg.replace(/\u0000/g, ""); + let result = arg.replace(/\u0000/g, "").replace(/\n|\r/g, " "); if (interpolation) { result = result @@ -64,6 +64,7 @@ function escapeArgPowerShell(arg, interpolation, quoted) { if (interpolation) { result = result + .replace(/\n|\r/g, " ") .replace(/(^|\s)((?:\*|[1-6])?)(>)/g, "$1$2`$3") .replace(/(^|\s)(<|@|#|-|\:|\])/g, "$1`$2") .replace(/(,|\;|\&|\|)/g, "`$1")
test/fixtures/unix.cjs+684 −0 modified@@ -30,6 +30,172 @@ module.exports.escape = { expected: { interpolation: "abc", noInterpolation: "abc" }, }, ], + "whitespace (\\s)": [ + { + input: "foo bar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo\nbar", + expected: { interpolation: "foo bar", noInterpolation: "foo\nbar" }, + }, + { + input: "foo\vbar", + expected: { interpolation: "foo\vbar", noInterpolation: "foo\vbar" }, + }, + { + input: "foo\fbar", + expected: { interpolation: "foo\fbar", noInterpolation: "foo\fbar" }, + }, + { + input: "foo\rbar", + expected: { interpolation: "foo\rbar", noInterpolation: "foo\rbar" }, + }, + { + input: "foo bar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo\u0085bar", + expected: { + interpolation: "foo\u0085bar", + noInterpolation: "foo\u0085bar", + }, + }, + { + input: "foo\u00A0bar", + expected: { + interpolation: "foo\u00A0bar", + noInterpolation: "foo\u00A0bar", + }, + }, + { + input: "foo\u2000bar", + expected: { + interpolation: "foo\u2000bar", + noInterpolation: "foo\u2000bar", + }, + }, + { + input: "foo\u2001bar", + expected: { + interpolation: "foo\u2001bar", + noInterpolation: "foo\u2001bar", + }, + }, + { + input: "foo\u2002bar", + expected: { + interpolation: "foo\u2002bar", + noInterpolation: "foo\u2002bar", + }, + }, + { + input: "foo\u2003bar", + expected: { + interpolation: "foo\u2003bar", + noInterpolation: "foo\u2003bar", + }, + }, + { + input: "foo\u2004bar", + expected: { + interpolation: "foo\u2004bar", + noInterpolation: "foo\u2004bar", + }, + }, + { + input: "foo\u2005bar", + expected: { + interpolation: "foo\u2005bar", + noInterpolation: "foo\u2005bar", + }, + }, + { + input: "foo\u2006bar", + expected: { + interpolation: "foo\u2006bar", + noInterpolation: "foo\u2006bar", + }, + }, + { + input: "foo\u2007bar", + expected: { + interpolation: "foo\u2007bar", + noInterpolation: "foo\u2007bar", + }, + }, + { + input: "foo\u2008bar", + expected: { + interpolation: "foo\u2008bar", + noInterpolation: "foo\u2008bar", + }, + }, + { + input: "foo\u2009bar", + expected: { + interpolation: "foo\u2009bar", + noInterpolation: "foo\u2009bar", + }, + }, + { + input: "foo\u200Abar", + expected: { + interpolation: "foo\u200Abar", + noInterpolation: "foo\u200Abar", + }, + }, + { + input: "foo\u2028bar", + expected: { + interpolation: "foo\u2028bar", + noInterpolation: "foo\u2028bar", + }, + }, + { + input: "foo\u2029bar", + expected: { + interpolation: "foo\u2029bar", + noInterpolation: "foo\u2029bar", + }, + }, + { + input: "foo\u202Fbar", + expected: { + interpolation: "foo\u202Fbar", + noInterpolation: "foo\u202Fbar", + }, + }, + { + input: "foo\u205Fbar", + expected: { + interpolation: "foo\u205Fbar", + noInterpolation: "foo\u205Fbar", + }, + }, + { + input: "foo\u3000bar", + expected: { + interpolation: "foo\u3000bar", + noInterpolation: "foo\u3000bar", + }, + }, + { + input: "foo\uFEFFbar", + expected: { + interpolation: "foo\uFEFFbar", + noInterpolation: "foo\uFEFFbar", + }, + }, + { + input: "foo\n\rbar", + expected: { + interpolation: "foo \rbar", + noInterpolation: "foo\n\rbar", + }, + }, + ], 'single quotes ("\'")': [ { input: "a'b", @@ -137,6 +303,24 @@ module.exports.escape = { input: "a=b:~:", expected: { interpolation: "a=b:\\~:", noInterpolation: "a=b:~:" }, }, + { + input: "a=\r:~:", + expected: { interpolation: "a=\r:\\~:", noInterpolation: "a=\r:~:" }, + }, + { + input: "a=\u2028:~:", + expected: { + interpolation: "a=\u2028:\\~:", + noInterpolation: "a=\u2028:~:", + }, + }, + { + input: "a=\u2029:~:", + expected: { + interpolation: "a=\u2029:\\~:", + noInterpolation: "a=\u2029:~:", + }, + }, { input: "a=b:~:c", expected: { interpolation: "a=b:\\~:c", noInterpolation: "a=b:~:c" }, @@ -391,6 +575,90 @@ module.exports.escape = { input: "a{0..2}b", expected: { interpolation: "a\\{0..2}b", noInterpolation: "a{0..2}b" }, }, + { + input: "a{\u000Db,c}d", + expected: { + interpolation: "a\\{\u000Db,c}d", + noInterpolation: "a{\u000Db,c}d", + }, + }, + { + input: "a{\u2028b,c}d", + expected: { + interpolation: "a\\{\u2028b,c}d", + noInterpolation: "a{\u2028b,c}d", + }, + }, + { + input: "a{\u2029b,c}d", + expected: { + interpolation: "a\\{\u2029b,c}d", + noInterpolation: "a{\u2029b,c}d", + }, + }, + { + input: "a{b,c\u000D}d", + expected: { + interpolation: "a\\{b,c\u000D}d", + noInterpolation: "a{b,c\u000D}d", + }, + }, + { + input: "a{b,c\u2028}d", + expected: { + interpolation: "a\\{b,c\u2028}d", + noInterpolation: "a{b,c\u2028}d", + }, + }, + { + input: "a{b,c\u2029}d", + expected: { + interpolation: "a\\{b,c\u2029}d", + noInterpolation: "a{b,c\u2029}d", + }, + }, + { + input: "a{\u000D0..2}b", + expected: { + interpolation: "a\\{\u000D0..2}b", + noInterpolation: "a{\u000D0..2}b", + }, + }, + { + input: "a{\u20280..2}b", + expected: { + interpolation: "a\\{\u20280..2}b", + noInterpolation: "a{\u20280..2}b", + }, + }, + { + input: "a{\u20290..2}b", + expected: { + interpolation: "a\\{\u20290..2}b", + noInterpolation: "a{\u20290..2}b", + }, + }, + { + input: "a{0..2\u000D}b", + expected: { + interpolation: "a\\{0..2\u000D}b", + noInterpolation: "a{0..2\u000D}b", + }, + }, + { + input: "a{0..2\u2028}b", + expected: { + interpolation: "a\\{0..2\u2028}b", + noInterpolation: "a{0..2\u2028}b", + }, + }, + { + input: "a{0..2\u2029}b", + expected: { + interpolation: "a\\{0..2\u2029}b", + noInterpolation: "a{0..2\u2029}b", + }, + }, ], "angle brackets ('<', '>')": [ { @@ -439,6 +707,172 @@ module.exports.escape = { expected: { interpolation: "abc", noInterpolation: "abc" }, }, ], + "whitespace (\\s)": [ + { + input: "foo bar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo\nbar", + expected: { interpolation: "foo bar", noInterpolation: "foo\nbar" }, + }, + { + input: "foo\vbar", + expected: { interpolation: "foo\vbar", noInterpolation: "foo\vbar" }, + }, + { + input: "foo\fbar", + expected: { interpolation: "foo\fbar", noInterpolation: "foo\fbar" }, + }, + { + input: "foo\rbar", + expected: { interpolation: "foo\rbar", noInterpolation: "foo\rbar" }, + }, + { + input: "foo bar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo\u0085bar", + expected: { + interpolation: "foo\u0085bar", + noInterpolation: "foo\u0085bar", + }, + }, + { + input: "foo\u00A0bar", + expected: { + interpolation: "foo\u00A0bar", + noInterpolation: "foo\u00A0bar", + }, + }, + { + input: "foo\u2000bar", + expected: { + interpolation: "foo\u2000bar", + noInterpolation: "foo\u2000bar", + }, + }, + { + input: "foo\u2001bar", + expected: { + interpolation: "foo\u2001bar", + noInterpolation: "foo\u2001bar", + }, + }, + { + input: "foo\u2002bar", + expected: { + interpolation: "foo\u2002bar", + noInterpolation: "foo\u2002bar", + }, + }, + { + input: "foo\u2003bar", + expected: { + interpolation: "foo\u2003bar", + noInterpolation: "foo\u2003bar", + }, + }, + { + input: "foo\u2004bar", + expected: { + interpolation: "foo\u2004bar", + noInterpolation: "foo\u2004bar", + }, + }, + { + input: "foo\u2005bar", + expected: { + interpolation: "foo\u2005bar", + noInterpolation: "foo\u2005bar", + }, + }, + { + input: "foo\u2006bar", + expected: { + interpolation: "foo\u2006bar", + noInterpolation: "foo\u2006bar", + }, + }, + { + input: "foo\u2007bar", + expected: { + interpolation: "foo\u2007bar", + noInterpolation: "foo\u2007bar", + }, + }, + { + input: "foo\u2008bar", + expected: { + interpolation: "foo\u2008bar", + noInterpolation: "foo\u2008bar", + }, + }, + { + input: "foo\u2009bar", + expected: { + interpolation: "foo\u2009bar", + noInterpolation: "foo\u2009bar", + }, + }, + { + input: "foo\u200Abar", + expected: { + interpolation: "foo\u200Abar", + noInterpolation: "foo\u200Abar", + }, + }, + { + input: "foo\u2028bar", + expected: { + interpolation: "foo\u2028bar", + noInterpolation: "foo\u2028bar", + }, + }, + { + input: "foo\u2029bar", + expected: { + interpolation: "foo\u2029bar", + noInterpolation: "foo\u2029bar", + }, + }, + { + input: "foo\u202Fbar", + expected: { + interpolation: "foo\u202Fbar", + noInterpolation: "foo\u202Fbar", + }, + }, + { + input: "foo\u205Fbar", + expected: { + interpolation: "foo\u205Fbar", + noInterpolation: "foo\u205Fbar", + }, + }, + { + input: "foo\u3000bar", + expected: { + interpolation: "foo\u3000bar", + noInterpolation: "foo\u3000bar", + }, + }, + { + input: "foo\uFEFFbar", + expected: { + interpolation: "foo\uFEFFbar", + noInterpolation: "foo\uFEFFbar", + }, + }, + { + input: "foo\n\rbar", + expected: { + interpolation: "foo \rbar", + noInterpolation: "foo\n\rbar", + }, + }, + ], 'single quotes ("\'")': [ { input: "a'b", @@ -800,6 +1234,90 @@ module.exports.escape = { input: "a{0..2}b", expected: { interpolation: "a\\{0..2}b", noInterpolation: "a{0..2}b" }, }, + { + input: "a{\u000Db,c}d", + expected: { + interpolation: "a\\{\u000Db,c}d", + noInterpolation: "a{\u000Db,c}d", + }, + }, + { + input: "a{\u2028b,c}d", + expected: { + interpolation: "a\\{\u2028b,c}d", + noInterpolation: "a{\u2028b,c}d", + }, + }, + { + input: "a{\u2029b,c}d", + expected: { + interpolation: "a\\{\u2029b,c}d", + noInterpolation: "a{\u2029b,c}d", + }, + }, + { + input: "a{b,c\u000D}d", + expected: { + interpolation: "a\\{b,c\u000D}d", + noInterpolation: "a{b,c\u000D}d", + }, + }, + { + input: "a{b,c\u2028}d", + expected: { + interpolation: "a\\{b,c\u2028}d", + noInterpolation: "a{b,c\u2028}d", + }, + }, + { + input: "a{b,c\u2029}d", + expected: { + interpolation: "a\\{b,c\u2029}d", + noInterpolation: "a{b,c\u2029}d", + }, + }, + { + input: "a{\u000D0..2}b", + expected: { + interpolation: "a\\{\u000D0..2}b", + noInterpolation: "a{\u000D0..2}b", + }, + }, + { + input: "a{\u20280..2}b", + expected: { + interpolation: "a\\{\u20280..2}b", + noInterpolation: "a{\u20280..2}b", + }, + }, + { + input: "a{\u20290..2}b", + expected: { + interpolation: "a\\{\u20290..2}b", + noInterpolation: "a{\u20290..2}b", + }, + }, + { + input: "a{0..2\u000D}b", + expected: { + interpolation: "a\\{0..2\u000D}b", + noInterpolation: "a{0..2\u000D}b", + }, + }, + { + input: "a{0..2\u2028}b", + expected: { + interpolation: "a\\{0..2\u2028}b", + noInterpolation: "a{0..2\u2028}b", + }, + }, + { + input: "a{0..2\u2029}b", + expected: { + interpolation: "a\\{0..2\u2029}b", + noInterpolation: "a{0..2\u2029}b", + }, + }, ], "angle brackets ('<', '>')": [ { @@ -848,6 +1366,172 @@ module.exports.escape = { expected: { interpolation: "abc", noInterpolation: "abc" }, }, ], + "whitespace (\\s)": [ + { + input: "foo bar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo\nbar", + expected: { interpolation: "foo bar", noInterpolation: "foo\nbar" }, + }, + { + input: "foo\vbar", + expected: { interpolation: "foo\vbar", noInterpolation: "foo\vbar" }, + }, + { + input: "foo\fbar", + expected: { interpolation: "foo\fbar", noInterpolation: "foo\fbar" }, + }, + { + input: "foo\rbar", + expected: { interpolation: "foo\rbar", noInterpolation: "foo\rbar" }, + }, + { + input: "foo bar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo\u0085bar", + expected: { + interpolation: "foo\u0085bar", + noInterpolation: "foo\u0085bar", + }, + }, + { + input: "foo\u00A0bar", + expected: { + interpolation: "foo\u00A0bar", + noInterpolation: "foo\u00A0bar", + }, + }, + { + input: "foo\u2000bar", + expected: { + interpolation: "foo\u2000bar", + noInterpolation: "foo\u2000bar", + }, + }, + { + input: "foo\u2001bar", + expected: { + interpolation: "foo\u2001bar", + noInterpolation: "foo\u2001bar", + }, + }, + { + input: "foo\u2002bar", + expected: { + interpolation: "foo\u2002bar", + noInterpolation: "foo\u2002bar", + }, + }, + { + input: "foo\u2003bar", + expected: { + interpolation: "foo\u2003bar", + noInterpolation: "foo\u2003bar", + }, + }, + { + input: "foo\u2004bar", + expected: { + interpolation: "foo\u2004bar", + noInterpolation: "foo\u2004bar", + }, + }, + { + input: "foo\u2005bar", + expected: { + interpolation: "foo\u2005bar", + noInterpolation: "foo\u2005bar", + }, + }, + { + input: "foo\u2006bar", + expected: { + interpolation: "foo\u2006bar", + noInterpolation: "foo\u2006bar", + }, + }, + { + input: "foo\u2007bar", + expected: { + interpolation: "foo\u2007bar", + noInterpolation: "foo\u2007bar", + }, + }, + { + input: "foo\u2008bar", + expected: { + interpolation: "foo\u2008bar", + noInterpolation: "foo\u2008bar", + }, + }, + { + input: "foo\u2009bar", + expected: { + interpolation: "foo\u2009bar", + noInterpolation: "foo\u2009bar", + }, + }, + { + input: "foo\u200Abar", + expected: { + interpolation: "foo\u200Abar", + noInterpolation: "foo\u200Abar", + }, + }, + { + input: "foo\u2028bar", + expected: { + interpolation: "foo\u2028bar", + noInterpolation: "foo\u2028bar", + }, + }, + { + input: "foo\u2029bar", + expected: { + interpolation: "foo\u2029bar", + noInterpolation: "foo\u2029bar", + }, + }, + { + input: "foo\u202Fbar", + expected: { + interpolation: "foo\u202Fbar", + noInterpolation: "foo\u202Fbar", + }, + }, + { + input: "foo\u205Fbar", + expected: { + interpolation: "foo\u205Fbar", + noInterpolation: "foo\u205Fbar", + }, + }, + { + input: "foo\u3000bar", + expected: { + interpolation: "foo\u3000bar", + noInterpolation: "foo\u3000bar", + }, + }, + { + input: "foo\uFEFFbar", + expected: { + interpolation: "foo\uFEFFbar", + noInterpolation: "foo\uFEFFbar", + }, + }, + { + input: "foo\n\rbar", + expected: { + interpolation: "foo \rbar", + noInterpolation: "foo\n\rbar", + }, + }, + ], 'single quotes ("\'")': [ { input: "a'b",
test/fixtures/win.cjs+346 −0 modified@@ -30,6 +30,179 @@ module.exports.escape = { expected: { interpolation: "abc", noInterpolation: "abc" }, }, ], + "whitespace (\\s)": [ + { + input: "foo bar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo\nbar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo\vbar", + expected: { interpolation: "foo\vbar", noInterpolation: "foo\vbar" }, + }, + { + input: "foo\fbar", + expected: { interpolation: "foo\fbar", noInterpolation: "foo\fbar" }, + }, + { + input: "foo\rbar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo bar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo\u0085bar", + expected: { + interpolation: "foo\u0085bar", + noInterpolation: "foo\u0085bar", + }, + }, + { + input: "foo\u00A0bar", + expected: { + interpolation: "foo\u00A0bar", + noInterpolation: "foo\u00A0bar", + }, + }, + { + input: "foo\u1680bar", + expected: { + interpolation: "foo\u1680bar", + noInterpolation: "foo\u1680bar", + }, + }, + { + input: "foo\u2000bar", + expected: { + interpolation: "foo\u2000bar", + noInterpolation: "foo\u2000bar", + }, + }, + { + input: "foo\u2001bar", + expected: { + interpolation: "foo\u2001bar", + noInterpolation: "foo\u2001bar", + }, + }, + { + input: "foo\u2002bar", + expected: { + interpolation: "foo\u2002bar", + noInterpolation: "foo\u2002bar", + }, + }, + { + input: "foo\u2003bar", + expected: { + interpolation: "foo\u2003bar", + noInterpolation: "foo\u2003bar", + }, + }, + { + input: "foo\u2004bar", + expected: { + interpolation: "foo\u2004bar", + noInterpolation: "foo\u2004bar", + }, + }, + { + input: "foo\u2005bar", + expected: { + interpolation: "foo\u2005bar", + noInterpolation: "foo\u2005bar", + }, + }, + { + input: "foo\u2006bar", + expected: { + interpolation: "foo\u2006bar", + noInterpolation: "foo\u2006bar", + }, + }, + { + input: "foo\u2007bar", + expected: { + interpolation: "foo\u2007bar", + noInterpolation: "foo\u2007bar", + }, + }, + { + input: "foo\u2008bar", + expected: { + interpolation: "foo\u2008bar", + noInterpolation: "foo\u2008bar", + }, + }, + { + input: "foo\u2009bar", + expected: { + interpolation: "foo\u2009bar", + noInterpolation: "foo\u2009bar", + }, + }, + { + input: "foo\u200Abar", + expected: { + interpolation: "foo\u200Abar", + noInterpolation: "foo\u200Abar", + }, + }, + { + input: "foo\u2028bar", + expected: { + interpolation: "foo\u2028bar", + noInterpolation: "foo\u2028bar", + }, + }, + { + input: "foo\u2029bar", + expected: { + interpolation: "foo\u2029bar", + noInterpolation: "foo\u2029bar", + }, + }, + { + input: "foo\u202Fbar", + expected: { + interpolation: "foo\u202Fbar", + noInterpolation: "foo\u202Fbar", + }, + }, + { + input: "foo\u205Fbar", + expected: { + interpolation: "foo\u205Fbar", + noInterpolation: "foo\u205Fbar", + }, + }, + { + input: "foo\u3000bar", + expected: { + interpolation: "foo\u3000bar", + noInterpolation: "foo\u3000bar", + }, + }, + { + input: "foo\uFEFFbar", + expected: { + interpolation: "foo\uFEFFbar", + noInterpolation: "foo\uFEFFbar", + }, + }, + { + input: "foo\n\rbar", + expected: { + interpolation: "foo bar", + noInterpolation: "foo bar", + }, + }, + ], 'single quotes ("\'")': [ { input: "a'b", @@ -399,6 +572,179 @@ module.exports.escape = { expected: { interpolation: "abc", noInterpolation: "abc" }, }, ], + "whitespace (\\s)": [ + { + input: "foo bar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo\nbar", + expected: { interpolation: "foo bar", noInterpolation: "foo\nbar" }, + }, + { + input: "foo\vbar", + expected: { interpolation: "foo\vbar", noInterpolation: "foo\vbar" }, + }, + { + input: "foo\fbar", + expected: { interpolation: "foo\fbar", noInterpolation: "foo\fbar" }, + }, + { + input: "foo\rbar", + expected: { interpolation: "foo bar", noInterpolation: "foo\rbar" }, + }, + { + input: "foo bar", + expected: { interpolation: "foo bar", noInterpolation: "foo bar" }, + }, + { + input: "foo\u0085bar", + expected: { + interpolation: "foo\u0085bar", + noInterpolation: "foo\u0085bar", + }, + }, + { + input: "foo\u00A0bar", + expected: { + interpolation: "foo\u00A0bar", + noInterpolation: "foo\u00A0bar", + }, + }, + { + input: "foo\u1680bar", + expected: { + interpolation: "foo\u1680bar", + noInterpolation: "foo\u1680bar", + }, + }, + { + input: "foo\u2000bar", + expected: { + interpolation: "foo\u2000bar", + noInterpolation: "foo\u2000bar", + }, + }, + { + input: "foo\u2001bar", + expected: { + interpolation: "foo\u2001bar", + noInterpolation: "foo\u2001bar", + }, + }, + { + input: "foo\u2002bar", + expected: { + interpolation: "foo\u2002bar", + noInterpolation: "foo\u2002bar", + }, + }, + { + input: "foo\u2003bar", + expected: { + interpolation: "foo\u2003bar", + noInterpolation: "foo\u2003bar", + }, + }, + { + input: "foo\u2004bar", + expected: { + interpolation: "foo\u2004bar", + noInterpolation: "foo\u2004bar", + }, + }, + { + input: "foo\u2005bar", + expected: { + interpolation: "foo\u2005bar", + noInterpolation: "foo\u2005bar", + }, + }, + { + input: "foo\u2006bar", + expected: { + interpolation: "foo\u2006bar", + noInterpolation: "foo\u2006bar", + }, + }, + { + input: "foo\u2007bar", + expected: { + interpolation: "foo\u2007bar", + noInterpolation: "foo\u2007bar", + }, + }, + { + input: "foo\u2008bar", + expected: { + interpolation: "foo\u2008bar", + noInterpolation: "foo\u2008bar", + }, + }, + { + input: "foo\u2009bar", + expected: { + interpolation: "foo\u2009bar", + noInterpolation: "foo\u2009bar", + }, + }, + { + input: "foo\u200Abar", + expected: { + interpolation: "foo\u200Abar", + noInterpolation: "foo\u200Abar", + }, + }, + { + input: "foo\u2028bar", + expected: { + interpolation: "foo\u2028bar", + noInterpolation: "foo\u2028bar", + }, + }, + { + input: "foo\u2029bar", + expected: { + interpolation: "foo\u2029bar", + noInterpolation: "foo\u2029bar", + }, + }, + { + input: "foo\u202Fbar", + expected: { + interpolation: "foo\u202Fbar", + noInterpolation: "foo\u202Fbar", + }, + }, + { + input: "foo\u205Fbar", + expected: { + interpolation: "foo\u205Fbar", + noInterpolation: "foo\u205Fbar", + }, + }, + { + input: "foo\u3000bar", + expected: { + interpolation: "foo\u3000bar", + noInterpolation: "foo\u3000bar", + }, + }, + { + input: "foo\uFEFFbar", + expected: { + interpolation: "foo\uFEFFbar", + noInterpolation: "foo\uFEFFbar", + }, + }, + { + input: "foo\n\rbar", + expected: { + interpolation: "foo bar", + noInterpolation: "foo\n\rbar", + }, + }, + ], 'single quotes ("\'")': [ { input: "a'b",
test/fuzz/_common.cjs+20 −17 modified@@ -25,26 +25,35 @@ function isShellPowerShell(shell) { } function getExpectedOutput({ arg, shell }, normalizeWhitespace) { - if (isShellCmd(shell)) { - arg = arg.replace(/[\n\r]+/g, ""); // Remove newline characters, like prep - } - arg = arg.replace(/\u{0}/gu, ""); // Remove null characters, like Shescape if (normalizeWhitespace) { - // The characters to normalize depend on the shell - // Trim the string like any shell would + // Trim the string, like the shell if (isShellPowerShell(shell)) { - arg = arg.replace(/^[\s\u0085]+|[\s\u0085]+$/g, ""); + arg = arg.replace( + /^[ \t\n\v\f\r\u0085\u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]+|[ \t\n\v\f\r\u0085\u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]+$/g, + "" + ); + } else if (isShellCmd(shell)) { + arg = arg.replace(/^[ \t\n\r]+|[ \t\n\r]+$/g, ""); } else { - arg = arg.replace(/^[ \t]+|[ \t]+$/g, ""); + arg = arg.replace(/^[ \t\n]+|[ \t\n]+$/g, ""); } - // Convert spacing between arguments to a single space, like the shell would + // Convert spacing between arguments to a single space, like the shell if (isShellPowerShell(shell)) { - arg = arg.replace(/(\s|\u0085)+/g, " "); + arg = arg.replace( + /[ \t\n\v\f\r\u0085\u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]+/g, + " " + ); + } else if (isShellCmd(shell)) { + arg = arg.replace(/[ \t\n\r]+/g, " "); } else { - arg = arg.replace(/[ \t]+/g, " "); + arg = arg.replace(/[ \t\n]+/g, " "); + } + } else { + if (isShellCmd(shell)) { + arg = arg.replace(/[\n\r]/g, " "); // Change newlines to spaces, like Shescape } } @@ -57,12 +66,6 @@ function getFuzzShell() { } function prepareArg({ arg, quoted, shell }, disableExtraWindowsPreparations) { - if (isShellCmd(shell)) { - // In CMD ignores everything after a newline (\n) character. This alteration - // is required even when `disableExtraWindowsPreparations` is true. - arg = arg.replace(/[\n\r]+/g, ""); - } - if (isWindows() && !disableExtraWindowsPreparations) { // Node on Windows ... if (isShellCmd(shell)) {
test/fuzz/corpus/036053b103374e82177446b4e083ccb6a22da06e0375582d622632775c8a938a+1 −0 added@@ -0,0 +1 @@ + � \ No newline at end of file
test/fuzz/corpus/31ed7643aba69fe2d776af3aee587bb7899165af5ed3846c6f70327f2eec4713+1 −0 added@@ -0,0 +1 @@ +u�0:�\(?\ \ No newline at end of file
test/fuzz/corpus/7ef6c55f814adceff17a05c032ba6ec89483e37addcfd96bd13281f5de6716fa+1 −0 added@@ -0,0 +1 @@ +!�! �
test/fuzz/corpus/dece2a606846120af17949c2d758b7df475449689d043a04a1ba63953326e5bb+2 −0 added@@ -0,0 +1,2 @@ +foo= +:~
test/fuzz/corpus/f1d97ce94d0c9dd109dc27538786781da634bbed1df58877cac3c44e4344f3dd+1 −0 added@@ -0,0 +1 @@ +#� ��"3dw�� \ No newline at end of file
test/fuzz/corpus/f28febc41472c437122c2a44b66ccf7dcefdd19876fe4d9370ece44b5b2deb13+1 −0 added@@ -0,0 +1 @@ +q=� �3�[�:~ \ No newline at end of file
test/fuzz/corpus/faf3c25ec7c017c2cc21a5af0f5584557d8a0c7340c68249076a86a2c4ce74fb+2 −0 added@@ -0,0 +1,2 @@ +a{ +b,c}d
test/fuzz/exec.test.cjs+0 −4 modified@@ -43,8 +43,6 @@ function checkWithShell(arg) { } function checkWithoutShellUsingInterpolation(arg) { - arg = arg.replace(/[\n\r]+/g, ""); - const argInfo = { arg, shell: undefined, quoted: false }; const preparedArg = common.prepareArg(argInfo); @@ -60,8 +58,6 @@ function checkWithoutShellUsingInterpolation(arg) { } function checkWithShellUsingInterpolation(arg) { - arg = arg.replace(/[\n\r]+/g, ""); - const shell = common.getFuzzShell() || true; const argInfo = { arg, shell, quoted: false }; const execOptions = { shell };
test/unit/_macros.js+27 −1 modified@@ -28,7 +28,33 @@ export const escape = test.macro({ t.is(actual, expected); }, title(_, { input, interpolation, quoted, shellName }) { - input = input.replace(/\u{0}/gu, "\\x00").replace(/\t/g, "\\t"); + input = input + .replace(/\u0000/g, "\\u{0000}") + .replace(/\u0009/g, "\\t") + .replace(/\u000A/g, "\\n") + .replace(/\u000B/g, "\\v") + .replace(/\u000C/g, "\\f") + .replace(/\u000D/g, "\\r") + .replace(/\u0085/g, "\\u{0085}") + .replace(/\u00A0/g, "\\u{00A0}") + .replace(/\u1680/g, "\\u{1680}") + .replace(/\u2000/g, "\\u{2000}") + .replace(/\u2001/g, "\\u{2001}") + .replace(/\u2002/g, "\\u{2002}") + .replace(/\u2003/g, "\\u{2003}") + .replace(/\u2004/g, "\\u{2004}") + .replace(/\u2005/g, "\\u{2005}") + .replace(/\u2006/g, "\\u{2006}") + .replace(/\u2007/g, "\\u{2007}") + .replace(/\u2008/g, "\\u{2008}") + .replace(/\u2009/g, "\\u{2009}") + .replace(/\u200A/g, "\\u{200A}") + .replace(/\u2028/g, "\\u{2028}") + .replace(/\u2029/g, "\\u{2029}") + .replace(/\u202F/g, "\\u{202F}") + .replace(/\u205F/g, "\\u{205F}") + .replace(/\u3000/g, "\\u{3000}") + .replace(/\uFEFF/g, "\\u{FEFF}"); interpolation = interpolation ? "interpolation" : "no interpolation"; quoted = quoted ? "quoted" : "not quoted";
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-jjc5-fp7p-6f8wghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-31179ghsaADVISORY
- github.com/ericcornelissen/shescape/commit/aceea7358f7222984e21260381ebc5ec4543b76fghsaWEB
- github.com/ericcornelissen/shescape/pull/332ghsax_refsource_MISCWEB
- github.com/ericcornelissen/shescape/releases/tag/v1.5.8ghsax_refsource_MISCWEB
- github.com/ericcornelissen/shescape/security/advisories/GHSA-jjc5-fp7p-6f8wghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.