From 28bb9c80b4fddcbf2e7488afd0ba5b9721dd50a4 Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Fri, 27 Sep 2019 19:03:55 -0700 Subject: [PATCH] validate existing anchor of validate rule --- pkg/api/kyverno/v1alpha1/utils.go | 13 +++ pkg/api/kyverno/v1alpha1/validate.go | 120 ++++++++++++++++++++++----- 2 files changed, 111 insertions(+), 22 deletions(-) diff --git a/pkg/api/kyverno/v1alpha1/utils.go b/pkg/api/kyverno/v1alpha1/utils.go index 50b3b55832..85fc4e1b70 100644 --- a/pkg/api/kyverno/v1alpha1/utils.go +++ b/pkg/api/kyverno/v1alpha1/utils.go @@ -69,3 +69,16 @@ func containString(list []string, element string) bool { } return false } + +// hasExstingAnchor checks if str has existing anchor +// strip anchor if necessary +func hasExstingAnchor(str string) (bool, string) { + left := "^(" + right := ")" + + if len(str) < len(left)+len(right) { + return false, str + } + + return (str[:len(left)] == left && str[len(str)-len(right):] == right), str[len(left) : len(str)-len(right)] +} diff --git a/pkg/api/kyverno/v1alpha1/validate.go b/pkg/api/kyverno/v1alpha1/validate.go index d58ecc68e7..774eabdf28 100644 --- a/pkg/api/kyverno/v1alpha1/validate.go +++ b/pkg/api/kyverno/v1alpha1/validate.go @@ -4,7 +4,9 @@ import ( "errors" "fmt" "reflect" + "strconv" + "github.com/golang/glog" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -12,8 +14,9 @@ func (p ClusterPolicy) Validate() error { var errs []error for _, rule := range p.Spec.Rules { - err := rule.Validate() - errs = append(errs, err...) + if ruleErrs := rule.Validate(); ruleErrs != nil { + errs = append(errs, ruleErrs...) + } } if err := p.ValidateUniqueRuleName(); err != nil { @@ -23,6 +26,19 @@ func (p ClusterPolicy) Validate() error { return joinErrs(errs) } +// ValidateUniqueRuleName checks if the rule names are unique across a policy +func (p ClusterPolicy) ValidateUniqueRuleName() error { + var ruleNames []string + + for _, rule := range p.Spec.Rules { + if containString(ruleNames, rule.Name) { + return fmt.Errorf(`duplicate rule name: '%s'`, rule.Name) + } + ruleNames = append(ruleNames, rule.Name) + } + return nil +} + // Validate checks if rule is not empty and all substructures are valid func (r Rule) Validate() []error { var errs []error @@ -46,6 +62,10 @@ func (r Rule) Validate() []error { errs = append(errs, err) } + if patternErrs := r.ValidateExistingAnchor(); patternErrs != nil { + errs = append(errs, patternErrs...) + } + return errs } @@ -66,26 +86,6 @@ func (r Rule) ValidateOverlayPattern() error { return nil } -// ValidateExistingAnchor -// existing acnchor must define on array -func (r Rule) ValidateExistingAnchor() error { - - return nil -} - -// ValidateUniqueRuleName checks if the rule names are unique across a policy -func (p ClusterPolicy) ValidateUniqueRuleName() error { - var ruleNames []string - - for _, rule := range p.Spec.Rules { - if containString(ruleNames, rule.Name) { - return fmt.Errorf(`duplicate rule name: '%s'`, rule.Name) - } - ruleNames = append(ruleNames, rule.Name) - } - return nil -} - // validateRuleType checks only one type of rule is defined per rule func (r Rule) ValidateRuleType() error { mutate := r.HasMutate() @@ -174,3 +174,79 @@ func (gen *Generation) Validate() error { } return nil } + +// ValidateExistingAnchor +// existing acnchor must define on array +func (r Rule) ValidateExistingAnchor() []error { + var errs []error + + if r.Validation.Pattern != nil { + if _, err := validateExistingAnchorOnPattern(r.Validation.Pattern, "/"); err != nil { + errs = append(errs, err) + } + } + + if len(r.Validation.AnyPattern) != 0 { + for _, pattern := range r.Validation.AnyPattern { + if _, err := validateExistingAnchorOnPattern(pattern, "/"); err != nil { + errs = append(errs, err) + } + } + } + + return errs +} + +// validateExistingAnchorOnPattern validates ^() only defined on array +func validateExistingAnchorOnPattern(pattern interface{}, path string) (string, error) { + switch typedPattern := pattern.(type) { + case map[string]interface{}: + return validateMap(typedPattern, path) + case []interface{}: + return validateArray(typedPattern, path) + case string, float64, int, int64, bool, nil: + // check on type string + if checkedPattern := reflect.ValueOf(pattern); checkedPattern.Kind() == reflect.String { + if hasAnchor, str := hasExstingAnchor(checkedPattern.String()); hasAnchor { + return path, fmt.Errorf("existing anchor at %s must be of type array, found: %T", path+str, checkedPattern.Kind()) + } + } + + // return nil on all other cases + return "", nil + default: + glog.V(4).Infof("Pattern contains unknown type %T. Path: %s", pattern, path) + return path, fmt.Errorf("pattern contains unknown type, path: %s", path) + } +} + +func validateMap(pattern map[string]interface{}, path string) (string, error) { + for key, patternElement := range pattern { + if hasAnchor, str := hasExstingAnchor(key); hasAnchor { + if checkedPattern := reflect.ValueOf(patternElement); checkedPattern.Kind() != reflect.Slice { + return path, fmt.Errorf("existing anchor at %s must be of type array, found: %T", path+str, patternElement) + } + } + + if path, err := validateExistingAnchorOnPattern(patternElement, path+key+"/"); err != nil { + return path, err + } + } + + return "", nil +} + +func validateArray(patternArray []interface{}, path string) (string, error) { + if len(patternArray) == 0 { + return path, fmt.Errorf("pattern array at %s is empty", path) + } + + for i, pattern := range patternArray { + currentPath := path + strconv.Itoa(i) + "/" + if path, err := validateExistingAnchorOnPattern(pattern, currentPath); err != nil { + return path, err + } + } + + return "", nil +}