From 937bfc7b2ee8e2fe86b26c89aa06326174ad0cbd Mon Sep 17 00:00:00 2001 From: Roberto Rojas Date: Fri, 26 Jul 2019 11:11:59 -0700 Subject: [PATCH] =?UTF-8?q?issue=20#344:=20Adds=20support=20for=20array=20?= =?UTF-8?q?comparison.=20Every=20element=20in=20the=20s=E2=80=A6=20(#367)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * issue #344: Adds support for array comparison. Every element in the source array must exist in the target array. * issue #344: Fixed typo and found if condition based on code review * adds unit tests for valid_elements comparison * removes spaces from split strings --- cfg/1.13-json/node.yaml | 4 +- check/test.go | 56 ++++++++++++++++++- check/test_test.go | 115 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 4 deletions(-) diff --git a/cfg/1.13-json/node.yaml b/cfg/1.13-json/node.yaml index 3f7c2b2..f938bc2 100644 --- a/cfg/1.13-json/node.yaml +++ b/cfg/1.13-json/node.yaml @@ -296,9 +296,9 @@ groups: audit: "cat $kubeletconf" tests: test_items: - - path: "{.tlsCipherSuites}" + - path: "{range .tlsCipherSuites[:]}{}{','}{end}" compare: - op: eq + op: valid_elements value: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256" set: true remediation: | diff --git a/check/test.go b/check/test.go index d79c72f..10c629b 100644 --- a/check/test.go +++ b/check/test.go @@ -37,8 +37,9 @@ import ( type binOp string const ( - and binOp = "and" - or = "or" + and binOp = "and" + or = "or" + defaultArraySeparator = "," ) type testItem struct { @@ -193,6 +194,13 @@ func compareOp(tCompareOp string, flagVal string, tCompareValue string) (string, expectedResultPattern = " '%s' matched by '%s'" opRe := regexp.MustCompile(tCompareValue) testResult = opRe.MatchString(flagVal) + + case "valid_elements": + expectedResultPattern = "'%s' contains valid elements from '%s'" + s := splitAndRemoveLastSeparator(flagVal, defaultArraySeparator) + target := splitAndRemoveLastSeparator(tCompareValue, defaultArraySeparator) + testResult = allElementsValid(s, target) + } if expectedResultPattern == "" { @@ -231,6 +239,50 @@ func executeJSONPath(path string, jsonInterface interface{}) (string, error) { return jsonpathResult, nil } +func allElementsValid(s, t []string) bool { + sourceEmpty := s == nil || len(s) == 0 + targetEmpty := t == nil || len(t) == 0 + + if sourceEmpty && targetEmpty { + return true + } + + // XOR comparison - + // if either value is empty and the other is not empty, + // not all elements are valid + if (sourceEmpty || targetEmpty) && !(sourceEmpty && targetEmpty) { + return false + } + + for _, sv := range s { + found := false + for _, tv := range t { + if sv == tv { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +func splitAndRemoveLastSeparator(s, sep string) []string { + cleanS := strings.TrimRight(strings.TrimSpace(s), sep) + if len(cleanS) == 0 { + return []string{} + } + + ts := strings.Split(cleanS, sep) + for i := range ts { + ts[i] = strings.TrimSpace(ts[i]) + } + + return ts +} + type tests struct { TestItems []*testItem `yaml:"test_items"` BinOp binOp `yaml:"bin_op"` diff --git a/check/test_test.go b/check/test_test.go index ab79fe9..bd168c2 100644 --- a/check/test_test.go +++ b/check/test_test.go @@ -323,6 +323,108 @@ func TestExecuteJSONPath(t *testing.T) { } } +func TestAllElementsValid(t *testing.T) { + cases := []struct { + source []string + target []string + valid bool + }{ + { + source: []string{}, + target: []string{}, + valid: true, + }, + { + source: []string{"blah"}, + target: []string{}, + valid: false, + }, + { + source: []string{}, + target: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_GCM_SHA256"}, + valid: false, + }, + { + source: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"}, + target: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_GCM_SHA256"}, + valid: true, + }, + { + source: []string{"blah"}, + target: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_GCM_SHA256"}, + valid: false, + }, + { + source: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "blah"}, + target: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_GCM_SHA256"}, + valid: false, + }, + } + for _, c := range cases { + if !allElementsValid(c.source, c.target) && c.valid { + t.Errorf("Not All Elements in %q are found in %q \n", c.source, c.target) + } + } +} + +func TestSplitAndRemoveLastSeparator(t *testing.T) { + cases := []struct { + source string + valid bool + elementCnt int + }{ + { + source: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256", + valid: true, + elementCnt: 8, + }, + { + source: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,", + valid: true, + elementCnt: 2, + }, + { + source: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,", + valid: true, + elementCnt: 2, + }, + { + source: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, ", + valid: true, + elementCnt: 2, + }, + { + source: " TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,", + valid: true, + elementCnt: 2, + }, + } + + for _, c := range cases { + as := splitAndRemoveLastSeparator(c.source, defaultArraySeparator) + if len(as) == 0 && c.valid { + t.Errorf("Split did not work with %q \n", c.source) + } + + if c.elementCnt != len(as) { + t.Errorf("Split did not work with %q expected: %d got: %d\n", c.source, c.elementCnt, len(as)) + } + + } +} + func TestCompareOp(t *testing.T) { cases := []struct { label string @@ -527,6 +629,19 @@ func TestCompareOp(t *testing.T) { {label: "op=gt, flagVal=empty", op: "regex", flagVal: "", compareValue: "blah", expectedResultPattern: " '' matched by 'blah'", testResult: false}, + + // Test Op "valid_elements" + {label: "op=valid_elements, valid_elements both empty", op: "valid_elements", flagVal: "", + compareValue: "", expectedResultPattern: "'' contains valid elements from ''", + testResult: true}, + + {label: "op=valid_elements, valid_elements flagVal empty", op: "valid_elements", flagVal: "", + compareValue: "a,b", expectedResultPattern: "'' contains valid elements from 'a,b'", + testResult: false}, + + {label: "op=valid_elements, valid_elements expectedResultPattern empty", op: "valid_elements", flagVal: "a,b", + compareValue: "", expectedResultPattern: "'a,b' contains valid elements from ''", + testResult: false}, } for _, c := range cases {