diff --git a/check/controls.go b/check/controls.go index 992b78d..d6f6721 100644 --- a/check/controls.go +++ b/check/controls.go @@ -75,7 +75,7 @@ func NewControls(t NodeType, in []byte) (*Controls, error) { } // RunChecks runs the checks with the given Runner. Only checks for which the filter Predicate returns `true` will run. -func (controls *Controls) RunChecks(runner Runner, filter Predicate) Summary { +func (controls *Controls) RunChecks(runner Runner, filter Predicate, skipIdMap map[string]bool) Summary { var g []*Group m := make(map[string]*Group) controls.Summary.Pass, controls.Summary.Fail, controls.Summary.Warn, controls.Info = 0, 0, 0, 0 @@ -87,8 +87,10 @@ func (controls *Controls) RunChecks(runner Runner, filter Predicate) Summary { continue } - // propagate skip type to check if set at the group level. - if group.Skip { + _, groupSkippedViaCmd := skipIdMap[group.ID] + _, checkSkippedViaCmd := skipIdMap[check.ID] + + if group.Skip || groupSkippedViaCmd || checkSkippedViaCmd { check.Type = SKIP } diff --git a/check/controls_test.go b/check/controls_test.go index 0d4cee6..34a76b8 100644 --- a/check/controls_test.go +++ b/check/controls_test.go @@ -69,7 +69,6 @@ func TestYamlFiles(t *testing.T) { } func TestNewControls(t *testing.T) { - t.Run("Should return error when node type is not specified", func(t *testing.T) { // given in := []byte(` @@ -95,9 +94,48 @@ groups: } +func TestControls_RunChecks_SkippedCmd(t *testing.T) { + t.Run("Should skip checks and groups specified by skipMap", func(t *testing.T) { + // given + normalRunner := &defaultRunner{} + // and + in := []byte(` +--- +type: "master" +groups: +- id: G1 + checks: + - id: G1/C1 + - id: G1/C2 + - id: G1/C3 +- id: G2 + checks: + - id: G2/C1 + - id: G2/C2 +`) + controls, err := NewControls(MASTER, in) + assert.NoError(t, err) + + var allChecks Predicate = func(group *Group, c *Check) bool { + return true + } + + skipMap := make(map[string]bool, 0) + skipMap["G1"] = true + skipMap["G2/C1"] = true + skipMap["G2/C2"] = true + controls.RunChecks(normalRunner, allChecks, skipMap) + + G1 := controls.Groups[0] + assertEqualGroupSummary(t, 0, 0, 3, 0, G1) + + G2 := controls.Groups[1] + assertEqualGroupSummary(t, 0, 0, 2, 0, G2) + }) +} func TestControls_RunChecks_Skipped(t *testing.T) { - t.Run("Should run checks matching the filter and update summaries", func(t *testing.T) { + t.Run("Should skip checks where the parent group is marked as skip", func(t *testing.T) { // given normalRunner := &defaultRunner{} // and @@ -116,12 +154,12 @@ groups: var allChecks Predicate = func(group *Group, c *Check) bool { return true } - controls.RunChecks(normalRunner, allChecks) + emptySkipList := make(map[string]bool, 0) + controls.RunChecks(normalRunner, allChecks, emptySkipList) G1 := controls.Groups[0] assertEqualGroupSummary(t, 0, 0, 1, 0, G1) }) - } func TestControls_RunChecks(t *testing.T) { @@ -162,8 +200,9 @@ groups: var runAll Predicate = func(group *Group, c *Check) bool { return true } + var emptySkipList = make(map[string]bool, 0) // when - controls.RunChecks(runner, runAll) + controls.RunChecks(runner, runAll, emptySkipList) // then assert.Equal(t, 2, len(controls.Groups)) // and diff --git a/cmd/common.go b/cmd/common.go index 49a4e2d..9e33098 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -32,7 +32,6 @@ import ( // NewRunFilter constructs a Predicate based on FilterOpts which determines whether tested Checks should be run or not. func NewRunFilter(opts FilterOpts) (check.Predicate, error) { - if opts.CheckList != "" && opts.GroupList != "" { return nil, fmt.Errorf("group option and check option can't be used together") } @@ -118,10 +117,20 @@ func runChecks(nodetype check.NodeType, testYamlFile string) { exitWithError(fmt.Errorf("error setting up run filter: %v", err)) } - controls.RunChecks(runner, filter) + controls.RunChecks(runner, filter, parseSkipIds(skipIds)) controlsCollection = append(controlsCollection, controls) } +func parseSkipIds(skipIds string) map[string]bool { + var skipIdMap = make(map[string]bool, 0) + if skipIds != "" { + for _, id := range strings.Split(skipIds, ",") { + skipIdMap[strings.Trim(id, " ")] = true + } + } + return skipIdMap +} + // colorPrint outputs the state in a specific colour, along with a message string func colorPrint(state check.State, s string) { colors[state].Printf("[%s] ", state) diff --git a/cmd/common_test.go b/cmd/common_test.go index 15f4164..774c4aa 100644 --- a/cmd/common_test.go +++ b/cmd/common_test.go @@ -30,6 +30,18 @@ import ( "github.com/stretchr/testify/assert" ) +func TestParseSkipIds(t *testing.T) { + skipMap := parseSkipIds("4.12,4.13,5") + _, fourTwelveExists := skipMap["4.12"] + _, fourThirteenExists := skipMap["4.13"] + _, fiveExists := skipMap["5"] + _, other := skipMap["G1"] + assert.True(t, fourThirteenExists) + assert.True(t, fourTwelveExists) + assert.True(t, fiveExists) + assert.False(t, other) +} + func TestNewRunFilter(t *testing.T) { type TestCase struct { diff --git a/cmd/root.go b/cmd/root.go index 432d326..41334a7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -51,6 +51,7 @@ var ( noResults bool noSummary bool noRemediations bool + skipIds string filterOpts FilterOpts includeTestOutput bool outputFile string @@ -153,6 +154,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().StringVar(&skipIds, "skip", "", "List of comma separated values of checks to be skipped") RootCmd.PersistentFlags().BoolVar(&includeTestOutput, "include-test-output", false, "Prints the actual result when test fails") RootCmd.PersistentFlags().StringVar(&outputFile, "outputfile", "", "Writes the JSON results to output file")