1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

refactor: match and exclude conflict validation (#3454)

Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>
This commit is contained in:
Charles-Edouard Brétéché 2022-03-23 12:34:17 +01:00 committed by GitHub
parent f34d3c342d
commit 3cf83bc77f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 246 additions and 317 deletions

View file

@ -149,3 +149,81 @@ func Test_Validate_RuleType_SingleRule(t *testing.T) {
assert.Assert(t, len(errs) == 0)
}
}
func Test_doesMatchExcludeConflict(t *testing.T) {
path := field.NewPath("dummy")
testcases := []struct {
description string
rule []byte
errors func(r *Rule) field.ErrorList
}{
{
description: "Same match and exclude",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
errors: func(r *Rule) (errs field.ErrorList) {
return append(errs, field.Invalid(path, r, "Rule is matching an empty set"))
},
},
{
description: "Failed to exclude kind",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
},
{
description: "Failed to exclude name",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something-*","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
},
{
description: "Failed to exclude namespace",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something3","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
},
{
description: "Failed to exclude labels",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"higha"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
},
{
description: "Failed to exclude expression",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["databases"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
},
{
description: "Failed to exclude subjects",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something2","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
},
{
description: "Failed to exclude clusterroles",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something3","something1"],"roles":["something","something1"]}}`),
},
{
description: "Failed to exclude roles",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something3","something1"]}}`),
},
{
description: "simple",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"]}},"exclude":{"resources":{"kinds":["Pod","Namespace","Job"],"name":"some*","namespaces":["something","something1","something2"]}}}`),
errors: func(r *Rule) (errs field.ErrorList) {
return append(errs, field.Invalid(path, r, "Rule is matching an empty set"))
},
},
{
description: "simple - fail",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"somxething","namespaces":["something","something1"]}},"exclude":{"resources":{"kinds":["Pod","Namespace","Job"],"name":"some*","namespaces":["something","something1","something2"]}}}`),
},
{
description: "empty case",
rule: []byte(`{"name":"check-allow-deletes","match":{"resources":{"selector":{"matchLabels":{"allow-deletes":"false"}}}},"exclude":{"clusterRoles":["random"]},"validate":{"message":"Deleting {{request.object.kind}}/{{request.object.metadata.name}} is not allowed","deny":{"conditions":{"all":[{"key":"{{request.operation}}","operator":"Equal","value":"DELETE"}]}}}}`),
},
}
for _, testcase := range testcases {
var rule Rule
err := json.Unmarshal(testcase.rule, &rule)
assert.NilError(t, err)
errs := rule.ValidateMathExcludeConflict(path)
var expectedErrs field.ErrorList
if testcase.errors != nil {
expectedErrs = testcase.errors(&rule)
}
assert.Equal(t, len(errs), len(expectedErrs))
for i := range errs {
assert.Equal(t, errs[i].Error(), expectedErrs[i].Error())
}
}
}

View file

@ -1,9 +1,11 @@
package v1
import (
"encoding/json"
"fmt"
"reflect"
"github.com/minio/pkg/wildcard"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/util/sets"
@ -130,10 +132,176 @@ func (r *Rule) ValidateRuleType(path *field.Path) field.ErrorList {
return errs
}
// ValidateMathExcludeConflict checks if the resultant of match and exclude block is not an empty set
func (r *Rule) ValidateMathExcludeConflict(path *field.Path) (errs field.ErrorList) {
if len(r.ExcludeResources.All) > 0 || len(r.MatchResources.All) > 0 {
return errs
}
// if both have any then no resource should be common
if len(r.MatchResources.Any) > 0 && len(r.ExcludeResources.Any) > 0 {
for _, rmr := range r.MatchResources.Any {
for _, rer := range r.ExcludeResources.Any {
if reflect.DeepEqual(rmr, rer) {
return append(errs, field.Invalid(path, r, "Rule is matching an empty set"))
}
}
}
return errs
}
if reflect.DeepEqual(r.ExcludeResources, MatchResources{}) {
return errs
}
excludeRoles := sets.NewString(r.ExcludeResources.Roles...)
excludeClusterRoles := sets.NewString(r.ExcludeResources.ClusterRoles...)
excludeKinds := sets.NewString(r.ExcludeResources.Kinds...)
excludeNamespaces := sets.NewString(r.ExcludeResources.Namespaces...)
excludeSubjects := sets.NewString()
for _, subject := range r.ExcludeResources.Subjects {
subjectRaw, _ := json.Marshal(subject)
excludeSubjects.Insert(string(subjectRaw))
}
excludeSelectorMatchExpressions := sets.NewString()
if r.ExcludeResources.Selector != nil {
for _, matchExpression := range r.ExcludeResources.Selector.MatchExpressions {
matchExpressionRaw, _ := json.Marshal(matchExpression)
excludeSelectorMatchExpressions.Insert(string(matchExpressionRaw))
}
}
excludeNamespaceSelectorMatchExpressions := sets.NewString()
if r.ExcludeResources.NamespaceSelector != nil {
for _, matchExpression := range r.ExcludeResources.NamespaceSelector.MatchExpressions {
matchExpressionRaw, _ := json.Marshal(matchExpression)
excludeNamespaceSelectorMatchExpressions.Insert(string(matchExpressionRaw))
}
}
if len(excludeRoles) > 0 {
if len(r.MatchResources.Roles) == 0 || !excludeRoles.HasAll(r.MatchResources.Roles...) {
return errs
}
}
if len(excludeClusterRoles) > 0 {
if len(r.MatchResources.ClusterRoles) == 0 || !excludeClusterRoles.HasAll(r.MatchResources.ClusterRoles...) {
return errs
}
}
if len(excludeSubjects) > 0 {
if len(r.MatchResources.Subjects) == 0 {
return errs
}
for _, subject := range r.MatchResources.UserInfo.Subjects {
subjectRaw, _ := json.Marshal(subject)
if !excludeSubjects.Has(string(subjectRaw)) {
return errs
}
}
}
if r.ExcludeResources.Name != "" {
if !wildcard.Match(r.ExcludeResources.Name, r.MatchResources.Name) {
return errs
}
}
if len(r.ExcludeResources.Names) > 0 {
excludeSlice := r.ExcludeResources.Names
matchSlice := r.MatchResources.Names
// if exclude block has something and match doesn't it means we
// have a non empty set
if len(r.MatchResources.Names) == 0 {
return errs
}
// if *any* name in match and exclude conflicts
// we want user to fix that
for _, matchName := range matchSlice {
for _, excludeName := range excludeSlice {
if wildcard.Match(excludeName, matchName) {
return append(errs, field.Invalid(path, r, "Rule is matching an empty set"))
}
}
}
return errs
}
if len(excludeNamespaces) > 0 {
if len(r.MatchResources.Namespaces) == 0 || !excludeNamespaces.HasAll(r.MatchResources.Namespaces...) {
return errs
}
}
if len(excludeKinds) > 0 {
if len(r.MatchResources.Kinds) == 0 || !excludeKinds.HasAll(r.MatchResources.Kinds...) {
return errs
}
}
if r.MatchResources.Selector != nil && r.ExcludeResources.Selector != nil {
if len(excludeSelectorMatchExpressions) > 0 {
if len(r.MatchResources.Selector.MatchExpressions) == 0 {
return errs
}
for _, matchExpression := range r.MatchResources.Selector.MatchExpressions {
matchExpressionRaw, _ := json.Marshal(matchExpression)
if !excludeSelectorMatchExpressions.Has(string(matchExpressionRaw)) {
return errs
}
}
}
if len(r.ExcludeResources.Selector.MatchLabels) > 0 {
if len(r.MatchResources.Selector.MatchLabels) == 0 {
return errs
}
for label, value := range r.MatchResources.Selector.MatchLabels {
if r.ExcludeResources.Selector.MatchLabels[label] != value {
return errs
}
}
}
}
if r.MatchResources.NamespaceSelector != nil && r.ExcludeResources.NamespaceSelector != nil {
if len(excludeNamespaceSelectorMatchExpressions) > 0 {
if len(r.MatchResources.NamespaceSelector.MatchExpressions) == 0 {
return errs
}
for _, matchExpression := range r.MatchResources.NamespaceSelector.MatchExpressions {
matchExpressionRaw, _ := json.Marshal(matchExpression)
if !excludeNamespaceSelectorMatchExpressions.Has(string(matchExpressionRaw)) {
return errs
}
}
}
if len(r.ExcludeResources.NamespaceSelector.MatchLabels) > 0 {
if len(r.MatchResources.NamespaceSelector.MatchLabels) == 0 {
return errs
}
for label, value := range r.MatchResources.NamespaceSelector.MatchLabels {
if r.ExcludeResources.NamespaceSelector.MatchLabels[label] != value {
return errs
}
}
}
}
if (r.MatchResources.Selector == nil && r.ExcludeResources.Selector != nil) ||
(r.MatchResources.Selector != nil && r.ExcludeResources.Selector == nil) {
return errs
}
if (r.MatchResources.NamespaceSelector == nil && r.ExcludeResources.NamespaceSelector != nil) ||
(r.MatchResources.NamespaceSelector != nil && r.ExcludeResources.NamespaceSelector == nil) {
return errs
}
if r.MatchResources.Annotations != nil && r.ExcludeResources.Annotations != nil {
if !(reflect.DeepEqual(r.MatchResources.Annotations, r.ExcludeResources.Annotations)) {
return errs
}
}
if (r.MatchResources.Annotations == nil && r.ExcludeResources.Annotations != nil) ||
(r.MatchResources.Annotations != nil && r.ExcludeResources.Annotations == nil) {
return errs
}
return append(errs, field.Invalid(path, r, "Rule is matching an empty set"))
}
// Validate implements programmatic validation
func (r *Rule) Validate(path *field.Path, namespaced bool, clusterResources sets.String) field.ErrorList {
var errs field.ErrorList
errs = append(errs, r.ValidateRuleType(path)...)
errs = append(errs, r.ValidateMathExcludeConflict(path)...)
errs = append(errs, r.MatchResources.Validate(path.Child("match"), namespaced, clusterResources)...)
errs = append(errs, r.ExcludeResources.Validate(path.Child("exclude"), namespaced, clusterResources)...)
return errs

View file

@ -21,7 +21,6 @@ import (
"github.com/kyverno/kyverno/pkg/kyverno/common"
"github.com/kyverno/kyverno/pkg/openapi"
"github.com/kyverno/kyverno/pkg/utils"
"github.com/minio/pkg/wildcard"
"github.com/pkg/errors"
v1beta1 "k8s.io/api/admission/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
@ -149,10 +148,6 @@ func Validate(policy *kyverno.ClusterPolicy, client *dclient.Client, mock bool,
return nil, checkClusterResourceInMatchAndExclude(rule, clusterResources, mock, res)
}
if doMatchAndExcludeConflict(rule) {
return nil, fmt.Errorf("path: spec.rules[%v]: rule is matching an empty set", rule.Name)
}
// validate rule actions
// - Mutate
// - Validate
@ -538,240 +533,6 @@ func validateMatchKindHelper(rule kyverno.Rule) error {
return fmt.Errorf("at least one element must be specified in a kind block, the kind attribute is mandatory when working with the resources element")
}
// doMatchAndExcludeConflict checks if the resultant
// of match and exclude block is not an empty set
// returns true if it is an empty set
func doMatchAndExcludeConflict(rule kyverno.Rule) bool {
if len(rule.ExcludeResources.All) > 0 || len(rule.MatchResources.All) > 0 {
return false
}
// if both have any then no resource should be common
if len(rule.MatchResources.Any) > 0 && len(rule.ExcludeResources.Any) > 0 {
for _, rmr := range rule.MatchResources.Any {
for _, rer := range rule.ExcludeResources.Any {
if reflect.DeepEqual(rmr, rer) {
return true
}
}
}
return false
}
if reflect.DeepEqual(rule.ExcludeResources, kyverno.MatchResources{}) {
return false
}
excludeRoles := make(map[string]bool)
for _, role := range rule.ExcludeResources.UserInfo.Roles {
excludeRoles[role] = true
}
excludeClusterRoles := make(map[string]bool)
for _, clusterRoles := range rule.ExcludeResources.UserInfo.ClusterRoles {
excludeClusterRoles[clusterRoles] = true
}
excludeSubjects := make(map[string]bool)
for _, subject := range rule.ExcludeResources.UserInfo.Subjects {
subjectRaw, _ := json.Marshal(subject)
excludeSubjects[string(subjectRaw)] = true
}
excludeKinds := make(map[string]bool)
for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds {
excludeKinds[kind] = true
}
excludeNamespaces := make(map[string]bool)
for _, namespace := range rule.ExcludeResources.ResourceDescription.Namespaces {
excludeNamespaces[namespace] = true
}
excludeSelectorMatchExpressions := make(map[string]bool)
if rule.ExcludeResources.ResourceDescription.Selector != nil {
for _, matchExpression := range rule.ExcludeResources.ResourceDescription.Selector.MatchExpressions {
matchExpressionRaw, _ := json.Marshal(matchExpression)
excludeSelectorMatchExpressions[string(matchExpressionRaw)] = true
}
}
excludeNamespaceSelectorMatchExpressions := make(map[string]bool)
if rule.ExcludeResources.ResourceDescription.NamespaceSelector != nil {
for _, matchExpression := range rule.ExcludeResources.ResourceDescription.NamespaceSelector.MatchExpressions {
matchExpressionRaw, _ := json.Marshal(matchExpression)
excludeNamespaceSelectorMatchExpressions[string(matchExpressionRaw)] = true
}
}
if len(excludeRoles) > 0 {
if len(rule.MatchResources.UserInfo.Roles) == 0 {
return false
}
for _, role := range rule.MatchResources.UserInfo.Roles {
if !excludeRoles[role] {
return false
}
}
}
if len(excludeClusterRoles) > 0 {
if len(rule.MatchResources.UserInfo.ClusterRoles) == 0 {
return false
}
for _, clusterRole := range rule.MatchResources.UserInfo.ClusterRoles {
if !excludeClusterRoles[clusterRole] {
return false
}
}
}
if len(excludeSubjects) > 0 {
if len(rule.MatchResources.UserInfo.Subjects) == 0 {
return false
}
for _, subject := range rule.MatchResources.UserInfo.Subjects {
subjectRaw, _ := json.Marshal(subject)
if !excludeSubjects[string(subjectRaw)] {
return false
}
}
}
if rule.ExcludeResources.ResourceDescription.Name != "" {
if !wildcard.Match(rule.ExcludeResources.ResourceDescription.Name, rule.MatchResources.ResourceDescription.Name) {
return false
}
}
if len(rule.ExcludeResources.ResourceDescription.Names) > 0 {
excludeSlice := rule.ExcludeResources.ResourceDescription.Names
matchSlice := rule.MatchResources.ResourceDescription.Names
// if exclude block has something and match doesn't it means we
// have a non empty set
if len(rule.MatchResources.ResourceDescription.Names) == 0 {
return false
}
// if *any* name in match and exclude conflicts
// we want user to fix that
for _, matchName := range matchSlice {
for _, excludeName := range excludeSlice {
if wildcard.Match(excludeName, matchName) {
return true
}
}
}
return false
}
if len(excludeNamespaces) > 0 {
if len(rule.MatchResources.ResourceDescription.Namespaces) == 0 {
return false
}
for _, namespace := range rule.MatchResources.ResourceDescription.Namespaces {
if !excludeNamespaces[namespace] {
return false
}
}
}
if len(excludeKinds) > 0 {
if len(rule.MatchResources.ResourceDescription.Kinds) == 0 {
return false
}
for _, kind := range rule.MatchResources.ResourceDescription.Kinds {
if !excludeKinds[kind] {
return false
}
}
}
if rule.MatchResources.ResourceDescription.Selector != nil && rule.ExcludeResources.ResourceDescription.Selector != nil {
if len(excludeSelectorMatchExpressions) > 0 {
if len(rule.MatchResources.ResourceDescription.Selector.MatchExpressions) == 0 {
return false
}
for _, matchExpression := range rule.MatchResources.ResourceDescription.Selector.MatchExpressions {
matchExpressionRaw, _ := json.Marshal(matchExpression)
if !excludeSelectorMatchExpressions[string(matchExpressionRaw)] {
return false
}
}
}
if len(rule.ExcludeResources.ResourceDescription.Selector.MatchLabels) > 0 {
if len(rule.MatchResources.ResourceDescription.Selector.MatchLabels) == 0 {
return false
}
for label, value := range rule.MatchResources.ResourceDescription.Selector.MatchLabels {
if rule.ExcludeResources.ResourceDescription.Selector.MatchLabels[label] != value {
return false
}
}
}
}
if rule.MatchResources.ResourceDescription.NamespaceSelector != nil && rule.ExcludeResources.ResourceDescription.NamespaceSelector != nil {
if len(excludeNamespaceSelectorMatchExpressions) > 0 {
if len(rule.MatchResources.ResourceDescription.NamespaceSelector.MatchExpressions) == 0 {
return false
}
for _, matchExpression := range rule.MatchResources.ResourceDescription.NamespaceSelector.MatchExpressions {
matchExpressionRaw, _ := json.Marshal(matchExpression)
if !excludeNamespaceSelectorMatchExpressions[string(matchExpressionRaw)] {
return false
}
}
}
if len(rule.ExcludeResources.ResourceDescription.NamespaceSelector.MatchLabels) > 0 {
if len(rule.MatchResources.ResourceDescription.NamespaceSelector.MatchLabels) == 0 {
return false
}
for label, value := range rule.MatchResources.ResourceDescription.NamespaceSelector.MatchLabels {
if rule.ExcludeResources.ResourceDescription.NamespaceSelector.MatchLabels[label] != value {
return false
}
}
}
}
if (rule.MatchResources.ResourceDescription.Selector == nil && rule.ExcludeResources.ResourceDescription.Selector != nil) ||
(rule.MatchResources.ResourceDescription.Selector != nil && rule.ExcludeResources.ResourceDescription.Selector == nil) {
return false
}
if (rule.MatchResources.ResourceDescription.NamespaceSelector == nil && rule.ExcludeResources.ResourceDescription.NamespaceSelector != nil) ||
(rule.MatchResources.ResourceDescription.NamespaceSelector != nil && rule.ExcludeResources.ResourceDescription.NamespaceSelector == nil) {
return false
}
if rule.MatchResources.Annotations != nil && rule.ExcludeResources.Annotations != nil {
if !(reflect.DeepEqual(rule.MatchResources.Annotations, rule.ExcludeResources.Annotations)) {
return false
}
}
if (rule.MatchResources.Annotations == nil && rule.ExcludeResources.Annotations != nil) ||
(rule.MatchResources.Annotations != nil && rule.ExcludeResources.Annotations == nil) {
return false
}
return true
}
// isLabelAndAnnotationsString :- Validate if labels and annotations contains only string values
func isLabelAndAnnotationsString(rule kyverno.Rule) bool {
// checkMetadata - Verify if the labels and annotations contains string value inside metadata

View file

@ -854,84 +854,6 @@ func Test_ruleOnlyDealsWithResourceMetaData(t *testing.T) {
}
}
func Test_doesMatchExcludeConflict(t *testing.T) {
testcases := []struct {
description string
rule []byte
expectedOutput bool
}{
{
description: "Same match and exclude",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: true,
},
{
description: "Failed to exclude kind",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude name",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something-*","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude namespace",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something3","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude labels",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"higha"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude expression",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["databases"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude subjects",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something2","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude clusterroles",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something3","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude roles",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something3","something1"]}}`),
expectedOutput: false,
},
{
description: "simple",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"]}},"exclude":{"resources":{"kinds":["Pod","Namespace","Job"],"name":"some*","namespaces":["something","something1","something2"]}}}`),
expectedOutput: true,
},
{
description: "simple - fail",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"somxething","namespaces":["something","something1"]}},"exclude":{"resources":{"kinds":["Pod","Namespace","Job"],"name":"some*","namespaces":["something","something1","something2"]}}}`),
expectedOutput: false,
},
{
description: "empty case",
rule: []byte(`{"name":"check-allow-deletes","match":{"resources":{"selector":{"matchLabels":{"allow-deletes":"false"}}}},"exclude":{"clusterRoles":["random"]},"validate":{"message":"Deleting {{request.object.kind}}/{{request.object.metadata.name}} is not allowed","deny":{"conditions":{"all":[{"key":"{{request.operation}}","operator":"Equal","value":"DELETE"}]}}}}`),
expectedOutput: false,
},
}
for i, testcase := range testcases {
var rule kyverno.Rule
_ = json.Unmarshal(testcase.rule, &rule)
if doMatchAndExcludeConflict(rule) != testcase.expectedOutput {
t.Errorf("Testcase [%d] failed - description - %v", i+1, testcase.description)
}
}
}
func Test_Validate_Kind(t *testing.T) {
rawPolicy := []byte(`
{