@@ -4,9 +4,11 @@ import assert from "node:assert";
44describe ( "safeSpawn" ,  ( )  =>  { 
55  let  safeSpawn ; 
66  let  spawnCalls  =  [ ] ; 
7+   let  os ; 
78
89  beforeEach ( async  ( )  =>  { 
910    spawnCalls  =  [ ] ; 
11+     os  =  "win32" ;  // Test Windows behavior by default 
1012
1113    // Mock child_process module to capture what command string gets built 
1214    mock . module ( "child_process" ,  { 
@@ -15,13 +17,27 @@ describe("safeSpawn", () => {
1517          spawnCalls . push ( {  command,  options } ) ; 
1618          return  { 
1719            on : ( event ,  callback )  =>  { 
18-               if  ( event  ===  ' close' )  { 
20+               if  ( event  ===  " close" )  { 
1921                // Simulate immediate success 
2022                setTimeout ( ( )  =>  callback ( 0 ) ,  0 ) ; 
2123              } 
22-             } 
24+             } , 
2325          } ; 
2426        } , 
27+         execSync : ( cmd )  =>  { 
28+           // Simulate 'command -v' returning full path 
29+           const  match  =  cmd . match ( / c o m m a n d   - v   ( .+ ) / ) ; 
30+           if  ( match )  { 
31+             return  `/usr/bin/${ match [ 1 ] }  ; 
32+           } 
33+           return  "" ; 
34+         } , 
35+       } , 
36+     } ) ; 
37+ 
38+     mock . module ( "os" ,  { 
39+       namedExports : { 
40+         platform : ( )  =>  os , 
2541      } , 
2642    } ) ; 
2743
@@ -86,4 +102,113 @@ describe("safeSpawn", () => {
86102    assert . strictEqual ( spawnCalls [ 0 ] . command ,  "npm install axios --save" ) ; 
87103    assert . strictEqual ( spawnCalls [ 0 ] . options . shell ,  true ) ; 
88104  } ) ; 
105+ 
106+   it ( `should escape ampersand character` ,  async  ( )  =>  { 
107+     await  safeSpawn ( "npx" ,  [ "cypress" ,  "run" ,  "--env" ,  "password=foo&bar" ] ) ; 
108+ 
109+     assert . strictEqual ( spawnCalls . length ,  1 ) ; 
110+     // & should be escaped by wrapping the arg in quotes 
111+     assert . strictEqual ( 
112+       spawnCalls [ 0 ] . command , 
113+       'npx cypress run --env "password=foo&bar"' 
114+     ) ; 
115+     assert . strictEqual ( spawnCalls [ 0 ] . options . shell ,  true ) ; 
116+   } ) ; 
117+ 
118+   it ( "should escape dollar signs to prevent variable expansion" ,  async  ( )  =>  { 
119+     await  safeSpawn ( "echo" ,  [ "$HOME/test" ] ) ; 
120+ 
121+     assert . strictEqual ( spawnCalls . length ,  1 ) ; 
122+     assert . strictEqual ( spawnCalls [ 0 ] . command ,  'echo "\\$HOME/test"' ) ; 
123+   } ) ; 
124+ 
125+   it ( "should escape backticks to prevent command substitution" ,  async  ( )  =>  { 
126+     await  safeSpawn ( "echo" ,  [ "file`whoami`.txt" ] ) ; 
127+ 
128+     assert . strictEqual ( spawnCalls . length ,  1 ) ; 
129+     assert . strictEqual ( spawnCalls [ 0 ] . command ,  'echo "file\\`whoami\\`.txt"' ) ; 
130+   } ) ; 
131+ 
132+   it ( "should escape backslashes properly" ,  async  ( )  =>  { 
133+     await  safeSpawn ( "echo" ,  [ "path\\with\\backslash" ] ) ; 
134+ 
135+     assert . strictEqual ( spawnCalls . length ,  1 ) ; 
136+     assert . strictEqual ( 
137+       spawnCalls [ 0 ] . command , 
138+       'echo "path\\\\with\\\\backslash"' 
139+     ) ; 
140+   } ) ; 
141+ 
142+   it ( "should handle multiple special characters in one argument" ,  async  ( )  =>  { 
143+     await  safeSpawn ( "cmd" ,  [ 'test "quoted" $var `cmd` & more' ] ) ; 
144+ 
145+     assert . strictEqual ( spawnCalls . length ,  1 ) ; 
146+     assert . strictEqual ( 
147+       spawnCalls [ 0 ] . command , 
148+       'cmd "test \\"quoted\\" \\$var \\`cmd\\` & more"' 
149+     ) ; 
150+   } ) ; 
151+ 
152+   it ( "should handle pipe character" ,  async  ( )  =>  { 
153+     await  safeSpawn ( "echo" ,  [ "foo|bar" ] ) ; 
154+ 
155+     assert . strictEqual ( spawnCalls . length ,  1 ) ; 
156+     assert . strictEqual ( spawnCalls [ 0 ] . command ,  'echo "foo|bar"' ) ; 
157+   } ) ; 
158+ 
159+   it ( "should handle parentheses" ,  async  ( )  =>  { 
160+     await  safeSpawn ( "echo" ,  [ "(test)" ] ) ; 
161+ 
162+     assert . strictEqual ( spawnCalls . length ,  1 ) ; 
163+     assert . strictEqual ( spawnCalls [ 0 ] . command ,  'echo "(test)"' ) ; 
164+   } ) ; 
165+ 
166+   it ( "should handle angle brackets for redirection" ,  async  ( )  =>  { 
167+     await  safeSpawn ( "echo" ,  [ "foo>output.txt" ] ) ; 
168+ 
169+     assert . strictEqual ( spawnCalls . length ,  1 ) ; 
170+     assert . strictEqual ( spawnCalls [ 0 ] . command ,  'echo "foo>output.txt"' ) ; 
171+   } ) ; 
172+ 
173+   it ( "should handle wildcard characters" ,  async  ( )  =>  { 
174+     await  safeSpawn ( "echo" ,  [ "*.txt" ] ) ; 
175+ 
176+     assert . strictEqual ( spawnCalls . length ,  1 ) ; 
177+     assert . strictEqual ( spawnCalls [ 0 ] . command ,  'echo "*.txt"' ) ; 
178+   } ) ; 
179+ 
180+   it ( "should handle multiple arguments with mixed escaping needs" ,  async  ( )  =>  { 
181+     await  safeSpawn ( "cmd" ,  [ "safe" ,  "needs space" ,  "$dangerous" ,  "also-safe" ] ) ; 
182+ 
183+     assert . strictEqual ( spawnCalls . length ,  1 ) ; 
184+     assert . strictEqual ( 
185+       spawnCalls [ 0 ] . command , 
186+       'cmd safe "needs space" "\\$dangerous" also-safe' 
187+     ) ; 
188+   } ) ; 
189+ 
190+   it ( "should reject command names with special characters" ,  async  ( )  =>  { 
191+     await  assert . rejects ( async  ( )  =>  await  safeSpawn ( "npm; echo hacked" ,  [ ] ) ,  { 
192+       message : "Invalid command name: npm; echo hacked" , 
193+     } ) ; 
194+   } ) ; 
195+ 
196+   it ( "should reject command names with spaces" ,  async  ( )  =>  { 
197+     await  assert . rejects ( async  ( )  =>  await  safeSpawn ( "npm install" ,  [ ] ) ,  { 
198+       message : "Invalid command name: npm install" , 
199+     } ) ; 
200+   } ) ; 
201+ 
202+   it ( "should reject command names with slashes" ,  async  ( )  =>  { 
203+     await  assert . rejects ( async  ( )  =>  await  safeSpawn ( "../../malicious" ,  [ ] ) ,  { 
204+       message : "Invalid command name: ../../malicious" , 
205+     } ) ; 
206+   } ) ; 
207+ 
208+   it ( "should accept valid command names with letters, numbers, underscores and hyphens" ,  async  ( )  =>  { 
209+     await  safeSpawn ( "valid_command-123" ,  [ ] ) ; 
210+ 
211+     assert . strictEqual ( spawnCalls . length ,  1 ) ; 
212+     assert . strictEqual ( spawnCalls [ 0 ] . command ,  "valid_command-123" ) ; 
213+   } ) ; 
89214} ) ; 
0 commit comments