From 5b89e2e5f80c078dabe21455202aec6e0ac48fb2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?=
 <charled.breteche@gmail.com>
Date: Fri, 2 Dec 2022 09:14:23 +0100
Subject: [PATCH] refactor: make policy context immutable and fields private
 (#5523)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor: make policy context immutable and fields private

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* refactor: make policy context immutable and fields private

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
Co-authored-by: shuting <shuting@nirmata.com>
---
 .../kubectl-kyverno/utils/common/common.go    |  29 +--
 pkg/background/common/context.go              |  20 +-
 pkg/background/generate/generate.go           |  11 +-
 pkg/controllers/report/utils/scanner.go       |  28 ++-
 pkg/cosign/client.go                          |   1 -
 pkg/engine/background.go                      |  36 ++--
 pkg/engine/generation.go                      |  12 +-
 pkg/engine/imageVerify.go                     |  40 ++--
 pkg/engine/imageVerifyValidate.go             |   8 +-
 pkg/engine/imageVerify_test.go                |  12 +-
 pkg/engine/jsonContext.go                     |  40 ++--
 pkg/engine/k8smanifest.go                     |   4 +-
 pkg/engine/k8smanifest_test.go                |  24 +--
 pkg/engine/loadtargets.go                     |  16 +-
 pkg/engine/mutation.go                        |  38 ++--
 pkg/engine/mutation_test.go                   |  82 ++++----
 pkg/engine/policyContext.go                   | 199 +++++++++++++++---
 pkg/engine/utils.go                           |   4 +-
 pkg/engine/validation.go                      | 100 ++++-----
 pkg/engine/validation_test.go                 | 112 +++++-----
 pkg/policy/apply.go                           |  24 +--
 pkg/testrunner/scenario.go                    |  30 +--
 .../resource/generation/generation.go         |   9 +-
 pkg/webhooks/resource/handlers.go             |   8 +-
 .../resource/imageverification/handler.go     |  15 +-
 pkg/webhooks/resource/mutation/mutation.go    |  20 +-
 pkg/webhooks/resource/updaterequest.go        |  12 +-
 .../resource/validation/validation.go         |  22 +-
 pkg/webhooks/resource/validation_test.go      |   8 +-
 pkg/webhooks/utils/policy_context_builder.go  |  44 +---
 30 files changed, 538 insertions(+), 470 deletions(-)

diff --git a/cmd/cli/kubectl-kyverno/utils/common/common.go b/cmd/cli/kubectl-kyverno/utils/common/common.go
index e147418f85..f8113f9904 100644
--- a/cmd/cli/kubectl-kyverno/utils/common/common.go
+++ b/cmd/cli/kubectl-kyverno/utils/common/common.go
@@ -450,14 +450,12 @@ OuterLoop:
 		log.Log.Error(err, "failed to add image variables to context")
 	}
 
-	policyContext := &engine.PolicyContext{
-		Policy:          c.Policy,
-		NewResource:     *updatedResource,
-		JSONContext:     ctx,
-		NamespaceLabels: namespaceLabels,
-		AdmissionInfo:   c.UserInfo,
-		Client:          c.Client,
-	}
+	policyContext := engine.NewPolicyContextWithJsonContext(ctx).
+		WithPolicy(c.Policy).
+		WithNewResource(*updatedResource).
+		WithNamespaceLabels(namespaceLabels).
+		WithAdmissionInfo(c.UserInfo).
+		WithClient(c.Client)
 
 	mutateResponse := engine.Mutate(policyContext)
 	if mutateResponse != nil {
@@ -478,7 +476,7 @@ OuterLoop:
 		}
 	}
 
-	policyContext.NewResource = mutateResponse.PatchedResource
+	policyContext = policyContext.WithNewResource(mutateResponse.PatchedResource)
 
 	var info Info
 	var validateResponse *response.EngineResponse
