Kyverno vulnerable to bypass of policy rules that use namespace selectors in match statements
Description
Kyverno is a policy engine designed for cloud native platform engineering teams. Prior to versions 1.13.5 and 1.14.0, it may happen that policy rules using namespace selector(s) in their match statements are mistakenly not applied during admission review request processing due to a missing error propagation in function GetNamespaceSelectorsFromNamespaceLister in pkg/utils/engine/labels.go. As a consequence, security-critical mutations and validations are bypassed, potentially allowing attackers with K8s API access to perform malicious operations. This issue has been patched in versions 1.13.5 and 1.14.0.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/kyverno/kyvernoGo | < 1.13.5 | 1.13.5 |
github.com/kyverno/kyvernoGo | >= 1.14.0-alpha.1, < 1.14.0 | 1.14.0 |
Affected products
1Patches
13ff923b7756eFix Namespace Selector Error Propagation and Scope Policy for Accurate Rule Evaluation (#12744)
13 files changed · +709 −21
api/kyverno/v1/spec_types.go+4 −0 modified@@ -311,6 +311,10 @@ func (s *Spec) GetApplyRules() ApplyRulesType { return *s.ApplyRules } +func (s *Spec) GetRules() []Rule { + return s.Rules +} + // ValidateRuleNames checks if the rule names are unique across a policy func (s *Spec) ValidateRuleNames(path *field.Path) (errs field.ErrorList) { names := sets.New[string]()
pkg/background/generate/controller.go+5 −1 modified@@ -214,7 +214,11 @@ func (c *GenerateController) applyGenerate(trigger unstructured.Unstructured, ur return nil, nil } - namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, logger) + namespaceLabels, err := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, []kyvernov1.PolicyInterface{p}, logger) + if err != nil { + return nil, err + } + policyContext, err := common.NewBackgroundContext(logger, c.client, ur.Spec.Context, p, &trigger, c.configuration, c.jp, namespaceLabels) if err != nil { return nil, err
pkg/background/mutate/mutate.go+7 −1 modified@@ -148,7 +148,13 @@ func (c *mutateExistingController) ProcessUR(ur *kyvernov2.UpdateRequest) error } } - namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, logger) + namespaceLabels, err := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), c.nsLister, []kyvernov1.PolicyInterface{policy}, logger) + if err != nil { + logger.WithName(rule.Name).Error(err, "failed to get namespace labels") + errs = append(errs, err) + continue + } + policyContext, err := common.NewBackgroundContext(logger, c.client, ur.Spec.Context, policy, trigger, c.configuration, c.jp, namespaceLabels) if err != nil { logger.WithName(rule.Name).Error(err, "failed to build policy context")
pkg/policy/generate.go+5 −1 modified@@ -105,7 +105,11 @@ func (pc *policyController) handleGenerateForExisting(policy kyvernov1.PolicyInt triggers = getTriggers(pc.client, rule, policy.IsNamespaced(), policy.GetNamespace(), pc.log) policyNew.GetSpec().SetRules([]kyvernov1.Rule{rule}) for _, trigger := range triggers { - namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), pc.nsLister, pc.log) + namespaceLabels, err := engineutils.GetNamespaceSelectorsFromNamespaceLister(trigger.GetKind(), trigger.GetNamespace(), pc.nsLister, []kyvernov1.PolicyInterface{policyNew}, pc.log) + if err != nil { + errors = append(errors, fmt.Errorf("failed to get namespace labels for rule %s: %w", rule.Name, err)) + continue + } policyContext, err := common.NewBackgroundContext(pc.log, pc.client, ur.Spec.Context, policy, trigger, pc.configuration, pc.jp, namespaceLabels) if err != nil { errors = append(errors, fmt.Errorf("failed to build policy context for rule %s: %w", rule.Name, err))
pkg/policy/policy_controller.go+5 −1 modified@@ -402,7 +402,11 @@ func (pc *policyController) requeuePolicies() { } func (pc *policyController) handleUpdateRequest(ur *kyvernov2.UpdateRequest, triggerResource *unstructured.Unstructured, ruleName string, policy kyvernov1.PolicyInterface) (skip bool, err error) { - namespaceLabels := engineutils.GetNamespaceSelectorsFromNamespaceLister(triggerResource.GetKind(), triggerResource.GetNamespace(), pc.nsLister, pc.log) + namespaceLabels, err := engineutils.GetNamespaceSelectorsFromNamespaceLister(triggerResource.GetKind(), triggerResource.GetNamespace(), pc.nsLister, []kyvernov1.PolicyInterface{policy}, pc.log) + if err != nil { + return false, fmt.Errorf("failed to get namespace labels for rule %s: %w", ruleName, err) + } + policyContext, err := backgroundcommon.NewBackgroundContext(pc.log, pc.client, ur.Spec.Context, policy, triggerResource, pc.configuration, pc.jp, namespaceLabels) if err != nil { return false, fmt.Errorf("failed to build policy context for rule %s: %w", ruleName, err)
pkg/utils/engine/labels.go+68 −4 modified@@ -1,21 +1,85 @@ package engine import ( + "fmt" + "github.com/go-logr/logr" + kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" "github.com/kyverno/kyverno/pkg/logging" corev1listers "k8s.io/client-go/listers/core/v1" ) // GetNamespaceSelectorsFromNamespaceLister - extract the namespacelabels when namespace lister is passed -func GetNamespaceSelectorsFromNamespaceLister(kind, namespaceOfResource string, nsLister corev1listers.NamespaceLister, logger logr.Logger) map[string]string { +func GetNamespaceSelectorsFromNamespaceLister(kind, namespaceOfResource string, nsLister corev1listers.NamespaceLister, policies []kyvernov1.PolicyInterface, logger logr.Logger) (map[string]string, error) { namespaceLabels := make(map[string]string) + if !hasNamespaceSelector(policies) { + return namespaceLabels, nil + } + if kind != "Namespace" && namespaceOfResource != "" { namespaceObj, err := nsLister.Get(namespaceOfResource) if err != nil { logging.Error(err, "failed to get the namespace", "name", namespaceOfResource) - return namespaceLabels + return namespaceLabels, err + } + + // Check if the namespace object is nil (namespace not found) + if namespaceObj == nil { + err := fmt.Errorf("namespace %s not found", namespaceOfResource) + logging.Error(err, "namespace object is nil", "name", namespaceOfResource) + return namespaceLabels, err } - return namespaceObj.DeepCopy().GetLabels() + + return namespaceObj.DeepCopy().GetLabels(), nil } - return namespaceLabels + + return namespaceLabels, nil +} + +func hasNamespaceSelector(policies []kyvernov1.PolicyInterface) bool { + for _, policy := range policies { + spec := policy.GetSpec() + if spec == nil { + continue + } + + rules := spec.GetRules() + for _, rule := range rules { + if rule.MatchResources.ResourceDescription.NamespaceSelector != nil { + return true + } + + if rule.ExcludeResources != nil && rule.ExcludeResources.ResourceDescription.NamespaceSelector != nil { + return true + } + + for _, ele := range rule.MatchResources.All { + if ele.ResourceDescription.NamespaceSelector != nil { + return true + } + } + + for _, ele := range rule.MatchResources.Any { + if ele.ResourceDescription.NamespaceSelector != nil { + return true + } + } + + if rule.ExcludeResources != nil { + for _, ele := range rule.ExcludeResources.All { + if ele.ResourceDescription.NamespaceSelector != nil { + return true + } + } + + for _, ele := range rule.ExcludeResources.Any { + if ele.ResourceDescription.NamespaceSelector != nil { + return true + } + } + } + } + } + + return false }
pkg/utils/engine/labels_test.go+581 −0 added@@ -0,0 +1,581 @@ +package engine + +import ( + "errors" + "fmt" + "testing" + + kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" + "github.com/kyverno/kyverno/pkg/logging" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + corev1listers "k8s.io/client-go/listers/core/v1" +) + +// mockNamespaceLister is a mock implementation of the corev1listers.NamespaceLister interface. +type mockNamespaceLister struct { + namespaces map[string]*corev1.Namespace + err error +} + +func (m *mockNamespaceLister) Get(name string) (*corev1.Namespace, error) { + if m.err != nil { + return nil, m.err + } + if ns, ok := m.namespaces[name]; ok { + return ns, nil + } + return nil, nil +} + +func (m *mockNamespaceLister) List(selector labels.Selector) ([]*corev1.Namespace, error) { + var nsList []*corev1.Namespace + for _, ns := range m.namespaces { + if selector.Matches(labels.Set(ns.Labels)) { + nsList = append(nsList, ns) + } + } + return nsList, nil +} + +func TestHasNamespaceSelector(t *testing.T) { + tests := []struct { + name string + policies []kyvernov1.PolicyInterface + want bool + }{ + { + name: "no policies", + policies: []kyvernov1.PolicyInterface{}, + want: false, + }, + { + name: "namespace selector in MatchResources", + policies: []kyvernov1.PolicyInterface{ + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"env": "prod"}, + }, + }, + }, + }, + }, + }, + }, + }, + want: true, + }, + { + name: "namespace selector in ExcludeResources.ResourceDescription", + policies: []kyvernov1.PolicyInterface{ + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + ExcludeResources: &kyvernov1.MatchResources{ + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"env": "dev"}, + }, + }, + }, + }, + }, + }, + }, + }, + want: true, + }, + { + name: "namespace selector in MatchResources.All slice", + policies: []kyvernov1.PolicyInterface{ + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + All: []kyvernov1.ResourceFilter{ + { + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"foo": "bar"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + want: true, + }, + { + name: "namespace selector in MatchResources.Any slice", + policies: []kyvernov1.PolicyInterface{ + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + Any: []kyvernov1.ResourceFilter{ + { + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"key": "value"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + want: true, + }, + { + name: "namespace selector in ExcludeResources.All slice", + policies: []kyvernov1.PolicyInterface{ + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + ExcludeResources: &kyvernov1.MatchResources{ + All: []kyvernov1.ResourceFilter{ + { + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"exclude": "all"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + want: true, + }, + { + name: "namespace selector in ExcludeResources.Any slice", + policies: []kyvernov1.PolicyInterface{ + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + ExcludeResources: &kyvernov1.MatchResources{ + Any: []kyvernov1.ResourceFilter{ + { + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"exclude": "any"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + want: true, + }, + { + name: "policy with no namespace selector anywhere", + policies: []kyvernov1.PolicyInterface{ + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + ResourceDescription: kyvernov1.ResourceDescription{ + Kinds: []string{"Pod"}, + }, + }, + ExcludeResources: &kyvernov1.MatchResources{ + ResourceDescription: kyvernov1.ResourceDescription{ + Kinds: []string{"Deployment"}, + }, + }, + }, + }, + }, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := hasNamespaceSelector(tt.policies) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestGetNamespaceSelectorsFromNamespaceLister(t *testing.T) { + tests := []struct { + name string + kind string + namespaceOfResource string + nsLister corev1listers.NamespaceLister + policies []kyvernov1.PolicyInterface + wantLabels map[string]string + wantErr bool + }{ + { + name: "no namespace selector in policies returns empty map", + kind: "Pod", + namespaceOfResource: "default", + nsLister: &mockNamespaceLister{ + namespaces: map[string]*corev1.Namespace{ + "default": { + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"env": "prod"}, + }, + }, + }, + }, + policies: []kyvernov1.PolicyInterface{ + // Policy with no namespace selector. + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + ResourceDescription: kyvernov1.ResourceDescription{ + Kinds: []string{"Pod"}, + }, + }, + }, + }, + }, + }, + }, + wantLabels: map[string]string{}, + wantErr: false, + }, + { + name: "resource kind Namespace bypass lookup", + kind: "Namespace", + namespaceOfResource: "default", + nsLister: &mockNamespaceLister{ + namespaces: map[string]*corev1.Namespace{ + "default": { + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"env": "prod"}, + }, + }, + }, + }, + policies: []kyvernov1.PolicyInterface{ + // Policy defines a namespace selector, but kind is "Namespace" so lookup is skipped. + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + }, + }, + wantLabels: map[string]string{}, + wantErr: false, + }, + { + name: "empty namespace returns empty map", + kind: "Pod", + namespaceOfResource: "", + nsLister: &mockNamespaceLister{ + namespaces: map[string]*corev1.Namespace{ + "default": { + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"env": "prod"}, + }, + }, + }, + }, + policies: []kyvernov1.PolicyInterface{ + // Policy has a namespace selector but namespaceOfResource is empty. + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + }, + }, + wantLabels: map[string]string{}, + wantErr: false, + }, + { + name: "lister returns error", + kind: "Pod", + namespaceOfResource: "default", + nsLister: &mockNamespaceLister{ + err: errors.New("lookup failure"), + }, + policies: []kyvernov1.PolicyInterface{ + // Policy requires namespace selector. + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + }, + }, + wantLabels: map[string]string{}, + wantErr: true, + }, + { + name: "namespace not found (nil object returned)", + kind: "Pod", + namespaceOfResource: "nonexistent", + nsLister: &mockNamespaceLister{ + namespaces: map[string]*corev1.Namespace{}, // lookup returns nil + }, + policies: []kyvernov1.PolicyInterface{ + // Policy requires namespace selector. + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + }, + }, + wantLabels: map[string]string{}, + wantErr: true, + }, + { + name: "successful lookup from MatchResources", + kind: "Pod", + namespaceOfResource: "default", + nsLister: &mockNamespaceLister{ + namespaces: map[string]*corev1.Namespace{ + "default": { + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"env": "prod", "tier": "frontend"}, + }, + }, + }, + }, + policies: []kyvernov1.PolicyInterface{ + // Policy with namespace selector in MatchResources. + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + }, + }, + wantLabels: map[string]string{"env": "prod", "tier": "frontend"}, + wantErr: false, + }, + { + name: "successful lookup from MatchResources.All slice", + kind: "Pod", + namespaceOfResource: "default", + nsLister: &mockNamespaceLister{ + namespaces: map[string]*corev1.Namespace{ + "default": { + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "myapp", "team": "devops"}, + }, + }, + }, + }, + policies: []kyvernov1.PolicyInterface{ + // Policy with namespace selector in MatchResources.All. + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + All: []kyvernov1.ResourceFilter{ + { + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + wantLabels: map[string]string{"app": "myapp", "team": "devops"}, + wantErr: false, + }, + { + name: "successful lookup from MatchResources.Any slice", + kind: "Pod", + namespaceOfResource: "default", + nsLister: &mockNamespaceLister{ + namespaces: map[string]*corev1.Namespace{ + "default": { + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"role": "backend", "zone": "us-east"}, + }, + }, + }, + }, + policies: []kyvernov1.PolicyInterface{ + // Policy with namespace selector in MatchResources.Any. + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + MatchResources: kyvernov1.MatchResources{ + Any: []kyvernov1.ResourceFilter{ + { + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + wantLabels: map[string]string{"role": "backend", "zone": "us-east"}, + wantErr: false, + }, + { + name: "successful lookup from ExcludeResources.All slice", + kind: "Pod", + namespaceOfResource: "default", + nsLister: &mockNamespaceLister{ + namespaces: map[string]*corev1.Namespace{ + "default": { + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"exclude": "all", "region": "east"}, + }, + }, + }, + }, + policies: []kyvernov1.PolicyInterface{ + // Policy with namespace selector in ExcludeResources.All. + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + ExcludeResources: &kyvernov1.MatchResources{ + All: []kyvernov1.ResourceFilter{ + { + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + wantLabels: map[string]string{"exclude": "all", "region": "east"}, + wantErr: false, + }, + { + name: "successful lookup from ExcludeResources.Any slice", + kind: "Pod", + namespaceOfResource: "default", + nsLister: &mockNamespaceLister{ + namespaces: map[string]*corev1.Namespace{ + "default": { + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"exclude": "any", "cluster": "main"}, + }, + }, + }, + }, + policies: []kyvernov1.PolicyInterface{ + // Policy with namespace selector in ExcludeResources.Any. + &kyvernov1.ClusterPolicy{ + Spec: kyvernov1.Spec{ + Rules: []kyvernov1.Rule{ + { + ExcludeResources: &kyvernov1.MatchResources{ + Any: []kyvernov1.ResourceFilter{ + { + ResourceDescription: kyvernov1.ResourceDescription{ + NamespaceSelector: &metav1.LabelSelector{}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + wantLabels: map[string]string{"exclude": "any", "cluster": "main"}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetNamespaceSelectorsFromNamespaceLister( + tt.kind, + tt.namespaceOfResource, + tt.nsLister, + tt.policies, + logging.GlobalLogger(), + ) + if tt.wantErr { + assert.Error(t, err, "expected error but got nil") + } else { + assert.NoError(t, err, fmt.Sprintf("unexpected error: %v", err)) + } + assert.Equal(t, tt.wantLabels, got) + }) + } +}
pkg/webhooks/resource/generation/handler.go+6 −1 modified@@ -121,7 +121,12 @@ func (h *generationHandler) handleTrigger( var appliedRules, failedRules []engineapi.RuleResponse policyContext := policyContext.WithPolicy(policy) if request.Kind.Kind != "Namespace" && request.Namespace != "" { - policyContext = policyContext.WithNamespaceLabels(utils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log)) + namespaceLabels, err := utils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, []kyvernov1.PolicyInterface{policy}, h.log) + if err != nil { + h.log.Error(err, "failed to get namespace labels for policy", "policy", policy.GetName()) + continue + } + policyContext = policyContext.WithNamespaceLabels(namespaceLabels) } engineResponse := h.engine.ApplyBackgroundChecks(ctx, policyContext) for _, rule := range engineResponse.PolicyResponse.Rules {
pkg/webhooks/resource/handlers.go+5 −2 modified@@ -301,14 +301,17 @@ func (h *resourceHandlers) retrieveAndCategorizePolicies( return policies, mutatePolicies, generatePolicies, imageVerifyValidatePolicies, auditWarnPolicies, nil } -func (h *resourceHandlers) buildPolicyContextFromAdmissionRequest(logger logr.Logger, request handlers.AdmissionRequest) (*policycontext.PolicyContext, error) { +func (h *resourceHandlers) buildPolicyContextFromAdmissionRequest(logger logr.Logger, request handlers.AdmissionRequest, policies []kyvernov1.PolicyInterface) (*policycontext.PolicyContext, error) { policyContext, err := h.pcBuilder.Build(request.AdmissionRequest, request.Roles, request.ClusterRoles, request.GroupVersionKind) if err != nil { return nil, err } namespaceLabels := make(map[string]string) if request.Kind.Kind != "Namespace" && request.Namespace != "" { - namespaceLabels = engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, logger) + namespaceLabels, err = engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, policies, logger) + if err != nil { + return nil, err + } } policyContext = policyContext.WithNamespaceLabels(namespaceLabels) return policyContext, nil
pkg/webhooks/resource/imageverification/handler.go+6 −1 modified@@ -110,7 +110,12 @@ func (h *imageVerificationHandler) handleVerifyImages( policyContext := policyContext.WithPolicy(policy) if request.Kind.Kind != "Namespace" && request.Namespace != "" { - policyContext = policyContext.WithNamespaceLabels(engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log)) + namespaceLabels, err := engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, []kyvernov1.PolicyInterface{policy}, h.log) + if err != nil { + h.log.Error(err, "failed to get namespace labels for policy", "policy", policy.GetName()) + return + } + policyContext = policyContext.WithNamespaceLabels(namespaceLabels) } resp, ivm := h.engine.VerifyAndPatchImages(ctx, policyContext)
pkg/webhooks/resource/mutation/mutation.go+8 −3 modified@@ -124,7 +124,7 @@ func (v *mutationHandler) applyMutations( failurePolicy = kyvernov1.Fail } - engineResponse, policyPatches, err := v.applyMutation(ctx, request.AdmissionRequest, currentContext, failurePolicy) + engineResponse, policyPatches, err := v.applyMutation(ctx, request.AdmissionRequest, currentContext, failurePolicy, policy) if err != nil { return fmt.Errorf("mutation policy %s error: %v", policy.GetName(), err) } @@ -172,9 +172,14 @@ func (v *mutationHandler) applyMutations( return jsonutils.JoinPatches(patch.ConvertPatches(patches...)...), engineResponses, nil } -func (h *mutationHandler) applyMutation(ctx context.Context, request admissionv1.AdmissionRequest, policyContext *engine.PolicyContext, failurePolicy kyvernov1.FailurePolicyType) (*engineapi.EngineResponse, []jsonpatch.JsonPatchOperation, error) { +func (h *mutationHandler) applyMutation(ctx context.Context, request admissionv1.AdmissionRequest, policyContext *engine.PolicyContext, failurePolicy kyvernov1.FailurePolicyType, policy kyvernov1.PolicyInterface) (*engineapi.EngineResponse, []jsonpatch.JsonPatchOperation, error) { if request.Kind.Kind != "Namespace" && request.Namespace != "" { - policyContext = policyContext.WithNamespaceLabels(engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log)) + namespaceLabels, err := engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, []kyvernov1.PolicyInterface{policy}, h.log) + if err != nil { + return nil, nil, err + } + + policyContext = policyContext.WithNamespaceLabels(namespaceLabels) } engineResponse := h.engine.Mutate(ctx, policyContext)
pkg/webhooks/resource/updaterequest.go+2 −2 modified@@ -26,7 +26,7 @@ func (h *resourceHandlers) handleBackgroundApplies(ctx context.Context, logger l } func (h *resourceHandlers) handleMutateExisting(ctx context.Context, logger logr.Logger, request handlers.AdmissionRequest, policies []kyvernov1.PolicyInterface, admissionRequestTimestamp time.Time) { - policyContext, err := h.buildPolicyContextFromAdmissionRequest(logger, request) + policyContext, err := h.buildPolicyContextFromAdmissionRequest(logger, request, policies) if err != nil { logger.Error(err, "failed to create policy context") return @@ -92,7 +92,7 @@ func (h *resourceHandlers) handleMutateExisting(ctx context.Context, logger logr } func (h *resourceHandlers) handleGenerate(ctx context.Context, logger logr.Logger, request handlers.AdmissionRequest, generatePolicies []kyvernov1.PolicyInterface, ts time.Time) { - policyContext, err := h.buildPolicyContextFromAdmissionRequest(logger, request) + policyContext, err := h.buildPolicyContextFromAdmissionRequest(logger, request, generatePolicies) if err != nil { logger.Error(err, "failed to create policy context") return
pkg/webhooks/resource/validation/validation.go+7 −4 modified@@ -94,7 +94,7 @@ func (v *validationHandler) HandleValidationEnforce( return true, "", nil, nil } - policyContext, err := v.buildPolicyContextFromAdmissionRequest(logger, request) + policyContext, err := v.buildPolicyContextFromAdmissionRequest(logger, request, policies) if err != nil { msg := fmt.Sprintf("failed to create policy context: %v", err) return false, msg, nil, nil @@ -184,7 +184,7 @@ func (v *validationHandler) HandleValidationAudit( return nil } - policyContext, err := v.buildPolicyContextFromAdmissionRequest(v.log, request) + policyContext, err := v.buildPolicyContextFromAdmissionRequest(v.log, request, policies) if err != nil { v.log.Error(err, "failed to build policy context") return nil @@ -233,14 +233,17 @@ func (v *validationHandler) buildAuditResponses( return responses, nil } -func (v *validationHandler) buildPolicyContextFromAdmissionRequest(logger logr.Logger, request handlers.AdmissionRequest) (*policycontext.PolicyContext, error) { +func (v *validationHandler) buildPolicyContextFromAdmissionRequest(logger logr.Logger, request handlers.AdmissionRequest, policies []kyvernov1.PolicyInterface) (*policycontext.PolicyContext, error) { policyContext, err := v.pcBuilder.Build(request.AdmissionRequest, request.Roles, request.ClusterRoles, request.GroupVersionKind) if err != nil { return nil, err } namespaceLabels := make(map[string]string) if request.Kind.Kind != "Namespace" && request.Namespace != "" { - namespaceLabels = engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, v.nsLister, logger) + namespaceLabels, err = engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, v.nsLister, policies, logger) + if err != nil { + return nil, err + } } policyContext = policyContext.WithNamespaceLabels(namespaceLabels) return policyContext, nil
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-jrr2-x33p-6hvcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-46342ghsaADVISORY
- github.com/kyverno/kyverno/commit/3ff923b7756e1681daf73849954bd88516589194ghsax_refsource_MISCWEB
- github.com/kyverno/kyverno/security/advisories/GHSA-jrr2-x33p-6hvcghsax_refsource_CONFIRMWEB
- pkg.go.dev/vuln/GO-2025-3652ghsaWEB
News mentions
0No linked articles in our index yet.