diff --git a/Changes b/Changes index c8f5a9b..c64f8b2 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,8 @@ This file documents the revision history for the Mod-Gearman-Worker-Go +next: + - fix running quoted commands with internal negate + 1.5.4 Fri Nov 22 14:09:37 CET 2024 - improve statistics log output diff --git a/pkg/modgearman/internal_negate.go b/pkg/modgearman/internal_negate.go index 6cc3c0f..2805ed6 100644 --- a/pkg/modgearman/internal_negate.go +++ b/pkg/modgearman/internal_negate.go @@ -5,6 +5,8 @@ import ( "fmt" "io" "strings" + + "github.com/sni/shelltoken" ) // NegateDefaultTimeout sets the default timeout if negate is used @@ -122,9 +124,28 @@ func ParseNegate(com *command) { return } + com.Command = com.Args[mainProgIndex] com.Args = com.Args[mainProgIndex+1:] com.Negate = negate + + if len(com.Args) > 0 { + return + } + + // recombine remaining program and args + remainingArgs := []string{} + _, subargs, suberr := shelltoken.SplitLinux(com.Command) + if suberr != nil { + log.Debugf("failed to parse shell args: %w: %s", suberr, suberr.Error()) + + return + } + + remainingArgs = append(remainingArgs, subargs...) + + com.Command = remainingArgs[0] + com.Args = remainingArgs[1:] } func (n *Negate) Apply(result *answer) { diff --git a/pkg/modgearman/internal_negate_test.go b/pkg/modgearman/internal_negate_test.go index 02b2bff..a65a442 100644 --- a/pkg/modgearman/internal_negate_test.go +++ b/pkg/modgearman/internal_negate_test.go @@ -6,16 +6,47 @@ import ( "github.com/stretchr/testify/assert" ) -func TestNegate(t *testing.T) { +func TestNegateParse(t *testing.T) { cmdLine := "ENV1=test ./lib/negate -w OK -c UNKNOWN -s /bin/command comArg1 comArg2" cmd := parseCommand(cmdLine, &config{internalNegate: true}) assert.Equal(t, Exec, cmd.ExecType, "exec type") assert.NotNil(t, cmd.Negate, "parsed negate") + assert.Equal(t, map[string]string{"ENV1": "test"}, cmd.Env) assert.Equal(t, "OK", cmd.Negate.WarningStatus, "parsed negate") assert.Equal(t, "UNKNOWN", cmd.Negate.CriticalStatus, "parsed negate") } +func TestNegateParseQuoted(t *testing.T) { + cmdLine := "./lib/negate \"/bin/bash comArg1 comArg2 ABC=123 -c 'te st'\"" + cmd := parseCommand(cmdLine, &config{internalNegate: true}) + + assert.Equal(t, Exec, cmd.ExecType, "exec type") + assert.NotNil(t, cmd.Negate, "parsed negate") + assert.Equal(t, "/bin/bash", cmd.Command, "remaining parsed command") + assert.Equal(t, []string{"comArg1", "comArg2", "ABC=123", "-c", "te st"}, cmd.Args, "remaining args") +} + +func TestNegateParseQuotedShell(t *testing.T) { + cmdLine := "lib/negate -t 1 -s /bin/sh -c 'sleep 3'" + cmd := parseCommand(cmdLine, &config{internalNegate: true}) + + assert.Equal(t, Exec, cmd.ExecType, "exec type") + assert.NotNil(t, cmd.Negate, "parsed negate") + assert.Equal(t, "/bin/sh", cmd.Command, "remaining parsed command") + assert.Equal(t, []string{"-c", "sleep 3"}, cmd.Args, "remaining args") +} + +func TestNegateParseQuotedShellChars(t *testing.T) { + cmdLine := "lib/negate -t 1 -s /bin/sh -c 'sleep 3; echo $SHELL'" + cmd := parseCommand(cmdLine, &config{internalNegate: true}) + + assert.Equal(t, Exec, cmd.ExecType, "exec type") + assert.NotNil(t, cmd.Negate, "parsed negate") + assert.Equal(t, "/bin/sh", cmd.Command, "remaining parsed command") + assert.Equal(t, []string{"-c", "sleep 3; echo $SHELL"}, cmd.Args, "remaining args") +} + func TestExecuteCommandWithNegateI(t *testing.T) { cfg := config{} cfg.setDefaultValues() @@ -31,6 +62,21 @@ func TestExecuteCommandWithNegateI(t *testing.T) { assert.Equal(t, "WARNING - failed", result.output, "output replaced") } +func TestExecuteCommandWithNegateII(t *testing.T) { + cfg := config{} + cfg.setDefaultValues() + cfg.encryption = false + result := &answer{} + + executeCommandLine(result, &request{ + commandLine: "lib/negate -o 1 \"/bin/sh -c 'echo OK'\"", + timeout: 10, + }, &cfg) + assert.Equal(t, "exec", result.execType, "exec type") + assert.Equal(t, 1, result.returnCode, "return code") + assert.Equal(t, "OK", result.output, "output replaced") +} + func TestExecuteCommandWithNegateNoOptions(t *testing.T) { cfg := config{} cfg.setDefaultValues()