@@ -505,16 +503,6 @@ OuterLoop:
 	}
 
 	if policyHasGenerate {
-		policyContext := &engine.PolicyContext{
-			NewResource:      *c.Resource,
-			Policy:           c.Policy,
-			ExcludeGroupRole: []string{},
-			ExcludeResourceFunc: func(s1, s2, s3 string) bool {
-				return false
-			},
-			JSONContext:     ctx,
-			NamespaceLabels: namespaceLabels,
-		}
 		generateResponse := engine.ApplyBackgroundChecks(policyContext)
 		if generateResponse != nil && !generateResponse.IsEmpty() {
 			newRuleResponse, err := handleGeneratePolicy(generateResponse, *policyContext, c.RuleToCloneSourceResource)
@@ -1011,7 +999,8 @@ func initializeMockController(objects []runtime.Object) (*generate.GenerateContr
 
 // handleGeneratePolicy returns a new RuleResponse with the Kyverno generated resource configuration by applying the generate rule.
 func handleGeneratePolicy(generateResponse *response.EngineResponse, policyContext engine.PolicyContext, ruleToCloneSourceResource map[string]string) ([]response.RuleResponse, error) {
-	objects := []runtime.Object{&policyContext.NewResource}
+	resource := policyContext.NewResource()
+	objects := []runtime.Object{&resource}
 	resources := []*unstructured.Unstructured{}
 	for _, rule := range generateResponse.PolicyResponse.Rules {
 		if path, ok := ruleToCloneSourceResource[rule.Name]; ok {
diff --git a/pkg/background/common/context.go b/pkg/background/common/context.go
index 36538a8b59..8178d630d5 100644
--- a/pkg/background/common/context.go
+++ b/pkg/background/common/context.go
@@ -77,18 +77,14 @@ func NewBackgroundContext(dclient dclient.Interface, ur *kyvernov1beta1.UpdateRe
 		logger.Error(err, "unable to add image info to variables context")
 	}
 
-	policyContext := &engine.PolicyContext{
-		NewResource:         *trigger,
-		OldResource:         old,
-		Policy:              policy,
-		AdmissionInfo:       ur.Spec.Context.UserRequestInfo,
-		ExcludeGroupRole:    cfg.GetExcludeGroupRole(),
-		ExcludeResourceFunc: cfg.ToFilter,
-		JSONContext:         ctx,
-		NamespaceLabels:     namespaceLabels,
-		Client:              dclient,
-		AdmissionOperation:  false,
-	}
+	policyContext := engine.NewPolicyContextWithJsonContext(ctx).
+		WithPolicy(policy).
+		WithNewResource(*trigger).
+		WithOldResource(old).
+		WithAdmissionInfo(ur.Spec.Context.UserRequestInfo).
+		WithConfiguration(cfg).
+		WithNamespaceLabels(namespaceLabels).
+		WithClient(dclient)
 
 	return policyContext, false, nil
 }
diff --git a/pkg/background/generate/generate.go b/pkg/background/generate/generate.go
index 03cb69b803..4d65581566 100644
--- a/pkg/background/generate/generate.go
+++ b/pkg/background/generate/generate.go
@@ -306,13 +306,12 @@ func updateStatus(statusControl common.StatusControlInterface, ur kyvernov1beta1
 func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext *engine.PolicyContext, ur kyvernov1beta1.UpdateRequest, applicableRules []string) (genResources []kyvernov1.ResourceSpec, processExisting bool, err error) {
 	// Get the response as the actions to be performed on the resource
 	// - - substitute values
-	policy := policyContext.Policy
-	resource := policyContext.NewResource
-
-	jsonContext := policyContext.JSONContext
+	policy := policyContext.Policy()
+	resource := policyContext.NewResource()
+	jsonContext := policyContext.JSONContext()
 	// To manage existing resources, we compare the creation time for the default resource to be generated and policy creation time
 	ruleNameToProcessingTime := make(map[string]time.Duration)
-	applyRules := policyContext.Policy.GetSpec().GetApplyRules()
+	applyRules := policy.GetSpec().GetApplyRules()
 	applyCount := 0
 
 	for _, rule := range autogen.ComputeRules(policy) {
@@ -347,7 +346,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
 			return nil, processExisting, err
 		}
 
-		if rule, err = variables.SubstituteAllInRule(log, policyContext.JSONContext, rule); err != nil {
+		if rule, err = variables.SubstituteAllInRule(log, policyContext.JSONContext(), rule); err != nil {
 			log.Error(err, "variable substitution failed for rule %s", rule.Name)
 			return nil, processExisting, err
 		}
diff --git a/pkg/controllers/report/utils/scanner.go b/pkg/controllers/report/utils/scanner.go
index 44e0861967..700dccf206 100644
--- a/pkg/controllers/report/utils/scanner.go
+++ b/pkg/controllers/report/utils/scanner.go
@@ -75,14 +75,12 @@ func (s *scanner) validateResource(resource unstructured.Unstructured, nsLabels
 	if err := ctx.AddOperation("CREATE"); err != nil {
 		return nil, err
 	}
-	policyCtx := &engine.PolicyContext{
-		Policy:           policy,
-		NewResource:      resource,
-		JSONContext:      ctx,
-		Client:           s.client,
-		NamespaceLabels:  nsLabels,
-		ExcludeGroupRole: s.excludeGroupRole,
-	}
+	policyCtx := engine.NewPolicyContextWithJsonContext(ctx).
+		WithNewResource(resource).
+		WithPolicy(policy).
+		WithClient(s.client).
+		WithNamespaceLabels(nsLabels).
+		WithExcludeGroupRole(s.excludeGroupRole...)
 	return engine.Validate(policyCtx), nil
 }
 
@@ -100,14 +98,12 @@ func (s *scanner) validateImages(resource unstructured.Unstructured, nsLabels ma
 	if err := ctx.AddOperation("CREATE"); err != nil {
 		return nil, err
 	}
-	policyCtx := &engine.PolicyContext{
-		Policy:           policy,
-		NewResource:      resource,
-		JSONContext:      ctx,
-		Client:           s.client,
-		NamespaceLabels:  nsLabels,
-		ExcludeGroupRole: s.excludeGroupRole,
-	}
+	policyCtx := engine.NewPolicyContextWithJsonContext(ctx).
+		WithNewResource(resource).
+		WithPolicy(policy).
+		WithClient(s.client).
+		WithNamespaceLabels(nsLabels).
+		WithExcludeGroupRole(s.excludeGroupRole...)
 	response, _ := engine.VerifyAndPatchImages(policyCtx)
 	if len(response.PolicyResponse.Rules) > 0 {
 		s.logger.Info("validateImages", "policy", policy, "response", response)
diff --git a/pkg/cosign/client.go b/pkg/cosign/client.go
index 981165025f..dd4a086527 100644
--- a/pkg/cosign/client.go
+++ b/pkg/cosign/client.go
@@ -12,7 +12,6 @@ var client Cosign = &driver{}
 
 type Cosign interface {
 	VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co *cosign.CheckOpts) ([]oci.Signature, bool, error)
-
 	VerifyImageAttestations(ctx context.Context, signedImgRef name.Reference, co *cosign.CheckOpts) (checkedAttestations []oci.Signature, bundleVerified bool, err error)
 }
 
diff --git a/pkg/engine/background.go b/pkg/engine/background.go
index 73b55c7bb9..3781961a66 100644
--- a/pkg/engine/background.go
+++ b/pkg/engine/background.go
@@ -22,15 +22,15 @@ func ApplyBackgroundChecks(policyContext *PolicyContext) (resp *response.EngineR
 }
 
 func filterRules(policyContext *PolicyContext, startTime time.Time) *response.EngineResponse {
-	kind := policyContext.NewResource.GetKind()
-	name := policyContext.NewResource.GetName()
-	namespace := policyContext.NewResource.GetNamespace()
-	apiVersion := policyContext.NewResource.GetAPIVersion()
+	kind := policyContext.newResource.GetKind()
+	name := policyContext.newResource.GetName()
+	namespace := policyContext.newResource.GetNamespace()
+	apiVersion := policyContext.newResource.GetAPIVersion()
 	resp := &response.EngineResponse{
 		PolicyResponse: response.PolicyResponse{
 			Policy: response.PolicySpec{
-				Name:      policyContext.Policy.GetName(),
-				Namespace: policyContext.Policy.GetNamespace(),
+				Name:      policyContext.policy.GetName(),
+				Namespace: policyContext.policy.GetNamespace(),
 			},
 			PolicyStats: response.PolicyStats{
 				PolicyExecutionTimestamp: startTime.Unix(),
@@ -44,13 +44,13 @@ func filterRules(policyContext *PolicyContext, startTime time.Time) *response.En
 		},
 	}
 
-	if policyContext.ExcludeResourceFunc(kind, namespace, name) {
+	if policyContext.excludeResourceFunc(kind, namespace, name) {
 		logging.WithName("ApplyBackgroundChecks").Info("resource excluded", "kind", kind, "namespace", namespace, "name", name)
 		return resp
 	}
 
-	applyRules := policyContext.Policy.GetSpec().GetApplyRules()
-	for _, rule := range autogen.ComputeRules(policyContext.Policy) {
+	applyRules := policyContext.policy.GetSpec().GetApplyRules()
+	for _, rule := range autogen.ComputeRules(policyContext.policy) {
 		if ruleResp := filterRule(rule, policyContext); ruleResp != nil {
 			resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
 			if applyRules == kyvernov1.ApplyOne && ruleResp.Status != response.RuleStatusSkip {
@@ -75,13 +75,13 @@ func filterRule(rule kyvernov1.Rule, policyContext *PolicyContext) *response.Rul
 	var err error
 	startTime := time.Now()
 
-	policy := policyContext.Policy
-	newResource := policyContext.NewResource
-	oldResource := policyContext.OldResource
-	admissionInfo := policyContext.AdmissionInfo
-	ctx := policyContext.JSONContext
-	excludeGroupRole := policyContext.ExcludeGroupRole
-	namespaceLabels := policyContext.NamespaceLabels
+	policy := policyContext.policy
+	newResource := policyContext.newResource
+	oldResource := policyContext.oldResource
+	admissionInfo := policyContext.admissionInfo
+	ctx := policyContext.jsonContext
+	excludeGroupRole := policyContext.excludeGroupRole
+	namespaceLabels := policyContext.namespaceLabels
 
 	logger := logging.WithName(string(ruleType)).WithValues("policy", policy.GetName(),
 		"kind", newResource.GetKind(), "namespace", newResource.GetNamespace(), "name", newResource.GetName())
@@ -105,8 +105,8 @@ func filterRule(rule kyvernov1.Rule, policyContext *PolicyContext) *response.Rul
 		return nil
 	}
 
-	policyContext.JSONContext.Checkpoint()
-	defer policyContext.JSONContext.Restore()
+	policyContext.jsonContext.Checkpoint()
+	defer policyContext.jsonContext.Restore()
 
 	if err = LoadContext(logger, rule.Context, policyContext, rule.Name); err != nil {
 		logger.V(4).Info("cannot add external data to the context", "reason", err.Error())
diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go
index c13fe0f8b9..521a6e17fe 100644
--- a/pkg/engine/generation.go
+++ b/pkg/engine/generation.go
@@ -17,10 +17,10 @@ func GenerateResponse(policyContext *PolicyContext, gr kyvernov1beta1.UpdateRequ
 }
 
 func filterGenerateRules(policyContext *PolicyContext, policyNameKey string, startTime time.Time) *response.EngineResponse {
-	kind := policyContext.NewResource.GetKind()
-	name := policyContext.NewResource.GetName()
-	namespace := policyContext.NewResource.GetNamespace()
-	apiVersion := policyContext.NewResource.GetAPIVersion()
+	kind := policyContext.newResource.GetKind()
+	name := policyContext.newResource.GetName()
+	namespace := policyContext.newResource.GetNamespace()
+	apiVersion := policyContext.newResource.GetAPIVersion()
 	pNamespace, pName, err := cache.SplitMetaNamespaceKey(policyNameKey)
 	if err != nil {
 		logging.Error(err, "failed to spilt name and namespace", policyNameKey)
@@ -44,12 +44,12 @@ func filterGenerateRules(policyContext *PolicyContext, policyNameKey string, sta
 		},
 	}
 
-	if policyContext.ExcludeResourceFunc(kind, namespace, name) {
+	if policyContext.excludeResourceFunc(kind, namespace, name) {
 		logging.WithName("Generate").Info("resource excluded", "kind", kind, "namespace", namespace, "name", name)
 		return resp
 	}
 
-	for _, rule := range autogen.ComputeRules(policyContext.Policy) {
+	for _, rule := range autogen.ComputeRules(policyContext.policy) {
 		if ruleResp := filterRule(rule, policyContext); ruleResp != nil {
 			resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
 		}
diff --git a/pkg/engine/imageVerify.go b/pkg/engine/imageVerify.go
index e977df16bc..fd7d87c166 100644
--- a/pkg/engine/imageVerify.go
+++ b/pkg/engine/imageVerify.go
@@ -48,10 +48,10 @@ func extractMatchingImages(policyContext *PolicyContext, rule *kyvernov1.Rule) (
 		images map[string]map[string]apiutils.ImageInfo
 		err    error
 	)
-	images = policyContext.JSONContext.ImageInfo()
+	images = policyContext.jsonContext.ImageInfo()
 	if rule.ImageExtractors != nil {
-		images, err = policyContext.JSONContext.GenerateCustomImageInfo(
-			&policyContext.NewResource, rule.ImageExtractors)
+		images, err = policyContext.jsonContext.GenerateCustomImageInfo(
+			&policyContext.newResource, rule.ImageExtractors)
 		if err != nil {
 			// if we get an error while generating custom images from image extractors,
 			// don't check for matching images in imageExtractors
@@ -65,8 +65,8 @@ func extractMatchingImages(policyContext *PolicyContext, rule *kyvernov1.Rule) (
 func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineResponse, *ImageVerificationMetadata) {
 	resp := &response.EngineResponse{}
 
-	policy := policyContext.Policy
-	patchedResource := policyContext.NewResource
+	policy := policyContext.policy
+	patchedResource := policyContext.newResource
 	logger := logging.WithName("EngineVerifyImages").WithValues("policy", policy.GetName(),
 		"kind", patchedResource.GetKind(), "namespace", patchedResource.GetNamespace(), "name", patchedResource.GetName())
 
@@ -78,8 +78,8 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons
 			"applied", resp.PolicyResponse.RulesAppliedCount, "successful", resp.IsSuccessful())
 	}()
 
-	policyContext.JSONContext.Checkpoint()
-	defer policyContext.JSONContext.Restore()
+	policyContext.jsonContext.Checkpoint()
+	defer policyContext.jsonContext.Restore()
 
 	// update image registry secrets
 	if err := registryclient.DefaultClient.RefreshKeychainPullSecrets(); err != nil {
@@ -87,7 +87,7 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons
 	}
 
 	ivm := &ImageVerificationMetadata{}
-	rules := autogen.ComputeRules(policyContext.Policy)
+	rules := autogen.ComputeRules(policyContext.policy)
 	applyRules := policy.GetSpec().GetApplyRules()
 
 	for i := range rules {
@@ -115,13 +115,13 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons
 			continue
 		}
 
-		policyContext.JSONContext.Restore()
+		policyContext.jsonContext.Restore()
 		if err := LoadContext(logger, rule.Context, policyContext, rule.Name); err != nil {
 			appendResponse(resp, rule, fmt.Sprintf("failed to load context: %s", err.Error()), response.RuleStatusError)
 			continue
 		}
 
-		ruleCopy, err := substituteVariables(rule, policyContext.JSONContext, logger)
+		ruleCopy, err := substituteVariables(rule, policyContext.jsonContext, logger)
 		if err != nil {
 			appendResponse(resp, rule, fmt.Sprintf("failed to substitute variables: %s", err.Error()), response.RuleStatusError)
 			continue
@@ -201,13 +201,13 @@ func (iv *imageVerifier) verify(imageVerify kyvernov1.ImageVerification, matched
 		}
 
 		pointer := jsonpointer.ParsePath(imageInfo.Pointer).JMESPath()
-		changed, err := iv.policyContext.JSONContext.HasChanged(pointer)
+		changed, err := iv.policyContext.jsonContext.HasChanged(pointer)
 		if err == nil && !changed {
 			iv.logger.V(4).Info("no change in image, skipping check", "image", image)
 			continue
 		}
 
-		verified, err := isImageVerified(iv.policyContext.NewResource, image, iv.logger)
+		verified, err := isImageVerified(iv.policyContext.newResource, image, iv.logger)
 		if err == nil && verified {
 			iv.logger.Info("image was previously verified, skipping check", "image", image)
 			continue
@@ -266,14 +266,14 @@ func (iv *imageVerifier) handleMutateDigest(digest string, imageInfo apiutils.Im
 }
 
 func hasImageVerifiedAnnotationChanged(ctx *PolicyContext, log logr.Logger) bool {
-	if reflect.DeepEqual(ctx.NewResource, unstructured.Unstructured{}) ||
-		reflect.DeepEqual(ctx.OldResource, unstructured.Unstructured{}) {
+	if reflect.DeepEqual(ctx.newResource, unstructured.Unstructured{}) ||
+		reflect.DeepEqual(ctx.oldResource, unstructured.Unstructured{}) {
 		return false
 	}
 
 	key := imageVerifyAnnotationKey
-	newValue := ctx.NewResource.GetAnnotations()[key]
-	oldValue := ctx.OldResource.GetAnnotations()[key]
+	newValue := ctx.newResource.GetAnnotations()[key]
+	oldValue := ctx.oldResource.GetAnnotations()[key]
 	result := newValue != oldValue
 	if result {
 		log.V(2).Info("annotation mismatch", "oldValue", oldValue, "newValue", newValue, "key", key)
@@ -301,7 +301,7 @@ func (iv *imageVerifier) verifyImage(imageVerify kyvernov1.ImageVerification, im
 	iv.logger.V(2).Info("verifying image signatures", "image", image,
 		"attestors", len(imageVerify.Attestors), "attestations", len(imageVerify.Attestations))
 
-	if err := iv.policyContext.JSONContext.AddImageInfo(imageInfo); err != nil {
+	if err := iv.policyContext.jsonContext.AddImageInfo(imageInfo); err != nil {
 		iv.logger.Error(err, "failed to add image to context")
 		msg := fmt.Sprintf("failed to add image to context %s: %s", image, err.Error())
 		return ruleResponse(*iv.rule, response.ImageVerify, msg, response.RuleStatusError, nil), ""
@@ -628,10 +628,10 @@ func (iv *imageVerifier) checkAttestations(a kyvernov1.Attestation, s map[string
 		return true, nil
 	}
 
-	iv.policyContext.JSONContext.Checkpoint()
-	defer iv.policyContext.JSONContext.Restore()
+	iv.policyContext.jsonContext.Checkpoint()
+	defer iv.policyContext.jsonContext.Restore()
 
-	return evaluateConditions(a.Conditions, iv.policyContext.JSONContext, s, iv.logger)
+	return evaluateConditions(a.Conditions, iv.policyContext.jsonContext, s, iv.logger)
 }
 
 func evaluateConditions(
diff --git a/pkg/engine/imageVerifyValidate.go b/pkg/engine/imageVerifyValidate.go
index 226c4ee5a0..ca1d7fd142 100644
--- a/pkg/engine/imageVerifyValidate.go
+++ b/pkg/engine/imageVerifyValidate.go
@@ -42,7 +42,7 @@ func processImageValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyver
 	}
 
 	if !preconditionsPassed {
-		if ctx.Policy.GetSpec().ValidationFailureAction.Audit() {
+		if ctx.policy.GetSpec().ValidationFailureAction.Audit() {
 			return nil
 		}
 
@@ -51,7 +51,7 @@ func processImageValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyver
 
 	for _, v := range rule.VerifyImages {
 		imageVerify := v.Convert()
-		for _, infoMap := range ctx.JSONContext.ImageInfo() {
+		for _, infoMap := range ctx.jsonContext.ImageInfo() {
 			for name, imageInfo := range infoMap {
 				image := imageInfo.String()
 				log = log.WithValues("rule", rule.Name)
@@ -80,8 +80,8 @@ func validateImage(ctx *PolicyContext, imageVerify *kyvernov1.ImageVerification,
 		return fmt.Errorf("missing digest for %s", image)
 	}
 
-	if imageVerify.Required && !reflect.DeepEqual(ctx.NewResource, unstructured.Unstructured{}) {
-		verified, err := isImageVerified(ctx.NewResource, image, log)
+	if imageVerify.Required && !reflect.DeepEqual(ctx.newResource, unstructured.Unstructured{}) {
+		verified, err := isImageVerified(ctx.newResource, image, log)
 		if err != nil {
 			return err
 		}
diff --git a/pkg/engine/imageVerify_test.go b/pkg/engine/imageVerify_test.go
index d4f03bc247..848dd8627a 100644
--- a/pkg/engine/imageVerify_test.go
+++ b/pkg/engine/imageVerify_test.go
@@ -181,9 +181,9 @@ func buildContext(t *testing.T, policy, resource string, oldResource string) *Po
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &cpol,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured,
+		policy:      &cpol,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured,
 	}
 
 	if oldResource != "" {
@@ -193,7 +193,7 @@ func buildContext(t *testing.T, policy, resource string, oldResource string) *Po
 		err = context.AddOldResource(ctx, []byte(oldResource))
 		assert.NilError(t, err)
 
-		policyContext.OldResource = *oldResourceUnstructured
+		policyContext.oldResource = *oldResourceUnstructured
 	}
 
 	if err := ctx.AddImageInfos(resourceUnstructured); err != nil {
@@ -418,7 +418,7 @@ func Test_ConfigMapMissingSuccess(t *testing.T) {
 func Test_ConfigMapMissingFailure(t *testing.T) {
 	ghcrImage := strings.Replace(testConfigMapMissingResource, "nginx:latest", "ghcr.io/kyverno/test-verify-image:signed", -1)
 	policyContext := buildContext(t, testConfigMapMissing, ghcrImage, "")
-	policyContext.Client = client.NewEmptyFakeClient()
+	policyContext.client = client.NewEmptyFakeClient()
 	cosign.ClearMock()
 	err, _ := VerifyAndPatchImages(policyContext)
 	assert.Equal(t, len(err.PolicyResponse.Rules), 1)
@@ -499,7 +499,7 @@ func Test_RuleSelectorImageVerify(t *testing.T) {
 
 	policyContext := buildContext(t, testSampleSingleKeyPolicy, testSampleResource, "")
 	rule := newStaticKeyRule("match-all", "*", testOtherKey)
-	spec := policyContext.Policy.GetSpec()
+	spec := policyContext.policy.GetSpec()
 	spec.Rules = append(spec.Rules, *rule)
 
 	applyAll := kyverno.ApplyAll
diff --git a/pkg/engine/jsonContext.go b/pkg/engine/jsonContext.go
index 301621263c..5945a42823 100644
--- a/pkg/engine/jsonContext.go
+++ b/pkg/engine/jsonContext.go
@@ -19,13 +19,13 @@ func LoadContext(logger logr.Logger, contextEntries []kyvernov1.ContextEntry, ct
 		return nil
 	}
 
-	policyName := ctx.Policy.GetName()
+	policyName := ctx.policy.GetName()
 	if store.GetMock() {
 		rule := store.GetPolicyRuleFromContext(policyName, ruleName)
 		if rule != nil && len(rule.Values) > 0 {
 			variables := rule.Values
 			for key, value := range variables {
-				if err := ctx.JSONContext.AddVariable(key, value); err != nil {
+				if err := ctx.jsonContext.AddVariable(key, value); err != nil {
 					return err
 				}
 			}
@@ -52,7 +52,7 @@ func LoadContext(logger logr.Logger, contextEntries []kyvernov1.ContextEntry, ct
 
 		if rule != nil && len(rule.ForeachValues) > 0 {
 			for key, value := range rule.ForeachValues {
-				if err := ctx.JSONContext.AddVariable(key, value[store.ForeachElement]); err != nil {
+				if err := ctx.jsonContext.AddVariable(key, value[store.ForeachElement]); err != nil {
 					return err
 				}
 			}
@@ -84,7 +84,7 @@ func LoadContext(logger logr.Logger, contextEntries []kyvernov1.ContextEntry, ct
 func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) (err error) {
 	path := ""
 	if entry.Variable.JMESPath != "" {
-		jp, err := variables.SubstituteAll(logger, ctx.JSONContext, entry.Variable.JMESPath)
+		jp, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.Variable.JMESPath)
 		if err != nil {
 			return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.JMESPath, err)
 		}
@@ -97,7 +97,7 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC
 		if err != nil {
 			return fmt.Errorf("invalid default for variable %s", entry.Name)
 		}
-		defaultValue, err = variables.SubstituteAll(logger, ctx.JSONContext, value)
+		defaultValue, err = variables.SubstituteAll(logger, ctx.jsonContext, value)
 		if err != nil {
 			return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.Default, err)
 		}
@@ -106,7 +106,7 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC
 	var output interface{} = defaultValue
 	if entry.Variable.Value != nil {
 		value, _ := variables.DocumentToUntyped(entry.Variable.Value)
-		variable, err := variables.SubstituteAll(logger, ctx.JSONContext, value)
+		variable, err := variables.SubstituteAll(logger, ctx.jsonContext, value)
 		if err != nil {
 			return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.Value, err)
 		}
@@ -122,7 +122,7 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC
 		}
 	} else {
 		if path != "" {
-			if variable, err := ctx.JSONContext.Query(path); err == nil {
+			if variable, err := ctx.jsonContext.Query(path); err == nil {
 				output = variable
 			} else if defaultValue == nil {
 				return fmt.Errorf("failed to apply jmespath %s to variable %v", path, err)
@@ -134,7 +134,7 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC
 		return fmt.Errorf("unable to add context entry for variable %s since it evaluated to nil", entry.Name)
 	}
 	if outputBytes, err := json.Marshal(output); err == nil {
-		return ctx.JSONContext.ReplaceContextEntry(entry.Name, outputBytes)
+		return ctx.jsonContext.ReplaceContextEntry(entry.Name, outputBytes)
 	} else {
 		return fmt.Errorf("unable to add context entry for variable %s: %w", entry.Name, err)
 	}
@@ -152,14 +152,14 @@ func loadImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Policy
 	if err != nil {
 		return err
 	}
-	if err := ctx.JSONContext.AddContextEntry(entry.Name, jsonBytes); err != nil {
+	if err := ctx.jsonContext.AddContextEntry(entry.Name, jsonBytes); err != nil {
 		return fmt.Errorf("failed to add resource data to context: contextEntry: %v, error: %v", entry, err)
 	}
 	return nil
 }
 
 func fetchImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) (interface{}, error) {
-	ref, err := variables.SubstituteAll(logger, ctx.JSONContext, entry.ImageRegistry.Reference)
+	ref, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.ImageRegistry.Reference)
 	if err != nil {
 		return nil, fmt.Errorf("ailed to substitute variables in context entry %s %s: %v", entry.Name, entry.ImageRegistry.Reference, err)
 	}
@@ -167,7 +167,7 @@ func fetchImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Polic
 	if !ok {
 		return nil, fmt.Errorf("invalid image reference %s, image reference must be a string", ref)
 	}
-	path, err := variables.SubstituteAll(logger, ctx.JSONContext, entry.ImageRegistry.JMESPath)
+	path, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.ImageRegistry.JMESPath)
 	if err != nil {
 		return nil, fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.ImageRegistry.JMESPath, err)
 	}
@@ -246,7 +246,7 @@ func loadAPIData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyCo
 	}
 
 	if entry.APICall.JMESPath == "" {
-		err = ctx.JSONContext.AddContextEntry(entry.Name, jsonData)
+		err = ctx.jsonContext.AddContextEntry(entry.Name, jsonData)
 		if err != nil {
 			return fmt.Errorf("failed to add resource data to context: contextEntry: %v, error: %v", entry, err)
 		}
@@ -254,7 +254,7 @@ func loadAPIData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyCo
 		return nil
 	}
 
-	path, err := variables.SubstituteAll(logger, ctx.JSONContext, entry.APICall.JMESPath)
+	path, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.APICall.JMESPath)
 	if err != nil {
 		return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.APICall.JMESPath, err)
 	}
@@ -269,7 +269,7 @@ func loadAPIData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyCo
 		return fmt.Errorf("failed to marshall data %v for context entry %v: %v", contextData, entry, err)
 	}
 
-	err = ctx.JSONContext.AddContextEntry(entry.Name, contextData)
+	err = ctx.jsonContext.AddContextEntry(entry.Name, contextData)
 	if err != nil {
 		return fmt.Errorf("failed to add JMESPath (%s) results to context, error: %v", entry.APICall.JMESPath, err)
 	}
@@ -301,7 +301,7 @@ func fetchAPIData(log logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyCont
 		return nil, fmt.Errorf("missing APICall in context entry %s %v", entry.Name, entry.APICall)
 	}
 
-	path, err := variables.SubstituteAll(log, ctx.JSONContext, entry.APICall.URLPath)
+	path, err := variables.SubstituteAll(log, ctx.jsonContext, entry.APICall.URLPath)
 	if err != nil {
 		return nil, fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.APICall.URLPath, err)
 	}
@@ -317,7 +317,7 @@ func fetchAPIData(log logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyCont
 }
 
 func getResource(ctx *PolicyContext, p string) ([]byte, error) {
-	return ctx.Client.RawAbsPath(context.TODO(), p)
+	return ctx.client.RawAbsPath(context.TODO(), p)
 }
 
 func loadConfigMap(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) error {
@@ -326,7 +326,7 @@ func loadConfigMap(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Policy
 		return fmt.Errorf("failed to retrieve config map for context entry %s: %v", entry.Name, err)
 	}
 
-	err = ctx.JSONContext.AddContextEntry(entry.Name, data)
+	err = ctx.jsonContext.AddContextEntry(entry.Name, data)
 	if err != nil {
 		return fmt.Errorf("failed to add config map for context entry %s: %v", entry.Name, err)
 	}
@@ -337,12 +337,12 @@ func loadConfigMap(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Policy
 func fetchConfigMap(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) ([]byte, error) {
 	contextData := make(map[string]interface{})
 
-	name, err := variables.SubstituteAll(logger, ctx.JSONContext, entry.ConfigMap.Name)
+	name, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.ConfigMap.Name)
 	if err != nil {
 		return nil, fmt.Errorf("failed to substitute variables in context %s configMap.name %s: %v", entry.Name, entry.ConfigMap.Name, err)
 	}
 
-	namespace, err := variables.SubstituteAll(logger, ctx.JSONContext, entry.ConfigMap.Namespace)
+	namespace, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.ConfigMap.Namespace)
 	if err != nil {
 		return nil, fmt.Errorf("failed to substitute variables in context %s configMap.namespace %s: %v", entry.Name, entry.ConfigMap.Namespace, err)
 	}
@@ -351,7 +351,7 @@ func fetchConfigMap(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Polic
 		namespace = "default"
 	}
 
-	obj, err := ctx.Client.GetResource(context.TODO(), "v1", "ConfigMap", namespace.(string), name.(string))
+	obj, err := ctx.client.GetResource(context.TODO(), "v1", "ConfigMap", namespace.(string), name.(string))
 	if err != nil {
 		return nil, fmt.Errorf("failed to get configmap %s/%s : %v", namespace, name, err)
 	}
diff --git a/pkg/engine/k8smanifest.go b/pkg/engine/k8smanifest.go
index 5c8ce01131..086d091464 100644
--- a/pkg/engine/k8smanifest.go
+++ b/pkg/engine/k8smanifest.go
@@ -58,7 +58,7 @@ func handleVerifyManifest(ctx *PolicyContext, rule *kyvernov1.Rule, logger logr.
 
 func verifyManifest(policyContext *PolicyContext, verifyRule kyvernov1.Manifests, logger logr.Logger) (bool, string, error) {
 	// load AdmissionRequest
-	request, err := policyContext.JSONContext.Query("request")
+	request, err := policyContext.jsonContext.Query("request")
 	if err != nil {
 		return false, "", errors.Wrapf(err, "failed to get a request from policyContext")
 	}
@@ -106,7 +106,7 @@ func verifyManifest(policyContext *PolicyContext, verifyRule kyvernov1.Manifests
 	}
 	if !vo.DisableDryRun {
 		// check if kyverno can 'create' dryrun resource
-		ok, err := checkDryRunPermission(policyContext.Client, adreq.Kind.Kind, vo.DryRunNamespace)
+		ok, err := checkDryRunPermission(policyContext.client, adreq.Kind.Kind, vo.DryRunNamespace)
 		if err != nil {
 			logger.V(1).Info("failed to check permissions to 'create' resource. disabled DryRun option.", "dryrun namespace", vo.DryRunNamespace, "kind", adreq.Kind.Kind, "error", err.Error())
 			vo.DisableDryRun = true
diff --git a/pkg/engine/k8smanifest_test.go b/pkg/engine/k8smanifest_test.go
index 3f0e29da27..2e3a85ac54 100644
--- a/pkg/engine/k8smanifest_test.go
+++ b/pkg/engine/k8smanifest_test.go
@@ -614,8 +614,8 @@ func Test_VerifyManifest_SignedYAML(t *testing.T) {
 	policyContext := buildContext(t, test_policy, signed_resource, "")
 	var request *v1.AdmissionRequest
 	_ = json.Unmarshal([]byte(signed_adreq), &request)
-	policyContext.JSONContext.AddRequest(request)
-	policyContext.Policy.SetName("test-policy")
+	policyContext.jsonContext.AddRequest(request)
+	policyContext.policy.SetName("test-policy")
 	verifyRule := kyvernov1.Manifests{}
 	verifyRule.Attestors = append(verifyRule.Attestors, kyvernov1.AttestorSet{
 		Entries: []kyvernov1.Attestor{
@@ -636,8 +636,8 @@ func Test_VerifyManifest_UnsignedYAML(t *testing.T) {
 	policyContext := buildContext(t, test_policy, unsigned_resource, "")
 	var request *v1.AdmissionRequest
 	_ = json.Unmarshal([]byte(unsigned_adreq), &request)
-	policyContext.JSONContext.AddRequest(request)
-	policyContext.Policy.SetName("test-policy")
+	policyContext.jsonContext.AddRequest(request)
+	policyContext.policy.SetName("test-policy")
 	verifyRule := kyvernov1.Manifests{}
 	verifyRule.Attestors = append(verifyRule.Attestors, kyvernov1.AttestorSet{
 		Entries: []kyvernov1.Attestor{
@@ -658,8 +658,8 @@ func Test_VerifyManifest_InvalidYAML(t *testing.T) {
 	policyContext := buildContext(t, test_policy, invalid_resource, "")
 	var request *v1.AdmissionRequest
 	_ = json.Unmarshal([]byte(invalid_adreq), &request)
-	policyContext.JSONContext.AddRequest(request)
-	policyContext.Policy.SetName("test-policy")
+	policyContext.jsonContext.AddRequest(request)
+	policyContext.policy.SetName("test-policy")
 	verifyRule := kyvernov1.Manifests{}
 	verifyRule.Attestors = append(verifyRule.Attestors, kyvernov1.AttestorSet{
 		Entries: []kyvernov1.Attestor{
@@ -680,8 +680,8 @@ func Test_VerifyManifest_MustAll_InvalidYAML(t *testing.T) {
 	policyContext := buildContext(t, test_policy, multi_sig_resource, "")
 	var request *v1.AdmissionRequest
 	_ = json.Unmarshal([]byte(multi_sig_adreq), &request)
-	policyContext.JSONContext.AddRequest(request)
-	policyContext.Policy.SetName("test-policy")
+	policyContext.jsonContext.AddRequest(request)
+	policyContext.policy.SetName("test-policy")
 	verifyRule := kyvernov1.Manifests{}
 	verifyRule.Attestors = append(verifyRule.Attestors, kyvernov1.AttestorSet{
 		Entries: []kyvernov1.Attestor{
@@ -708,8 +708,8 @@ func Test_VerifyManifest_MustAll_ValidYAML(t *testing.T) {
 	policyContext := buildContext(t, test_policy, multi_sig2_resource, "")
 	var request *v1.AdmissionRequest
 	_ = json.Unmarshal([]byte(multi_sig2_adreq), &request)
-	policyContext.JSONContext.AddRequest(request)
-	policyContext.Policy.SetName("test-policy")
+	policyContext.jsonContext.AddRequest(request)
+	policyContext.policy.SetName("test-policy")
 	verifyRule := kyvernov1.Manifests{}
 	count := 3
 	verifyRule.Attestors = append(verifyRule.Attestors, kyvernov1.AttestorSet{
@@ -740,8 +740,8 @@ func Test_VerifyManifest_AtLeastOne(t *testing.T) {
 	policyContext := buildContext(t, test_policy, multi_sig_resource, "")
 	var request *v1.AdmissionRequest
 	_ = json.Unmarshal([]byte(multi_sig_adreq), &request)
-	policyContext.JSONContext.AddRequest(request)
-	policyContext.Policy.SetName("test-policy")
+	policyContext.jsonContext.AddRequest(request)
+	policyContext.policy.SetName("test-policy")
 	verifyRule := kyvernov1.Manifests{}
 	count := 1
 	verifyRule.Attestors = append(verifyRule.Attestors, kyvernov1.AttestorSet{
diff --git a/pkg/engine/loadtargets.go b/pkg/engine/loadtargets.go
index b4015344f6..94ed0b49f3 100644
--- a/pkg/engine/loadtargets.go
+++ b/pkg/engine/loadtargets.go
@@ -36,22 +36,22 @@ func loadTargets(targets []kyvernov1.ResourceSpec, ctx *PolicyContext, logger lo
 }
 
 func resolveSpec(i int, target kyvernov1.ResourceSpec, ctx *PolicyContext, logger logr.Logger) (kyvernov1.ResourceSpec, error) {
-	kind, err := variables.SubstituteAll(logger, ctx.JSONContext, target.Kind)
+	kind, err := variables.SubstituteAll(logger, ctx.jsonContext, target.Kind)
 	if err != nil {
 		return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Kind %s: %v", i, target.Kind, err)
 	}
 
-	apiversion, err := variables.SubstituteAll(logger, ctx.JSONContext, target.APIVersion)
+	apiversion, err := variables.SubstituteAll(logger, ctx.jsonContext, target.APIVersion)
 	if err != nil {
 		return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].APIVersion %s: %v", i, target.APIVersion, err)
 	}
 
-	namespace, err := variables.SubstituteAll(logger, ctx.JSONContext, target.Namespace)
+	namespace, err := variables.SubstituteAll(logger, ctx.jsonContext, target.Namespace)
 	if err != nil {
 		return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Namespace %s: %v", i, target.Namespace, err)
 	}
 
-	name, err := variables.SubstituteAll(logger, ctx.JSONContext, target.Name)
+	name, err := variables.SubstituteAll(logger, ctx.jsonContext, target.Name)
 	if err != nil {
 		return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Name %s: %v", i, target.Name, err)
 	}
@@ -70,13 +70,13 @@ func getTargets(target kyvernov1.ResourceSpec, ctx *PolicyContext, logger logr.L
 	name := target.Name
 
 	// if it's namespaced policy, targets has to be loaded only from the policy's namespace
-	if ctx.Policy.IsNamespaced() {
-		namespace = ctx.Policy.GetNamespace()
+	if ctx.policy.IsNamespaced() {
+		namespace = ctx.policy.GetNamespace()
 	}
 
 	if namespace != "" && name != "" &&
 		!wildcard.ContainsWildcard(namespace) && !wildcard.ContainsWildcard(name) {
-		obj, err := ctx.Client.GetResource(context.TODO(), target.APIVersion, target.Kind, namespace, name)
+		obj, err := ctx.client.GetResource(context.TODO(), target.APIVersion, target.Kind, namespace, name)
 		if err != nil {
 			return nil, fmt.Errorf("failed to get target %s/%s %s/%s : %v", target.APIVersion, target.Kind, namespace, name, err)
 		}
@@ -85,7 +85,7 @@ func getTargets(target kyvernov1.ResourceSpec, ctx *PolicyContext, logger logr.L
 	}
 
 	// list all targets if wildcard is specified
-	objList, err := ctx.Client.ListResource(context.TODO(), target.APIVersion, target.Kind, "", nil)
+	objList, err := ctx.client.ListResource(context.TODO(), target.APIVersion, target.Kind, "", nil)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go
index fcf733f21c..8f53219abd 100644
--- a/pkg/engine/mutation.go
+++ b/pkg/engine/mutation.go
@@ -19,12 +19,12 @@ import (
 // Mutate performs mutation. Overlay first and then mutation patches
 func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
 	startTime := time.Now()
-	policy := policyContext.Policy
+	policy := policyContext.policy
 	resp = &response.EngineResponse{
 		Policy: policy,
 	}
-	matchedResource := policyContext.NewResource
-	ctx := policyContext.JSONContext
+	matchedResource := policyContext.newResource
+	ctx := policyContext.jsonContext
 	var skippedRules []string
 
 	logger := logging.WithName("EngineMutate").WithValues("policy", policy.GetName(), "kind", matchedResource.GetKind(),
@@ -35,8 +35,8 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
 	startMutateResultResponse(resp, policy, matchedResource)
 	defer endMutateResultResponse(logger, resp, startTime)
 
-	policyContext.JSONContext.Checkpoint()
-	defer policyContext.JSONContext.Restore()
+	policyContext.jsonContext.Checkpoint()
+	defer policyContext.jsonContext.Restore()
 
 	var err error
 	applyRules := policy.GetSpec().GetApplyRules()
@@ -48,19 +48,19 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
 
 		logger := logger.WithValues("rule", rule.Name)
 		var excludeResource []string
-		if len(policyContext.ExcludeGroupRole) > 0 {
-			excludeResource = policyContext.ExcludeGroupRole
+		if len(policyContext.excludeGroupRole) > 0 {
+			excludeResource = policyContext.excludeGroupRole
 		}
 
-		if err = MatchesResourceDescription(matchedResource, rule, policyContext.AdmissionInfo, excludeResource, policyContext.NamespaceLabels, policyContext.Policy.GetNamespace()); err != nil {
+		if err = MatchesResourceDescription(matchedResource, rule, policyContext.admissionInfo, excludeResource, policyContext.namespaceLabels, policyContext.policy.GetNamespace()); err != nil {
 			logger.V(4).Info("rule not matched", "reason", err.Error())
 			skippedRules = append(skippedRules, rule.Name)
 			continue
 		}
 
 		logger.V(3).Info("processing mutate rule", "applyRules", applyRules)
-		resource, err := policyContext.JSONContext.Query("request.object")
-		policyContext.JSONContext.Reset()
+		resource, err := policyContext.jsonContext.Query("request.object")
+		policyContext.jsonContext.Reset()
 		if err == nil && resource != nil {
 			if err := ctx.AddResource(resource.(map[string]interface{})); err != nil {
 				logger.Error(err, "unable to update resource object")
@@ -80,7 +80,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
 
 		ruleCopy := rule.DeepCopy()
 		var patchedResources []unstructured.Unstructured
-		if !policyContext.AdmissionOperation && rule.IsMutateExisting() {
+		if !policyContext.admissionOperation && rule.IsMutateExisting() {
 			targets, err := loadTargets(ruleCopy.Mutation.Targets, policyContext, logger)
 			if err != nil {
 				rr := ruleResponse(rule, response.Mutation, err.Error(), response.RuleStatusError, nil)
@@ -97,9 +97,9 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
 				continue
 			}
 
-			if !policyContext.AdmissionOperation && rule.IsMutateExisting() {
+			if !policyContext.admissionOperation && rule.IsMutateExisting() {
 				policyContext := policyContext.Copy()
-				if err := policyContext.JSONContext.AddTargetResource(patchedResource.Object); err != nil {
+				if err := policyContext.jsonContext.AddTargetResource(patchedResource.Object); err != nil {
 					logging.Error(err, "failed to add target resource to the context")
 					continue
 				}
@@ -153,7 +153,7 @@ func mutateResource(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructu
 		return ruleResponse(*rule, response.Mutation, "preconditions not met", response.RuleStatusSkip, &resource), resource
 	}
 
-	mutateResp := mutate.Mutate(rule, ctx.JSONContext, resource, logger)
+	mutateResp := mutate.Mutate(rule, ctx.jsonContext, resource, logger)
 	ruleResp := buildRuleResponse(rule, mutateResp, &mutateResp.PatchedResource)
 	return ruleResp, mutateResp.PatchedResource
 }
@@ -183,7 +183,7 @@ func mutateForEach(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructur
 			return ruleResponse(*rule, response.Mutation, "preconditions not met", response.RuleStatusSkip, &patchedResource), resource
 		}
 
-		elements, err := evaluateList(foreach.List, ctx.JSONContext)
+		elements, err := evaluateList(foreach.List, ctx.jsonContext)
 		if err != nil {
 			msg := fmt.Sprintf("failed to evaluate list %s", foreach.List)
 			return ruleError(rule, response.Mutation, msg, err), resource
@@ -214,8 +214,8 @@ func mutateForEach(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructur
 }
 
 func mutateElements(name string, foreach kyvernov1.ForEachMutation, ctx *PolicyContext, elements []interface{}, resource unstructured.Unstructured, logger logr.Logger) *mutate.Response {
-	ctx.JSONContext.Checkpoint()
-	defer ctx.JSONContext.Restore()
+	ctx.jsonContext.Checkpoint()
+	defer ctx.jsonContext.Restore()
 
 	patchedResource := resource
 	var allPatches [][]byte
@@ -227,7 +227,7 @@ func mutateElements(name string, foreach kyvernov1.ForEachMutation, ctx *PolicyC
 		if e == nil {
 			continue
 		}
-		ctx.JSONContext.Reset()
+		ctx.jsonContext.Reset()
 		ctx := ctx.Copy()
 		store.SetForeachElement(i)
 		falseVar := false
@@ -249,7 +249,7 @@ func mutateElements(name string, foreach kyvernov1.ForEachMutation, ctx *PolicyC
 			continue
 		}
 
-		mutateResp := mutate.ForEach(name, foreach, ctx.JSONContext, patchedResource, logger)
+		mutateResp := mutate.ForEach(name, foreach, ctx.jsonContext, patchedResource, logger)
 		if mutateResp.Status == response.RuleStatusFail || mutateResp.Status == response.RuleStatusError {
 			return mutateResp
 		}
diff --git a/pkg/engine/mutation_test.go b/pkg/engine/mutation_test.go
index f9e476f6f8..2f18417bfc 100644
--- a/pkg/engine/mutation_test.go
+++ b/pkg/engine/mutation_test.go
@@ -90,9 +90,9 @@ func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) {
 		t.Error(err)
 	}
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured}
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured}
 	er := Mutate(policyContext)
 	t.Log(string(expectedPatch))
 
@@ -163,9 +163,9 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured}
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured}
 	er := Mutate(policyContext)
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message, "Unknown key \"name1\" in path"))
@@ -258,9 +258,9 @@ func Test_variableSubstitutionCLI(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured,
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured,
 	}
 
 	er := Mutate(policyContext)
@@ -361,9 +361,9 @@ func Test_chained_rules(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resource,
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resource,
 	}
 
 	err = ctx.AddImageInfos(resource)
@@ -455,9 +455,9 @@ func Test_precondition(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured,
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured,
 	}
 
 	er := Mutate(policyContext)
@@ -552,9 +552,9 @@ func Test_nonZeroIndexNumberPatchesJson6902(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured,
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured,
 	}
 
 	er := Mutate(policyContext)
@@ -640,9 +640,9 @@ func Test_foreach(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resource,
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resource,
 	}
 
 	err = ctx.AddImageInfos(resource)
@@ -747,9 +747,9 @@ func Test_foreach_element_mutation(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resource,
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resource,
 	}
 
 	err = ctx.AddImageInfos(resource)
@@ -873,9 +873,9 @@ func Test_Container_InitContainer_foreach(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resource,
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resource,
 	}
 
 	err = ctx.AddImageInfos(resource)
@@ -1000,9 +1000,9 @@ func Test_foreach_order_mutation_(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resource,
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resource,
 	}
 
 	err = ctx.AddImageInfos(resource)
@@ -1451,10 +1451,10 @@ func Test_mutate_existing_resources(t *testing.T) {
 			assert.NilError(t, err)
 
 			policyContext = &PolicyContext{
-				Client:      dclient,
-				Policy:      &policy,
-				JSONContext: ctx,
-				NewResource: *trigger,
+				client:      dclient,
+				policy:      &policy,
+				jsonContext: ctx,
+				newResource: *trigger,
 			}
 		}
 		er := Mutate(policyContext)
@@ -1560,9 +1560,9 @@ func Test_RuleSelectorMutate(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured,
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured,
 	}
 
 	er := Mutate(policyContext)
@@ -1578,7 +1578,7 @@ func Test_RuleSelectorMutate(t *testing.T) {
 	}
 
 	applyOne := kyverno.ApplyOne
-	policyContext.Policy.GetSpec().ApplyRules = &applyOne
+	policyContext.policy.GetSpec().ApplyRules = &applyOne
 
 	er = Mutate(policyContext)
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
@@ -1941,9 +1941,9 @@ func Test_SpecialCharacters(t *testing.T) {
 
 			// Create policy context.
 			policyContext := &PolicyContext{
-				Policy:      &policy,
-				JSONContext: ctx,
-				NewResource: *resource,
+				policy:      &policy,
+				jsonContext: ctx,
+				newResource: *resource,
 			}
 
 			// Mutate and make sure that we got the expected amount of rules.
diff --git a/pkg/engine/policyContext.go b/pkg/engine/policyContext.go
index 9e22d57290..fcb3e76306 100644
--- a/pkg/engine/policyContext.go
+++ b/pkg/engine/policyContext.go
@@ -4,55 +4,196 @@ import (
 	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
 	kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
 	"github.com/kyverno/kyverno/pkg/clients/dclient"
+	"github.com/kyverno/kyverno/pkg/config"
 	"github.com/kyverno/kyverno/pkg/engine/context"
+	enginectx "github.com/kyverno/kyverno/pkg/engine/context"
+	"github.com/kyverno/kyverno/pkg/utils"
+	"github.com/pkg/errors"
+	admissionv1 "k8s.io/api/admission/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
 
+// ExcludeFunc is a function used to determine if a resource is excluded
+type ExcludeFunc = func(kind, namespace, name string) bool
+
 // PolicyContext contains the contexts for engine to process
 type PolicyContext struct {
-	// Policy is the policy to be processed
-	Policy kyvernov1.PolicyInterface
+	// policy is the policy to be processed
+	policy kyvernov1.PolicyInterface
 
-	// NewResource is the resource to be processed
-	NewResource unstructured.Unstructured
+	// newResource is the resource to be processed
+	newResource unstructured.Unstructured
 
-	// OldResource is the prior resource for an update, or nil
-	OldResource unstructured.Unstructured
+	// oldResource is the prior resource for an update, or nil
+	oldResource unstructured.Unstructured
 
-	// Element is set when the context is used for processing a foreach loop
-	Element unstructured.Unstructured
+	// element is set when the context is used for processing a foreach loop
+	element unstructured.Unstructured
 
-	// AdmissionInfo contains the admission request information
-	AdmissionInfo kyvernov1beta1.RequestInfo
+	// admissionInfo contains the admission request information
+	admissionInfo kyvernov1beta1.RequestInfo
 
 	// Dynamic client - used for api lookups
-	Client dclient.Interface
+	client dclient.Interface
 
 	// Config handler
-	ExcludeGroupRole []string
+	excludeGroupRole []string
 
-	ExcludeResourceFunc func(kind, namespace, name string) bool
+	excludeResourceFunc ExcludeFunc
 
-	// JSONContext is the variable context
-	JSONContext context.Interface
+	// jsonContext is the variable context
+	jsonContext context.Interface
 
-	// NamespaceLabels stores the label of namespace to be processed by namespace selector
-	NamespaceLabels map[string]string
+	// namespaceLabels stores the label of namespace to be processed by namespace selector
+	namespaceLabels map[string]string
 
-	// AdmissionOperation represents if the caller is from the webhook server
-	AdmissionOperation bool
+	// admissionOperation represents if the caller is from the webhook server
+	admissionOperation bool
 }
 
-func (pc *PolicyContext) Copy() *PolicyContext {
+// Getters
+
+func (c *PolicyContext) Policy() kyvernov1.PolicyInterface {
+	return c.policy
+}
+
+func (c *PolicyContext) NewResource() unstructured.Unstructured {
+	return c.newResource
+}
+
+func (c *PolicyContext) OldResource() unstructured.Unstructured {
+	return c.oldResource
+}
+
+func (c *PolicyContext) AdmissionInfo() kyvernov1beta1.RequestInfo {
+	return c.admissionInfo
+}
+
+func (c *PolicyContext) JSONContext() context.Interface {
+	return c.jsonContext
+}
+
+// Mutators
+
+func (c *PolicyContext) WithPolicy(policy kyvernov1.PolicyInterface) *PolicyContext {
+	copy := c.Copy()
+	copy.policy = policy
+	return copy
+}
+
+func (c *PolicyContext) WithNamespaceLabels(namespaceLabels map[string]string) *PolicyContext {
+	copy := c.Copy()
+	copy.namespaceLabels = namespaceLabels
+	return copy
+}
+
+func (c *PolicyContext) WithAdmissionInfo(admissionInfo kyvernov1beta1.RequestInfo) *PolicyContext {
+	copy := c.Copy()
+	copy.admissionInfo = admissionInfo
+	return copy
+}
+
+func (c *PolicyContext) WithNewResource(resource unstructured.Unstructured) *PolicyContext {
+	copy := c.Copy()
+	copy.newResource = resource
+	return copy
+}
+
+func (c *PolicyContext) WithOldResource(resource unstructured.Unstructured) *PolicyContext {
+	copy := c.Copy()
+	copy.oldResource = resource
+	return copy
+}
+
+func (c *PolicyContext) WithResources(newResource unstructured.Unstructured, oldResource unstructured.Unstructured) *PolicyContext {
+	return c.WithNewResource(newResource).WithOldResource(oldResource)
+}
+
+func (c *PolicyContext) WithClient(client dclient.Interface) *PolicyContext {
+	copy := c.Copy()
+	copy.client = client
+	return copy
+}
+
+func (c *PolicyContext) WithExcludeGroupRole(excludeGroupRole ...string) *PolicyContext {
+	copy := c.Copy()
+	copy.excludeGroupRole = excludeGroupRole
+	return copy
+}
+
+func (c *PolicyContext) WithExcludeResourceFunc(excludeResourceFunc ExcludeFunc) *PolicyContext {
+	copy := c.Copy()
+	copy.excludeResourceFunc = excludeResourceFunc
+	return copy
+}
+
+func (c *PolicyContext) WithConfiguration(configuration config.Configuration) *PolicyContext {
+	return c.WithExcludeResourceFunc(configuration.ToFilter).WithExcludeGroupRole(configuration.GetExcludeGroupRole()...)
+}
+
+func (c *PolicyContext) WithAdmissionOperation(admissionOperation bool) *PolicyContext {
+	copy := c.Copy()
+	copy.admissionOperation = admissionOperation
+	return copy
+}
+
+// Constructors
+
+func NewPolicyContextWithJsonContext(jsonContext context.Interface) *PolicyContext {
 	return &PolicyContext{
-		Policy:              pc.Policy,
-		NewResource:         pc.NewResource,
-		OldResource:         pc.OldResource,
-		AdmissionInfo:       pc.AdmissionInfo,
-		Client:              pc.Client,
-		ExcludeGroupRole:    pc.ExcludeGroupRole,
-		ExcludeResourceFunc: pc.ExcludeResourceFunc,
-		JSONContext:         pc.JSONContext,
-		NamespaceLabels:     pc.NamespaceLabels,
+		jsonContext:      jsonContext,
+		excludeGroupRole: []string{},
+		excludeResourceFunc: func(string, string, string) bool {
+			return false
+		},
 	}
 }
+
+func NewPolicyContext() *PolicyContext {
+	return NewPolicyContextWithJsonContext(context.NewContext())
+}
+
+func NewPolicyContextFromAdmissionRequest(
+	request *admissionv1.AdmissionRequest,
+	admissionInfo kyvernov1beta1.RequestInfo,
+	configuration config.Configuration,
+	client dclient.Interface,
+) (*PolicyContext, error) {
+	ctx, err := newVariablesContext(request, &admissionInfo)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to create policy rule context")
+	}
+	newResource, oldResource, err := utils.ExtractResources(nil, request)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to parse resource")
+	}
+	if err := ctx.AddImageInfos(&newResource); err != nil {
+		return nil, errors.Wrap(err, "failed to add image information to the policy rule context")
+	}
+	policyContext := NewPolicyContextWithJsonContext(ctx).
+		WithNewResource(newResource).
+		WithOldResource(oldResource).
+		WithAdmissionInfo(admissionInfo).
+		WithConfiguration(configuration).
+		WithClient(client).
+		WithAdmissionOperation(true)
+	return policyContext, nil
+}
+
+func (c PolicyContext) Copy() *PolicyContext {
+	return &c
+}
+
+func newVariablesContext(request *admissionv1.AdmissionRequest, userRequestInfo *kyvernov1beta1.RequestInfo) (enginectx.Interface, error) {
+	ctx := enginectx.NewContext()
+	if err := ctx.AddRequest(request); err != nil {
+		return nil, errors.Wrap(err, "failed to load incoming request in context")
+	}
+	if err := ctx.AddUserInfo(*userRequestInfo); err != nil {
+		return nil, errors.Wrap(err, "failed to load userInfo in context")
+	}
+	if err := ctx.AddServiceAccount(userRequestInfo.AdmissionUserInfo.Username); err != nil {
+		return nil, errors.Wrap(err, "failed to load service account in context")
+	}
+	return ctx, nil
+}
diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go
index d9f99b577d..ec1a1eea0f 100644
--- a/pkg/engine/utils.go
+++ b/pkg/engine/utils.go
@@ -426,7 +426,7 @@ func ManagedPodResource(policy kyvernov1.PolicyInterface, resource unstructured.
 }
 
 func checkPreconditions(logger logr.Logger, ctx *PolicyContext, anyAllConditions apiextensions.JSON) (bool, error) {
-	preconditions, err := variables.SubstituteAllInPreconditions(logger, ctx.JSONContext, anyAllConditions)
+	preconditions, err := variables.SubstituteAllInPreconditions(logger, ctx.jsonContext, anyAllConditions)
 	if err != nil {
 		return false, errors.Wrapf(err, "failed to substitute variables in preconditions")
 	}
@@ -436,7 +436,7 @@ func checkPreconditions(logger logr.Logger, ctx *PolicyContext, anyAllConditions
 		return false, errors.Wrapf(err, "failed to parse preconditions")
 	}
 
-	pass := variables.EvaluateConditions(logger, ctx.JSONContext, typeConditions)
+	pass := variables.EvaluateConditions(logger, ctx.jsonContext, typeConditions)
 	return pass, nil
 }
 
diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go
index 25f78a8dee..ae03700af8 100644
--- a/pkg/engine/validation.go
+++ b/pkg/engine/validation.go
@@ -46,11 +46,11 @@ func Validate(policyContext *PolicyContext) (resp *response.EngineResponse) {
 }
 
 func buildLogger(ctx *PolicyContext) logr.Logger {
-	logger := logging.WithName("EngineValidate").WithValues("policy", ctx.Policy.GetName())
-	if reflect.DeepEqual(ctx.NewResource, unstructured.Unstructured{}) {
-		logger = logger.WithValues("kind", ctx.OldResource.GetKind(), "namespace", ctx.OldResource.GetNamespace(), "name", ctx.OldResource.GetName())
+	logger := logging.WithName("EngineValidate").WithValues("policy", ctx.policy.GetName())
+	if reflect.DeepEqual(ctx.newResource, unstructured.Unstructured{}) {
+		logger = logger.WithValues("kind", ctx.oldResource.GetKind(), "namespace", ctx.oldResource.GetNamespace(), "name", ctx.oldResource.GetName())
 	} else {
-		logger = logger.WithValues("kind", ctx.NewResource.GetKind(), "namespace", ctx.NewResource.GetNamespace(), "name", ctx.NewResource.GetName())
+		logger = logger.WithValues("kind", ctx.newResource.GetKind(), "namespace", ctx.newResource.GetNamespace(), "name", ctx.newResource.GetName())
 	}
 
 	return logger
@@ -63,24 +63,24 @@ func buildResponse(ctx *PolicyContext, resp *response.EngineResponse, startTime
 
 	if reflect.DeepEqual(resp.PatchedResource, unstructured.Unstructured{}) {
 		// for delete requests patched resource will be oldResource since newResource is empty
-		resource := ctx.NewResource
-		if reflect.DeepEqual(ctx.NewResource, unstructured.Unstructured{}) {
-			resource = ctx.OldResource
+		resource := ctx.newResource
+		if reflect.DeepEqual(ctx.newResource, unstructured.Unstructured{}) {
+			resource = ctx.oldResource
 		}
 
 		resp.PatchedResource = resource
 	}
 
-	resp.Policy = ctx.Policy
-	resp.PolicyResponse.Policy.Name = ctx.Policy.GetName()
-	resp.PolicyResponse.Policy.Namespace = ctx.Policy.GetNamespace()
+	resp.Policy = ctx.policy
+	resp.PolicyResponse.Policy.Name = ctx.policy.GetName()
+	resp.PolicyResponse.Policy.Namespace = ctx.policy.GetNamespace()
 	resp.PolicyResponse.Resource.Name = resp.PatchedResource.GetName()
 	resp.PolicyResponse.Resource.Namespace = resp.PatchedResource.GetNamespace()
 	resp.PolicyResponse.Resource.Kind = resp.PatchedResource.GetKind()
 	resp.PolicyResponse.Resource.APIVersion = resp.PatchedResource.GetAPIVersion()
-	resp.PolicyResponse.ValidationFailureAction = ctx.Policy.GetSpec().ValidationFailureAction
+	resp.PolicyResponse.ValidationFailureAction = ctx.policy.GetSpec().ValidationFailureAction
 
-	for _, v := range ctx.Policy.GetSpec().ValidationFailureActionOverrides {
+	for _, v := range ctx.policy.GetSpec().ValidationFailureActionOverrides {
 		resp.PolicyResponse.ValidationFailureActionOverrides = append(resp.PolicyResponse.ValidationFailureActionOverrides, response.ValidationFailureActionOverride{Action: v.Action, Namespaces: v.Namespaces})
 	}
 
@@ -91,19 +91,19 @@ func buildResponse(ctx *PolicyContext, resp *response.EngineResponse, startTime
 func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineResponse {
 	resp := &response.EngineResponse{}
 
-	ctx.JSONContext.Checkpoint()
-	defer ctx.JSONContext.Restore()
+	ctx.jsonContext.Checkpoint()
+	defer ctx.jsonContext.Restore()
 
-	rules := autogen.ComputeRules(ctx.Policy)
+	rules := autogen.ComputeRules(ctx.policy)
 	matchCount := 0
-	applyRules := ctx.Policy.GetSpec().GetApplyRules()
+	applyRules := ctx.policy.GetSpec().GetApplyRules()
 
-	if ctx.Policy.IsNamespaced() {
-		polNs := ctx.Policy.GetNamespace()
-		if ctx.NewResource.Object != nil && (ctx.NewResource.GetNamespace() != polNs || ctx.NewResource.GetNamespace() == "") {
+	if ctx.policy.IsNamespaced() {
+		polNs := ctx.policy.GetNamespace()
+		if ctx.newResource.Object != nil && (ctx.newResource.GetNamespace() != polNs || ctx.newResource.GetNamespace() == "") {
 			return resp
 		}
-		if ctx.OldResource.Object != nil && (ctx.OldResource.GetNamespace() != polNs || ctx.OldResource.GetNamespace() == "") {
+		if ctx.oldResource.Object != nil && (ctx.oldResource.GetNamespace() != polNs || ctx.oldResource.GetNamespace() == "") {
 			return resp
 		}
 	}
@@ -123,7 +123,7 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
 		}
 
 		log.V(3).Info("processing validation rule", "matchCount", matchCount, "applyRules", applyRules)
-		ctx.JSONContext.Reset()
+		ctx.jsonContext.Reset()
 		startTime := time.Now()
 
 		var ruleResp *response.RuleResponse
@@ -148,14 +148,14 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
 
 func validateOldObject(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) (*response.RuleResponse, error) {
 	ctxCopy := ctx.Copy()
-	ctxCopy.NewResource = *ctxCopy.OldResource.DeepCopy()
-	ctxCopy.OldResource = unstructured.Unstructured{}
+	ctxCopy.newResource = *ctxCopy.oldResource.DeepCopy()
+	ctxCopy.oldResource = unstructured.Unstructured{}
 
-	if err := context.ReplaceResource(ctxCopy.JSONContext, ctxCopy.NewResource.Object); err != nil {
+	if err := context.ReplaceResource(ctxCopy.jsonContext, ctxCopy.newResource.Object); err != nil {
 		return nil, errors.Wrapf(err, "failed to replace object in the JSON context")
 	}
 
-	if err := context.ReplaceOldResource(ctxCopy.JSONContext, ctxCopy.OldResource.Object); err != nil {
+	if err := context.ReplaceOldResource(ctxCopy.jsonContext, ctxCopy.oldResource.Object); err != nil {
 		return nil, errors.Wrapf(err, "failed to replace old object in the JSON context")
 	}
 
@@ -300,7 +300,7 @@ func (v *validator) validateForEach() *response.RuleResponse {
 	}
 
 	for _, foreach := range foreachList {
-		elements, err := evaluateList(foreach.List, v.ctx.JSONContext)
+		elements, err := evaluateList(foreach.List, v.ctx.jsonContext)
 		if err != nil {
 			v.log.V(2).Info("failed to evaluate list", "list", foreach.List, "error", err.Error())
 			continue
@@ -321,8 +321,8 @@ func (v *validator) validateForEach() *response.RuleResponse {
 }
 
 func (v *validator) validateElements(foreach kyvernov1.ForEachValidation, elements []interface{}, elementScope *bool) (*response.RuleResponse, int) {
-	v.ctx.JSONContext.Checkpoint()
-	defer v.ctx.JSONContext.Restore()
+	v.ctx.jsonContext.Checkpoint()
+	defer v.ctx.jsonContext.Restore()
 	applyCount := 0
 
 	for i, e := range elements {
@@ -330,7 +330,7 @@ func (v *validator) validateElements(foreach kyvernov1.ForEachValidation, elemen
 			continue
 		}
 		store.SetForeachElement(i)
-		v.ctx.JSONContext.Reset()
+		v.ctx.jsonContext.Reset()
 
 		ctx := v.ctx.Copy()
 		if err := addElementToContext(ctx, e, i, elementScope); err != nil {
@@ -369,7 +369,7 @@ func addElementToContext(ctx *PolicyContext, e interface{}, elementIndex int, el
 	if err != nil {
 		return err
 	}
-	if err := ctx.JSONContext.AddElement(data, elementIndex); err != nil {
+	if err := ctx.jsonContext.AddElement(data, elementIndex); err != nil {
 		return errors.Wrapf(err, "failed to add element (%v) to JSON context", e)
 	}
 	dataMap, ok := data.(map[string]interface{})
@@ -392,7 +392,7 @@ func addElementToContext(ctx *PolicyContext, e interface{}, elementIndex int, el
 	if scoped {
 		u := unstructured.Unstructured{}
 		u.SetUnstructuredContent(dataMap)
-		ctx.Element = u
+		ctx.element = u
 	}
 	return nil
 }
@@ -413,7 +413,7 @@ func (v *validator) loadContext() error {
 
 func (v *validator) validateDeny() *response.RuleResponse {
 	anyAllCond := v.deny.GetAnyAllConditions()
-	anyAllCond, err := variables.SubstituteAll(v.log, v.ctx.JSONContext, anyAllCond)
+	anyAllCond, err := variables.SubstituteAll(v.log, v.ctx.jsonContext, anyAllCond)
 	if err != nil {
 		return ruleError(v.rule, response.Validation, "failed to substitute variables in deny conditions", err)
 	}
@@ -427,7 +427,7 @@ func (v *validator) validateDeny() *response.RuleResponse {
 		return ruleError(v.rule, response.Validation, "invalid deny conditions", err)
 	}
 
-	deny := variables.EvaluateConditions(v.log, v.ctx.JSONContext, denyConditions)
+	deny := variables.EvaluateConditions(v.log, v.ctx.jsonContext, denyConditions)
 	if deny {
 		return ruleResponse(*v.rule, response.Validation, v.getDenyMessage(deny), response.RuleStatusFail, nil)
 	}
@@ -445,7 +445,7 @@ func (v *validator) getDenyMessage(deny bool) string {
 		return fmt.Sprintf("validation error: rule %s failed", v.rule.Name)
 	}
 
-	raw, err := variables.SubstituteAll(v.log, v.ctx.JSONContext, msg)
+	raw, err := variables.SubstituteAll(v.log, v.ctx.jsonContext, msg)
 	if err != nil {
 		return msg
 	}
@@ -454,12 +454,12 @@ func (v *validator) getDenyMessage(deny bool) string {
 }
 
 func getSpec(v *validator) (podSpec *corev1.PodSpec, metadata *metav1.ObjectMeta, err error) {
-	kind := v.ctx.NewResource.GetKind()
+	kind := v.ctx.newResource.GetKind()
 
 	if kind == "DaemonSet" || kind == "Deployment" || kind == "Job" || kind == "StatefulSet" || kind == "ReplicaSet" || kind == "ReplicationController" {
 		var deployment appsv1.Deployment
 
-		resourceBytes, err := v.ctx.NewResource.MarshalJSON()
+		resourceBytes, err := v.ctx.newResource.MarshalJSON()
 		if err != nil {
 			return nil, nil, err
 		}
@@ -473,7 +473,7 @@ func getSpec(v *validator) (podSpec *corev1.PodSpec, metadata *metav1.ObjectMeta
 	} else if kind == "CronJob" {
 		var cronJob batchv1.CronJob
 
-		resourceBytes, err := v.ctx.NewResource.MarshalJSON()
+		resourceBytes, err := v.ctx.newResource.MarshalJSON()
 		if err != nil {
 			return nil, nil, err
 		}
@@ -486,7 +486,7 @@ func getSpec(v *validator) (podSpec *corev1.PodSpec, metadata *metav1.ObjectMeta
 	} else if kind == "Pod" {
 		var pod corev1.Pod
 
-		resourceBytes, err := v.ctx.NewResource.MarshalJSON()
+		resourceBytes, err := v.ctx.newResource.MarshalJSON()
 		if err != nil {
 			return nil, nil, err
 		}
@@ -531,8 +531,8 @@ func (v *validator) validatePodSecurity() *response.RuleResponse {
 }
 
 func (v *validator) validateResourceWithRule() *response.RuleResponse {
-	if !isEmptyUnstructured(&v.ctx.Element) {
-		return v.validatePatterns(v.ctx.Element)
+	if !isEmptyUnstructured(&v.ctx.element) {
+		return v.validatePatterns(v.ctx.element)
 	}
 
 	if isDeleteRequest(v.ctx) {
@@ -540,18 +540,18 @@ func (v *validator) validateResourceWithRule() *response.RuleResponse {
 		return nil
 	}
 
-	resp := v.validatePatterns(v.ctx.NewResource)
+	resp := v.validatePatterns(v.ctx.newResource)
 	return resp
 }
 
 func isDeleteRequest(ctx *PolicyContext) bool {
 	// if the OldResource is not empty, and the NewResource is empty, the request is a DELETE
-	return isEmptyUnstructured(&ctx.NewResource)
+	return isEmptyUnstructured(&ctx.newResource)
 }
 
 func isUpdateRequest(ctx *PolicyContext) bool {
 	// is the OldObject and NewObject are available, the request is an UPDATE
-	return !isEmptyUnstructured(&ctx.OldResource) && !isEmptyUnstructured(&ctx.NewResource)
+	return !isEmptyUnstructured(&ctx.oldResource) && !isEmptyUnstructured(&ctx.newResource)
 }
 
 func isEmptyUnstructured(u *unstructured.Unstructured) bool {
@@ -568,13 +568,13 @@ func isEmptyUnstructured(u *unstructured.Unstructured) bool {
 
 // matches checks if either the new or old resource satisfies the filter conditions defined in the rule
 func matches(logger logr.Logger, rule *kyvernov1.Rule, ctx *PolicyContext) bool {
-	err := MatchesResourceDescription(ctx.NewResource, *rule, ctx.AdmissionInfo, ctx.ExcludeGroupRole, ctx.NamespaceLabels, "")
+	err := MatchesResourceDescription(ctx.newResource, *rule, ctx.admissionInfo, ctx.excludeGroupRole, ctx.namespaceLabels, "")
 	if err == nil {
 		return true
 	}
 
-	if !reflect.DeepEqual(ctx.OldResource, unstructured.Unstructured{}) {
-		err := MatchesResourceDescription(ctx.OldResource, *rule, ctx.AdmissionInfo, ctx.ExcludeGroupRole, ctx.NamespaceLabels, "")
+	if !reflect.DeepEqual(ctx.oldResource, unstructured.Unstructured{}) {
+		err := MatchesResourceDescription(ctx.oldResource, *rule, ctx.admissionInfo, ctx.excludeGroupRole, ctx.namespaceLabels, "")
 		if err == nil {
 			return true
 		}
@@ -718,7 +718,7 @@ func (v *validator) buildErrorMessage(err error, path string) string {
 		return fmt.Sprintf("validation error: rule %s execution error: %s", v.rule.Name, err.Error())
 	}
 
-	msgRaw, sErr := variables.SubstituteAll(v.log, v.ctx.JSONContext, v.rule.Validation.Message)
+	msgRaw, sErr := variables.SubstituteAll(v.log, v.ctx.jsonContext, v.rule.Validation.Message)
 	if sErr != nil {
 		v.log.V(2).Info("failed to substitute variables in message", "error", sErr)
 		return fmt.Sprintf("validation error: variables substitution error in rule %s execution error: %s", v.rule.Name, err.Error())
@@ -749,7 +749,7 @@ func buildAnyPatternErrorMessage(rule *kyvernov1.Rule, errors []string) string {
 
 func (v *validator) substitutePatterns() error {
 	if v.pattern != nil {
-		i, err := variables.SubstituteAll(v.log, v.ctx.JSONContext, v.pattern)
+		i, err := variables.SubstituteAll(v.log, v.ctx.jsonContext, v.pattern)
 		if err != nil {
 			return err
 		}
@@ -759,7 +759,7 @@ func (v *validator) substitutePatterns() error {
 	}
 
 	if v.anyPattern != nil {
-		i, err := variables.SubstituteAll(v.log, v.ctx.JSONContext, v.anyPattern)
+		i, err := variables.SubstituteAll(v.log, v.ctx.jsonContext, v.anyPattern)
 		if err != nil {
 			return err
 		}
@@ -776,7 +776,7 @@ func (v *validator) substituteDeny() error {
 		return nil
 	}
 
-	i, err := variables.SubstituteAll(v.log, v.ctx.JSONContext, v.deny)
+	i, err := variables.SubstituteAll(v.log, v.ctx.jsonContext, v.deny)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go
index 7a5e71d039..0437e2576c 100644
--- a/pkg/engine/validation_test.go
+++ b/pkg/engine/validation_test.go
@@ -131,7 +131,7 @@ func TestValidate_image_tag_fail(t *testing.T) {
 		"validation error: imagePullPolicy 'Always' required with tag 'latest'. rule validate-latest failed at path /spec/containers/0/imagePullPolicy/",
 	}
 
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	for index, r := range er.PolicyResponse.Rules {
 		assert.Equal(t, r.Message, msgs[index])
 	}
@@ -231,7 +231,7 @@ func TestValidate_image_tag_pass(t *testing.T) {
 		"validation rule 'validate-tag' passed.",
 		"validation rule 'validate-latest' passed.",
 	}
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	for index, r := range er.PolicyResponse.Rules {
 		assert.Equal(t, r.Message, msgs[index])
 	}
@@ -305,7 +305,7 @@ func TestValidate_Fail_anyPattern(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	assert.Assert(t, !er.IsSuccessful())
 
 	msgs := []string{"validation error: A namespace is required. rule check-default-namespace[0] failed at path /metadata/namespace/ rule check-default-namespace[1] failed at path /metadata/namespace/"}
@@ -388,7 +388,7 @@ func TestValidate_host_network_port(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation error: Host network and port are not allowed. rule validate-host-network-port failed at path /spec/containers/0/ports/0/hostPort/"}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -478,7 +478,7 @@ func TestValidate_anchor_arraymap_pass(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation rule 'validate-host-path' passed."}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -566,7 +566,7 @@ func TestValidate_anchor_arraymap_fail(t *testing.T) {
 	assert.NilError(t, err)
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation error: Host path '/var/lib/' is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/path/"}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -636,7 +636,7 @@ func TestValidate_anchor_map_notfound(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation rule 'pod rule 2' passed."}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -709,7 +709,7 @@ func TestValidate_anchor_map_found_valid(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation rule 'pod rule 2' passed."}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -783,7 +783,7 @@ func TestValidate_inequality_List_Processing(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation rule 'pod rule 2' passed."}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -863,7 +863,7 @@ func TestValidate_inequality_List_ProcessingBrackets(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation rule 'pod rule 2' passed."}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -937,7 +937,7 @@ func TestValidate_anchor_map_found_invalid(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation error: pod: validate run as non root user. rule pod rule 2 failed at path /spec/securityContext/runAsNonRoot/"}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -1012,7 +1012,7 @@ func TestValidate_AnchorList_pass(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation rule 'pod image rule' passed."}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -1087,7 +1087,7 @@ func TestValidate_AnchorList_fail(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	assert.Assert(t, !er.IsSuccessful())
 }
 
@@ -1157,7 +1157,7 @@ func TestValidate_existenceAnchor_fail(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	assert.Assert(t, !er.IsSuccessful())
 }
 
@@ -1227,7 +1227,7 @@ func TestValidate_existenceAnchor_pass(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation rule 'pod image rule' passed."}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -1315,7 +1315,7 @@ func TestValidate_negationAnchor_deny(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation error: Host path is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/"}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -1402,7 +1402,7 @@ func TestValidate_negationAnchor_pass(t *testing.T) {
 
 	resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
 	assert.NilError(t, err)
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation rule 'validate-host-path' passed."}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -1475,9 +1475,9 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured}
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured}
 	er := Validate(policyContext)
 
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
@@ -1568,9 +1568,9 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSu
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured}
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured}
 	er := Validate(policyContext)
 
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
@@ -1629,9 +1629,9 @@ func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured}
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured}
 	er := Validate(policyContext)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Message, "validation error: rule not-operator-with-variable-should-alway-fail-validation failed at path /spec/content/")
@@ -1720,9 +1720,9 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured}
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured}
 	er := Validate(policyContext)
 
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
@@ -1813,9 +1813,9 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured}
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured}
 	er := Validate(policyContext)
 
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
@@ -1918,9 +1918,9 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing.
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured}
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured}
 	er := Validate(policyContext)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Message, "The animal cow is not in the allowed list of animals.")
@@ -1971,9 +1971,9 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
 		assert.NilError(t, err)
 
 		policyContext := &PolicyContext{
-			Policy:      &policy,
-			JSONContext: ctx,
-			NewResource: *resourceUnstructured}
+			policy:      &policy,
+			jsonContext: ctx,
+			newResource: *resourceUnstructured}
 		er := Validate(policyContext)
 
 		for i, rule := range er.PolicyResponse.Rules {
@@ -2133,11 +2133,11 @@ func executeTest(t *testing.T, test testCase) {
 	}
 
 	pc := &PolicyContext{
-		Policy:        &policy,
-		NewResource:   newR,
-		OldResource:   oldR,
-		AdmissionInfo: userInfo,
-		JSONContext:   ctx,
+		policy:        &policy,
+		newResource:   newR,
+		oldResource:   oldR,
+		admissionInfo: userInfo,
+		jsonContext:   ctx,
 	}
 
 	resp := Validate(pc)
@@ -2239,7 +2239,7 @@ func TestValidate_context_variable_substitution_CLI(t *testing.T) {
 	msgs := []string{
 		"restrict pod counts to be no more than 10 on node minikube",
 	}
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	for index, r := range er.PolicyResponse.Rules {
 		assert.Equal(t, r.Message, msgs[index])
 	}
@@ -2328,7 +2328,7 @@ func Test_EmptyStringInDenyCondition(t *testing.T) {
 	resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
 	assert.NilError(t, err)
 
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: ctx})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx})
 	assert.Assert(t, !er.IsSuccessful())
 }
 
@@ -2417,7 +2417,7 @@ func Test_StringInDenyCondition(t *testing.T) {
 	resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
 	assert.NilError(t, err)
 
-	er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: ctx})
+	er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx})
 	assert.Assert(t, er.IsSuccessful())
 }
 
@@ -3001,9 +3001,9 @@ func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string,
 	assert.NilError(t, err)
 
 	policyContext := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured}
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured}
 	er := Validate(policyContext)
 
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, status)
@@ -3065,17 +3065,17 @@ func Test_delete_ignore_pattern(t *testing.T) {
 	assert.NilError(t, err)
 
 	policyContextCreate := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		NewResource: *resourceUnstructured}
+		policy:      &policy,
+		jsonContext: ctx,
+		newResource: *resourceUnstructured}
 	engineResponseCreate := Validate(policyContextCreate)
 	assert.Equal(t, len(engineResponseCreate.PolicyResponse.Rules), 1)
 	assert.Equal(t, engineResponseCreate.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
 
 	policyContextDelete := &PolicyContext{
-		Policy:      &policy,
-		JSONContext: ctx,
-		OldResource: *resourceUnstructured}
+		policy:      &policy,
+		jsonContext: ctx,
+		oldResource: *resourceUnstructured}
 	engineResponseDelete := Validate(policyContextDelete)
 	assert.Equal(t, len(engineResponseDelete.PolicyResponse.Rules), 0)
 }
@@ -3135,7 +3135,7 @@ func Test_ValidatePattern_anyPattern(t *testing.T) {
 			resourceUnstructured, err := utils.ConvertToUnstructured(tc.rawResource)
 			assert.NilError(t, err)
 
-			er := Validate(&PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+			er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 			if tc.expectedFailed {
 				assert.Assert(t, er.IsFailed())
 			} else if tc.expectedSkipped {
diff --git a/pkg/policy/apply.go b/pkg/policy/apply.go
index bc16c80439..2e1e5db47a 100644
--- a/pkg/policy/apply.go
+++ b/pkg/policy/apply.go
@@ -61,14 +61,12 @@ func applyPolicy(policy kyvernov1.PolicyInterface, resource unstructured.Unstruc
 		logger.Error(err, "failed to process mutation rule")
 	}
 
-	policyCtx := &engine.PolicyContext{
-		Policy:           policy,
-		NewResource:      resource,
-		ExcludeGroupRole: excludeGroupRole,
-		JSONContext:      ctx,
-		Client:           client,
-		NamespaceLabels:  namespaceLabels,
-	}
+	policyCtx := engine.NewPolicyContextWithJsonContext(ctx).
+		WithPolicy(policy).
+		WithNewResource(resource).
+		WithNamespaceLabels(namespaceLabels).
+		WithClient(client).
+		WithExcludeGroupRole(excludeGroupRole...)
 
 	engineResponseValidation = engine.Validate(policyCtx)
 	engineResponses = append(engineResponses, mergeRuleRespose(engineResponseMutation, engineResponseValidation))
@@ -77,12 +75,10 @@ func applyPolicy(policy kyvernov1.PolicyInterface, resource unstructured.Unstruc
 }
 
 func mutation(policy kyvernov1.PolicyInterface, resource unstructured.Unstructured, log logr.Logger, jsonContext context.Interface, namespaceLabels map[string]string) (*response.EngineResponse, error) {
-	policyContext := &engine.PolicyContext{
-		Policy:          policy,
-		NewResource:     resource,
-		JSONContext:     jsonContext,
-		NamespaceLabels: namespaceLabels,
-	}
+	policyContext := engine.NewPolicyContextWithJsonContext(jsonContext).
+		WithPolicy(policy).
+		WithNamespaceLabels(namespaceLabels).
+		WithNewResource(resource)
 
 	engineResponse := engine.Mutate(policyContext)
 	if !engineResponse.IsSuccessful() {
diff --git a/pkg/testrunner/scenario.go b/pkg/testrunner/scenario.go
index 92bcff2d4b..085ecc9eef 100644
--- a/pkg/testrunner/scenario.go
+++ b/pkg/testrunner/scenario.go
@@ -14,7 +14,6 @@ import (
 	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
 	"github.com/kyverno/kyverno/pkg/clients/dclient"
 	"github.com/kyverno/kyverno/pkg/engine"
-	enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/stretchr/testify/assert"
 	"gopkg.in/yaml.v3"
@@ -144,14 +143,9 @@ func runTestCase(t *testing.T, tc TestCase) bool {
 		t.FailNow()
 	}
 
-	ctx := &engine.PolicyContext{
-		Policy:           policy,
-		NewResource:      *resource,
-		ExcludeGroupRole: []string{},
-		JSONContext:      enginecontext.NewContext(),
-	}
+	policyContext := engine.NewPolicyContext().WithPolicy(policy).WithNewResource(*resource)
 
-	er := engine.Mutate(ctx)
+	er := engine.Mutate(policyContext)
 	t.Log("---Mutation---")
 	validateResource(t, er.PatchedResource, tc.Expected.Mutation.PatchedResource)
 	validateResponse(t, er.PolicyResponse, tc.Expected.Mutation.PolicyResponse)
@@ -161,14 +155,9 @@ func runTestCase(t *testing.T, tc TestCase) bool {
 		resource = &er.PatchedResource
 	}
 
-	ctx = &engine.PolicyContext{
-		Policy:           policy,
-		NewResource:      *resource,
-		ExcludeGroupRole: []string{},
-		JSONContext:      enginecontext.NewContext(),
-	}
+	policyContext = policyContext.WithNewResource(*resource)
 
-	er = engine.Validate(ctx)
+	er = engine.Validate(policyContext)
 	t.Log("---Validation---")
 	validateResponse(t, er.PolicyResponse, tc.Expected.Validation.PolicyResponse)
 
@@ -182,16 +171,7 @@ func runTestCase(t *testing.T, tc TestCase) bool {
 		if err := createNamespace(client, resource); err != nil {
 			t.Error(err)
 		} else {
-			policyContext := &engine.PolicyContext{
-				NewResource:      *resource,
-				Policy:           policy,
-				Client:           client,
-				ExcludeGroupRole: []string{},
-				ExcludeResourceFunc: func(s1, s2, s3 string) bool {
-					return false
-				},
-				JSONContext: enginecontext.NewContext(),
-			}
+			policyContext := policyContext.WithClient(client)
 
 			er = engine.ApplyBackgroundChecks(policyContext)
 			t.Log(("---Generation---"))
diff --git a/pkg/webhooks/resource/generation/generation.go b/pkg/webhooks/resource/generation/generation.go
index ad8850fbc0..1d0ad7660b 100644
--- a/pkg/webhooks/resource/generation/generation.go
+++ b/pkg/webhooks/resource/generation/generation.go
@@ -88,9 +88,9 @@ func (h *generationHandler) Handle(
 	if (request.Operation == admissionv1.Create || request.Operation == admissionv1.Update) && len(policies) != 0 {
 		for _, policy := range policies {
 			var rules []response.RuleResponse
-			policyContext.Policy = policy
+			policyContext := policyContext.WithPolicy(policy)
 			if request.Kind.Kind != "Namespace" && request.Namespace != "" {
-				policyContext.NamespaceLabels = common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log)
+				policyContext = policyContext.WithNamespaceLabels(common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log))
 			}
 			engineResponse := engine.ApplyBackgroundChecks(policyContext)
 			for _, rule := range engineResponse.PolicyResponse.Rules {
@@ -113,11 +113,12 @@ func (h *generationHandler) Handle(
 			go webhookutils.RegisterPolicyExecutionDurationMetricGenerate(context.TODO(), h.log, metricsConfig, string(request.Operation), policy, *engineResponse)
 		}
 
-		if failedResponse := applyUpdateRequest(request, kyvernov1beta1.Generate, h.urGenerator, policyContext.AdmissionInfo, request.Operation, engineResponses...); failedResponse != nil {
+		if failedResponse := applyUpdateRequest(request, kyvernov1beta1.Generate, h.urGenerator, policyContext.AdmissionInfo(), request.Operation, engineResponses...); failedResponse != nil {
 			// report failure event
 			for _, failedUR := range failedResponse {
 				err := fmt.Errorf("failed to create Update Request: %v", failedUR.err)
-				e := event.NewBackgroundFailedEvent(err, failedUR.ur.Policy, "", event.GeneratePolicyController, &policyContext.NewResource)
+				newResource := policyContext.NewResource()
+				e := event.NewBackgroundFailedEvent(err, failedUR.ur.Policy, "", event.GeneratePolicyController, &newResource)
 				h.eventGen.Add(e...)
 			}
 		}
diff --git a/pkg/webhooks/resource/handlers.go b/pkg/webhooks/resource/handlers.go
index 10f35ee836..55c1ff894b 100644
--- a/pkg/webhooks/resource/handlers.go
+++ b/pkg/webhooks/resource/handlers.go
@@ -117,7 +117,7 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
 
 	logger.V(4).Info("processing policies for validate admission request", "validate", len(policies), "mutate", len(mutatePolicies), "generate", len(generatePolicies))
 
-	policyContext, err := h.pcBuilder.Build(request, generatePolicies...)
+	policyContext, err := h.pcBuilder.Build(request)
 	if err != nil {
 		return errorResponse(logger, request.UID, err, "failed create policy context")
 	}
@@ -152,13 +152,13 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi
 		return admissionutils.ResponseSuccess(request.UID)
 	}
 	logger.V(4).Info("processing policies for mutate admission request", "mutatePolicies", len(mutatePolicies), "verifyImagesPolicies", len(verifyImagesPolicies))
-	policyContext, err := h.pcBuilder.Build(request, mutatePolicies...)
+	policyContext, err := h.pcBuilder.Build(request)
 	if err != nil {
 		logger.Error(err, "failed to build policy context")
 		return admissionutils.Response(request.UID, err)
 	}
 	// update container images to a canonical form
-	if err := enginectx.MutateResourceWithImageInfo(request.Object.Raw, policyContext.JSONContext); err != nil {
+	if err := enginectx.MutateResourceWithImageInfo(request.Object.Raw, policyContext.JSONContext()); err != nil {
 		logger.Error(err, "failed to patch images info to resource, policies that mutate images may be impacted")
 	}
 	mh := mutation.NewMutationHandler(logger, h.eventGen, h.openApiManager, h.nsLister)
@@ -169,7 +169,7 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi
 	}
 	newRequest := patchRequest(mutatePatches, request, logger)
 	// rebuild context to process images updated via mutate policies
-	policyContext, err = h.pcBuilder.Build(newRequest, mutatePolicies...)
+	policyContext, err = h.pcBuilder.Build(newRequest)
 	if err != nil {
 		logger.Error(err, "failed to build policy context")
 		return admissionutils.Response(request.UID, err)
diff --git a/pkg/webhooks/resource/imageverification/handler.go b/pkg/webhooks/resource/imageverification/handler.go
index 003aa89996..21199dbd0f 100644
--- a/pkg/webhooks/resource/imageverification/handler.go
+++ b/pkg/webhooks/resource/imageverification/handler.go
@@ -75,7 +75,7 @@ func (h *imageVerificationHandler) handleVerifyImages(logger logr.Logger, reques
 	var patches [][]byte
 	verifiedImageData := &engine.ImageVerificationMetadata{}
 	for _, p := range policies {
-		policyContext.Policy = p
+		policyContext := policyContext.WithPolicy(p)
 		resp, ivm := engine.VerifyAndPatchImages(policyContext)
 
 		engineResponses = append(engineResponses, resp)
@@ -83,7 +83,7 @@ func (h *imageVerificationHandler) handleVerifyImages(logger logr.Logger, reques
 		verifiedImageData.Merge(ivm)
 	}
 
-	failurePolicy := policyContext.Policy.GetSpec().GetFailurePolicy()
+	failurePolicy := policies[0].GetSpec().GetFailurePolicy()
 	blocked := webhookutils.BlockRequest(engineResponses, failurePolicy, logger)
 	if !isResourceDeleted(policyContext) {
 		events := webhookutils.GenerateEvents(engineResponses, blocked)
@@ -106,23 +106,26 @@ func (h *imageVerificationHandler) handleVerifyImages(logger logr.Logger, reques
 		}
 	}
 
-	go h.handleAudit(policyContext.NewResource, request, nil, engineResponses...)
+	go h.handleAudit(policyContext.NewResource(), request, nil, engineResponses...)
 
 	warnings := webhookutils.GetWarningMessages(engineResponses)
 	return true, "", jsonutils.JoinPatches(patches...), warnings
 }
 
 func hasAnnotations(context *engine.PolicyContext) bool {
-	annotations := context.NewResource.GetAnnotations()
+	newResource := context.NewResource()
+	annotations := newResource.GetAnnotations()
 	return len(annotations) != 0
 }
 
 func isResourceDeleted(policyContext *engine.PolicyContext) bool {
 	var deletionTimeStamp *metav1.Time
 	if reflect.DeepEqual(policyContext.NewResource, unstructured.Unstructured{}) {
-		deletionTimeStamp = policyContext.NewResource.GetDeletionTimestamp()
+		resource := policyContext.NewResource()
+		deletionTimeStamp = resource.GetDeletionTimestamp()
 	} else {
-		deletionTimeStamp = policyContext.OldResource.GetDeletionTimestamp()
+		resource := policyContext.OldResource()
+		deletionTimeStamp = resource.GetDeletionTimestamp()
 	}
 	return deletionTimeStamp != nil
 }
diff --git a/pkg/webhooks/resource/mutation/mutation.go b/pkg/webhooks/resource/mutation/mutation.go
index a9c661c3d9..9c09e7ac3b 100644
--- a/pkg/webhooks/resource/mutation/mutation.go
+++ b/pkg/webhooks/resource/mutation/mutation.go
@@ -100,8 +100,8 @@ func (v *mutationHandler) applyMutations(
 			continue
 		}
 		v.log.V(3).Info("applying policy mutate rules", "policy", policy.GetName())
-		policyContext.Policy = policy
-		engineResponse, policyPatches, err := v.applyMutation(request, policyContext)
+		currentContext := policyContext.WithPolicy(policy)
+		engineResponse, policyPatches, err := v.applyMutation(request, currentContext)
 		if err != nil {
 			return nil, nil, fmt.Errorf("mutation policy %s error: %v", policy.GetName(), err)
 		}
@@ -114,7 +114,7 @@ func (v *mutationHandler) applyMutations(
 			}
 		}
 
-		policyContext.NewResource = engineResponse.PatchedResource
+		policyContext = currentContext.WithNewResource(engineResponse.PatchedResource)
 		engineResponses = append(engineResponses, engineResponse)
 
 		// registering the kyverno_policy_results_total metric concurrently
@@ -141,20 +141,20 @@ func (v *mutationHandler) applyMutations(
 
 func (h *mutationHandler) applyMutation(request *admissionv1.AdmissionRequest, policyContext *engine.PolicyContext) (*response.EngineResponse, [][]byte, error) {
 	if request.Kind.Kind != "Namespace" && request.Namespace != "" {
-		policyContext.NamespaceLabels = common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log)
+		policyContext = policyContext.WithNamespaceLabels(common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log))
 	}
 
 	engineResponse := engine.Mutate(policyContext)
 	policyPatches := engineResponse.GetPatches()
 
 	if !engineResponse.IsSuccessful() {
-		return nil, nil, fmt.Errorf("failed to apply policy %s rules %v", policyContext.Policy.GetName(), engineResponse.GetFailedRules())
+		return nil, nil, fmt.Errorf("failed to apply policy %s rules %v", policyContext.Policy().GetName(), engineResponse.GetFailedRules())
 	}
 
-	if policyContext.Policy.ValidateSchema() && engineResponse.PatchedResource.GetKind() != "*" {
+	if policyContext.Policy().ValidateSchema() && engineResponse.PatchedResource.GetKind() != "*" {
 		err := h.openApiManager.ValidateResource(*engineResponse.PatchedResource.DeepCopy(), engineResponse.PatchedResource.GetAPIVersion(), engineResponse.PatchedResource.GetKind())
 		if err != nil {
-			return nil, nil, errors.Wrapf(err, "failed to validate resource mutated by policy %s", policyContext.Policy.GetName())
+			return nil, nil, errors.Wrapf(err, "failed to validate resource mutated by policy %s", policyContext.Policy().GetName())
 		}
 	}
 
@@ -175,9 +175,11 @@ func logMutationResponse(patches [][]byte, engineResponses []*response.EngineRes
 func isResourceDeleted(policyContext *engine.PolicyContext) bool {
 	var deletionTimeStamp *metav1.Time
 	if reflect.DeepEqual(policyContext.NewResource, unstructured.Unstructured{}) {
-		deletionTimeStamp = policyContext.NewResource.GetDeletionTimestamp()
+		resource := policyContext.NewResource()
+		deletionTimeStamp = resource.GetDeletionTimestamp()
 	} else {
-		deletionTimeStamp = policyContext.OldResource.GetDeletionTimestamp()
+		resource := policyContext.OldResource()
+		deletionTimeStamp = resource.GetDeletionTimestamp()
 	}
 	return deletionTimeStamp != nil
 }
diff --git a/pkg/webhooks/resource/updaterequest.go b/pkg/webhooks/resource/updaterequest.go
index cdab47ece0..418b254876 100644
--- a/pkg/webhooks/resource/updaterequest.go
+++ b/pkg/webhooks/resource/updaterequest.go
@@ -25,10 +25,11 @@ func (h *handlers) createUpdateRequests(logger logr.Logger, request *admissionv1
 
 func (h *handlers) handleMutateExisting(logger logr.Logger, request *admissionv1.AdmissionRequest, policies []kyvernov1.PolicyInterface, policyContext *engine.PolicyContext, admissionRequestTimestamp time.Time) {
 	if request.Operation == admissionv1.Delete {
-		policyContext.NewResource = policyContext.OldResource
+		policyContext = policyContext.WithNewResource(policyContext.OldResource())
 	}
 
-	if request.Operation == admissionv1.Update && policyContext.NewResource.GetDeletionTimestamp() != nil {
+	resource := policyContext.NewResource()
+	if request.Operation == admissionv1.Update && resource.GetDeletionTimestamp() != nil {
 		logger.V(4).Info("skip creating UR for the trigger resource that is in termination")
 		return
 	}
@@ -41,7 +42,7 @@ func (h *handlers) handleMutateExisting(logger logr.Logger, request *admissionv1
 		logger.V(4).Info("update request for mutateExisting policy")
 
 		var rules []response.RuleResponse
-		policyContext.Policy = policy
+		policyContext := policyContext.WithPolicy(policy)
 		engineResponse := engine.ApplyBackgroundChecks(policyContext)
 
 		for _, rule := range engineResponse.PolicyResponse.Rules {
@@ -61,10 +62,11 @@ func (h *handlers) handleMutateExisting(logger logr.Logger, request *admissionv1
 		go webhookutils.RegisterPolicyExecutionDurationMetricMutate(context.TODO(), logger, h.metricsConfig, string(request.Operation), policy, *engineResponse)
 	}
 
-	if failedResponse := applyUpdateRequest(request, kyvernov1beta1.Mutate, h.urGenerator, policyContext.AdmissionInfo, request.Operation, engineResponses...); failedResponse != nil {
+	if failedResponse := applyUpdateRequest(request, kyvernov1beta1.Mutate, h.urGenerator, policyContext.AdmissionInfo(), request.Operation, engineResponses...); failedResponse != nil {
 		for _, failedUR := range failedResponse {
 			err := fmt.Errorf("failed to create update request: %v", failedUR.err)
-			events := event.NewBackgroundFailedEvent(err, failedUR.ur.Policy, "", event.GeneratePolicyController, &policyContext.NewResource)
+			resource := policyContext.NewResource()
+			events := event.NewBackgroundFailedEvent(err, failedUR.ur.Policy, "", event.GeneratePolicyController, &resource)
 			h.eventGen.Add(events...)
 		}
 	}
diff --git a/pkg/webhooks/resource/validation/validation.go b/pkg/webhooks/resource/validation/validation.go
index 1682fe7f7e..63e4cbfb90 100644
--- a/pkg/webhooks/resource/validation/validation.go
+++ b/pkg/webhooks/resource/validation/validation.go
@@ -67,7 +67,7 @@ func (v *validationHandler) HandleValidation(
 ) (bool, string, []string) {
 	if len(policies) == 0 {
 		// invoke handleAudit as we may have some policies in audit mode to consider
-		go v.handleAudit(policyContext.NewResource, request, namespaceLabels)
+		go v.handleAudit(policyContext.NewResource(), request, namespaceLabels)
 		return true, "", nil
 	}
 
@@ -76,9 +76,11 @@ func (v *validationHandler) HandleValidation(
 
 	var deletionTimeStamp *metav1.Time
 	if reflect.DeepEqual(policyContext.NewResource, unstructured.Unstructured{}) {
-		deletionTimeStamp = policyContext.NewResource.GetDeletionTimestamp()
+		resource := policyContext.NewResource()
+		deletionTimeStamp = resource.GetDeletionTimestamp()
 	} else {
-		deletionTimeStamp = policyContext.OldResource.GetDeletionTimestamp()
+		resource := policyContext.OldResource()
+		deletionTimeStamp = resource.GetDeletionTimestamp()
 	}
 
 	if deletionTimeStamp != nil && request.Operation == admissionv1.Update {
@@ -88,8 +90,7 @@ func (v *validationHandler) HandleValidation(
 	var engineResponses []*response.EngineResponse
 	failurePolicy := kyvernov1.Ignore
 	for _, policy := range policies {
-		policyContext.Policy = policy
-		policyContext.NamespaceLabels = namespaceLabels
+		policyContext := policyContext.WithPolicy(policy).WithNamespaceLabels(namespaceLabels)
 		if policy.GetSpec().GetFailurePolicy() == kyvernov1.Fail {
 			failurePolicy = kyvernov1.Fail
 		}
@@ -101,8 +102,8 @@ func (v *validationHandler) HandleValidation(
 			continue
 		}
 
-		go webhookutils.RegisterPolicyResultsMetricValidation(context.TODO(), logger, metricsConfig, string(request.Operation), policyContext.Policy, *engineResponse)
-		go webhookutils.RegisterPolicyExecutionDurationMetricValidate(context.TODO(), logger, metricsConfig, string(request.Operation), policyContext.Policy, *engineResponse)
+		go webhookutils.RegisterPolicyResultsMetricValidation(context.TODO(), logger, metricsConfig, string(request.Operation), policyContext.Policy(), *engineResponse)
+		go webhookutils.RegisterPolicyExecutionDurationMetricValidate(context.TODO(), logger, metricsConfig, string(request.Operation), policyContext.Policy(), *engineResponse)
 
 		engineResponses = append(engineResponses, engineResponse)
 		if !engineResponse.IsSuccessful() {
@@ -126,7 +127,7 @@ func (v *validationHandler) HandleValidation(
 		return false, webhookutils.GetBlockedMessages(engineResponses), nil
 	}
 
-	go v.handleAudit(policyContext.NewResource, request, namespaceLabels, engineResponses...)
+	go v.handleAudit(policyContext.NewResource(), request, namespaceLabels, engineResponses...)
 
 	warnings := webhookutils.GetWarningMessages(engineResponses)
 	return true, "", warnings
@@ -134,14 +135,13 @@ func (v *validationHandler) HandleValidation(
 
 func (v *validationHandler) buildAuditResponses(resource unstructured.Unstructured, request *admissionv1.AdmissionRequest, namespaceLabels map[string]string) ([]*response.EngineResponse, error) {
 	policies := v.pCache.GetPolicies(policycache.ValidateAudit, request.Kind.Kind, request.Namespace)
-	policyContext, err := v.pcBuilder.Build(request, policies...)
+	policyContext, err := v.pcBuilder.Build(request)
 	if err != nil {
 		return nil, err
 	}
 	var responses []*response.EngineResponse
 	for _, policy := range policies {
-		policyContext.Policy = policy
-		policyContext.NamespaceLabels = namespaceLabels
+		policyContext := policyContext.WithPolicy(policy).WithNamespaceLabels(namespaceLabels)
 		responses = append(responses, engine.Validate(policyContext))
 	}
 	return responses, nil
diff --git a/pkg/webhooks/resource/validation_test.go b/pkg/webhooks/resource/validation_test.go
index 2141af6ec4..dd167dc71a 100644
--- a/pkg/webhooks/resource/validation_test.go
+++ b/pkg/webhooks/resource/validation_test.go
@@ -9,7 +9,6 @@ import (
 
 	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
 	"github.com/kyverno/kyverno/pkg/engine"
-	"github.com/kyverno/kyverno/pkg/engine/context"
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/engine/utils"
 	webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils"
@@ -531,7 +530,9 @@ func TestValidate_failure_action_overrides(t *testing.T) {
 			resourceUnstructured, err := utils.ConvertToUnstructured(tc.rawResource)
 			assert.NilError(t, err)
 
-			er := engine.Validate(&engine.PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
+			er := engine.Validate(
+				engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured),
+			)
 			if tc.blocked && tc.messages != nil {
 				for _, r := range er.PolicyResponse.Rules {
 					msg := tc.messages[r.Name]
@@ -589,7 +590,8 @@ func Test_RuleSelector(t *testing.T) {
 	assert.NilError(t, err)
 	assert.Assert(t, resourceUnstructured != nil)
 
-	ctx := &engine.PolicyContext{Policy: &policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()}
+	ctx := engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured)
+
 	resp := engine.Validate(ctx)
 	assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 2)
 	assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0)
diff --git a/pkg/webhooks/utils/policy_context_builder.go b/pkg/webhooks/utils/policy_context_builder.go
index 1cca4f730d..8c7d99b1fc 100644
--- a/pkg/webhooks/utils/policy_context_builder.go
+++ b/pkg/webhooks/utils/policy_context_builder.go
@@ -1,35 +1,18 @@
 package utils
 
 import (
-	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
 	kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
 	"github.com/kyverno/kyverno/pkg/clients/dclient"
 	"github.com/kyverno/kyverno/pkg/config"
 	"github.com/kyverno/kyverno/pkg/engine"
-	enginectx "github.com/kyverno/kyverno/pkg/engine/context"
 	"github.com/kyverno/kyverno/pkg/userinfo"
-	"github.com/kyverno/kyverno/pkg/utils"
 	"github.com/pkg/errors"
 	admissionv1 "k8s.io/api/admission/v1"
 	rbacv1listers "k8s.io/client-go/listers/rbac/v1"
 )
 
 type PolicyContextBuilder interface {
-	Build(*admissionv1.AdmissionRequest, ...kyvernov1.PolicyInterface) (*engine.PolicyContext, error)
-}
-
-func newVariablesContext(request *admissionv1.AdmissionRequest, userRequestInfo *kyvernov1beta1.RequestInfo) (enginectx.Interface, error) {
-	ctx := enginectx.NewContext()
-	if err := ctx.AddRequest(request); err != nil {
-		return nil, errors.Wrap(err, "failed to load incoming request in context")
-	}
-	if err := ctx.AddUserInfo(*userRequestInfo); err != nil {
-		return nil, errors.Wrap(err, "failed to load userInfo in context")
-	}
-	if err := ctx.AddServiceAccount(userRequestInfo.AdmissionUserInfo.Username); err != nil {
-		return nil, errors.Wrap(err, "failed to load service account in context")
-	}
-	return ctx, nil
+	Build(*admissionv1.AdmissionRequest) (*engine.PolicyContext, error)
 }
 
 type policyContextBuilder struct {
@@ -53,7 +36,7 @@ func NewPolicyContextBuilder(
 	}
 }
 
-func (b *policyContextBuilder) Build(request *admissionv1.AdmissionRequest, policies ...kyvernov1.PolicyInterface) (*engine.PolicyContext, error) {
+func (b *policyContextBuilder) Build(request *admissionv1.AdmissionRequest) (*engine.PolicyContext, error) {
 	userRequestInfo := kyvernov1beta1.RequestInfo{
 		AdmissionUserInfo: *request.UserInfo.DeepCopy(),
 	}
@@ -63,26 +46,5 @@ func (b *policyContextBuilder) Build(request *admissionv1.AdmissionRequest, poli
 		userRequestInfo.Roles = roles
 		userRequestInfo.ClusterRoles = clusterRoles
 	}
-	ctx, err := newVariablesContext(request, &userRequestInfo)
-	if err != nil {
-		return nil, errors.Wrap(err, "failed to create policy rule context")
-	}
-	newResource, oldResource, err := utils.ExtractResources(nil, request)
-	if err != nil {
-		return nil, errors.Wrap(err, "failed to parse resource")
-	}
-	if err := ctx.AddImageInfos(&newResource); err != nil {
-		return nil, errors.Wrap(err, "failed to add image information to the policy rule context")
-	}
-	policyContext := &engine.PolicyContext{
-		NewResource:         newResource,
-		OldResource:         oldResource,
-		AdmissionInfo:       userRequestInfo,
-		ExcludeGroupRole:    b.configuration.GetExcludeGroupRole(),
-		ExcludeResourceFunc: b.configuration.ToFilter,
-		JSONContext:         ctx,
-		Client:              b.client,
-		AdmissionOperation:  true,
-	}
-	return policyContext, nil
+	return engine.NewPolicyContextFromAdmissionRequest(request, userRequestInfo, b.configuration, b.client)
 }