Skip to content

Commit f57cf02

Browse files
committed
feat: bumping shescape, adding hardening
1 parent f83f67a commit f57cf02

File tree

2 files changed

+25
-5
lines changed

2 files changed

+25
-5
lines changed

lib/sub-process.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as childProcess from 'child_process';
2-
import { quoteAll } from 'shescape/stateless';
2+
import { escapeAll, quoteAll } from 'shescape/stateless';
3+
import * as os from 'node:os';
34

45
export function execute(
56
command: string,
@@ -9,13 +10,26 @@ export function execute(
910
const spawnOptions: {
1011
shell: boolean;
1112
cwd?: string;
12-
} = { shell: true };
13+
} = { shell: false };
1314
if (options && options.cwd) {
1415
spawnOptions.cwd = options.cwd;
1516
}
1617

17-
args = quoteAll(args, { flagProtection: false });
18-
18+
if (args) {
19+
// Best practices, also security-wise, is to not invoke processes in a shell, but as a stand-alone command.
20+
// However, on Windows, we need to invoke the command in a shell, due to internal NodeJS problems with this approach
21+
// see: https://nodejs.org/docs/latest-v24.x/api/child_process.html#spawning-bat-and-cmd-files-on-windows
22+
const isWinLocal = /^win/.test(os.platform());
23+
if (isWinLocal) {
24+
spawnOptions.shell = true;
25+
// Further, we distinguish between quoting and escaping arguments since quoteAll does not support quoting without
26+
// supplying a shell, but escapeAll does.
27+
// See this (very long) discussion for more details: https://github.com/ericcornelissen/shescape/issues/2009
28+
args = quoteAll(args, { ...spawnOptions, flagProtection: false });
29+
} else {
30+
args = escapeAll(args, { ...spawnOptions, flagProtection: false });
31+
}
32+
}
1933
return new Promise((resolve, reject) => {
2034
let stdout = '';
2135
let stderr = '';
@@ -28,6 +42,12 @@ export function execute(
2842
stderr = stderr + data;
2943
});
3044

45+
// Handle spawn errors (e.g., ENOENT when command doesn't exist)
46+
proc.on('error', (error) => {
47+
stderr = error.message;
48+
reject({ stdout, stderr });
49+
});
50+
3151
proc.on('close', (code: number) => {
3252
if (code !== 0) {
3353
return reject(new Error(stdout || stderr));

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"@snyk/cli-interface": "^2.11.0",
3535
"@snyk/cocoapods-lockfile-parser": "3.6.2",
3636
"@snyk/dep-graph": "^1.23.1",
37-
"shescape": "2.1.4",
37+
"shescape": "2.1.6",
3838
"source-map-support": "^0.5.7",
3939
"tslib": "^2.0.0"
4040
},

0 commit comments

Comments
 (0)