VYPR
High severity7.7NVD Advisory· Published Apr 24, 2026· Updated Apr 27, 2026

CVE-2026-41485

CVE-2026-41485

Description

Kyverno is a policy engine designed for cloud native platform engineering teams. Prior to versions 1.17.2 and 1.16.4, an unchecked type assertion in the forEach mutation handler allows any user with permission to create a Policy or ClusterPolicy to crash the cluster-wide background controller into a persistent CrashLoopBackOff. The same bug also causes the admission controller to drop connections and block all matching resource operations. The crash loop persists until the policy is deleted. The vulnerability is confined to the legacy engine, and CEL-based policies are unaffected. Versions 1.17.2 and 1.16.4 fix the issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/kyverno/kyvernoGo
>= 1.13.0, < 1.16.41.16.4
github.com/kyverno/kyvernoGo
>= 1.17.0-rc.1, < 1.17.21.17.2

Affected products

2
  • Kyverno/Kyverno2 versions
    cpe:2.3:a:kyverno:kyverno:*:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:a:kyverno:kyverno:*:*:*:*:*:*:*:*range: >=1.17.0,<1.17.2
    • cpe:2.3:a:kyverno:kyverno:*:-:*:*:*:*:*:*range: >=1.13.0,<1.16.4

Patches

2
80e728c2283a

Merge commit from fork (#15887)

https://github.com/kyverno/kyvernoshutingApr 17, 2026via ghsa
2 files changed · +37 1
  • pkg/engine/mutate/mutation.go+2 1 modified
    @@ -78,7 +78,8 @@ func ForEach(name string, foreach kyvernov1.ForEachMutation, policyContext engin
     	if err != nil {
     		return NewErrorResponse("variable substitution failed", err)
     	}
    -	patcher := NewPatcher(fe["patchStrategicMerge"], fe["patchesJson6902"].(string))
    +	jsonPatch, _ := fe["patchesJson6902"].(string)
    +	patcher := NewPatcher(fe["patchStrategicMerge"], jsonPatch)
     	if patcher == nil {
     		return NewErrorResponse("empty mutate rule", nil)
     	}
    
  • pkg/engine/mutate/mutation_test.go+35 0 modified
    @@ -238,6 +238,16 @@ func TestProcessPatches_RemovePathDoesntExist_NotEmptyResult(t *testing.T) {
     	require.Equal(t, resource, patched)
     }
     
    +type MockPolicyContext struct {
    +	engineapi.PolicyContext
    +	mock.Mock
    +}
    +
    +func (m *MockPolicyContext) JSONContext() context.Interface {
    +	args := m.Called()
    +	return args.Get(0).(context.Interface)
    +}
    +
     type MockContext struct {
     	context.Interface
     	mock.Mock
    @@ -253,6 +263,31 @@ func (m *MockContext) QueryOperation() string {
     	return args.Get(0).(string)
     }
     
    +func TestForEach_NilPatchesJSON6902_NoPanic(t *testing.T) {
    +	ctx := &MockContext{}
    +	// Variable resolves to nil, which caused a bare type assertion panic before the fix
    +	ctx.On("Query", mock.Anything).Return(nil, nil)
    +	ctx.On("QueryOperation").Return("CREATE")
    +
    +	foreach := v1.ForEachMutation{
    +		PatchesJSON6902: "{{ element.nonexistent }}",
    +	}
    +
    +	var resource unstructured.Unstructured
    +	resource.SetUnstructuredContent(map[string]interface{}{
    +		"apiVersion": "v1",
    +		"kind":       "ConfigMap",
    +		"metadata":   map[string]interface{}{"name": "test"},
    +	})
    +
    +	policyContext := &MockPolicyContext{}
    +	policyContext.On("JSONContext").Return(ctx)
    +
    +	resp := ForEach("test-rule", foreach, policyContext, resource, nil, logr.Discard())
    +	assert.NotNil(t, resp)
    +	assert.Equal(t, engineapi.RuleStatusError, resp.Status)
    +}
    +
     func TestSubstituteAllInForEach_InvalidTypeConversion(t *testing.T) {
     	ctx := &MockContext{}
     	// Simulate a scenario where the substitution returns an unexpected type
    
76c8fdbe8732

Merge commit from fork (#15888)

https://github.com/kyverno/kyvernoshutingApr 17, 2026via ghsa
2 files changed · +37 1
  • pkg/engine/mutate/mutation.go+2 1 modified
    @@ -78,7 +78,8 @@ func ForEach(name string, foreach kyvernov1.ForEachMutation, policyContext engin
     	if err != nil {
     		return NewErrorResponse("variable substitution failed", err)
     	}
    -	patcher := NewPatcher(fe["patchStrategicMerge"], fe["patchesJson6902"].(string))
    +	jsonPatch, _ := fe["patchesJson6902"].(string)
    +	patcher := NewPatcher(fe["patchStrategicMerge"], jsonPatch)
     	if patcher == nil {
     		return NewErrorResponse("empty mutate rule", nil)
     	}
    
  • pkg/engine/mutate/mutation_test.go+35 0 modified
    @@ -238,6 +238,16 @@ func TestProcessPatches_RemovePathDoesntExist_NotEmptyResult(t *testing.T) {
     	require.Equal(t, resource, patched)
     }
     
    +type MockPolicyContext struct {
    +	engineapi.PolicyContext
    +	mock.Mock
    +}
    +
    +func (m *MockPolicyContext) JSONContext() context.Interface {
    +	args := m.Called()
    +	return args.Get(0).(context.Interface)
    +}
    +
     type MockContext struct {
     	context.Interface
     	mock.Mock
    @@ -253,6 +263,31 @@ func (m *MockContext) QueryOperation() string {
     	return args.Get(0).(string)
     }
     
    +func TestForEach_NilPatchesJSON6902_NoPanic(t *testing.T) {
    +	ctx := &MockContext{}
    +	// Variable resolves to nil, which caused a bare type assertion panic before the fix
    +	ctx.On("Query", mock.Anything).Return(nil, nil)
    +	ctx.On("QueryOperation").Return("CREATE")
    +
    +	foreach := v1.ForEachMutation{
    +		PatchesJSON6902: "{{ element.nonexistent }}",
    +	}
    +
    +	var resource unstructured.Unstructured
    +	resource.SetUnstructuredContent(map[string]interface{}{
    +		"apiVersion": "v1",
    +		"kind":       "ConfigMap",
    +		"metadata":   map[string]interface{}{"name": "test"},
    +	})
    +
    +	policyContext := &MockPolicyContext{}
    +	policyContext.On("JSONContext").Return(ctx)
    +
    +	resp := ForEach("test-rule", foreach, policyContext, resource, nil, logr.Discard())
    +	assert.NotNil(t, resp)
    +	assert.Equal(t, engineapi.RuleStatusError, resp.Status)
    +}
    +
     func TestSubstituteAllInForEach_InvalidTypeConversion(t *testing.T) {
     	ctx := &MockContext{}
     	// Simulate a scenario where the substitution returns an unexpected type
    

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.