@@ -13,7 +13,7 @@ const CHIA_START_ARGS = Object.freeze(['start', 'daemon', '--skip-keyring']);
1313let pyProc = null ;
1414
1515let IS_PACKAGED = null ;
16- const EXEC_PATH_CACHE = { } ; // {[execName]: execPath }
16+ const LAUNCH_CACHE = { } ; // {[execName]: { command, prefixArgs } }
1717
1818const guessPackaged = ( ) => {
1919 // This is very important. This means guessing whether it's packaged is checked only once in a process lifetime.
@@ -32,11 +32,16 @@ const getVirtualEnvExecDir = () => {
3232 return null ;
3333} ;
3434
35- const getExecutablePath = ( execName ) => {
36- // This also means getting exec path is done only once
37- // to prevent to run a different executable with the same name in a process lifetime.
38- if ( Object . prototype . hasOwnProperty . call ( EXEC_PATH_CACHE , execName ) ) {
39- return EXEC_PATH_CACHE [ execName ] ;
35+ /**
36+ * Resolve a spawnable chia launch without using shell:true.
37+ * On Windows venvs that only ship chia.cmd, spawn python.exe + the chia script
38+ * (same as chia.cmd) to avoid EINVAL under CVE-2024-27980.
39+ */
40+ const getChiaLaunch = ( execName = PY_CHIA_EXEC ) => {
41+ // Getting launch config is done only once to prevent running a different
42+ // executable with the same name in a process lifetime.
43+ if ( Object . prototype . hasOwnProperty . call ( LAUNCH_CACHE , execName ) ) {
44+ return LAUNCH_CACHE [ execName ] ;
4045 }
4146
4247 const execDir = guessPackaged ( ) ? PY_DIST_FOLDER : getVirtualEnvExecDir ( ) ;
@@ -45,38 +50,45 @@ const getExecutablePath = (execName) => {
4550 }
4651
4752 if ( process . platform === 'win32' ) {
48- let execPath = path . join ( execDir , `${ execName } .exe` ) ;
49- if ( fs . existsSync ( execPath ) ) {
50- EXEC_PATH_CACHE [ execName ] = execPath ;
51- return execPath ;
53+ const exePath = path . join ( execDir , `${ execName } .exe` ) ;
54+ if ( fs . existsSync ( exePath ) ) {
55+ LAUNCH_CACHE [ execName ] = { command : exePath , prefixArgs : [ ] } ;
56+ return LAUNCH_CACHE [ execName ] ;
5257 }
53- execPath = path . join ( execDir , `${ execName } .cmd` ) . replace ( new RegExp ( path . posix . sep , 'g' ) , path . win32 . sep ) ;
54- if ( fs . existsSync ( execPath ) ) {
55- EXEC_PATH_CACHE [ execName ] = execPath ;
56- return execPath ;
58+
59+ const pythonPath = path . join ( execDir , 'python.exe' ) ;
60+ const scriptPath = path . join ( execDir , execName ) ;
61+ if ( ! fs . existsSync ( pythonPath ) ) {
62+ throw new Error ( `python.exe could not be found in: ${ execDir } ` ) ;
63+ }
64+ if ( ! fs . existsSync ( scriptPath ) ) {
65+ throw new Error ( `chia script could not be found in: ${ execDir } ` ) ;
5766 }
58- throw new Error ( `chia executable could not be found in: ${ execDir } ` ) ;
67+
68+ LAUNCH_CACHE [ execName ] = { command : pythonPath , prefixArgs : [ scriptPath ] } ;
69+ return LAUNCH_CACHE [ execName ] ;
5970 }
6071
61- EXEC_PATH_CACHE [ execName ] = path . join ( execDir , execName ) ;
62- return EXEC_PATH_CACHE [ execName ] ;
72+ LAUNCH_CACHE [ execName ] = { command : path . join ( execDir , execName ) , prefixArgs : [ ] } ;
73+ return LAUNCH_CACHE [ execName ] ;
6374} ;
6475
6576const getChiaVersion = ( ) => {
66- const chiaExecPath = getExecutablePath ( PY_CHIA_EXEC ) ;
77+ const { command , prefixArgs } = getChiaLaunch ( ) ;
6778 return childProcess
68- . execFileSync ( chiaExecPath , [ 'version' ] , {
79+ . execFileSync ( command , [ ... prefixArgs , 'version' ] , {
6980 encoding : 'UTF-8' ,
7081 } )
7182 . trim ( ) ;
7283} ;
7384
7485const chiaInit = ( ) => {
75- const chiaExecPath = getExecutablePath ( PY_CHIA_EXEC ) ;
76- console . info ( `Executing: ${ chiaExecPath } init` ) ;
86+ const { command, prefixArgs } = getChiaLaunch ( ) ;
87+ const args = [ ...prefixArgs , 'init' ] ;
88+ console . info ( `Executing: ${ command } ${ args . join ( ' ' ) } ` ) ;
7789
7890 try {
79- const output = childProcess . execFileSync ( chiaExecPath , [ 'init' ] ) ;
91+ const output = childProcess . execFileSync ( command , args ) ;
8092 console . info ( output . toString ( ) ) ;
8193 return true ;
8294 } catch ( e ) {
@@ -97,12 +109,13 @@ const startChiaDaemon = () => {
97109 } ;
98110 }
99111
100- const chiaExec = getExecutablePath ( PY_CHIA_EXEC ) ;
112+ const { command, prefixArgs } = getChiaLaunch ( ) ;
113+ const args = [ ...prefixArgs , ...CHIA_START_ARGS ] ;
101114 console . info ( 'Running python executable: ' ) ;
102- console . info ( `Script: ${ chiaExec } ${ CHIA_START_ARGS . join ( ' ' ) } ` ) ;
115+ console . info ( `Script: ${ command } ${ args . join ( ' ' ) } ` ) ;
103116
104117 try {
105- pyProc = childProcess . spawn ( chiaExec , CHIA_START_ARGS , procOption ) ;
118+ pyProc = childProcess . spawn ( command , args , procOption ) ;
106119 } catch ( e ) {
107120 console . error ( 'Running python executable: Error: ' ) ;
108121 console . error ( e ) ;
0 commit comments