issue #344: Adds support for array comparison. Every element in the s… (#367)

* 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
pull/375/head^2
Roberto Rojas 5 years ago committed by Liz Rice
parent dab5e92bb5
commit 937bfc7b2e

@ -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: |

@ -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"`

@ -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 {

Loading…
Cancel
Save