@@ -262,6 +262,8 @@ func (snc *Agent) makeCmd(ctx context.Context, command string) (*exec.Cmd, error
262262 cmd := execCommandContext (ctx , "powershell" , env )
263263 cmd .SysProcAttr .CmdLine = fmt .Sprintf (`%s -command %s; exit($LASTEXITCODE)` , POWERSHELL , command )
264264
265+ log .Tracef ("cmd.SysProcAttr.CmdLine: %s" , cmd .SysProcAttr .CmdLine )
266+
265267 return cmd , nil
266268
267269 // command does not exist
@@ -283,17 +285,34 @@ func (snc *Agent) makeCmd(ctx context.Context, command string) (*exec.Cmd, error
283285 strings .Join (cmdArgs , " " ),
284286 )
285287
288+ log .Tracef ("cmd.SysProcAttr.CmdLine: %s" , cmd .SysProcAttr .CmdLine )
289+
286290 return cmd , nil
287291
288292 // powershell files
289293 case isPsFile (cmdName ):
290- for i , ca := range cmdArgs {
291- if strings .ContainsAny (ca , " \t " ) {
292- cmdArgs [i ] = `'` + ca + `'`
293- }
294+ // parse the command one more time, this time adding the shelltoken.KeepQuoutes option
295+ cmdName , cmdArgs , _ , err = snc .shellParse (command , shelltoken .SplitKeepQuotes )
296+ if err != nil {
297+ return nil , err
298+ }
299+
300+ cmdArgsModified := make ([]string , 0 , len (cmdArgs ))
301+ for _ , cmdArg := range cmdArgs {
302+ // parsed arguments might include double quoutes.
303+ // cmd.SysProcAttr.CmdLine is set so that the arguments are found within double quoutes inside the -Command parameter
304+ // to include a double quoute here, you have to add three double quoutes.
305+ // windows has very confusing, runtime-dependent command line argument parsing
306+ // This is done with the assumption that its using GetCommandLineW, and it works for now
307+ cmdArg = strings .ReplaceAll (cmdArg , `"` , `"""` )
308+
309+ cmdArgsModified = append (cmdArgsModified , cmdArg )
294310 }
311+
295312 cmd := execCommandContext (ctx , "powershell" , env )
296- cmd .SysProcAttr .CmdLine = fmt .Sprintf (`%s -Command ". '%s' %s; exit($LASTEXITCODE)"` , POWERSHELL , cmdName , strings .Join (cmdArgs , " " ))
313+ cmd .SysProcAttr .CmdLine = fmt .Sprintf (`%s -Command ". '%s' %s ; exit($LASTEXITCODE)"` , POWERSHELL , cmdName , strings .Join (cmdArgsModified , " " ))
314+
315+ log .Tracef ("cmd.SysProcAttr.CmdLine: %s" , cmd .SysProcAttr .CmdLine )
297316
298317 return cmd , nil
299318
@@ -312,12 +331,29 @@ func (snc *Agent) makeCmd(ctx context.Context, command string) (*exec.Cmd, error
312331 shell ,
313332 strings .Replace (command , cmdName , syscall .EscapeArg (cmdName ), 1 ))
314333
334+ log .Tracef ("cmd.SysProcAttr.CmdLine: %s" , cmd .SysProcAttr .CmdLine )
335+
315336 return cmd , nil
316337 }
317338}
318339
319- func (snc * Agent ) shellParse (command string ) (cmdName string , args []string , hasShellCode bool , err error ) {
320- args , err = shelltoken .SplitQuotes (command , shelltoken .Whitespace , shelltoken .SplitKeepBackslashes | shelltoken .SplitContinueOnShellCharacters )
340+ func (snc * Agent ) shellParse (command string , additionalOptions ... shelltoken.SplitOption ) (cmdName string , args []string , hasShellCode bool , err error ) {
341+ options := shelltoken .SplitKeepBackslashes | shelltoken .SplitContinueOnShellCharacters
342+
343+ // shelltoken.SplitKeepQuotes may be passed as an additional Option
344+ // when invoking a script, the script might use $ARG1$ macro
345+ // arg1 here might be a single string composed of many arguments, e.g:
346+ // powershell_detail_arg1 "-option1 option1 -option2 `'option2`' -option3 `"option3`" -option4 `'foo,bar`' -option5 `"baz,xyz`" "
347+ // if sheltoken.SplitKeepQuoutes option is not set, it strips the quotation marks from each option value
348+ // this leads to arguments like foo,bar not being quouted and being left as is
349+ // powershell then thinks its an array due to comma
350+ // if they were quouted, it would think that they are a string that includes comma character
351+
352+ for _ , opt := range additionalOptions {
353+ options |= opt
354+ }
355+
356+ args , err = shelltoken .SplitQuotes (command , shelltoken .Whitespace , options )
321357 if err != nil {
322358 tst := & shelltoken.ShellCharactersFoundError {}
323359 if errors .As (err , & tst ) {
0 commit comments