From 3aa28c4c32369bf98879b308b62b9c993e1796bc Mon Sep 17 00:00:00 2001 From: Yoav Hizkiahou Date: Sun, 5 May 2019 10:52:28 +0300 Subject: [PATCH 1/5] Printing the actual test result of failed tests - when a flag is raised fix #110 --- cmd/common.go | 11 +++++++++++ cmd/root.go | 2 ++ 2 files changed, 13 insertions(+) diff --git a/cmd/common.go b/cmd/common.go index ef09d82..de24273 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -19,6 +19,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "github.com/aquasecurity/kube-bench/check" "github.com/golang/glog" @@ -142,6 +143,10 @@ func prettyPrint(r *check.Controls, summary check.Summary) { colorPrint(check.INFO, fmt.Sprintf("%s %s\n", g.ID, g.Text)) for _, c := range g.Checks { colorPrint(c.State, fmt.Sprintf("%s %s\n", c.ID, c.Text)) + + if includeTestOutput && c.State == check.FAIL && len(c.ActualValue) > 0 { + printRawOutput(c.ActualValue) + } } } @@ -240,3 +245,9 @@ func isMaster() bool { } return true } + +func printRawOutput(output string) { + for _, row := range strings.Split(output, "\n") { + fmt.Println(fmt.Sprintf("\t %s", row)) + } +} diff --git a/cmd/root.go b/cmd/root.go index 2f481ba..7b09fcd 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -47,6 +47,7 @@ var ( noSummary bool noRemediations bool filterOpts FilterOpts + includeTestOutput bool ) // RootCmd represents the base command when called without any subcommands @@ -87,6 +88,7 @@ func init() { RootCmd.PersistentFlags().BoolVar(&pgSQL, "pgsql", false, "Save the results to PostgreSQL") RootCmd.PersistentFlags().BoolVar(&filterOpts.Scored, "scored", true, "Run the scored CIS checks") RootCmd.PersistentFlags().BoolVar(&filterOpts.Unscored, "unscored", true, "Run the unscored CIS checks") + RootCmd.PersistentFlags().BoolVar(&includeTestOutput, "include-test-output", false, "Prints the actual result when test fails") RootCmd.PersistentFlags().StringVarP( &filterOpts.CheckList, From 240c8ad5b05946d92809fdc2b27ab58a30babbe0 Mon Sep 17 00:00:00 2001 From: Yoav Hizkiahou Date: Sun, 5 May 2019 10:35:55 +0300 Subject: [PATCH 2/5] The check's actual result property is now set to be the audit command's output fix #280 --- check/test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/check/test.go b/check/test.go index 9ddb469..059642f 100644 --- a/check/test.go +++ b/check/test.go @@ -135,7 +135,6 @@ func (t *testItem) execute(s string) *testOutput { } } - result.actualResult = strings.ToLower(flagVal) switch t.Compare.Op { case "eq": value := strings.ToLower(flagVal) @@ -232,6 +231,10 @@ func (ts *tests) execute(s string) *testOutput { finalOutput.testResult = result finalOutput.actualResult = res[0].actualResult + if finalOutput.actualResult == "" { + finalOutput.actualResult = s + } + return finalOutput } From e7a8c147150d2fd930da367ebfd250782038cea4 Mon Sep 17 00:00:00 2001 From: Yoav Hizkiahou Date: Sun, 19 May 2019 11:23:44 +0300 Subject: [PATCH 3/5] Save the audit command when requesting json output under the "audit" key --- check/check.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/check/check.go b/check/check.go index 6a95496..3c7884f 100644 --- a/check/check.go +++ b/check/check.go @@ -62,7 +62,7 @@ func handleError(err error, context string) (errmsg string) { type Check struct { ID string `yaml:"id" json:"test_number"` Text string `json:"test_desc"` - Audit string `json:"omit"` + Audit string `json:"audit"` Type string `json:"type"` Commands []*exec.Cmd `json:"omit"` Tests *tests `json:"omit"` From d1c3e3163b42a1022c3084759fb6789327335b88 Mon Sep 17 00:00:00 2001 From: Yoav Hizkiahou Date: Mon, 6 May 2019 12:30:11 +0300 Subject: [PATCH 4/5] Genereate expected result automatically for each test --- check/check.go | 2 ++ check/test.go | 21 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/check/check.go b/check/check.go index 3c7884f..485cca9 100644 --- a/check/check.go +++ b/check/check.go @@ -72,6 +72,7 @@ type Check struct { State `json:"status"` ActualValue string `json:"actual_value"` Scored bool `json:"scored"` + ExpectedResult string `json:"expected_result"` } // Runner wraps the basic Run method. @@ -188,6 +189,7 @@ func (c *Check) run() State { finalOutput := c.Tests.execute(out.String()) if finalOutput != nil { c.ActualValue = finalOutput.actualResult + c.ExpectedResult = finalOutput.ExpectedResult if finalOutput.testResult { c.State = PASS } else { diff --git a/check/test.go b/check/test.go index 059642f..9e2746b 100644 --- a/check/test.go +++ b/check/test.go @@ -58,6 +58,7 @@ type compare struct { type testOutput struct { testResult bool actualResult string + ExpectedResult string } func failTestItem(s string) *testOutput { @@ -135,8 +136,10 @@ func (t *testItem) execute(s string) *testOutput { } } + expectedResultPattern := "" switch t.Compare.Op { case "eq": + expectedResultPattern = "'%s' is equal to '%s'" value := strings.ToLower(flagVal) // Do case insensitive comparaison for booleans ... if value == "false" || value == "true" { @@ -146,6 +149,7 @@ func (t *testItem) execute(s string) *testOutput { } case "noteq": + expectedResultPattern = "'%s' is not equal to '%s'" value := strings.ToLower(flagVal) // Do case insensitive comparaison for booleans ... if value == "false" || value == "true" { @@ -155,32 +159,41 @@ func (t *testItem) execute(s string) *testOutput { } case "gt": + expectedResultPattern = "%s is greater then %s" a, b := toNumeric(flagVal, t.Compare.Value) result.testResult = a > b case "gte": + expectedResultPattern = "%s is greater or equal to %s" a, b := toNumeric(flagVal, t.Compare.Value) result.testResult = a >= b case "lt": + expectedResultPattern = "%s is lower then %s" a, b := toNumeric(flagVal, t.Compare.Value) result.testResult = a < b case "lte": + expectedResultPattern = "%s is lower or equal to %s" a, b := toNumeric(flagVal, t.Compare.Value) result.testResult = a <= b case "has": + expectedResultPattern = "'%s' has '%s'" result.testResult = strings.Contains(flagVal, t.Compare.Value) case "nothave": + expectedResultPattern = " '%s' not have '%s'" result.testResult = !strings.Contains(flagVal, t.Compare.Value) } + + result.ExpectedResult = fmt.Sprintf(expectedResultPattern, t.Flag, t.Compare.Value) } else { + result.ExpectedResult = fmt.Sprintf("'%s' is present", t.Flag) result.testResult = isset } - } else { + result.ExpectedResult = fmt.Sprintf("'%s' is not present", t.Flag) notset := !match result.testResult = notset } @@ -219,13 +232,19 @@ func (ts *tests) execute(s string) *testOutput { case and, "": result = true for i := range res { + finalOutput.ExpectedResult += fmt.Sprintf("%s AND ", res[i].ExpectedResult) result = result && res[i].testResult } + // Delete last iteration ' AND ' + finalOutput.ExpectedResult = finalOutput.ExpectedResult[:len(finalOutput.ExpectedResult)-5] case or: result = false for i := range res { + finalOutput.ExpectedResult += fmt.Sprintf("%s OR ", res[i].ExpectedResult) result = result || res[i].testResult } + // Delete last iteration ' OR ' + finalOutput.ExpectedResult = finalOutput.ExpectedResult[:len(finalOutput.ExpectedResult)-4] } finalOutput.testResult = result From ddb677bc69e941dc33d62f6e4ff5877e29462943 Mon Sep 17 00:00:00 2001 From: Yoav Hizkiahou Date: Tue, 21 May 2019 14:10:11 +0300 Subject: [PATCH 5/5] Generate expected result by strings join --- check/test.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/check/test.go b/check/test.go index 9e2746b..d27750a 100644 --- a/check/test.go +++ b/check/test.go @@ -219,8 +219,11 @@ func (ts *tests) execute(s string) *testOutput { return finalOutput } + expectedResultArr := make([]string, len(res)) + for i, t := range ts.TestItems { res[i] = *(t.execute(s)) + expectedResultArr[i] = res[i].ExpectedResult } var result bool @@ -232,19 +235,18 @@ func (ts *tests) execute(s string) *testOutput { case and, "": result = true for i := range res { - finalOutput.ExpectedResult += fmt.Sprintf("%s AND ", res[i].ExpectedResult) result = result && res[i].testResult } - // Delete last iteration ' AND ' - finalOutput.ExpectedResult = finalOutput.ExpectedResult[:len(finalOutput.ExpectedResult)-5] + // Generate an AND expected result + finalOutput.ExpectedResult = strings.Join(expectedResultArr, " AND ") + case or: result = false for i := range res { - finalOutput.ExpectedResult += fmt.Sprintf("%s OR ", res[i].ExpectedResult) result = result || res[i].testResult } - // Delete last iteration ' OR ' - finalOutput.ExpectedResult = finalOutput.ExpectedResult[:len(finalOutput.ExpectedResult)-4] + // Generate an OR expected result + finalOutput.ExpectedResult = strings.Join(expectedResultArr, " OR ") } finalOutput.testResult = result