diff --git a/api/kyverno/v1/match_resources_types.go b/api/kyverno/v1/match_resources_types.go index 19441c8a04..bf16f0b1ee 100644 --- a/api/kyverno/v1/match_resources_types.go +++ b/api/kyverno/v1/match_resources_types.go @@ -46,6 +46,9 @@ func (m *MatchResources) GetKinds() []string { // Validate implements programmatic validation func (m *MatchResources) Validate(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) { + if m == nil { + return errs + } if len(m.Any) > 0 && len(m.All) > 0 { errs = append(errs, field.Invalid(path, m, "Can't specify any and all together")) } diff --git a/api/kyverno/v1/rule_types.go b/api/kyverno/v1/rule_types.go index 92e5b6fd60..755ef52e13 100644 --- a/api/kyverno/v1/rule_types.go +++ b/api/kyverno/v1/rule_types.go @@ -65,7 +65,7 @@ type Rule struct { // criteria can include resource information (e.g. kind, name, namespace, labels) // and admission review request information like the name or role. // +optional - ExcludeResources MatchResources `json:"exclude,omitempty"` + ExcludeResources *MatchResources `json:"exclude,omitempty"` // ImageExtractors defines a mapping from kinds to ImageExtractorConfigs. // This config is only valid for verifyImages rules. @@ -252,6 +252,9 @@ func (r *Rule) ValidateRuleType(path *field.Path) (errs field.ErrorList) { // ValidateMatchExcludeConflict checks if the resultant of match and exclude block is not an empty set func (r *Rule) ValidateMatchExcludeConflict(path *field.Path) (errs field.ErrorList) { + if r.ExcludeResources == nil { + return errs + } if len(r.ExcludeResources.All) > 0 || len(r.MatchResources.All) > 0 { return errs } @@ -266,7 +269,7 @@ func (r *Rule) ValidateMatchExcludeConflict(path *field.Path) (errs field.ErrorL } return errs } - if datautils.DeepEqual(r.ExcludeResources, MatchResources{}) { + if datautils.DeepEqual(*r.ExcludeResources, MatchResources{}) { return errs } excludeRoles := sets.New(r.ExcludeResources.Roles...) diff --git a/api/kyverno/v1/zz_generated.deepcopy.go b/api/kyverno/v1/zz_generated.deepcopy.go index e317397785..d1cc33eb32 100755 --- a/api/kyverno/v1/zz_generated.deepcopy.go +++ b/api/kyverno/v1/zz_generated.deepcopy.go @@ -1396,7 +1396,11 @@ func (in *Rule) DeepCopyInto(out *Rule) { } } in.MatchResources.DeepCopyInto(&out.MatchResources) - in.ExcludeResources.DeepCopyInto(&out.ExcludeResources) + if in.ExcludeResources != nil { + in, out := &in.ExcludeResources, &out.ExcludeResources + *out = new(MatchResources) + (*in).DeepCopyInto(*out) + } if in.ImageExtractors != nil { in, out := &in.ImageExtractors, &out.ImageExtractors *out = make(ImageExtractorConfigs, len(*in)) diff --git a/api/kyverno/v2beta1/match_resources_types.go b/api/kyverno/v2beta1/match_resources_types.go index ba1a676e72..974c00f46e 100644 --- a/api/kyverno/v2beta1/match_resources_types.go +++ b/api/kyverno/v2beta1/match_resources_types.go @@ -41,6 +41,9 @@ func (m *MatchResources) GetKinds() []string { // ValidateNoUserInfo verifies that no user info is used func (m *MatchResources) ValidateNoUserInfo(path *field.Path) (errs field.ErrorList) { + if m == nil { + return errs + } anyPath := path.Child("any") for i, filter := range m.Any { errs = append(errs, filter.UserInfo.ValidateNoUserInfo(anyPath.Index(i))...) @@ -54,6 +57,9 @@ func (m *MatchResources) ValidateNoUserInfo(path *field.Path) (errs field.ErrorL // ValidateResourceWithNoUserInfo implements programmatic validation and verifies that no user info is used func (m *MatchResources) ValidateResourceWithNoUserInfo(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) { + if m == nil { + return errs + } if len(m.Any) > 0 && len(m.All) > 0 { errs = append(errs, field.Invalid(path, m, "Can't specify any and all together")) } @@ -72,6 +78,9 @@ func (m *MatchResources) ValidateResourceWithNoUserInfo(path *field.Path, namesp // Validate implements programmatic validation func (m *MatchResources) Validate(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) { + if m == nil { + return errs + } if len(m.Any) > 0 && len(m.All) > 0 { errs = append(errs, field.Invalid(path, m, "Can't specify any and all together")) } diff --git a/api/kyverno/v2beta1/rule_types.go b/api/kyverno/v2beta1/rule_types.go index b469c77faa..986d3835b8 100644 --- a/api/kyverno/v2beta1/rule_types.go +++ b/api/kyverno/v2beta1/rule_types.go @@ -32,7 +32,7 @@ type Rule struct { // criteria can include resource information (e.g. kind, name, namespace, labels) // and admission review request information like the name or role. // +optional - ExcludeResources MatchResources `json:"exclude,omitempty"` + ExcludeResources *MatchResources `json:"exclude,omitempty"` // ImageExtractors defines a mapping from kinds to ImageExtractorConfigs. // This config is only valid for verifyImages rules. @@ -160,6 +160,9 @@ func (r *Rule) ValidateRuleType(path *field.Path) (errs field.ErrorList) { // ValidateMatchExcludeConflict checks if the resultant of match and exclude block is not an empty set func (r *Rule) ValidateMatchExcludeConflict(path *field.Path) (errs field.ErrorList) { + if r.ExcludeResources == nil { + return errs + } if len(r.ExcludeResources.All) > 0 || len(r.MatchResources.All) > 0 { return errs } diff --git a/api/kyverno/v2beta1/zz_generated.deepcopy.go b/api/kyverno/v2beta1/zz_generated.deepcopy.go index c23558c1f4..67d9d7c124 100755 --- a/api/kyverno/v2beta1/zz_generated.deepcopy.go +++ b/api/kyverno/v2beta1/zz_generated.deepcopy.go @@ -712,7 +712,11 @@ func (in *Rule) DeepCopyInto(out *Rule) { } } in.MatchResources.DeepCopyInto(&out.MatchResources) - in.ExcludeResources.DeepCopyInto(&out.ExcludeResources) + if in.ExcludeResources != nil { + in, out := &in.ExcludeResources, &out.ExcludeResources + *out = new(MatchResources) + (*in).DeepCopyInto(*out) + } if in.ImageExtractors != nil { in, out := &in.ImageExtractors, &out.ImageExtractors *out = make(v1.ImageExtractorConfigs, len(*in)) diff --git a/cmd/cli/kubectl-kyverno/utils/common/common.go b/cmd/cli/kubectl-kyverno/utils/common/common.go index 90ca9c1512..03436be721 100644 --- a/cmd/cli/kubectl-kyverno/utils/common/common.go +++ b/cmd/cli/kubectl-kyverno/utils/common/common.go @@ -98,13 +98,15 @@ func GetKindsFromPolicy(out io.Writer, policy kyvernov1.PolicyInterface, subreso } knownkinds.Insert(k) } - for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds { - k, err := getKind(kind, subresources, dClient) - if err != nil { - fmt.Fprintf(out, "Error: %s", err.Error()) - continue + if rule.ExcludeResources != nil { + for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds { + k, err := getKind(kind, subresources, dClient) + if err != nil { + fmt.Fprintf(out, "Error: %s", err.Error()) + continue + } + knownkinds.Insert(k) } - knownkinds.Insert(k) } } return knownkinds diff --git a/pkg/autogen/autogen.go b/pkg/autogen/autogen.go index 837c654cd8..35a8ed8800 100644 --- a/pkg/autogen/autogen.go +++ b/pkg/autogen/autogen.go @@ -77,8 +77,8 @@ func CanAutoGen(spec *kyvernov1.Spec) (applyAutoGen bool, controllers sets.Set[s return false, sets.New("none") } } - match, exclude := rule.MatchResources, rule.ExcludeResources - if !checkAutogenSupport(&needed, match.ResourceDescription, exclude.ResourceDescription) { + match := rule.MatchResources + if !checkAutogenSupport(&needed, match.ResourceDescription) { debug.Info("skip generating rule on pod controllers: Name / Selector in resource description may not be applicable.", "rule", rule.Name) return false, sets.New[string]() } @@ -94,16 +94,22 @@ func CanAutoGen(spec *kyvernov1.Spec) (applyAutoGen bool, controllers sets.Set[s return false, sets.New[string]() } } - for _, value := range exclude.Any { - if !checkAutogenSupport(&needed, value.ResourceDescription) { - debug.Info("skip generating rule on pod controllers: Name / Selector in exclude any block is not applicable.", "rule", rule.Name) + if exclude := rule.ExcludeResources; exclude != nil { + if !checkAutogenSupport(&needed, exclude.ResourceDescription) { + debug.Info("skip generating rule on pod controllers: Name / Selector in resource description may not be applicable.", "rule", rule.Name) return false, sets.New[string]() } - } - for _, value := range exclude.All { - if !checkAutogenSupport(&needed, value.ResourceDescription) { - debug.Info("skip generating rule on pod controllers: Name / Selector in exclud all block is not applicable.", "rule", rule.Name) - return false, sets.New[string]() + for _, value := range exclude.Any { + if !checkAutogenSupport(&needed, value.ResourceDescription) { + debug.Info("skip generating rule on pod controllers: Name / Selector in exclude any block is not applicable.", "rule", rule.Name) + return false, sets.New[string]() + } + } + for _, value := range exclude.All { + if !checkAutogenSupport(&needed, value.ResourceDescription) { + debug.Info("skip generating rule on pod controllers: Name / Selector in exclud all block is not applicable.", "rule", rule.Name) + return false, sets.New[string]() + } } } } @@ -210,7 +216,7 @@ func convertRule(rule kyvernoRule, kind string) (*kyvernov1.Rule, error) { out.MatchResources = *rule.MatchResources } if rule.ExcludeResources != nil { - out.ExcludeResources = *rule.ExcludeResources + out.ExcludeResources = rule.ExcludeResources } if rule.Context != nil { out.Context = *rule.Context diff --git a/pkg/autogen/rule.go b/pkg/autogen/rule.go index e84df54ef1..78bafe1b28 100644 --- a/pkg/autogen/rule.go +++ b/pkg/autogen/rule.go @@ -42,7 +42,7 @@ func createRule(rule *kyvernov1.Rule) *kyvernoRule { if !datautils.DeepEqual(rule.MatchResources, kyvernov1.MatchResources{}) { jsonFriendlyStruct.MatchResources = rule.MatchResources.DeepCopy() } - if !datautils.DeepEqual(rule.ExcludeResources, kyvernov1.MatchResources{}) { + if rule.ExcludeResources != nil && !datautils.DeepEqual(*rule.ExcludeResources, kyvernov1.MatchResources{}) { jsonFriendlyStruct.ExcludeResources = rule.ExcludeResources.DeepCopy() } if !datautils.DeepEqual(rule.Mutation, kyvernov1.Mutation{}) { @@ -84,13 +84,15 @@ func generateRule(name string, rule *kyvernov1.Rule, tplKey, shift string, kinds } else { rule.MatchResources.Kinds = kinds } - if len(rule.ExcludeResources.Any) > 0 { - rule.ExcludeResources.Any = grf(rule.ExcludeResources.Any, kinds) - } else if len(rule.ExcludeResources.All) > 0 { - rule.ExcludeResources.All = grf(rule.ExcludeResources.All, kinds) - } else { - if len(rule.ExcludeResources.Kinds) != 0 { - rule.ExcludeResources.Kinds = kinds + if rule.ExcludeResources != nil { + if len(rule.ExcludeResources.Any) > 0 { + rule.ExcludeResources.Any = grf(rule.ExcludeResources.Any, kinds) + } else if len(rule.ExcludeResources.All) > 0 { + rule.ExcludeResources.All = grf(rule.ExcludeResources.All, kinds) + } else { + if len(rule.ExcludeResources.Kinds) != 0 { + rule.ExcludeResources.Kinds = kinds + } } } if target := rule.Mutation.GetPatchStrategicMerge(); target != nil { @@ -255,8 +257,12 @@ func generateRuleForControllers(rule *kyvernov1.Rule, controllers string) *kyver return nil } debug.Info("processing rule", "rulename", rule.Name) - match, exclude := rule.MatchResources, rule.ExcludeResources - matchKinds, excludeKinds := match.GetKinds(), exclude.GetKinds() + match := rule.MatchResources + matchKinds := match.GetKinds() + var excludeKinds []string + if exclude := rule.ExcludeResources; exclude != nil { + excludeKinds = exclude.GetKinds() + } if !kubeutils.ContainsKind(matchKinds, "Pod") || (len(excludeKinds) != 0 && !kubeutils.ContainsKind(excludeKinds, "Pod")) { return nil } diff --git a/pkg/controllers/webhook/controller_test.go b/pkg/controllers/webhook/controller_test.go index 80eacc3494..6f89db23c3 100644 --- a/pkg/controllers/webhook/controller_test.go +++ b/pkg/controllers/webhook/controller_test.go @@ -144,7 +144,7 @@ func TestAddOperationsForValidatingWebhookConf(t *testing.T) { Kinds: []string{"ConfigMap"}, }, }, - ExcludeResources: kyverno.MatchResources{ + ExcludeResources: &kyverno.MatchResources{ ResourceDescription: kyverno.ResourceDescription{ Operations: []kyverno.AdmissionOperation{"DELETE", "CONNECT", "CREATE"}, }, @@ -263,7 +263,7 @@ func TestAddOperationsForMutatingtingWebhookConf(t *testing.T) { Kinds: []string{"Secret"}, }, }, - ExcludeResources: kyverno.MatchResources{ + ExcludeResources: &kyverno.MatchResources{ ResourceDescription: kyverno.ResourceDescription{ Operations: []kyverno.AdmissionOperation{"UPDATE"}, }, diff --git a/pkg/controllers/webhook/utils.go b/pkg/controllers/webhook/utils.go index ccdda98eaa..c093ae3f32 100644 --- a/pkg/controllers/webhook/utils.go +++ b/pkg/controllers/webhook/utils.go @@ -263,16 +263,18 @@ func computeOperationsForValidatingWebhookConf(r kyvernov1.Rule, operationStatus operationStatusMap[webhookConnect] = true operationStatusMap[webhookDelete] = true } - if r.ExcludeResources.ResourceDescription.Operations != nil { - for _, o := range r.ExcludeResources.ResourceDescription.Operations { - operationStatusMap[string(o)] = false + if r.ExcludeResources != nil { + if r.ExcludeResources.ResourceDescription.Operations != nil { + for _, o := range r.ExcludeResources.ResourceDescription.Operations { + operationStatusMap[string(o)] = false + } + } + if len(r.ExcludeResources.Any) != 0 { + _, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.Any, operationStatusMap) + } + if len(r.ExcludeResources.All) != 0 { + _, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.All, operationStatusMap) } - } - if len(r.ExcludeResources.Any) != 0 { - _, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.Any, operationStatusMap) - } - if len(r.ExcludeResources.All) != 0 { - _, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.All, operationStatusMap) } return operationStatusMap } @@ -307,16 +309,18 @@ func computeOperationsForMutatingWebhookConf(r kyvernov1.Rule, operationStatusMa operationStatusMap[webhookCreate] = true operationStatusMap[webhookUpdate] = true } - if r.ExcludeResources.ResourceDescription.Operations != nil { - for _, o := range r.ExcludeResources.ResourceDescription.Operations { - operationStatusMap[string(o)] = false + if r.ExcludeResources != nil { + if r.ExcludeResources.ResourceDescription.Operations != nil { + for _, o := range r.ExcludeResources.ResourceDescription.Operations { + operationStatusMap[string(o)] = false + } + } + if len(r.ExcludeResources.Any) != 0 { + _, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.Any, operationStatusMap) + } + if len(r.ExcludeResources.All) != 0 { + _, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.All, operationStatusMap) } - } - if len(r.ExcludeResources.Any) != 0 { - _, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.Any, operationStatusMap) - } - if len(r.ExcludeResources.All) != 0 { - _, operationStatusMap = scanResourceFilterForExclude(r.ExcludeResources.All, operationStatusMap) } } return operationStatusMap @@ -375,17 +379,19 @@ func computeResourcesOfRule(r kyvernov1.Rule) []string { if len(r.MatchResources.All) != 0 { resources = scanResourceFilterForResources(r.MatchResources.Any) } - if len(r.ExcludeResources.Any) != 0 { - resources = scanResourceFilterForResources(r.MatchResources.Any) - } - if len(r.ExcludeResources.All) != 0 { - resources = scanResourceFilterForResources(r.MatchResources.Any) - } if r.MatchResources.ResourceDescription.Kinds != nil { resources = append(resources, r.MatchResources.ResourceDescription.Kinds...) } - if r.ExcludeResources.ResourceDescription.Kinds != nil { - resources = append(resources, r.ExcludeResources.ResourceDescription.Kinds...) + if r.ExcludeResources != nil { + if len(r.ExcludeResources.Any) != 0 { + resources = scanResourceFilterForResources(r.MatchResources.Any) + } + if len(r.ExcludeResources.All) != 0 { + resources = scanResourceFilterForResources(r.MatchResources.Any) + } + if r.ExcludeResources.ResourceDescription.Kinds != nil { + resources = append(resources, r.ExcludeResources.ResourceDescription.Kinds...) + } } return resources } diff --git a/pkg/controllers/webhook/utils_test.go b/pkg/controllers/webhook/utils_test.go index d6b01c82f9..29d6e5bf2c 100644 --- a/pkg/controllers/webhook/utils_test.go +++ b/pkg/controllers/webhook/utils_test.go @@ -241,14 +241,14 @@ func TestComputeOperationsForMutatingWebhookConf(t *testing.T) { PatchesJSON6902: "add", }, MatchResources: kyvernov1.MatchResources{}, - ExcludeResources: kyvernov1.MatchResources{}, + ExcludeResources: &kyvernov1.MatchResources{}, }, { Mutation: kyvernov1.Mutation{ PatchesJSON6902: "add", }, MatchResources: kyvernov1.MatchResources{}, - ExcludeResources: kyvernov1.MatchResources{}, + ExcludeResources: &kyvernov1.MatchResources{}, }, }, expectedResult: map[string]bool{ @@ -264,7 +264,7 @@ func TestComputeOperationsForMutatingWebhookConf(t *testing.T) { PatchesJSON6902: "add", }, MatchResources: kyvernov1.MatchResources{}, - ExcludeResources: kyvernov1.MatchResources{ + ExcludeResources: &kyvernov1.MatchResources{ ResourceDescription: kyvernov1.ResourceDescription{ Operations: []kyvernov1.AdmissionOperation{webhookCreate}, }, @@ -317,7 +317,7 @@ func TestComputeOperationsForValidatingWebhookConf(t *testing.T) { rules: []kyvernov1.Rule{ { MatchResources: kyvernov1.MatchResources{}, - ExcludeResources: kyvernov1.MatchResources{}, + ExcludeResources: &kyvernov1.MatchResources{}, }, }, expectedResult: map[string]bool{ @@ -336,7 +336,7 @@ func TestComputeOperationsForValidatingWebhookConf(t *testing.T) { Operations: []kyvernov1.AdmissionOperation{webhookCreate, webhookUpdate}, }, }, - ExcludeResources: kyvernov1.MatchResources{ + ExcludeResources: &kyvernov1.MatchResources{ ResourceDescription: kyvernov1.ResourceDescription{ Operations: []kyvernov1.AdmissionOperation{webhookDelete}, }, diff --git a/pkg/engine/handlers/validation/utils.go b/pkg/engine/handlers/validation/utils.go index 33f7043c06..2984e2b051 100644 --- a/pkg/engine/handlers/validation/utils.go +++ b/pkg/engine/handlers/validation/utils.go @@ -25,21 +25,22 @@ func matchResource(resource unstructured.Unstructured, rule kyvernov1.Rule) bool return false } } - - if rule.ExcludeResources.All != nil || rule.ExcludeResources.Any != nil { - excluded := match.CheckMatchesResources( - resource, - kyvernov2beta1.MatchResources{ - Any: rule.ExcludeResources.Any, - All: rule.ExcludeResources.All, - }, - make(map[string]string), - kyvernov2.RequestInfo{}, - resource.GroupVersionKind(), - "", - ) - if excluded == nil { - return false + if rule.ExcludeResources != nil { + if rule.ExcludeResources.All != nil || rule.ExcludeResources.Any != nil { + excluded := match.CheckMatchesResources( + resource, + kyvernov2beta1.MatchResources{ + Any: rule.ExcludeResources.Any, + All: rule.ExcludeResources.All, + }, + make(map[string]string), + kyvernov2.RequestInfo{}, + resource.GroupVersionKind(), + "", + ) + if excluded == nil { + return false + } } } return true diff --git a/pkg/engine/utils/match.go b/pkg/engine/utils/match.go index 1488b789b6..e085d16833 100644 --- a/pkg/engine/utils/match.go +++ b/pkg/engine/utils/match.go @@ -209,7 +209,7 @@ func MatchesResourceDescription( } // check exlude conditions only if match succeeds - if len(reasonsForFailure) == 0 { + if len(reasonsForFailure) == 0 && rule.ExcludeResources != nil { if len(rule.ExcludeResources.Any) > 0 { // exclude the object if ANY of the criteria match for _, rer := range rule.ExcludeResources.Any { diff --git a/pkg/engine/utils/utils_test.go b/pkg/engine/utils/utils_test.go index cec5c59ddc..bd6c7cc003 100644 --- a/pkg/engine/utils/utils_test.go +++ b/pkg/engine/utils/utils_test.go @@ -1963,7 +1963,7 @@ func TestResourceDescriptionMatch_ExcludeDefaultGroups(t *testing.T) { }, }, }}, - ExcludeResources: v1.MatchResources{}, + ExcludeResources: &v1.MatchResources{}, } // this is the request info that was also passed with the mocked pod @@ -1996,7 +1996,7 @@ func TestResourceDescriptionMatch_ExcludeDefaultGroups(t *testing.T) { }, }, }}, - ExcludeResources: v1.MatchResources{Any: v1.ResourceFilters{}}, + ExcludeResources: &v1.MatchResources{Any: v1.ResourceFilters{}}, } // Second test: confirm that matching this rule does not create any errors (and raise if err != nil) @@ -2005,7 +2005,7 @@ func TestResourceDescriptionMatch_ExcludeDefaultGroups(t *testing.T) { } // Now we extend the previous rule to have an Exclude part. Making it 'not-empty' should make the exclude-code run. - rule2.ExcludeResources = v1.MatchResources{Any: v1.ResourceFilters{ + rule2.ExcludeResources = &v1.MatchResources{Any: v1.ResourceFilters{ v1.ResourceFilter{ ResourceDescription: v1.ResourceDescription{ Kinds: []string{"Pod"}, @@ -2461,7 +2461,7 @@ func TestResourceDescriptionExclude_Label_Expression_Match(t *testing.T) { rule := v1.Rule{ MatchResources: v1.MatchResources{ResourceDescription: resourceDescription}, - ExcludeResources: v1.MatchResources{ResourceDescription: resourceDescriptionExclude}, + ExcludeResources: &v1.MatchResources{ResourceDescription: resourceDescriptionExclude}, } if err := MatchesResourceDescription(*resource, rule, v2.RequestInfo{}, nil, "", resource.GroupVersionKind(), "", "CREATE"); err == nil { diff --git a/pkg/utils/fuzz/policy_spec.go b/pkg/utils/fuzz/policy_spec.go index 021f8abfcc..fb9042c9a2 100644 --- a/pkg/utils/fuzz/policy_spec.go +++ b/pkg/utils/fuzz/policy_spec.go @@ -179,7 +179,7 @@ func createRule(f *fuzz.ConsumeFuzzer) (*kyvernov1.Rule, error) { if err != nil { return rule, err } - rule.ExcludeResources = *er + rule.ExcludeResources = er } setRawAnyAllConditions, err := f.GetBool() diff --git a/pkg/validatingadmissionpolicy/builder.go b/pkg/validatingadmissionpolicy/builder.go index 6453a180a3..4201c10b67 100644 --- a/pkg/validatingadmissionpolicy/builder.go +++ b/pkg/validatingadmissionpolicy/builder.go @@ -57,21 +57,22 @@ func BuildValidatingAdmissionPolicy( } // convert the exclude block - exclude := rule.ExcludeResources - if !exclude.ResourceDescription.IsEmpty() { - if err := translateResource(discoveryClient, &matchResources, &excludeRules, exclude.ResourceDescription, false); err != nil { - return err + if exclude := rule.ExcludeResources; exclude != nil { + if !exclude.ResourceDescription.IsEmpty() { + if err := translateResource(discoveryClient, &matchResources, &excludeRules, exclude.ResourceDescription, false); err != nil { + return err + } } - } - if exclude.Any != nil { - if err := translateResourceFilters(discoveryClient, &matchResources, &excludeRules, exclude.Any, false); err != nil { - return err + if exclude.Any != nil { + if err := translateResourceFilters(discoveryClient, &matchResources, &excludeRules, exclude.Any, false); err != nil { + return err + } } - } - if exclude.All != nil { - if err := translateResourceFilters(discoveryClient, &matchResources, &excludeRules, exclude.All, false); err != nil { - return err + if exclude.All != nil { + if err := translateResourceFilters(discoveryClient, &matchResources, &excludeRules, exclude.All, false); err != nil { + return err + } } } diff --git a/pkg/validatingadmissionpolicy/kyvernopolicy_checker.go b/pkg/validatingadmissionpolicy/kyvernopolicy_checker.go index 9e02e77bdc..ab76e5252c 100644 --- a/pkg/validatingadmissionpolicy/kyvernopolicy_checker.go +++ b/pkg/validatingadmissionpolicy/kyvernopolicy_checker.go @@ -72,25 +72,16 @@ func checkPolicy(spec *kyvernov1.Spec) (bool, string) { } // check the matched/excluded resources of the CEL rule. - match, exclude := rule.MatchResources, rule.ExcludeResources + match := rule.MatchResources if ok, msg := checkUserInfo(match.UserInfo); !ok { return false, msg } - if ok, msg := checkUserInfo(exclude.UserInfo); !ok { - return false, msg - } - if ok, msg := checkResources(match.ResourceDescription, true); !ok { return false, msg } - if ok, msg := checkResources(exclude.ResourceDescription, false); !ok { - return false, msg - } - if ok, msg := checkResourceFilter(match.Any, true); !ok { return false, msg } - if len(match.All) > 1 { msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the match block is not applicable." return false, msg @@ -98,17 +89,24 @@ func checkPolicy(spec *kyvernov1.Spec) (bool, string) { if ok, msg := checkResourceFilter(match.All, true); !ok { return false, msg } - - if ok, msg := checkResourceFilter(exclude.Any, false); !ok { - return false, msg - } - - if len(exclude.All) > 1 { - msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the exclude block is not applicable." - return false, msg - } - if ok, msg := checkResourceFilter(exclude.All, false); !ok { - return false, msg + if rule.ExcludeResources != nil { + exclude := rule.ExcludeResources + if ok, msg := checkUserInfo(exclude.UserInfo); !ok { + return false, msg + } + if ok, msg := checkResources(exclude.ResourceDescription, false); !ok { + return false, msg + } + if ok, msg := checkResourceFilter(exclude.Any, false); !ok { + return false, msg + } + if len(exclude.All) > 1 { + msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the exclude block is not applicable." + return false, msg + } + if ok, msg := checkResourceFilter(exclude.All, false); !ok { + return false, msg + } } return true, msg diff --git a/pkg/validation/policy/background.go b/pkg/validation/policy/background.go index 773e85ee8b..7f512d3731 100644 --- a/pkg/validation/policy/background.go +++ b/pkg/validation/policy/background.go @@ -43,11 +43,6 @@ func hasUserMatchExclude(idx int, rule *kyvernov1.Rule) error { if path := userInfoDefined(rule.MatchResources.UserInfo); path != "" { return fmt.Errorf("invalid variable used at path: spec/rules[%d]/match/%s", idx, path) } - - if path := userInfoDefined(rule.ExcludeResources.UserInfo); path != "" { - return fmt.Errorf("invalid variable used at path: spec/rules[%d]/exclude/%s", idx, path) - } - if len(rule.MatchResources.Any) > 0 { for i, value := range rule.MatchResources.Any { if path := userInfoDefined(value.UserInfo); path != "" { @@ -55,7 +50,6 @@ func hasUserMatchExclude(idx int, rule *kyvernov1.Rule) error { } } } - if len(rule.MatchResources.All) > 0 { for i, value := range rule.MatchResources.All { if path := userInfoDefined(value.UserInfo); path != "" { @@ -63,23 +57,25 @@ func hasUserMatchExclude(idx int, rule *kyvernov1.Rule) error { } } } - - if len(rule.ExcludeResources.All) > 0 { - for i, value := range rule.ExcludeResources.All { - if path := userInfoDefined(value.UserInfo); path != "" { - return fmt.Errorf("invalid variable used at path: spec/rules[%d]/exclude/all[%d]/%s", idx, i, path) + if rule.ExcludeResources != nil { + if path := userInfoDefined(rule.ExcludeResources.UserInfo); path != "" { + return fmt.Errorf("invalid variable used at path: spec/rules[%d]/exclude/%s", idx, path) + } + if len(rule.ExcludeResources.All) > 0 { + for i, value := range rule.ExcludeResources.All { + if path := userInfoDefined(value.UserInfo); path != "" { + return fmt.Errorf("invalid variable used at path: spec/rules[%d]/exclude/all[%d]/%s", idx, i, path) + } + } + } + if len(rule.ExcludeResources.Any) > 0 { + for i, value := range rule.ExcludeResources.Any { + if path := userInfoDefined(value.UserInfo); path != "" { + return fmt.Errorf("invalid variable used at path: spec/rules[%d]/exclude/any[%d]/%s", idx, i, path) + } } } } - - if len(rule.ExcludeResources.Any) > 0 { - for i, value := range rule.ExcludeResources.Any { - if path := userInfoDefined(value.UserInfo); path != "" { - return fmt.Errorf("invalid variable used at path: spec/rules[%d]/exclude/any[%d]/%s", idx, i, path) - } - } - } - return nil } diff --git a/pkg/validation/policy/validate.go b/pkg/validation/policy/validate.go index f75cf3d125..384c3bf67e 100644 --- a/pkg/validation/policy/validate.go +++ b/pkg/validation/policy/validate.go @@ -236,7 +236,6 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf for i, rule := range rules { match := rule.MatchResources - exclude := rule.ExcludeResources for j, value := range match.Any { if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil { return warnings, fmt.Errorf("path: spec.rules[%d].match.any[%d].kinds: %v", i, j, err) @@ -247,23 +246,23 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf return warnings, fmt.Errorf("path: spec.rules[%d].match.all[%d].kinds: %v", i, j, err) } } - for j, value := range exclude.Any { - if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil { - return warnings, fmt.Errorf("path: spec.rules[%d].exclude.any[%d].kinds: %v", i, j, err) - } - } - for j, value := range exclude.All { - if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil { - return warnings, fmt.Errorf("path: spec.rules[%d].exclude.all[%d].kinds: %v", i, j, err) - } - } - if err := validateKinds(rule.MatchResources.Kinds, rule, mock, background, client); err != nil { return warnings, fmt.Errorf("path: spec.rules[%d].match.kinds: %v", i, err) } - - if err := validateKinds(rule.ExcludeResources.Kinds, rule, mock, background, client); err != nil { - return warnings, fmt.Errorf("path: spec.rules[%d].exclude.kinds: %v", i, err) + if exclude := rule.ExcludeResources; exclude != nil { + for j, value := range exclude.Any { + if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil { + return warnings, fmt.Errorf("path: spec.rules[%d].exclude.any[%d].kinds: %v", i, j, err) + } + } + for j, value := range exclude.All { + if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil { + return warnings, fmt.Errorf("path: spec.rules[%d].exclude.all[%d].kinds: %v", i, j, err) + } + } + if err := validateKinds(exclude.Kinds, rule, mock, background, client); err != nil { + return warnings, fmt.Errorf("path: spec.rules[%d].exclude.kinds: %v", i, err) + } } } @@ -377,12 +376,13 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf } match := rule.MatchResources - exclude := rule.ExcludeResources matchKinds := match.GetKinds() - excludeKinds := exclude.GetKinds() - allKinds := make([]string, 0, len(matchKinds)+len(excludeKinds)) + var allKinds []string allKinds = append(allKinds, matchKinds...) - allKinds = append(allKinds, excludeKinds...) + if exclude := rule.ExcludeResources; exclude != nil { + excludeKinds := exclude.GetKinds() + allKinds = append(allKinds, excludeKinds...) + } if rule.HasValidate() { validationElem := rule.Validation.DeepCopy() if validationElem.Deny != nil { @@ -691,17 +691,16 @@ func jsonPatchPathHasVariables(patch string) error { return nil } -func objectHasVariables(object interface{}) error { - var err error - objectJSON, err := json.Marshal(object) - if err != nil { - return err +func objectHasVariables(object any) error { + if object != nil { + objectJSON, err := json.Marshal(object) + if err != nil { + return err + } + if len(regexVariables.FindAllStringSubmatch(string(objectJSON), -1)) > 0 { + return fmt.Errorf("invalid variables") + } } - - if len(regexVariables.FindAllStringSubmatch(string(objectJSON), -1)) > 0 { - return fmt.Errorf("invalid variables") - } - return nil } @@ -963,22 +962,22 @@ func ruleOnlyDealsWithResourceMetaData(rule kyvernov1.Rule) bool { func validateResources(path *field.Path, rule kyvernov1.Rule) (string, error) { // validate userInfo in match and exclude - if errs := rule.ExcludeResources.UserInfo.Validate(path.Child("exclude")); len(errs) != 0 { - return "exclude", errs.ToAggregate() + if exclude := rule.ExcludeResources; exclude != nil { + if errs := exclude.UserInfo.Validate(path.Child("exclude")); len(errs) != 0 { + return "exclude", errs.ToAggregate() + } + if (len(exclude.Any) > 0 || len(exclude.All) > 0) && !datautils.DeepEqual(exclude.ResourceDescription, kyvernov1.ResourceDescription{}) { + return "exclude.", fmt.Errorf("can't specify any/all together with exclude resources") + } + if len(exclude.Any) > 0 && len(exclude.All) > 0 { + return "match.", fmt.Errorf("can't specify any and all together") + } } if (len(rule.MatchResources.Any) > 0 || len(rule.MatchResources.All) > 0) && !datautils.DeepEqual(rule.MatchResources.ResourceDescription, kyvernov1.ResourceDescription{}) { return "match.", fmt.Errorf("can't specify any/all together with match resources") } - if (len(rule.ExcludeResources.Any) > 0 || len(rule.ExcludeResources.All) > 0) && !datautils.DeepEqual(rule.ExcludeResources.ResourceDescription, kyvernov1.ResourceDescription{}) { - return "exclude.", fmt.Errorf("can't specify any/all together with exclude resources") - } - - if len(rule.ExcludeResources.Any) > 0 && len(rule.ExcludeResources.All) > 0 { - return "match.", fmt.Errorf("can't specify any and all together") - } - if len(rule.MatchResources.Any) > 0 { for _, rmr := range rule.MatchResources.Any { // matched resources