@@ -14,15 +14,37 @@ exports.detectLanguage = (cypress_config_filename) => {
1414}
1515
1616function resolveTsConfigPath ( bsConfig , cypress_config_filepath ) {
17+ const utils = require ( "./utils" ) ;
1718 const working_dir = path . dirname ( cypress_config_filepath ) ;
19+ const project_root = process . cwd ( ) ;
1820
19- // Priority order for finding tsconfig
20- const candidates = [
21- bsConfig . run_settings && bsConfig . run_settings . ts_config_file_path , // User specified
22- path . join ( working_dir , 'tsconfig.json' ) , // Same directory as cypress config
23- path . join ( working_dir , '..' , 'tsconfig.json' ) , // Parent directory
24- path . join ( process . cwd ( ) , 'tsconfig.json' ) // Project root
25- ] . filter ( Boolean ) . map ( p => path . resolve ( p ) ) ;
21+ // Priority order for finding tsconfig with secure path validation
22+ const candidates = [ ] ;
23+
24+ // User specified path - validate it's within project bounds
25+ if ( bsConfig . run_settings && bsConfig . run_settings . ts_config_file_path ) {
26+ try {
27+ const safePath = utils . validateSecurePath ( bsConfig . run_settings . ts_config_file_path , project_root ) ;
28+ candidates . push ( safePath ) ;
29+ } catch ( error ) {
30+ logger . warn ( `Invalid user-specified tsconfig path: ${ error . message } ` ) ;
31+ }
32+ }
33+
34+ // Safe predefined paths
35+ try {
36+ candidates . push ( utils . validateSecurePath ( path . join ( working_dir , 'tsconfig.json' ) , project_root ) ) ;
37+ } catch ( error ) {
38+ logger . debug ( `Working directory tsconfig path validation failed: ${ error . message } ` ) ;
39+ }
40+
41+ try {
42+ candidates . push ( utils . validateSecurePath ( path . join ( working_dir , '..' , 'tsconfig.json' ) , project_root ) ) ;
43+ } catch ( error ) {
44+ logger . debug ( `Parent directory tsconfig path validation failed: ${ error . message } ` ) ;
45+ }
46+
47+ candidates . push ( path . join ( project_root , 'tsconfig.json' ) ) ;
2648
2749 for ( const candidate of candidates ) {
2850 if ( fs . existsSync ( candidate ) ) {
@@ -35,12 +57,32 @@ function resolveTsConfigPath(bsConfig, cypress_config_filepath) {
3557}
3658
3759function generateTscCommandAndTempTsConfig ( bsConfig , bstack_node_modules_path , complied_js_dir , cypress_config_filepath ) {
60+ const utils = require ( "./utils" ) ;
3861 const working_dir = path . dirname ( cypress_config_filepath ) ;
39- const typescript_path = path . join ( bstack_node_modules_path , 'typescript' , 'bin' , 'tsc' ) ;
40- const tsc_alias_path = path . join ( bstack_node_modules_path , 'tsc-alias' , 'dist' , 'bin' , 'index.js' ) ;
62+ const project_root = process . cwd ( ) ;
63+
64+ // Sanitize and validate paths to prevent command injection
65+ let safe_bstack_node_modules_path ;
66+ let safe_cypress_config_filepath ;
67+ let safe_working_dir ;
68+
69+ try {
70+ safe_bstack_node_modules_path = utils . sanitizeCommandPath ( bstack_node_modules_path ) ;
71+ safe_cypress_config_filepath = utils . sanitizeCommandPath ( cypress_config_filepath ) ;
72+ safe_working_dir = utils . sanitizeCommandPath ( working_dir ) ;
73+
74+ // Additional validation - ensure paths are within expected bounds
75+ utils . validateSecurePath ( safe_bstack_node_modules_path , project_root ) ;
76+ utils . validateSecurePath ( safe_cypress_config_filepath , project_root ) ;
77+ } catch ( error ) {
78+ throw new Error ( `Invalid file paths detected: ${ error . message } ` ) ;
79+ }
80+
81+ const typescript_path = path . join ( safe_bstack_node_modules_path , 'typescript' , 'bin' , 'tsc' ) ;
82+ const tsc_alias_path = path . join ( safe_bstack_node_modules_path , 'tsc-alias' , 'dist' , 'bin' , 'index.js' ) ;
4183
4284 // Smart tsconfig detection and validation
43- const resolvedTsConfigPath = resolveTsConfigPath ( bsConfig , cypress_config_filepath ) ;
85+ const resolvedTsConfigPath = resolveTsConfigPath ( bsConfig , safe_cypress_config_filepath ) ;
4486 let hasValidTsConfig = false ;
4587
4688 if ( resolvedTsConfigPath ) {
@@ -72,7 +114,7 @@ function generateTscCommandAndTempTsConfig(bsConfig, bstack_node_modules_path, c
72114 "allowSyntheticDefaultImports" : true ,
73115 "esModuleInterop" : true
74116 } ,
75- include : [ cypress_config_filepath ]
117+ include : [ safe_cypress_config_filepath ]
76118 } ;
77119 } else {
78120 // Scenario 2: No tsconfig or invalid tsconfig - create standalone with all basic parameters
@@ -96,31 +138,28 @@ function generateTscCommandAndTempTsConfig(bsConfig, bstack_node_modules_path, c
96138 "strict" : false , // Avoid breaking existing code
97139 "noEmitOnError" : false // Continue compilation even with errors
98140 } ,
99- include : [ cypress_config_filepath ] ,
141+ include : [ safe_cypress_config_filepath ] ,
100142 exclude : [ "node_modules" , "dist" , "build" ]
101143 } ;
102144 }
103145
104146 // Write the temporary tsconfig
105- const tempTsConfigPath = path . join ( working_dir , 'tsconfig.singlefile.tmp.json' ) ;
147+ const tempTsConfigPath = path . join ( safe_working_dir , 'tsconfig.singlefile.tmp.json' ) ;
106148 fs . writeFileSync ( tempTsConfigPath , JSON . stringify ( tempTsConfig , null , 2 ) ) ;
107149 logger . info ( `Temporary tsconfig created at: ${ tempTsConfigPath } ` ) ;
108150
109- // Platform-specific command generation
151+ // Platform-specific command generation with sanitized paths
110152 const isWindows = / ^ w i n / . test ( process . platform ) ;
111153
112154 if ( isWindows ) {
113155 // Windows: Use && to chain commands, no space after SET
114- const setNodePath = isWindows
115- ? `set NODE_PATH=${ bstack_node_modules_path } `
116- : `NODE_PATH="${ bstack_node_modules_path } "` ;
117-
156+ const setNodePath = `set NODE_PATH=${ safe_bstack_node_modules_path } ` ;
118157 const tscCommand = `${ setNodePath } && node "${ typescript_path } " --project "${ tempTsConfigPath } " && ${ setNodePath } && node "${ tsc_alias_path } " --project "${ tempTsConfigPath } " --verbose` ;
119158 logger . info ( `TypeScript compilation command: ${ tscCommand } ` ) ;
120159 return { tscCommand, tempTsConfigPath } ;
121160 } else {
122161 // Unix/Linux/macOS: Use ; to separate commands or && to chain
123- const nodePathPrefix = `NODE_PATH=${ bstack_node_modules_path } ` ;
162+ const nodePathPrefix = `NODE_PATH=${ safe_bstack_node_modules_path } ` ;
124163 const tscCommand = `${ nodePathPrefix } node "${ typescript_path } " --project "${ tempTsConfigPath } " && ${ nodePathPrefix } node "${ tsc_alias_path } " --project "${ tempTsConfigPath } " --verbose` ;
125164 logger . info ( `TypeScript compilation command: ${ tscCommand } ` ) ;
126165 return { tscCommand, tempTsConfigPath } ;
0 commit comments