From 32f13d5894d4b7bde81cd6d61b24569ec373d8fd Mon Sep 17 00:00:00 2001
From: Vishal Choudhary <vishal.choudhary@nirmata.com>
Date: Thu, 6 Mar 2025 01:23:19 +0530
Subject: [PATCH] fix: use object key in json image verification (#12298)

---
 pkg/imageverification/evaluator/compiler.go   |  8 +-
 pkg/imageverification/evaluator/eval_test.go  |  2 +-
 pkg/imageverification/evaluator/policy.go     |  6 +-
 pkg/imageverification/variables/images.go     | 19 ++---
 .../variables/images_test.go                  | 77 +++++++++++--------
 5 files changed, 55 insertions(+), 57 deletions(-)

diff --git a/pkg/imageverification/evaluator/compiler.go b/pkg/imageverification/evaluator/compiler.go
index 50c356aac7..0508af15ff 100644
--- a/pkg/imageverification/evaluator/compiler.go
+++ b/pkg/imageverification/evaluator/compiler.go
@@ -64,16 +64,12 @@ func (c *compiler) Compile(logger logr.Logger, ivpolicy *policiesv1alpha1.ImageV
 		options = append(options, cel.Variable(ObjectKey, cel.DynType))
 		options = append(options, cel.Variable(OldObjectKey, cel.DynType))
 	} else {
-		options = append(options, cel.Variable(RequestKey, cel.DynType))
+		options = append(options, cel.Variable(ObjectKey, cel.DynType))
 	}
 
 	for _, declType := range declTypes {
 		options = append(options, cel.Types(declType.CelType()))
 	}
-	if err != nil {
-		// TODO: proper error handling
-		panic(err)
-	}
 	options = append(options, imageverifierfunctions.Lib(logger, c.ictx, ivpolicy, c.lister))
 	env, err := base.Extend(options...)
 	if err != nil {
@@ -94,7 +90,7 @@ func (c *compiler) Compile(logger logr.Logger, ivpolicy *policiesv1alpha1.ImageV
 		return nil, append(allErrs, errs...)
 	}
 
-	imageExtractors, errs := variables.CompileImageExtractors(path.Child("images"), ivpolicy.Spec.Images, c.reqGVR)
+	imageExtractors, errs := variables.CompileImageExtractors(path.Child("images"), ivpolicy.Spec.Images, c.reqGVR, options)
 	if errs != nil {
 		return nil, append(allErrs, errs...)
 	}
diff --git a/pkg/imageverification/evaluator/eval_test.go b/pkg/imageverification/evaluator/eval_test.go
index 32279894da..09cf4e1646 100644
--- a/pkg/imageverification/evaluator/eval_test.go
+++ b/pkg/imageverification/evaluator/eval_test.go
@@ -35,7 +35,7 @@ var (
 			Images: []policiesv1alpha1.Image{
 				{
 					Name:       "bar",
-					Expression: "[request.foo.bar]",
+					Expression: "[object.foo.bar]",
 				},
 			},
 			Attestors: []policiesv1alpha1.Attestor{
diff --git a/pkg/imageverification/evaluator/policy.go b/pkg/imageverification/evaluator/policy.go
index d1338f35ca..ca15791850 100644
--- a/pkg/imageverification/evaluator/policy.go
+++ b/pkg/imageverification/evaluator/policy.go
@@ -73,10 +73,10 @@ func (c *compiledPolicy) Evaluate(ctx context.Context, ictx imagedataloader.Imag
 		data[ObjectKey] = objectVal
 		data[OldObjectKey] = oldObjectVal
 	} else {
-		data[RequestKey] = request
+		data[ObjectKey] = request
 	}
 
-	images, err := variables.ExtractImages(c.imageExtractors, request)
+	images, err := variables.ExtractImages(c.imageExtractors, data)
 	if err != nil {
 		return nil, err
 	}
@@ -151,7 +151,7 @@ func (p *compiledPolicy) match(
 		data[NamespaceObjectKey] = namespaceVal
 		data[RequestKey] = requestVal.Object
 	} else {
-		data[RequestKey] = request
+		data[ObjectKey] = request
 	}
 
 	var errs []error
diff --git a/pkg/imageverification/variables/images.go b/pkg/imageverification/variables/images.go
index 77ad9096de..b3a65deab2 100644
--- a/pkg/imageverification/variables/images.go
+++ b/pkg/imageverification/variables/images.go
@@ -2,9 +2,7 @@ package variables
 
 import (
 	"github.com/google/cel-go/cel"
-	"github.com/google/cel-go/common/types"
 	"github.com/kyverno/kyverno/api/policies.kyverno.io/v1alpha1"
-	"github.com/kyverno/kyverno/pkg/cel/policy"
 	"github.com/kyverno/kyverno/pkg/cel/utils"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/validation/field"
@@ -60,10 +58,8 @@ type CompiledImageExtractor struct {
 	e   cel.Program
 }
 
-func (c *CompiledImageExtractor) GetImages(request interface{}) (string, []string, error) {
-	out, _, err := c.e.Eval(map[string]any{
-		policy.RequestKey: request,
-	})
+func (c *CompiledImageExtractor) GetImages(data map[string]any) (string, []string, error) {
+	out, _, err := c.e.Eval(data)
 	if err != nil {
 		return "", nil, err
 	}
@@ -76,17 +72,14 @@ func (c *CompiledImageExtractor) GetImages(request interface{}) (string, []strin
 	return c.key, result, nil
 }
 
-func CompileImageExtractors(path *field.Path, imageExtractors []v1alpha1.Image, gvr *metav1.GroupVersionResource) ([]*CompiledImageExtractor, field.ErrorList) {
+func CompileImageExtractors(path *field.Path, imageExtractors []v1alpha1.Image, gvr *metav1.GroupVersionResource, envOpts []cel.EnvOption) ([]*CompiledImageExtractor, field.ErrorList) {
 	var allErrs field.ErrorList
 	if gvr != nil {
 		imageExtractors = append(imageExtractors, getExtractorForGVR(gvr)...)
 	}
 
 	compiledMatches := make([]*CompiledImageExtractor, 0, len(imageExtractors))
-	e, err := cel.NewEnv(
-		// this uses dyn type to allow unstructured data
-		cel.Variable(policy.RequestKey, types.DynType),
-	)
+	e, err := cel.NewEnv(envOpts...)
 	if err != nil {
 		return nil, append(allErrs, field.Invalid(path, imageExtractors, err.Error()))
 	}
@@ -111,10 +104,10 @@ func CompileImageExtractors(path *field.Path, imageExtractors []v1alpha1.Image,
 	return compiledMatches, nil
 }
 
-func ExtractImages(c []*CompiledImageExtractor, request interface{}) (map[string][]string, error) {
+func ExtractImages(c []*CompiledImageExtractor, data map[string]any) (map[string][]string, error) {
 	result := make(map[string][]string)
 	for _, v := range c {
-		if key, images, err := v.GetImages(request); err != nil {
+		if key, images, err := v.GetImages(data); err != nil {
 			return nil, err
 		} else {
 			result[key] = images
diff --git a/pkg/imageverification/variables/images_test.go b/pkg/imageverification/variables/images_test.go
index 00fe0a00ad..1fbd3c76de 100644
--- a/pkg/imageverification/variables/images_test.go
+++ b/pkg/imageverification/variables/images_test.go
@@ -5,7 +5,10 @@ import (
 	"slices"
 	"testing"
 
+	"github.com/google/cel-go/cel"
+	"github.com/google/cel-go/common/types"
 	"github.com/kyverno/kyverno/api/policies.kyverno.io/v1alpha1"
+	"github.com/kyverno/kyverno/pkg/cel/policy"
 	"github.com/stretchr/testify/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/validation/field"
@@ -15,7 +18,7 @@ func Test_Match(t *testing.T) {
 	tests := []struct {
 		name           string
 		imageExtractor []v1alpha1.Image
-		request        any
+		request        map[string]any
 		gvr            *metav1.GroupVersionResource
 		wantResult     map[string][]string
 		wantErr        bool
@@ -28,10 +31,12 @@ func Test_Match(t *testing.T) {
 					Expression: "request.images",
 				},
 			},
-			request: map[string][]string{
-				"images": {
-					"nginx:latest",
-					"alpine:latest",
+			request: map[string]any{
+				"request": map[string][]string{
+					"images": {
+						"nginx:latest",
+						"alpine:latest",
+					},
 				},
 			},
 			wantResult: map[string][]string{
@@ -52,34 +57,36 @@ func Test_Match(t *testing.T) {
 				},
 			},
 			request: map[string]any{
-				"images": []string{
-					"nginx:latest",
-					"alpine:latest",
-				},
-				"object": map[string]any{
-					"spec": map[string]any{
-						"containers": []map[string]string{
-							{
-								"image": "kyverno/image-one",
+				"request": map[string]any{
+					"images": []string{
+						"nginx:latest",
+						"alpine:latest",
+					},
+					"object": map[string]any{
+						"spec": map[string]any{
+							"containers": []map[string]string{
+								{
+									"image": "kyverno/image-one",
+								},
+								{
+									"image": "kyverno/image-two",
+								},
 							},
-							{
-								"image": "kyverno/image-two",
+							"initContainers": []map[string]string{
+								{
+									"image": "kyverno/init-image-one",
+								},
+								{
+									"image": "kyverno/init-image-two",
+								},
 							},
-						},
-						"initContainers": []map[string]string{
-							{
-								"image": "kyverno/init-image-one",
-							},
-							{
-								"image": "kyverno/init-image-two",
-							},
-						},
-						"ephemeralContainers": []map[string]string{
-							{
-								"image": "kyverno/ephr-image-one",
-							},
-							{
-								"image": "kyverno/ephr-image-two",
+							"ephemeralContainers": []map[string]string{
+								{
+									"image": "kyverno/ephr-image-one",
+								},
+								{
+									"image": "kyverno/ephr-image-two",
+								},
 							},
 						},
 					},
@@ -114,8 +121,10 @@ func Test_Match(t *testing.T) {
 					Expression: "request.images",
 				},
 			},
-			request: map[string][]int{
-				"images": {0, 1},
+			request: map[string]any{
+				"request": map[string][]int{
+					"images": {0, 1},
+				},
 			},
 			gvr:     nil,
 			wantErr: true,
@@ -123,7 +132,7 @@ func Test_Match(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			c, errList := CompileImageExtractors(field.NewPath("spec", "images"), tt.imageExtractor, tt.gvr)
+			c, errList := CompileImageExtractors(field.NewPath("spec", "images"), tt.imageExtractor, tt.gvr, []cel.EnvOption{cel.Variable(policy.RequestKey, types.DynType)})
 			assert.Nil(t, errList)
 			images, err := ExtractImages(c, tt.request)
 			if tt.wantErr {