11import * as childProcess from "child_process" ;
2- import { quoteAll } from "shescape" ;
2+ import * as os from "node:os" ;
3+ import { escapeAll , quoteAll } from "shescape/stateless" ;
34
45export { execute , CmdOutput } ;
56interface CmdOutput {
@@ -13,13 +14,28 @@ function execute(
1314 options ?,
1415) : Promise < CmdOutput > {
1516 const spawnOptions : any = {
16- shell : true ,
17+ shell : false ,
1718 env : { ...process . env } ,
1819 } ;
1920 if ( options && options . cwd ) {
2021 spawnOptions . cwd = options . cwd ;
2122 }
22- args = quoteAll ( args , spawnOptions ) ;
23+
24+ if ( args ) {
25+ // Best practices, also security-wise, is to not invoke processes in a shell, but as a stand-alone command.
26+ // However, on Windows, we need to invoke the command in a shell, due to internal NodeJS problems with this approach
27+ // see: https://nodejs.org/docs/latest-v24.x/api/child_process.html#spawning-bat-and-cmd-files-on-windows
28+ const isWinLocal = / ^ w i n / . test ( os . platform ( ) ) ;
29+ if ( isWinLocal ) {
30+ spawnOptions . shell = true ;
31+ // Further, we distinguish between quoting and escaping arguments since quoteAll does not support quoting without
32+ // supplying a shell, but escapeAll does.
33+ // See this (very long) discussion for more details: https://github.com/ericcornelissen/shescape/issues/2009
34+ args = quoteAll ( args , { ...spawnOptions , flagProtection : false } ) ;
35+ } else {
36+ args = escapeAll ( args , { ...spawnOptions , flagProtection : false } ) ;
37+ }
38+ }
2339
2440 // Before spawning an external process, we look if we need to restore the system proxy configuration,
2541 // which overrides the cli internal proxy configuration.
@@ -38,6 +54,13 @@ function execute(
3854 let stderr = "" ;
3955
4056 const proc = childProcess . spawn ( command , args , spawnOptions ) ;
57+
58+ // Handle spawn errors (e.g., ENOENT when command doesn't exist)
59+ proc . on ( "error" , ( error ) => {
60+ stderr = error . message ;
61+ reject ( { stdout, stderr } ) ;
62+ } ) ;
63+
4164 proc . stdout . on ( "data" , ( data ) => {
4265 stdout = stdout + data ;
4366 } ) ;
0 commit comments