@@ -292,7 +292,46 @@ pub fn get_process_cmd(handler: isize) -> Result<String> {
292292 std:: slice:: from_raw_parts ( cmd_buffer. Buffer , ( cmd_buffer. Length / 2 ) as usize )
293293 } ) ;
294294
295- Ok ( cmd)
295+ // Only keep the first few arguments to avoid capturing credentials
296+ // that may appear in later command-line arguments
297+ Ok ( truncate_cmd_args ( & cmd, super :: MAX_CMD_ARGS ) )
298+ }
299+
300+ /// Truncate a command line string to at most `max_args` arguments,
301+ /// respecting double-quoted strings that may contain whitespace.
302+ fn truncate_cmd_args ( cmd : & str , max_args : usize ) -> String {
303+ let mut args = Vec :: new ( ) ;
304+ let mut chars = cmd. chars ( ) . peekable ( ) ;
305+
306+ while args. len ( ) < max_args {
307+ // Skip whitespace between arguments
308+ while chars. peek ( ) . is_some_and ( |c| c. is_whitespace ( ) ) {
309+ chars. next ( ) ;
310+ }
311+ if chars. peek ( ) . is_none ( ) {
312+ break ;
313+ }
314+
315+ let mut arg = String :: new ( ) ;
316+ if chars. peek ( ) == Some ( & '"' ) {
317+ // Quoted argument — consume until closing quote
318+ arg. push ( chars. next ( ) . unwrap ( ) ) ; // opening "
319+ for c in chars. by_ref ( ) {
320+ arg. push ( c) ;
321+ if c == '"' {
322+ break ;
323+ }
324+ }
325+ } else {
326+ // Unquoted argument — consume until whitespace
327+ while chars. peek ( ) . is_some_and ( |c| !c. is_whitespace ( ) ) {
328+ arg. push ( chars. next ( ) . unwrap ( ) ) ;
329+ }
330+ }
331+ args. push ( arg) ;
332+ }
333+
334+ args. join ( " " )
296335}
297336
298337#[ allow( dead_code) ]
@@ -379,4 +418,35 @@ mod tests {
379418 ) ;
380419 assert ! ( !cmd. is_empty( ) , "process cmd should not be empty" ) ;
381420 }
421+
422+ #[ test]
423+ fn truncate_cmd_args_tests ( ) {
424+ // no arguments
425+ let result = super :: truncate_cmd_args ( "" , 4 ) ;
426+ assert_eq ! ( result, "" , "empty input should return empty string" ) ;
427+
428+ // fewer than max args
429+ let result = super :: truncate_cmd_args ( "app.exe arg1" , 4 ) ;
430+ assert_eq ! (
431+ result, "app.exe arg1" ,
432+ "should return all args when fewer than max"
433+ ) ;
434+
435+ // exactly max args
436+ let result = super :: truncate_cmd_args ( "app.exe arg1 arg2 arg3 --secret=password" , 4 ) ;
437+ assert_eq ! (
438+ result, "app.exe arg1 arg2 arg3" ,
439+ "should truncate to first 4 args"
440+ ) ;
441+
442+ // more than max args and quoted arg with spaces
443+ let result = super :: truncate_cmd_args (
444+ r#""C:\Program Files\app.exe" arg1 "arg with spaces" arg3 --secret=password"# ,
445+ 4 ,
446+ ) ;
447+ assert_eq ! (
448+ result, r#""C:\Program Files\app.exe" arg1 "arg with spaces" arg3"# ,
449+ "should treat quoted strings with whitespace as single args"
450+ ) ;
451+ }
382452}
0 commit comments