From 39ac8391c65d4c5a32c02ef8fef8de6e24af9b72 Mon Sep 17 00:00:00 2001 From: shravan Date: Wed, 26 Feb 2020 16:08:56 +0530 Subject: [PATCH] 658 prototype with testcases --- pkg/policy/validate.go | 42 +++++++++++++++++++++++++++ pkg/policy/validate_test.go | 58 +++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/pkg/policy/validate.go b/pkg/policy/validate.go index b5071a01a3..bc44d7524f 100644 --- a/pkg/policy/validate.go +++ b/pkg/policy/validate.go @@ -69,11 +69,53 @@ func Validate(p kyverno.ClusterPolicy) error { return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", i, path, err) } } + + // If a rules match block does not match any kind, + // we should only allow such rules to have metadata in its overlay + if len(rule.MatchResources.Kinds) == 0 { + if !ruleOnlyDealsWithResourceMetaData(rule) { + return fmt.Errorf("policy can only deal with the metadata field of the resource if" + + " the rule does not match an kind") + } + } } return nil } +func ruleOnlyDealsWithResourceMetaData(rule kyverno.Rule) bool { + overlayMap, _ := rule.Mutation.Overlay.(map[string]interface{}) + for k := range overlayMap { + if k != "metadata" { + return false + } + } + + for _, patch := range rule.Mutation.Patches { + if !strings.HasPrefix(patch.Path, "/metadata") { + return false + } + } + + patternMap, _ := rule.Validation.Pattern.(map[string]interface{}) + for k := range patternMap { + if k != "metadata" { + return false + } + } + + for _, pattern := range rule.Validation.AnyPattern { + patternMap, _ := pattern.(map[string]interface{}) + for k := range patternMap { + if k != "metadata" { + return false + } + } + } + + return true +} + func validateResources(rule kyverno.Rule) (string, error) { // validate userInfo in match and exclude if path, err := validateUserInfo(rule); err != nil { diff --git a/pkg/policy/validate_test.go b/pkg/policy/validate_test.go index e9e08ab240..387c87707b 100644 --- a/pkg/policy/validate_test.go +++ b/pkg/policy/validate_test.go @@ -1483,3 +1483,61 @@ func Test_BackGroundUserInfo_validate_anyPattern_serviceAccount(t *testing.T) { t.Error("Incorrect Path") } } + +func Test_ruleOnlyDealsWithResourceMetaData(t *testing.T) { + testcases := []struct { + description string + rule []byte + expectedOutput bool + }{ + { + description: "Test mutate overlay - pass", + rule: []byte(`{"name":"test","mutate":{"overlay":{"metadata":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}`), + expectedOutput: true, + }, + { + description: "Test mutate overlay - fail", + rule: []byte(`{"name":"test","mutate":{"overlay":{"spec":{"containers":[{"(image)":"*","imagePullPolicy":"IfNotPresent"}]}}}}`), + expectedOutput: false, + }, + { + description: "Test mutate patch - pass", + rule: []byte(`{"name":"testPatches","mutate":{"patches":[{"path":"/metadata/labels/isMutated","op":"add","value":"true"},{"path":"/metadata/labels/app","op":"replace","value":"nginx_is_mutated"}]}}`), + expectedOutput: true, + }, + { + description: "Test mutate patch - fail", + rule: []byte(`{"name":"testPatches","mutate":{"patches":[{"path":"/spec/labels/isMutated","op":"add","value":"true"},{"path":"/metadata/labels/app","op":"replace","value":"nginx_is_mutated"}]}}`), + expectedOutput: false, + }, + { + description: "Test validate - pass", + rule: []byte(`{"name":"testValidate","validate":{"message":"CPU and memory resource requests and limits are required","pattern":{"metadata":{"containers":[{"(name)":"*","ports":[{"containerPort":80}]}]}}}}`), + expectedOutput: true, + }, + { + description: "Test validate - fail", + rule: []byte(`{"name":"testValidate","validate":{"message":"CPU and memory resource requests and limits are required","pattern":{"spec":{"containers":[{"(name)":"*","ports":[{"containerPort":80}]}]}}}}`), + expectedOutput: false, + }, + { + description: "Test validate any pattern - pass", + rule: []byte(`{"name":"testValidateAnyPattern","validate":{"message":"Volumes white list","anyPattern":[{"metadata":{"volumes":[{"hostPath":"*"}]}},{"metadata":{"volumes":[{"emptyDir":"*"}]}},{"metadata":{"volumes":[{"configMap":"*"}]}}]}}`), + expectedOutput: true, + }, + { + description: "Test validate any pattern - fail", + rule: []byte(`{"name":"testValidateAnyPattern","validate":{"message":"Volumes white list","anyPattern":[{"spec":{"volumes":[{"hostPath":"*"}]}},{"metadata":{"volumes":[{"emptyDir":"*"}]}},{"metadata":{"volumes":[{"configMap":"*"}]}}]}}`), + expectedOutput: false, + }, + } + + for i, testcase := range testcases { + var rule kyverno.Rule + _ = json.Unmarshal(testcase.rule, &rule) + output := ruleOnlyDealsWithResourceMetaData(rule) + if output != testcase.expectedOutput { + t.Errorf("Testcase [%d] failed", i+1) + } + } +}