VYPR
High severityNVD Advisory· Published Apr 30, 2025· Updated Apr 30, 2025

Kyverno vulnerable to bypass of policy rules that use namespace selectors in match statements

CVE-2025-46342

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.

PackageAffected versionsPatched versions
github.com/kyverno/kyvernoGo
< 1.13.51.13.5
github.com/kyverno/kyvernoGo
>= 1.14.0-alpha.1, < 1.14.01.14.0

Affected products

1

Patches

1
3ff923b7756e

Fix Namespace Selector Error Propagation and Scope Policy for Accurate Rule Evaluation (#12744)

https://github.com/kyverno/kyvernoJigar JoshiApr 14, 2025via ghsa
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

News mentions

0

No linked articles in our index yet.