From f74f59fb874552b38d8ba5f61213da15f02e3c79 Mon Sep 17 00:00:00 2001 From: Yoav Rotem Date: Mon, 16 Nov 2020 15:47:13 +0200 Subject: [PATCH 1/3] Update check.go Add option to run scripts in audit command --- check/check.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/check/check.go b/check/check.go index 1b88d70..c36e4ca 100644 --- a/check/check.go +++ b/check/check.go @@ -15,6 +15,7 @@ package check import ( + "bytes" "fmt" "os/exec" "strings" @@ -38,7 +39,8 @@ type Audit string // Execute method called by the main logic to execute the Audit's Execute type. func (audit Audit) Execute(customConfig ...interface{}) (result string, errMessage string, state State) { - res, err := exec.Command("sh", "-c", string(audit)).CombinedOutput() + res, err := runAudit(string(audit)) + // Errors mean the audit command failed, but that might be what we expect // for example, if we grep for something that is not found, there is a non-zero exit code // It is a problem if we can't find one of the audit commands to execute, but we deal @@ -207,6 +209,30 @@ func (c *Check) Run(definedConstraints map[string][]string) { } } +func runAudit(audit string) (output string, err error) { + var out bytes.Buffer + + audit = strings.TrimSpace(audit) + if len(audit) == 0 { + return output, err + } + + cmd := exec.Command("/bin/sh") + cmd.Stdin = strings.NewReader(audit) + cmd.Stdout = &out + cmd.Stderr = &out + err = cmd.Run() + output = out.String() + + if err != nil { + err = fmt.Errorf("failed to run: %q, output: %q, error: %s", audit, output, err) + } else { + glog.V(3).Infof("Command %q\n - Output:\n %q", audit, output) + + } + return output, err +} + func runAuditCommands(c BaseCheck) (output, errMessage string, state State) { // If check type is manual, force result to WARN. From 6e5f78ba18387bb6218c3eb8df545362e41882f4 Mon Sep 17 00:00:00 2001 From: Yoav Rotem Date: Mon, 16 Nov 2020 15:48:49 +0200 Subject: [PATCH 2/3] Add check for runAudit Add check for runAudit (running shall script and single commands) --- check/check_test.go | 62 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/check/check_test.go b/check/check_test.go index d444443..cce7b92 100644 --- a/check/check_test.go +++ b/check/check_test.go @@ -219,6 +219,68 @@ func TestGetFirstValidSubCheck(t *testing.T) { } } +func Test_runAudit(t *testing.T) { + type args struct { + audit string + output string + } + tests := []struct { + name string + args args + errMsg string + output string + }{ + { + name: "run success", + args: args{ + audit: "echo 'hello world'", + }, + errMsg: "", + output: "hello world\n", + }, + { + name: "run multiple lines script", + args: args{ + audit: ` +hello() { + echo "hello world" +} + +hello +`, + }, + errMsg: "", + output: "hello world\n", + }, + { + name: "run failed", + args: args{ + audit: "unknown_command", + }, + errMsg: "failed to run: \"unknown_command\", output: \"/bin/sh: ", + output: "not found\n", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var errMsg string + output, err := runAudit(tt.args.audit) + if err != nil { + errMsg = err.Error() + } + if errMsg != "" && !strings.Contains(errMsg, tt.errMsg) { + t.Errorf("name %s errMsg = %q, want %q", tt.name, errMsg, tt.errMsg) + } + if errMsg == "" && output != tt.output { + t.Errorf("name %s output = %q, want %q", tt.name, output, tt.output) + } + if errMsg != "" && !strings.Contains(output, tt.output) { + t.Errorf("name %s output = %q, want %q", tt.name, output, tt.output) + } + }) + } +} + func TestRunAuditCommands(t *testing.T) { cases := []struct { From dd3ad04e5d0d7008b88b3d57f91813259f6c02a0 Mon Sep 17 00:00:00 2001 From: Yoav Rotem Date: Sun, 22 Nov 2020 13:47:47 +0200 Subject: [PATCH 3/3] Update check_test.go Change from sh to bin/sh --- check/check_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/check/check_test.go b/check/check_test.go index cce7b92..0a83d27 100644 --- a/check/check_test.go +++ b/check/check_test.go @@ -302,7 +302,7 @@ func TestRunAuditCommands(t *testing.T) { // If the audit command can't be run, we eventually report FAIL but this is done in // (c *Check) Run() based on the final output b: BaseCheck{auditer: Audit("anything")}, - s: "", err: true, o: "sh: 1: anything: not found", + s: "", err: true, o: "/bin/sh: 1: anything: not found", }, { // 3 b: BaseCheck{auditer: Audit("echo hello")}, @@ -324,7 +324,7 @@ func TestRunAuditCommands(t *testing.T) { // Like in test #2 the final state will be fail, but currently in runAuditCommands // is just an empty string b: BaseCheck{auditer: Audit("echo $(ls . | grep 'bench') | anything")}, - s: "", err: true, o: "sh: 1: anything: not found", + s: "", err: true, o: "/bin/sh: 1: anything: not found", }, }