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 {