diff --git a/cmd/cli/kubectl-kyverno/utils/common/common.go b/cmd/cli/kubectl-kyverno/utils/common/common.go
index 4b2edb9680..5f6f991dff 100644
--- a/cmd/cli/kubectl-kyverno/utils/common/common.go
+++ b/cmd/cli/kubectl-kyverno/utils/common/common.go
@@ -26,6 +26,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	ut "github.com/kyverno/kyverno/pkg/engine/utils"
 	"github.com/kyverno/kyverno/pkg/engine/variables"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
 	yamlv2 "gopkg.in/yaml.v2"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -457,7 +458,7 @@ OuterLoop:
 		WithAdmissionInfo(c.UserInfo).
 		WithClient(c.Client)
 
-	mutateResponse := engine.Mutate(policyContext)
+	mutateResponse := engine.Mutate(registryclient.NewOrDie(), policyContext)
 	if mutateResponse != nil {
 		engineResponses = append(engineResponses, mutateResponse)
 	}
@@ -481,7 +482,7 @@ OuterLoop:
 	var info Info
 	var validateResponse *response.EngineResponse
 	if policyHasValidate {
-		validateResponse = engine.Validate(policyContext)
+		validateResponse = engine.Validate(registryclient.NewOrDie(), policyContext)
 		info = ProcessValidateEngineResponse(c.Policy, validateResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn)
 	}
 
@@ -489,7 +490,7 @@ OuterLoop:
 		engineResponses = append(engineResponses, validateResponse)
 	}
 
-	verifyImageResponse, _ := engine.VerifyAndPatchImages(policyContext)
+	verifyImageResponse, _ := engine.VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	if verifyImageResponse != nil && !verifyImageResponse.IsEmpty() {
 		engineResponses = append(engineResponses, verifyImageResponse)
 		info = ProcessValidateEngineResponse(c.Policy, verifyImageResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn)
@@ -503,7 +504,7 @@ OuterLoop:
 	}
 
 	if policyHasGenerate {
-		generateResponse := engine.ApplyBackgroundChecks(policyContext)
+		generateResponse := engine.ApplyBackgroundChecks(registryclient.NewOrDie(), policyContext)
 		if generateResponse != nil && !generateResponse.IsEmpty() {
 			newRuleResponse, err := handleGeneratePolicy(generateResponse, *policyContext, c.RuleToCloneSourceResource)
 			if err != nil {
diff --git a/cmd/cli/kubectl-kyverno/utils/store/store.go b/cmd/cli/kubectl-kyverno/utils/store/store.go
index 93e431f8fd..aa4dc55208 100644
--- a/cmd/cli/kubectl-kyverno/utils/store/store.go
+++ b/cmd/cli/kubectl-kyverno/utils/store/store.go
@@ -7,7 +7,7 @@ import (
 
 var (
 	Mock           bool
-	RegistryAccess bool
+	registryClient registryclient.Client
 	AllowApiCalls  bool
 	ContextVar     Context
 	ForeachElement int
@@ -32,13 +32,16 @@ func GetForeachElement() int {
 
 func SetRegistryAccess(access bool) {
 	if access {
-		registryclient.DefaultClient.UseLocalKeychain()
+		registryClient = registryclient.NewOrDie(registryclient.WithLocalKeychain())
 	}
-	RegistryAccess = access
 }
 
 func GetRegistryAccess() bool {
-	return RegistryAccess
+	return registryClient != nil
+}
+
+func GetRegistryClient() registryclient.Client {
+	return registryClient
 }
 
 func SetContext(context Context) {
diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go
index c9b59f9edd..199a1734d3 100644
--- a/cmd/kyverno/main.go
+++ b/cmd/kyverno/main.go
@@ -61,23 +61,24 @@ const (
 	resyncPeriod = 15 * time.Minute
 )
 
-func setupRegistryClient(logger logr.Logger, kubeClient kubernetes.Interface, imagePullSecrets string, allowInsecureRegistry bool) error {
+func setupRegistryClient(logger logr.Logger, kubeClient kubernetes.Interface, imagePullSecrets string, allowInsecureRegistry bool) (registryclient.Client, error) {
 	logger = logger.WithName("registry-client")
 	logger.Info("setup registry client...", "secrets", imagePullSecrets, "insecure", allowInsecureRegistry)
 	var registryOptions []registryclient.Option
 	secrets := strings.Split(imagePullSecrets, ",")
 	if imagePullSecrets != "" && len(secrets) > 0 {
-		registryOptions = append(registryOptions, registryclient.WithKeychainPullSecrets(kubeClient, config.KyvernoNamespace(), "", secrets))
+		registryOptions = append(registryOptions, registryclient.WithKeychainPullSecrets(
+			context.TODO(),
+			kubeClient,
+			config.KyvernoNamespace(),
+			"",
+			secrets...,
+		))
 	}
 	if allowInsecureRegistry {
 		registryOptions = append(registryOptions, registryclient.WithAllowInsecureRegistry())
 	}
-	client, err := registryclient.InitClient(registryOptions...)
-	if err != nil {
-		return err
-	}
-	registryclient.DefaultClient = client
-	return nil
+	return registryclient.New(registryOptions...)
 }
 
 func setupCosign(logger logr.Logger, imageSignatureRepository string) {
@@ -115,6 +116,7 @@ func createNonLeaderControllers(
 	kubeClient kubernetes.Interface,
 	kyvernoClient versioned.Interface,
 	dynamicClient dclient.Interface,
+	rclient registryclient.Client,
 	configuration config.Configuration,
 	policyCache policycache.Cache,
 	eventGenerator event.Interface,
@@ -136,6 +138,7 @@ func createNonLeaderControllers(
 	updateRequestController := background.NewController(
 		kyvernoClient,
 		dynamicClient,
+		rclient,
 		kyvernoInformer.Kyverno().V1().ClusterPolicies(),
 		kyvernoInformer.Kyverno().V1().Policies(),
 		kyvernoInformer.Kyverno().V1beta1().UpdateRequests(),
@@ -162,6 +165,7 @@ func createReportControllers(
 	backgroundScanWorkers int,
 	client dclient.Interface,
 	kyvernoClient versioned.Interface,
+	rclient registryclient.Client,
 	metadataFactory metadatainformers.SharedInformerFactory,
 	kubeInformer kubeinformers.SharedInformerFactory,
 	kyvernoInformer kyvernoinformer.SharedInformerFactory,
@@ -212,6 +216,7 @@ func createReportControllers(
 				backgroundscancontroller.NewController(
 					client,
 					kyvernoClient,
+					rclient,
 					metadataFactory,
 					kyvernoV1.Policies(),
 					kyvernoV1.ClusterPolicies(),
@@ -247,6 +252,7 @@ func createrLeaderControllers(
 	kubeClient kubernetes.Interface,
 	kyvernoClient versioned.Interface,
 	dynamicClient dclient.Interface,
+	rclient registryclient.Client,
 	configuration config.Configuration,
 	metricsConfig metrics.MetricsConfigManager,
 	eventGenerator event.Interface,
@@ -256,6 +262,7 @@ func createrLeaderControllers(
 	policyCtrl, err := policy.NewPolicyController(
 		kyvernoClient,
 		dynamicClient,
+		rclient,
 		kyvernoInformer.Kyverno().V1().ClusterPolicies(),
 		kyvernoInformer.Kyverno().V1().Policies(),
 		kyvernoInformer.Kyverno().V1beta1().UpdateRequests(),
@@ -300,6 +307,7 @@ func createrLeaderControllers(
 		backgroundScanWorkers,
 		dynamicClient,
 		kyvernoClient,
+		rclient,
 		metadataInformer,
 		kubeInformer,
 		kyvernoInformer,
@@ -390,7 +398,8 @@ func main() {
 		os.Exit(1)
 	}
 	// setup registry client
-	if err := setupRegistryClient(logger, kubeClient, imagePullSecrets, allowInsecureRegistry); err != nil {
+	rclient, err := setupRegistryClient(logger, kubeClient, imagePullSecrets, allowInsecureRegistry)
+	if err != nil {
 		logger.Error(err, "failed to setup registry client")
 		os.Exit(1)
 	}
@@ -476,6 +485,7 @@ func main() {
 		kubeClient,
 		kyvernoClient,
 		dClient,
+		rclient,
 		configuration,
 		policyCache,
 		eventGenerator,
@@ -531,6 +541,7 @@ func main() {
 				kubeClient,
 				kyvernoClient,
 				dClient,
+				rclient,
 				configuration,
 				metricsConfig,
 				eventGenerator,
@@ -595,6 +606,7 @@ func main() {
 	resourceHandlers := webhooksresource.NewHandlers(
 		dClient,
 		kyvernoClient,
+		rclient,
 		configuration,
 		metricsConfig,
 		policyCache,
diff --git a/pkg/background/generate/generate.go b/pkg/background/generate/generate.go
index 4d65581566..9d3acb353c 100644
--- a/pkg/background/generate/generate.go
+++ b/pkg/background/generate/generate.go
@@ -27,6 +27,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/engine/utils"
 	"github.com/kyverno/kyverno/pkg/engine/variables"
 	"github.com/kyverno/kyverno/pkg/event"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	kyvernoutils "github.com/kyverno/kyverno/pkg/utils"
 	kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
 	"golang.org/x/exp/slices"
@@ -43,6 +44,7 @@ type GenerateController struct {
 	client        dclient.Interface
 	kyvernoClient versioned.Interface
 	statusControl common.StatusControlInterface
+	rclient       registryclient.Client
 
 	// listers
 	urLister      kyvernov1beta1listers.UpdateRequestNamespaceLister
@@ -61,6 +63,7 @@ func NewGenerateController(
 	client dclient.Interface,
 	kyvernoClient versioned.Interface,
 	statusControl common.StatusControlInterface,
+	rclient registryclient.Client,
 	policyLister kyvernov1listers.ClusterPolicyLister,
 	npolicyLister kyvernov1listers.PolicyLister,
 	urLister kyvernov1beta1listers.UpdateRequestNamespaceLister,
@@ -73,6 +76,7 @@ func NewGenerateController(
 		client:        client,
 		kyvernoClient: kyvernoClient,
 		statusControl: statusControl,
+		rclient:       rclient,
 		policyLister:  policyLister,
 		npolicyLister: npolicyLister,
 		urLister:      urLister,
@@ -195,7 +199,7 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u
 	}
 
 	// check if the policy still applies to the resource
-	engineResponse := engine.GenerateResponse(policyContext, ur)
+	engineResponse := engine.GenerateResponse(c.rclient, policyContext, ur)
 	if len(engineResponse.PolicyResponse.Rules) == 0 {
 		logger.V(4).Info(doesNotApply)
 		return nil, false, errors.New(doesNotApply)
@@ -341,7 +345,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext
 		}
 
 		// add configmap json data to context
-		if err := engine.LoadContext(log, rule.Context, policyContext, rule.Name); err != nil {
+		if err := engine.LoadContext(log, c.rclient, rule.Context, policyContext, rule.Name); err != nil {
 			log.Error(err, "cannot add configmaps to context")
 			return nil, processExisting, err
 		}
diff --git a/pkg/background/mutate/mutate.go b/pkg/background/mutate/mutate.go
index b83e7daaf2..570f12f5c2 100644
--- a/pkg/background/mutate/mutate.go
+++ b/pkg/background/mutate/mutate.go
@@ -15,6 +15,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/engine"
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/event"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	"github.com/kyverno/kyverno/pkg/utils"
 	"go.uber.org/multierr"
 	yamlv2 "gopkg.in/yaml.v2"
@@ -28,7 +29,7 @@ type MutateExistingController struct {
 	// clients
 	client        dclient.Interface
 	statusControl common.StatusControlInterface
-
+	rclient       registryclient.Client
 	// listers
 	policyLister  kyvernov1listers.ClusterPolicyLister
 	npolicyLister kyvernov1listers.PolicyLister
@@ -43,6 +44,7 @@ type MutateExistingController struct {
 func NewMutateExistingController(
 	client dclient.Interface,
 	statusControl common.StatusControlInterface,
+	rclient registryclient.Client,
 	policyLister kyvernov1listers.ClusterPolicyLister,
 	npolicyLister kyvernov1listers.PolicyLister,
 	dynamicConfig config.Configuration,
@@ -52,6 +54,7 @@ func NewMutateExistingController(
 	c := MutateExistingController{
 		client:        client,
 		statusControl: statusControl,
+		rclient:       rclient,
 		policyLister:  policyLister,
 		npolicyLister: npolicyLister,
 		configuration: dynamicConfig,
@@ -90,7 +93,7 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e
 			continue
 		}
 
-		er := engine.Mutate(policyContext)
+		er := engine.Mutate(c.rclient, policyContext)
 		for _, r := range er.PolicyResponse.Rules {
 			patched := r.PatchedTarget
 			switch r.Status {
diff --git a/pkg/background/update_request_controller.go b/pkg/background/update_request_controller.go
index 58fc342ed0..d62d016362 100644
--- a/pkg/background/update_request_controller.go
+++ b/pkg/background/update_request_controller.go
@@ -19,6 +19,7 @@ import (
 	pkgCommon "github.com/kyverno/kyverno/pkg/common"
 	"github.com/kyverno/kyverno/pkg/config"
 	"github.com/kyverno/kyverno/pkg/event"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -46,6 +47,7 @@ type controller struct {
 	// clients
 	client        dclient.Interface
 	kyvernoClient versioned.Interface
+	rclient       registryclient.Client
 
 	// listers
 	cpolLister kyvernov1listers.ClusterPolicyLister
@@ -67,6 +69,7 @@ type controller struct {
 func NewController(
 	kyvernoClient versioned.Interface,
 	client dclient.Interface,
+	rclient registryclient.Client,
 	cpolInformer kyvernov1informers.ClusterPolicyInformer,
 	polInformer kyvernov1informers.PolicyInformer,
 	urInformer kyvernov1beta1informers.UpdateRequestInformer,
@@ -79,6 +82,7 @@ func NewController(
 	c := controller{
 		client:        client,
 		kyvernoClient: kyvernoClient,
+		rclient:       rclient,
 		cpolLister:    cpolInformer.Lister(),
 		polLister:     polInformer.Lister(),
 		urLister:      urLister,
@@ -405,10 +409,10 @@ func (c *controller) processUR(ur *kyvernov1beta1.UpdateRequest) error {
 	statusControl := common.NewStatusControl(c.kyvernoClient, c.urLister)
 	switch ur.Spec.Type {
 	case kyvernov1beta1.Mutate:
-		ctrl := mutate.NewMutateExistingController(c.client, statusControl, c.cpolLister, c.polLister, c.configuration, c.eventGen, logger)
+		ctrl := mutate.NewMutateExistingController(c.client, statusControl, c.rclient, c.cpolLister, c.polLister, c.configuration, c.eventGen, logger)
 		return ctrl.ProcessUR(ur)
 	case kyvernov1beta1.Generate:
-		ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.eventGen, logger)
+		ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.rclient, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.eventGen, logger)
 		return ctrl.ProcessUR(ur)
 	}
 	return nil
diff --git a/pkg/controllers/report/background/controller.go b/pkg/controllers/report/background/controller.go
index d6c8d5393b..92893e5f7d 100644
--- a/pkg/controllers/report/background/controller.go
+++ b/pkg/controllers/report/background/controller.go
@@ -16,6 +16,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/controllers/report/resource"
 	"github.com/kyverno/kyverno/pkg/controllers/report/utils"
 	"github.com/kyverno/kyverno/pkg/engine/response"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
 	reportutils "github.com/kyverno/kyverno/pkg/utils/report"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -41,6 +42,7 @@ type controller struct {
 	// clients
 	client        dclient.Interface
 	kyvernoClient versioned.Interface
+	rclient       registryclient.Client
 
 	// listers
 	polLister      kyvernov1listers.PolicyLister
@@ -61,6 +63,7 @@ type controller struct {
 func NewController(
 	client dclient.Interface,
 	kyvernoClient versioned.Interface,
+	rclient registryclient.Client,
 	metadataFactory metadatainformers.SharedInformerFactory,
 	polInformer kyvernov1informers.PolicyInformer,
 	cpolInformer kyvernov1informers.ClusterPolicyInformer,
@@ -73,6 +76,7 @@ func NewController(
 	c := controller{
 		client:         client,
 		kyvernoClient:  kyvernoClient,
+		rclient:        rclient,
 		polLister:      polInformer.Lister(),
 		cpolLister:     cpolInformer.Lister(),
 		bgscanrLister:  bgscanr.Lister(),
@@ -214,7 +218,7 @@ func (c *controller) updateReport(ctx context.Context, meta metav1.Object, gvk s
 	}
 	//	if the resource changed, we need to rebuild the report
 	if !reportutils.CompareHash(meta, resource.Hash) {
-		scanner := utils.NewScanner(logger, c.client)
+		scanner := utils.NewScanner(logger, c.client, c.rclient)
 		before, err := c.getReport(ctx, meta.GetNamespace(), meta.GetName())
 		if err != nil {
 			return nil
@@ -300,7 +304,7 @@ func (c *controller) updateReport(ctx context.Context, meta metav1.Object, gvk s
 		}
 		// creations
 		if len(toCreate) > 0 {
-			scanner := utils.NewScanner(logger, c.client)
+			scanner := utils.NewScanner(logger, c.client, c.rclient)
 			resource, err := c.client.GetResource(ctx, gvk.GroupVersion().String(), gvk.Kind, resource.Namespace, resource.Name)
 			if err != nil {
 				return err
diff --git a/pkg/controllers/report/utils/scanner.go b/pkg/controllers/report/utils/scanner.go
index 700dccf206..368284a344 100644
--- a/pkg/controllers/report/utils/scanner.go
+++ b/pkg/controllers/report/utils/scanner.go
@@ -7,6 +7,7 @@ import (
 	"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/registryclient"
 	"go.uber.org/multierr"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
@@ -14,6 +15,7 @@ import (
 type scanner struct {
 	logger           logr.Logger
 	client           dclient.Interface
+	rclient          registryclient.Client
 	excludeGroupRole []string
 }
 
@@ -26,10 +28,11 @@ type Scanner interface {
 	ScanResource(unstructured.Unstructured, map[string]string, ...kyvernov1.PolicyInterface) map[kyvernov1.PolicyInterface]ScanResult
 }
 
-func NewScanner(logger logr.Logger, client dclient.Interface, excludeGroupRole ...string) Scanner {
+func NewScanner(logger logr.Logger, client dclient.Interface, rclient registryclient.Client, excludeGroupRole ...string) Scanner {
 	return &scanner{
 		logger:           logger,
 		client:           client,
+		rclient:          rclient,
 		excludeGroupRole: excludeGroupRole,
 	}
 }
@@ -81,7 +84,7 @@ func (s *scanner) validateResource(resource unstructured.Unstructured, nsLabels
 		WithClient(s.client).
 		WithNamespaceLabels(nsLabels).
 		WithExcludeGroupRole(s.excludeGroupRole...)
-	return engine.Validate(policyCtx), nil
+	return engine.Validate(s.rclient, policyCtx), nil
 }
 
 func (s *scanner) validateImages(resource unstructured.Unstructured, nsLabels map[string]string, policy kyvernov1.PolicyInterface) (*response.EngineResponse, error) {
@@ -104,7 +107,7 @@ func (s *scanner) validateImages(resource unstructured.Unstructured, nsLabels ma
 		WithClient(s.client).
 		WithNamespaceLabels(nsLabels).
 		WithExcludeGroupRole(s.excludeGroupRole...)
-	response, _ := engine.VerifyAndPatchImages(policyCtx)
+	response, _ := engine.VerifyAndPatchImages(s.rclient, policyCtx)
 	if len(response.PolicyResponse.Rules) > 0 {
 		s.logger.Info("validateImages", "policy", policy, "response", response)
 	}
diff --git a/pkg/cosign/cosign.go b/pkg/cosign/cosign.go
index 92d959c979..540ec7f657 100644
--- a/pkg/cosign/cosign.go
+++ b/pkg/cosign/cosign.go
@@ -59,13 +59,13 @@ type Response struct {
 type CosignError struct{}
 
 // VerifySignature verifies that the image has the expected signatures
-func VerifySignature(opts Options) (*Response, error) {
+func VerifySignature(rclient registryclient.Client, opts Options) (*Response, error) {
 	ref, err := name.ParseReference(opts.ImageRef)
 	if err != nil {
 		return nil, fmt.Errorf("failed to parse image %s", opts.ImageRef)
 	}
 
-	cosignOpts, err := buildCosignOptions(opts)
+	cosignOpts, err := buildCosignOptions(rclient, opts)
 	if err != nil {
 		return nil, err
 	}
@@ -110,7 +110,7 @@ func VerifySignature(opts Options) (*Response, error) {
 	return &Response{Digest: digest}, nil
 }
 
-func buildCosignOptions(opts Options) (*cosign.CheckOpts, error) {
+func buildCosignOptions(rclient registryclient.Client, opts Options) (*cosign.CheckOpts, error) {
 	var remoteOpts []remote.Option
 	var err error
 	signatureAlgorithmMap := map[string]crypto.Hash{
@@ -123,7 +123,7 @@ func buildCosignOptions(opts Options) (*cosign.CheckOpts, error) {
 	if err != nil {
 		return nil, errors.Wrap(err, "constructing client options")
 	}
-	remoteOpts = append(remoteOpts, registryclient.BuildRemoteOption(registryclient.DefaultClient))
+	remoteOpts = append(remoteOpts, rclient.BuildRemoteOption())
 	cosignOpts := &cosign.CheckOpts{
 		Annotations:        map[string]interface{}{},
 		RegistryClientOpts: remoteOpts,
@@ -254,8 +254,8 @@ func loadCertChain(pem []byte) ([]*x509.Certificate, error) {
 
 // FetchAttestations retrieves signed attestations and decodes them into in-toto statements
 // https://github.com/in-toto/attestation/blob/main/spec/README.md#statement
-func FetchAttestations(opts Options) (*Response, error) {
-	cosignOpts, err := buildCosignOptions(opts)
+func FetchAttestations(rclient registryclient.Client, opts Options) (*Response, error) {
+	cosignOpts, err := buildCosignOptions(rclient, opts)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/cosign/cosign_test.go b/pkg/cosign/cosign_test.go
index f3a2c584cd..e3967fca20 100644
--- a/pkg/cosign/cosign_test.go
+++ b/pkg/cosign/cosign_test.go
@@ -8,6 +8,7 @@ import (
 
 	v1 "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/types"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	"github.com/sigstore/cosign/pkg/cosign"
 	"github.com/sigstore/cosign/pkg/cosign/bundle"
 	"github.com/sigstore/cosign/pkg/oci"
@@ -76,15 +77,18 @@ func TestCosignKeyless(t *testing.T) {
 		Subject:  "jim",
 	}
 
-	_, err := VerifySignature(opts)
+	client, err := registryclient.New()
+	assert.NilError(t, err)
+
+	_, err = VerifySignature(client, opts)
 	assert.ErrorContains(t, err, "subject mismatch: expected jim, received jim@nirmata.com")
 
 	opts.Subject = "jim@nirmata.com"
-	_, err = VerifySignature(opts)
+	_, err = VerifySignature(client, opts)
 	assert.ErrorContains(t, err, "issuer mismatch: expected https://github.com/, received https://github.com/login/oauth")
 
 	opts.Issuer = "https://github.com/login/oauth"
-	_, err = VerifySignature(opts)
+	_, err = VerifySignature(client, opts)
 	assert.NilError(t, err)
 }
 
diff --git a/pkg/engine/background.go b/pkg/engine/background.go
index 3781961a66..74b6b95159 100644
--- a/pkg/engine/background.go
+++ b/pkg/engine/background.go
@@ -9,6 +9,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/engine/variables"
 	"github.com/kyverno/kyverno/pkg/logging"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 )
 
 // ApplyBackgroundChecks checks for validity of generate and mutateExisting rules on the resource
@@ -16,12 +17,12 @@ import (
 //   - the caller has to check the ruleResponse to determine whether the path exist
 //
 // 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed
-func ApplyBackgroundChecks(policyContext *PolicyContext) (resp *response.EngineResponse) {
+func ApplyBackgroundChecks(rclient registryclient.Client, policyContext *PolicyContext) (resp *response.EngineResponse) {
 	policyStartTime := time.Now()
-	return filterRules(policyContext, policyStartTime)
+	return filterRules(rclient, policyContext, policyStartTime)
 }
 
-func filterRules(policyContext *PolicyContext, startTime time.Time) *response.EngineResponse {
+func filterRules(rclient registryclient.Client, policyContext *PolicyContext, startTime time.Time) *response.EngineResponse {
 	kind := policyContext.newResource.GetKind()
 	name := policyContext.newResource.GetName()
 	namespace := policyContext.newResource.GetNamespace()
@@ -51,7 +52,7 @@ func filterRules(policyContext *PolicyContext, startTime time.Time) *response.En
 
 	applyRules := policyContext.policy.GetSpec().GetApplyRules()
 	for _, rule := range autogen.ComputeRules(policyContext.policy) {
-		if ruleResp := filterRule(rule, policyContext); ruleResp != nil {
+		if ruleResp := filterRule(rclient, rule, policyContext); ruleResp != nil {
 			resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
 			if applyRules == kyvernov1.ApplyOne && ruleResp.Status != response.RuleStatusSkip {
 				break
@@ -62,7 +63,7 @@ func filterRules(policyContext *PolicyContext, startTime time.Time) *response.En
 	return resp
 }
 
-func filterRule(rule kyvernov1.Rule, policyContext *PolicyContext) *response.RuleResponse {
+func filterRule(rclient registryclient.Client, rule kyvernov1.Rule, policyContext *PolicyContext) *response.RuleResponse {
 	if !rule.HasGenerate() && !rule.IsMutateExisting() {
 		return nil
 	}
@@ -108,7 +109,7 @@ func filterRule(rule kyvernov1.Rule, policyContext *PolicyContext) *response.Rul
 	policyContext.jsonContext.Checkpoint()
 	defer policyContext.jsonContext.Restore()
 
-	if err = LoadContext(logger, rule.Context, policyContext, rule.Name); err != nil {
+	if err = LoadContext(logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
 		logger.V(4).Info("cannot add external data to the context", "reason", err.Error())
 		return nil
 	}
diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go
index 521a6e17fe..c8e313f36f 100644
--- a/pkg/engine/generation.go
+++ b/pkg/engine/generation.go
@@ -7,16 +7,17 @@ import (
 	"github.com/kyverno/kyverno/pkg/autogen"
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/logging"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	"k8s.io/client-go/tools/cache"
 )
 
 // GenerateResponse checks for validity of generate rule on the resource
-func GenerateResponse(policyContext *PolicyContext, gr kyvernov1beta1.UpdateRequest) (resp *response.EngineResponse) {
+func GenerateResponse(rclient registryclient.Client, policyContext *PolicyContext, gr kyvernov1beta1.UpdateRequest) (resp *response.EngineResponse) {
 	policyStartTime := time.Now()
-	return filterGenerateRules(policyContext, gr.Spec.Policy, policyStartTime)
+	return filterGenerateRules(rclient, policyContext, gr.Spec.Policy, policyStartTime)
 }
 
-func filterGenerateRules(policyContext *PolicyContext, policyNameKey string, startTime time.Time) *response.EngineResponse {
+func filterGenerateRules(rclient registryclient.Client, policyContext *PolicyContext, policyNameKey string, startTime time.Time) *response.EngineResponse {
 	kind := policyContext.newResource.GetKind()
 	name := policyContext.newResource.GetName()
 	namespace := policyContext.newResource.GetNamespace()
@@ -50,7 +51,7 @@ func filterGenerateRules(policyContext *PolicyContext, policyNameKey string, sta
 	}
 
 	for _, rule := range autogen.ComputeRules(policyContext.policy) {
-		if ruleResp := filterRule(rule, policyContext); ruleResp != nil {
+		if ruleResp := filterRule(rclient, 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 fd7d87c166..a02fcb15a4 100644
--- a/pkg/engine/imageVerify.go
+++ b/pkg/engine/imageVerify.go
@@ -1,6 +1,7 @@
 package engine
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 	"net"
@@ -12,7 +13,7 @@ import (
 	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
 	"github.com/kyverno/kyverno/pkg/autogen"
 	"github.com/kyverno/kyverno/pkg/cosign"
-	"github.com/kyverno/kyverno/pkg/engine/context"
+	enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/engine/variables"
 	"github.com/kyverno/kyverno/pkg/logging"
@@ -62,7 +63,7 @@ func extractMatchingImages(policyContext *PolicyContext, rule *kyvernov1.Rule) (
 	return matchingImages, imageRefs, nil
 }
 
-func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineResponse, *ImageVerificationMetadata) {
+func VerifyAndPatchImages(rclient registryclient.Client, policyContext *PolicyContext) (*response.EngineResponse, *ImageVerificationMetadata) {
 	resp := &response.EngineResponse{}
 
 	policy := policyContext.policy
@@ -82,7 +83,7 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons
 	defer policyContext.jsonContext.Restore()
 
 	// update image registry secrets
-	if err := registryclient.DefaultClient.RefreshKeychainPullSecrets(); err != nil {
+	if err := rclient.RefreshKeychainPullSecrets(context.TODO()); err != nil {
 		logger.Error(err, "failed to update image pull secrets")
 	}
 
@@ -116,7 +117,7 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons
 		}
 
 		policyContext.jsonContext.Restore()
-		if err := LoadContext(logger, rule.Context, policyContext, rule.Name); err != nil {
+		if err := LoadContext(logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
 			appendResponse(resp, rule, fmt.Sprintf("failed to load context: %s", err.Error()), response.RuleStatusError)
 			continue
 		}
@@ -136,7 +137,7 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons
 		}
 
 		for _, imageVerify := range ruleCopy.VerifyImages {
-			iv.verify(imageVerify, ruleImages)
+			iv.verify(rclient, imageVerify, ruleImages)
 		}
 
 		if applyRules == kyvernov1.ApplyOne && resp.PolicyResponse.RulesAppliedCount > 0 {
@@ -153,7 +154,7 @@ func appendResponse(resp *response.EngineResponse, rule *kyvernov1.Rule, msg str
 	incrementErrorCount(resp)
 }
 
-func substituteVariables(rule *kyvernov1.Rule, ctx context.EvalInterface, logger logr.Logger) (*kyvernov1.Rule, error) {
+func substituteVariables(rule *kyvernov1.Rule, ctx enginecontext.EvalInterface, logger logr.Logger) (*kyvernov1.Rule, error) {
 	// remove attestations as variables are not substituted in them
 	ruleCopy := *rule.DeepCopy()
 	for i := range ruleCopy.VerifyImages {
@@ -184,7 +185,7 @@ type imageVerifier struct {
 
 // verify applies policy rules to each matching image. The policy rule results and annotation patches are
 // added to tme imageVerifier `resp` and `ivm` fields.
-func (iv *imageVerifier) verify(imageVerify kyvernov1.ImageVerification, matchedImageInfos []apiutils.ImageInfo) {
+func (iv *imageVerifier) verify(rclient registryclient.Client, imageVerify kyvernov1.ImageVerification, matchedImageInfos []apiutils.ImageInfo) {
 	// for backward compatibility
 	imageVerify = *imageVerify.Convert()
 
@@ -213,10 +214,10 @@ func (iv *imageVerifier) verify(imageVerify kyvernov1.ImageVerification, matched
 			continue
 		}
 
-		ruleResp, digest := iv.verifyImage(imageVerify, imageInfo)
+		ruleResp, digest := iv.verifyImage(rclient, imageVerify, imageInfo)
 
 		if imageVerify.MutateDigest {
-			patch, retrievedDigest, err := iv.handleMutateDigest(digest, imageInfo)
+			patch, retrievedDigest, err := iv.handleMutateDigest(rclient, digest, imageInfo)
 			if err != nil {
 				ruleResp = ruleError(iv.rule, response.ImageVerify, "failed to update digest", err)
 			} else if patch != nil {
@@ -242,13 +243,13 @@ func (iv *imageVerifier) verify(imageVerify kyvernov1.ImageVerification, matched
 	}
 }
 
-func (iv *imageVerifier) handleMutateDigest(digest string, imageInfo apiutils.ImageInfo) ([]byte, string, error) {
+func (iv *imageVerifier) handleMutateDigest(rclient registryclient.Client, digest string, imageInfo apiutils.ImageInfo) ([]byte, string, error) {
 	if imageInfo.Digest != "" {
 		return nil, "", nil
 	}
 
 	if digest == "" {
-		desc, err := registryclient.DefaultClient.FetchImageDescriptor(imageInfo.String())
+		desc, err := rclient.FetchImageDescriptor(context.TODO(), imageInfo.String())
 		if err != nil {
 			return nil, "", err
 		}
@@ -292,7 +293,7 @@ func imageMatches(image string, imagePatterns []string) bool {
 	return false
 }
 
-func (iv *imageVerifier) verifyImage(imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo) (*response.RuleResponse, string) {
+func (iv *imageVerifier) verifyImage(rclient registryclient.Client, imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo) (*response.RuleResponse, string) {
 	if len(imageVerify.Attestors) <= 0 && len(imageVerify.Attestations) <= 0 {
 		return nil, ""
 	}
@@ -308,16 +309,16 @@ func (iv *imageVerifier) verifyImage(imageVerify kyvernov1.ImageVerification, im
 	}
 
 	if len(imageVerify.Attestors) > 0 {
-		ruleResp, _, _ := iv.verifyAttestors(imageVerify.Attestors, imageVerify, imageInfo, "")
+		ruleResp, _, _ := iv.verifyAttestors(rclient, imageVerify.Attestors, imageVerify, imageInfo, "")
 		if ruleResp.Status != response.RuleStatusPass {
 			return ruleResp, ""
 		}
 	}
 
-	return iv.verifyAttestations(imageVerify, imageInfo)
+	return iv.verifyAttestations(rclient, imageVerify, imageInfo)
 }
 
-func (iv *imageVerifier) verifyAttestors(attestors []kyvernov1.AttestorSet, imageVerify kyvernov1.ImageVerification,
+func (iv *imageVerifier) verifyAttestors(rclient registryclient.Client, attestors []kyvernov1.AttestorSet, imageVerify kyvernov1.ImageVerification,
 	imageInfo apiutils.ImageInfo, predicateType string,
 ) (*response.RuleResponse, *cosign.Response, []kyvernov1.AttestorSet) {
 	var cosignResponse *cosign.Response
@@ -328,7 +329,7 @@ func (iv *imageVerifier) verifyAttestors(attestors []kyvernov1.AttestorSet, imag
 		var err error
 		path := fmt.Sprintf(".attestors[%d]", i)
 		iv.logger.V(4).Info("verifying attestors", "path", path)
-		cosignResponse, err = iv.verifyAttestorSet(attestorSet, imageVerify, imageInfo, path, predicateType)
+		cosignResponse, err = iv.verifyAttestorSet(rclient, attestorSet, imageVerify, imageInfo, path, predicateType)
 		if err != nil {
 			iv.logger.Error(err, "failed to verify image")
 			msg := fmt.Sprintf("failed to verify image %s: %s", image, err.Error())
@@ -352,7 +353,7 @@ func (iv *imageVerifier) verifyAttestors(attestors []kyvernov1.AttestorSet, imag
 	return ruleResponse(*iv.rule, response.ImageVerify, msg, response.RuleStatusPass, nil), cosignResponse, newAttestors
 }
 
-func (iv *imageVerifier) verifyAttestations(imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo) (*response.RuleResponse, string) {
+func (iv *imageVerifier) verifyAttestations(rclient registryclient.Client, imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo) (*response.RuleResponse, string) {
 	image := imageInfo.String()
 	for i, attestation := range imageVerify.Attestations {
 		var attestationError error
@@ -377,7 +378,7 @@ func (iv *imageVerifier) verifyAttestations(imageVerify kyvernov1.ImageVerificat
 			for _, a := range entries {
 				entryPath := fmt.Sprintf("%s.entries[%d]", attestorPath, i)
 				opts, subPath := iv.buildOptionsAndPath(a, imageVerify, image, attestation)
-				cosignResp, err := cosign.FetchAttestations(*opts)
+				cosignResp, err := cosign.FetchAttestations(rclient, *opts)
 				if err != nil {
 					iv.logger.Error(err, "failed to fetch attestations")
 					msg := fmt.Sprintf("failed to fetch attestations %s: %s", image, err.Error())
@@ -412,7 +413,7 @@ func (iv *imageVerifier) verifyAttestations(imageVerify kyvernov1.ImageVerificat
 	return ruleResponse(*iv.rule, response.ImageVerify, msg, response.RuleStatusPass, nil), ""
 }
 
-func (iv *imageVerifier) verifyAttestorSet(attestorSet kyvernov1.AttestorSet, imageVerify kyvernov1.ImageVerification,
+func (iv *imageVerifier) verifyAttestorSet(rclient registryclient.Client, attestorSet kyvernov1.AttestorSet, imageVerify kyvernov1.ImageVerification,
 	imageInfo apiutils.ImageInfo, path, predicateType string,
 ) (*cosign.Response, error) {
 	var errorList []error
@@ -433,11 +434,11 @@ func (iv *imageVerifier) verifyAttestorSet(attestorSet kyvernov1.AttestorSet, im
 				entryError = errors.Wrapf(err, "failed to unmarshal nested attestor %s", attestorPath)
 			} else {
 				attestorPath += ".attestor"
-				cosignResp, entryError = iv.verifyAttestorSet(*nestedAttestorSet, imageVerify, imageInfo, attestorPath, predicateType)
+				cosignResp, entryError = iv.verifyAttestorSet(rclient, *nestedAttestorSet, imageVerify, imageInfo, attestorPath, predicateType)
 			}
 		} else {
 			opts, subPath := iv.buildOptionsAndPath(a, imageVerify, image, kyvernov1.Attestation{PredicateType: predicateType})
-			cosignResp, entryError = cosign.VerifySignature(*opts)
+			cosignResp, entryError = cosign.VerifySignature(rclient, *opts)
 			if entryError != nil {
 				entryError = errors.Wrapf(entryError, attestorPath+subPath)
 			}
@@ -636,7 +637,7 @@ func (iv *imageVerifier) checkAttestations(a kyvernov1.Attestation, s map[string
 
 func evaluateConditions(
 	conditions []kyvernov1.AnyAllConditions,
-	ctx context.Interface,
+	ctx enginecontext.Interface,
 	s map[string]interface{},
 	log logr.Logger,
 ) (bool, error) {
@@ -645,7 +646,7 @@ func evaluateConditions(
 		return false, fmt.Errorf("failed to extract predicate from statement: %v", s)
 	}
 
-	if err := context.AddJSONObject(ctx, predicate); err != nil {
+	if err := enginecontext.AddJSONObject(ctx, predicate); err != nil {
 		return false, errors.Wrapf(err, fmt.Sprintf("failed to add Statement to the context %v", s))
 	}
 
diff --git a/pkg/engine/imageVerifyValidate.go b/pkg/engine/imageVerifyValidate.go
index ca1d7fd142..940d48115f 100644
--- a/pkg/engine/imageVerifyValidate.go
+++ b/pkg/engine/imageVerifyValidate.go
@@ -8,12 +8,13 @@ import (
 	gojmespath "github.com/jmespath/go-jmespath"
 	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
 	"github.com/kyverno/kyverno/pkg/engine/response"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	apiutils "github.com/kyverno/kyverno/pkg/utils/api"
 	"github.com/pkg/errors"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
 
-func processImageValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse {
+func processImageValidationRule(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse {
 	if isDeleteRequest(ctx) {
 		return nil
 	}
@@ -26,7 +27,7 @@ func processImageValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyver
 	if len(matchingImages) == 0 {
 		return ruleResponse(*rule, response.Validation, "image verified", response.RuleStatusSkip, nil)
 	}
-	if err := LoadContext(log, rule.Context, ctx, rule.Name); err != nil {
+	if err := LoadContext(log, rclient, rule.Context, ctx, rule.Name); err != nil {
 		if _, ok := err.(gojmespath.NotFoundError); ok {
 			log.V(3).Info("failed to load context", "reason", err.Error())
 		} else {
diff --git a/pkg/engine/imageVerify_test.go b/pkg/engine/imageVerify_test.go
index e4ead76a3f..887b587a2d 100644
--- a/pkg/engine/imageVerify_test.go
+++ b/pkg/engine/imageVerify_test.go
@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/kyverno/kyverno/pkg/logging"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	kubefake "k8s.io/client-go/kubernetes/fake"
 
@@ -149,7 +150,7 @@ func Test_CosignMockAttest(t *testing.T) {
 	err := cosign.SetMock("ghcr.io/jimbugwadia/pause2:latest", attestationPayloads)
 	assert.NilError(t, err)
 
-	er, ivm := VerifyAndPatchImages(policyContext)
+	er, ivm := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass,
 		fmt.Sprintf("expected: %v, got: %v, failure: %v",
@@ -163,7 +164,7 @@ func Test_CosignMockAttest_fail(t *testing.T) {
 	err := cosign.SetMock("ghcr.io/jimbugwadia/pause2:latest", attestationPayloads)
 	assert.NilError(t, err)
 
-	er, _ := VerifyAndPatchImages(policyContext)
+	er, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
 }
@@ -411,7 +412,7 @@ var testOtherKey = `-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
 func Test_ConfigMapMissingSuccess(t *testing.T) {
 	policyContext := buildContext(t, testConfigMapMissing, testConfigMapMissingResource, "")
 	cosign.ClearMock()
-	err, _ := VerifyAndPatchImages(policyContext)
+	err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(err.PolicyResponse.Rules), 1)
 	assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusSkip, err.PolicyResponse.Rules[0].Message)
 }
@@ -423,7 +424,7 @@ func Test_ConfigMapMissingFailure(t *testing.T) {
 	assert.NilError(t, err)
 	policyContext.informerCacheResolvers = resolver
 	cosign.ClearMock()
-	resp, _ := VerifyAndPatchImages(policyContext)
+	resp, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(resp.PolicyResponse.Rules), 1)
 	assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusError, resp.PolicyResponse.Rules[0].Message)
 }
@@ -431,7 +432,7 @@ func Test_ConfigMapMissingFailure(t *testing.T) {
 func Test_SignatureGoodSigned(t *testing.T) {
 	policyContext := buildContext(t, testSampleSingleKeyPolicy, testSampleResource, "")
 	cosign.ClearMock()
-	err, _ := VerifyAndPatchImages(policyContext)
+	err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(err.PolicyResponse.Rules), 1)
 	assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass, err.PolicyResponse.Rules[0].Message)
 }
@@ -440,7 +441,7 @@ func Test_SignatureUnsigned(t *testing.T) {
 	cosign.ClearMock()
 	unsigned := strings.Replace(testSampleResource, ":signed", ":unsigned", -1)
 	policyContext := buildContext(t, testSampleSingleKeyPolicy, unsigned, "")
-	err, _ := VerifyAndPatchImages(policyContext)
+	err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(err.PolicyResponse.Rules), 1)
 	assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail, err.PolicyResponse.Rules[0].Message)
 }
@@ -449,7 +450,7 @@ func Test_SignatureWrongKey(t *testing.T) {
 	cosign.ClearMock()
 	otherKey := strings.Replace(testSampleResource, ":signed", ":signed-by-someone-else", -1)
 	policyContext := buildContext(t, testSampleSingleKeyPolicy, otherKey, "")
-	err, _ := VerifyAndPatchImages(policyContext)
+	err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(err.PolicyResponse.Rules), 1)
 	assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail, err.PolicyResponse.Rules[0].Message)
 }
@@ -460,7 +461,7 @@ func Test_SignaturesMultiKey(t *testing.T) {
 	policy = strings.Replace(policy, "KEY2", testVerifyImageKey, -1)
 	policy = strings.Replace(policy, "COUNT", "0", -1)
 	policyContext := buildContext(t, policy, testSampleResource, "")
-	err, _ := VerifyAndPatchImages(policyContext)
+	err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(err.PolicyResponse.Rules), 1)
 	assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass, err.PolicyResponse.Rules[0].Message)
 }
@@ -470,7 +471,7 @@ func Test_SignaturesMultiKeyFail(t *testing.T) {
 	policy := strings.Replace(testSampleMultipleKeyPolicy, "KEY1", testVerifyImageKey, -1)
 	policy = strings.Replace(policy, "COUNT", "0", -1)
 	policyContext := buildContext(t, policy, testSampleResource, "")
-	err, _ := VerifyAndPatchImages(policyContext)
+	err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(err.PolicyResponse.Rules), 1)
 	assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail, err.PolicyResponse.Rules[0].Message)
 }
@@ -481,7 +482,7 @@ func Test_SignaturesMultiKeyOneGoodKey(t *testing.T) {
 	policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
 	policy = strings.Replace(policy, "COUNT", "1", -1)
 	policyContext := buildContext(t, policy, testSampleResource, "")
-	err, _ := VerifyAndPatchImages(policyContext)
+	err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(err.PolicyResponse.Rules), 1)
 	assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass, err.PolicyResponse.Rules[0].Message)
 }
@@ -492,7 +493,7 @@ func Test_SignaturesMultiKeyZeroGoodKey(t *testing.T) {
 	policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
 	policy = strings.Replace(policy, "COUNT", "1", -1)
 	policyContext := buildContext(t, policy, testSampleResource, "")
-	resp, _ := VerifyAndPatchImages(policyContext)
+	resp, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(resp.PolicyResponse.Rules), 1)
 	assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusFail, resp.PolicyResponse.Rules[0].Message)
 }
@@ -508,14 +509,14 @@ func Test_RuleSelectorImageVerify(t *testing.T) {
 	applyAll := kyverno.ApplyAll
 	spec.ApplyRules = &applyAll
 
-	resp, _ := VerifyAndPatchImages(policyContext)
+	resp, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(resp.PolicyResponse.Rules), 2)
 	assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusPass, resp.PolicyResponse.Rules[0].Message)
 	assert.Equal(t, resp.PolicyResponse.Rules[1].Status, response.RuleStatusFail, resp.PolicyResponse.Rules[1].Message)
 
 	applyOne := kyverno.ApplyOne
 	spec.ApplyRules = &applyOne
-	resp, _ = VerifyAndPatchImages(policyContext)
+	resp, _ = VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(resp.PolicyResponse.Rules), 1)
 	assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusPass, resp.PolicyResponse.Rules[0].Message)
 }
@@ -619,7 +620,7 @@ func Test_NestedAttestors(t *testing.T) {
 	policy = strings.Replace(policy, "KEY2", testVerifyImageKey, -1)
 	policy = strings.Replace(policy, "COUNT", "0", -1)
 	policyContext := buildContext(t, policy, testSampleResource, "")
-	err, _ := VerifyAndPatchImages(policyContext)
+	err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(err.PolicyResponse.Rules), 1)
 	assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
 
@@ -627,7 +628,7 @@ func Test_NestedAttestors(t *testing.T) {
 	policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
 	policy = strings.Replace(policy, "COUNT", "0", -1)
 	policyContext = buildContext(t, policy, testSampleResource, "")
-	err, _ = VerifyAndPatchImages(policyContext)
+	err, _ = VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(err.PolicyResponse.Rules), 1)
 	assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
 
@@ -635,7 +636,7 @@ func Test_NestedAttestors(t *testing.T) {
 	policy = strings.Replace(policy, "KEY2", testOtherKey, -1)
 	policy = strings.Replace(policy, "COUNT", "1", -1)
 	policyContext = buildContext(t, policy, testSampleResource, "")
-	err, _ = VerifyAndPatchImages(policyContext)
+	err, _ = VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(err.PolicyResponse.Rules), 1)
 	assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
 }
@@ -728,7 +729,7 @@ func Test_MarkImageVerified(t *testing.T) {
 	err := cosign.SetMock(image, attestationPayloads)
 	assert.NilError(t, err)
 
-	engineResponse, verifiedImages := VerifyAndPatchImages(policyContext)
+	engineResponse, verifiedImages := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Assert(t, engineResponse != nil)
 	assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1)
 	assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
@@ -821,7 +822,7 @@ func Test_ParsePEMDelimited(t *testing.T) {
 	err := cosign.SetMock(image, signaturePayloads)
 	assert.NilError(t, err)
 
-	engineResponse, verifiedImages := VerifyAndPatchImages(policyContext)
+	engineResponse, verifiedImages := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 	assert.Assert(t, engineResponse != nil)
 	assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1)
 	assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
diff --git a/pkg/engine/jsonContext.go b/pkg/engine/jsonContext.go
index 33bec04852..361c5c9c1a 100644
--- a/pkg/engine/jsonContext.go
+++ b/pkg/engine/jsonContext.go
@@ -14,7 +14,7 @@ import (
 )
 
 // LoadContext - Fetches and adds external data to the Context.
-func LoadContext(logger logr.Logger, contextEntries []kyvernov1.ContextEntry, ctx *PolicyContext, ruleName string) error {
+func LoadContext(logger logr.Logger, rclient registryclient.Client, contextEntries []kyvernov1.ContextEntry, ctx *PolicyContext, ruleName string) error {
 	if len(contextEntries) == 0 {
 		return nil
 	}
@@ -36,7 +36,8 @@ func LoadContext(logger logr.Logger, contextEntries []kyvernov1.ContextEntry, ct
 		// Context Variable should be loaded after the values loaded from values file
 		for _, entry := range contextEntries {
 			if entry.ImageRegistry != nil && hasRegistryAccess {
-				if err := loadImageData(logger, entry, ctx); err != nil {
+				// rclient := store.GetRegistryClient()
+				if err := loadImageData(rclient, logger, entry, ctx); err != nil {
 					return err
 				}
 			} else if entry.Variable != nil {
@@ -68,7 +69,7 @@ func LoadContext(logger logr.Logger, contextEntries []kyvernov1.ContextEntry, ct
 					return err
 				}
 			} else if entry.ImageRegistry != nil {
-				if err := loadImageData(logger, entry, ctx); err != nil {
+				if err := loadImageData(rclient, logger, entry, ctx); err != nil {
 					return err
 				}
 			} else if entry.Variable != nil {
@@ -140,11 +141,11 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC
 	}
 }
 
-func loadImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) error {
-	if err := registryclient.DefaultClient.RefreshKeychainPullSecrets(); err != nil {
+func loadImageData(rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) error {
+	if err := rclient.RefreshKeychainPullSecrets(context.TODO()); err != nil {
 		return fmt.Errorf("unable to load image registry credentials, %w", err)
 	}
-	imageData, err := fetchImageData(logger, entry, ctx)
+	imageData, err := fetchImageData(rclient, logger, entry, ctx)
 	if err != nil {
 		return err
 	}
@@ -158,7 +159,7 @@ func loadImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Policy
 	return nil
 }
 
-func fetchImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) (interface{}, error) {
+func fetchImageData(rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) (interface{}, error) {
 	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)
@@ -171,7 +172,7 @@ func fetchImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Polic
 	if err != nil {
 		return nil, fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.ImageRegistry.JMESPath, err)
 	}
-	imageData, err := fetchImageDataMap(refString)
+	imageData, err := fetchImageDataMap(rclient, refString)
 	if err != nil {
 		return nil, err
 	}
@@ -185,8 +186,8 @@ func fetchImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Polic
 }
 
 // FetchImageDataMap fetches image information from the remote registry.
-func fetchImageDataMap(ref string) (interface{}, error) {
-	desc, err := registryclient.DefaultClient.FetchImageDescriptor(ref)
+func fetchImageDataMap(rclient registryclient.Client, ref string) (interface{}, error) {
+	desc, err := rclient.FetchImageDescriptor(context.TODO(), ref)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go
index 8f53219abd..6f94dbbfc0 100644
--- a/pkg/engine/mutation.go
+++ b/pkg/engine/mutation.go
@@ -13,11 +13,12 @@ import (
 	"github.com/kyverno/kyverno/pkg/engine/mutate"
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/logging"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
 
 // Mutate performs mutation. Overlay first and then mutation patches
-func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
+func Mutate(rclient registryclient.Client, policyContext *PolicyContext) (resp *response.EngineResponse) {
 	startTime := time.Now()
 	policy := policyContext.policy
 	resp = &response.EngineResponse{
@@ -69,7 +70,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
 			logger.Error(err, "failed to query resource object")
 		}
 
-		if err := LoadContext(logger, rule.Context, policyContext, rule.Name); err != nil {
+		if err := LoadContext(logger, rclient, rule.Context, policyContext, rule.Name); err != nil {
 			if _, ok := err.(gojmespath.NotFoundError); ok {
 				logger.V(3).Info("failed to load context", "reason", err.Error())
 			} else {
@@ -108,7 +109,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
 			logger.V(4).Info("apply rule to resource", "rule", rule.Name, "resource namespace", patchedResource.GetNamespace(), "resource name", patchedResource.GetName())
 			var ruleResp *response.RuleResponse
 			if rule.Mutation.ForEachMutation != nil {
-				ruleResp, patchedResource = mutateForEach(ruleCopy, policyContext, patchedResource, logger)
+				ruleResp, patchedResource = mutateForEach(rclient, ruleCopy, policyContext, patchedResource, logger)
 			} else {
 				ruleResp, patchedResource = mutateResource(ruleCopy, policyContext, patchedResource, logger)
 			}
@@ -158,7 +159,7 @@ func mutateResource(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructu
 	return ruleResp, mutateResp.PatchedResource
 }
 
-func mutateForEach(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructured.Unstructured, logger logr.Logger) (*response.RuleResponse, unstructured.Unstructured) {
+func mutateForEach(rclient registryclient.Client, rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructured.Unstructured, logger logr.Logger) (*response.RuleResponse, unstructured.Unstructured) {
 	foreachList := rule.Mutation.ForEachMutation
 	if foreachList == nil {
 		return nil, resource
@@ -169,7 +170,7 @@ func mutateForEach(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructur
 	allPatches := make([][]byte, 0)
 
 	for _, foreach := range foreachList {
-		if err := LoadContext(logger, rule.Context, ctx, rule.Name); err != nil {
+		if err := LoadContext(logger, rclient, rule.Context, ctx, rule.Name); err != nil {
 			logger.Error(err, "failed to load context")
 			return ruleError(rule, response.Mutation, "failed to load context", err), resource
 		}
@@ -189,7 +190,7 @@ func mutateForEach(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructur
 			return ruleError(rule, response.Mutation, msg, err), resource
 		}
 
-		mutateResp := mutateElements(rule.Name, foreach, ctx, elements, patchedResource, logger)
+		mutateResp := mutateElements(rclient, rule.Name, foreach, ctx, elements, patchedResource, logger)
 		if mutateResp.Status == response.RuleStatusError {
 			logger.Error(err, "failed to mutate elements")
 			return buildRuleResponse(rule, mutateResp, nil), resource
@@ -213,7 +214,7 @@ func mutateForEach(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructur
 	return r, patchedResource
 }
 
-func mutateElements(name string, foreach kyvernov1.ForEachMutation, ctx *PolicyContext, elements []interface{}, resource unstructured.Unstructured, logger logr.Logger) *mutate.Response {
+func mutateElements(rclient registryclient.Client, name string, foreach kyvernov1.ForEachMutation, ctx *PolicyContext, elements []interface{}, resource unstructured.Unstructured, logger logr.Logger) *mutate.Response {
 	ctx.jsonContext.Checkpoint()
 	defer ctx.jsonContext.Restore()
 
@@ -235,7 +236,7 @@ func mutateElements(name string, foreach kyvernov1.ForEachMutation, ctx *PolicyC
 			return mutateError(err, fmt.Sprintf("failed to add element to mutate.foreach[%d].context", i))
 		}
 
-		if err := LoadContext(logger, foreach.Context, ctx, name); err != nil {
+		if err := LoadContext(logger, rclient, foreach.Context, ctx, name); err != nil {
 			return mutateError(err, fmt.Sprintf("failed to load to mutate.foreach[%d].context", i))
 		}
 
diff --git a/pkg/engine/mutation_test.go b/pkg/engine/mutation_test.go
index 2f18417bfc..ec39af0517 100644
--- a/pkg/engine/mutation_test.go
+++ b/pkg/engine/mutation_test.go
@@ -13,6 +13,7 @@ import (
 	enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/engine/utils"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	"gotest.tools/assert"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -93,7 +94,7 @@ func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) {
 		policy:      &policy,
 		jsonContext: ctx,
 		newResource: *resourceUnstructured}
-	er := Mutate(policyContext)
+	er := Mutate(registryclient.NewOrDie(), policyContext)
 	t.Log(string(expectedPatch))
 
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
@@ -166,7 +167,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) {
 		policy:      &policy,
 		jsonContext: ctx,
 		newResource: *resourceUnstructured}
-	er := Mutate(policyContext)
+	er := Mutate(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message, "Unknown key \"name1\" in path"))
 }
@@ -263,7 +264,7 @@ func Test_variableSubstitutionCLI(t *testing.T) {
 		newResource: *resourceUnstructured,
 	}
 
-	er := Mutate(policyContext)
+	er := Mutate(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1)
 	t.Log(string(expectedPatch))
@@ -372,7 +373,7 @@ func Test_chained_rules(t *testing.T) {
 	err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
 	assert.NilError(t, err)
 
-	er := Mutate(policyContext)
+	er := Mutate(registryclient.NewOrDie(), policyContext)
 	containers, _, err := unstructured.NestedSlice(er.PatchedResource.Object, "spec", "containers")
 	assert.NilError(t, err)
 	assert.Equal(t, containers[0].(map[string]interface{})["image"], "otherregistry.corp.com/foo/bash:5.0")
@@ -460,7 +461,7 @@ func Test_precondition(t *testing.T) {
 		newResource: *resourceUnstructured,
 	}
 
-	er := Mutate(policyContext)
+	er := Mutate(registryclient.NewOrDie(), policyContext)
 	t.Log(string(expectedPatch))
 	t.Log(string(er.PolicyResponse.Rules[0].Patches[0]))
 	if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) {
@@ -557,7 +558,7 @@ func Test_nonZeroIndexNumberPatchesJson6902(t *testing.T) {
 		newResource: *resourceUnstructured,
 	}
 
-	er := Mutate(policyContext)
+	er := Mutate(registryclient.NewOrDie(), policyContext)
 	t.Log(string(expectedPatch))
 	t.Log(string(er.PolicyResponse.Rules[0].Patches[0]))
 	if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) {
@@ -651,7 +652,7 @@ func Test_foreach(t *testing.T) {
 	err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
 	assert.NilError(t, err)
 
-	er := Mutate(policyContext)
+	er := Mutate(registryclient.NewOrDie(), policyContext)
 
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
@@ -758,7 +759,7 @@ func Test_foreach_element_mutation(t *testing.T) {
 	err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
 	assert.NilError(t, err)
 
-	er := Mutate(policyContext)
+	er := Mutate(registryclient.NewOrDie(), policyContext)
 
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
@@ -884,7 +885,7 @@ func Test_Container_InitContainer_foreach(t *testing.T) {
 	err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
 	assert.NilError(t, err)
 
-	er := Mutate(policyContext)
+	er := Mutate(registryclient.NewOrDie(), policyContext)
 
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
@@ -1011,7 +1012,7 @@ func Test_foreach_order_mutation_(t *testing.T) {
 	err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx)
 	assert.NilError(t, err)
 
-	er := Mutate(policyContext)
+	er := Mutate(registryclient.NewOrDie(), policyContext)
 
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass)
@@ -1457,7 +1458,7 @@ func Test_mutate_existing_resources(t *testing.T) {
 				newResource: *trigger,
 			}
 		}
-		er := Mutate(policyContext)
+		er := Mutate(registryclient.NewOrDie(), policyContext)
 
 		for _, rr := range er.PolicyResponse.Rules {
 			for i, p := range rr.Patches {
@@ -1565,7 +1566,7 @@ func Test_RuleSelectorMutate(t *testing.T) {
 		newResource: *resourceUnstructured,
 	}
 
-	er := Mutate(policyContext)
+	er := Mutate(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(er.PolicyResponse.Rules), 2)
 	assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1)
 	assert.Equal(t, len(er.PolicyResponse.Rules[1].Patches), 1)
@@ -1580,7 +1581,7 @@ func Test_RuleSelectorMutate(t *testing.T) {
 	applyOne := kyverno.ApplyOne
 	policyContext.policy.GetSpec().ApplyRules = &applyOne
 
-	er = Mutate(policyContext)
+	er = Mutate(registryclient.NewOrDie(), policyContext)
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1)
 
@@ -1947,7 +1948,7 @@ func Test_SpecialCharacters(t *testing.T) {
 			}
 
 			// Mutate and make sure that we got the expected amount of rules.
-			patches := Mutate(policyContext).GetPatches()
+			patches := Mutate(registryclient.NewOrDie(), policyContext).GetPatches()
 			if !reflect.DeepEqual(patches, tt.want) {
 				t.Errorf("Mutate() got patches %s, expected %s", patches, tt.want)
 			}
diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go
index ae03700af8..c2902c2ec8 100644
--- a/pkg/engine/validation.go
+++ b/pkg/engine/validation.go
@@ -19,6 +19,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/engine/variables"
 	"github.com/kyverno/kyverno/pkg/logging"
 	"github.com/kyverno/kyverno/pkg/pss"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	"github.com/kyverno/kyverno/pkg/utils"
 	"github.com/pkg/errors"
 	appsv1 "k8s.io/api/apps/v1"
@@ -30,7 +31,7 @@ import (
 )
 
 // Validate applies validation rules from policy on the resource
-func Validate(policyContext *PolicyContext) (resp *response.EngineResponse) {
+func Validate(rclient registryclient.Client, policyContext *PolicyContext) (resp *response.EngineResponse) {
 	resp = &response.EngineResponse{}
 	startTime := time.Now()
 
@@ -41,7 +42,7 @@ func Validate(policyContext *PolicyContext) (resp *response.EngineResponse) {
 		logger.V(4).Info("finished policy processing", "processingTime", resp.PolicyResponse.ProcessingTime.String(), "validationRulesApplied", resp.PolicyResponse.RulesAppliedCount)
 	}()
 
-	resp = validateResource(logger, policyContext)
+	resp = validateResource(logger, rclient, policyContext)
 	return
 }
 
@@ -88,7 +89,7 @@ func buildResponse(ctx *PolicyContext, resp *response.EngineResponse, startTime
 	resp.PolicyResponse.PolicyExecutionTimestamp = startTime.Unix()
 }
 
-func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineResponse {
+func validateResource(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext) *response.EngineResponse {
 	resp := &response.EngineResponse{}
 
 	ctx.jsonContext.Checkpoint()
@@ -128,9 +129,9 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
 
 		var ruleResp *response.RuleResponse
 		if hasValidate && !hasYAMLSignatureVerify {
-			ruleResp = processValidationRule(log, ctx, rule)
+			ruleResp = processValidationRule(log, rclient, ctx, rule)
 		} else if hasValidateImage {
-			ruleResp = processImageValidationRule(log, ctx, rule)
+			ruleResp = processImageValidationRule(log, rclient, ctx, rule)
 		} else if hasYAMLSignatureVerify {
 			ruleResp = processYAMLValidationRule(log, ctx, rule)
 		}
@@ -146,7 +147,7 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
 	return resp
 }
 
-func validateOldObject(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) (*response.RuleResponse, error) {
+func validateOldObject(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) (*response.RuleResponse, error) {
 	ctxCopy := ctx.Copy()
 	ctxCopy.newResource = *ctxCopy.oldResource.DeepCopy()
 	ctxCopy.oldResource = unstructured.Unstructured{}
@@ -159,11 +160,11 @@ func validateOldObject(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule
 		return nil, errors.Wrapf(err, "failed to replace old object in the JSON context")
 	}
 
-	return processValidationRule(log, ctxCopy, rule), nil
+	return processValidationRule(log, rclient, ctxCopy, rule), nil
 }
 
-func processValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse {
-	v := newValidator(log, ctx, rule)
+func processValidationRule(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse {
+	v := newValidator(log, rclient, ctx, rule)
 	if rule.Validation.ForEachValidation != nil {
 		return v.validateForEach()
 	}
@@ -195,9 +196,10 @@ type validator struct {
 	anyPattern       apiextensions.JSON
 	deny             *kyvernov1.Deny
 	podSecurity      *kyvernov1.PodSecurity
+	rclient          registryclient.Client
 }
 
-func newValidator(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) *validator {
+func newValidator(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) *validator {
 	ruleCopy := rule.DeepCopy()
 	return &validator{
 		log:              log,
@@ -209,10 +211,11 @@ func newValidator(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) *va
 		anyPattern:       ruleCopy.Validation.GetAnyPattern(),
 		deny:             ruleCopy.Validation.Deny,
 		podSecurity:      ruleCopy.Validation.PodSecurity,
+		rclient:          rclient,
 	}
 }
 
-func newForeachValidator(foreach kyvernov1.ForEachValidation, rule *kyvernov1.Rule, ctx *PolicyContext, log logr.Logger) *validator {
+func newForeachValidator(log logr.Logger, rclient registryclient.Client, foreach kyvernov1.ForEachValidation, rule *kyvernov1.Rule, ctx *PolicyContext) *validator {
 	ruleCopy := rule.DeepCopy()
 	anyAllConditions, err := utils.ToMap(foreach.AnyAllConditions)
 	if err != nil {
@@ -228,6 +231,7 @@ func newForeachValidator(foreach kyvernov1.ForEachValidation, rule *kyvernov1.Ru
 		pattern:          foreach.GetPattern(),
 		anyPattern:       foreach.GetAnyPattern(),
 		deny:             foreach.Deny,
+		rclient:          rclient,
 	}
 }
 
@@ -256,7 +260,7 @@ func (v *validator) validate() *response.RuleResponse {
 
 		ruleResponse := v.validateResourceWithRule()
 		if isUpdateRequest(v.ctx) {
-			priorResp, err := validateOldObject(v.log, v.ctx, v.rule)
+			priorResp, err := validateOldObject(v.log, v.rclient, v.ctx, v.rule)
 			if err != nil {
 				return ruleError(v.rule, response.Validation, "failed to validate old object", err)
 			}
@@ -338,7 +342,7 @@ func (v *validator) validateElements(foreach kyvernov1.ForEachValidation, elemen
 			return ruleError(v.rule, response.Validation, "failed to process foreach", err), applyCount
 		}
 
-		foreachValidator := newForeachValidator(foreach, v.rule, ctx, v.log)
+		foreachValidator := newForeachValidator(v.log, v.rclient, foreach, v.rule, ctx)
 		r := foreachValidator.validate()
 		if r == nil {
 			v.log.V(2).Info("skip rule due to empty result")
@@ -398,7 +402,7 @@ func addElementToContext(ctx *PolicyContext, e interface{}, elementIndex int, el
 }
 
 func (v *validator) loadContext() error {
-	if err := LoadContext(v.log, v.contextEntries, v.ctx, v.rule.Name); err != nil {
+	if err := LoadContext(v.log, v.rclient, v.contextEntries, v.ctx, v.rule.Name); err != nil {
 		if _, ok := err.(gojmespath.NotFoundError); ok {
 			v.log.V(3).Info("failed to load context", "reason", err.Error())
 		} else {
diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go
index 0437e2576c..ea46946b11 100644
--- a/pkg/engine/validation_test.go
+++ b/pkg/engine/validation_test.go
@@ -11,6 +11,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/engine/context"
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/engine/utils"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	utils2 "github.com/kyverno/kyverno/pkg/utils"
 	"gotest.tools/assert"
 	admissionv1 "k8s.io/api/admission/v1"
@@ -131,7 +132,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(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	for index, r := range er.PolicyResponse.Rules {
 		assert.Equal(t, r.Message, msgs[index])
 	}
@@ -231,7 +232,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(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	for index, r := range er.PolicyResponse.Rules {
 		assert.Equal(t, r.Message, msgs[index])
 	}
@@ -305,7 +306,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(registryclient.NewOrDie(), &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 +389,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(registryclient.NewOrDie(), &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 +479,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(registryclient.NewOrDie(), &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 +567,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(registryclient.NewOrDie(), &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 +637,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(registryclient.NewOrDie(), &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 +710,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(registryclient.NewOrDie(), &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 +784,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(registryclient.NewOrDie(), &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 +864,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(registryclient.NewOrDie(), &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 +938,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(registryclient.NewOrDie(), &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 +1013,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(registryclient.NewOrDie(), &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 +1088,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(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	assert.Assert(t, !er.IsSuccessful())
 }
 
@@ -1157,7 +1158,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(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	assert.Assert(t, !er.IsSuccessful())
 }
 
@@ -1227,7 +1228,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(registryclient.NewOrDie(), &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 +1316,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(registryclient.NewOrDie(), &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 +1403,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(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	msgs := []string{"validation rule 'validate-host-path' passed."}
 
 	for index, r := range er.PolicyResponse.Rules {
@@ -1478,7 +1479,7 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) {
 		policy:      &policy,
 		jsonContext: ctx,
 		newResource: *resourceUnstructured}
-	er := Validate(policyContext)
+	er := Validate(registryclient.NewOrDie(), policyContext)
 
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusError)
@@ -1571,7 +1572,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSu
 		policy:      &policy,
 		jsonContext: ctx,
 		newResource: *resourceUnstructured}
-	er := Validate(policyContext)
+	er := Validate(registryclient.NewOrDie(), policyContext)
 
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusError)
@@ -1632,7 +1633,7 @@ func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) {
 		policy:      &policy,
 		jsonContext: ctx,
 		newResource: *resourceUnstructured}
-	er := Validate(policyContext)
+	er := Validate(registryclient.NewOrDie(), 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/")
 }
@@ -1723,7 +1724,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test
 		policy:      &policy,
 		jsonContext: ctx,
 		newResource: *resourceUnstructured}
-	er := Validate(policyContext)
+	er := Validate(registryclient.NewOrDie(), policyContext)
 
 	assert.Equal(t, len(er.PolicyResponse.Rules), 1)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusError)
@@ -1816,7 +1817,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter
 		policy:      &policy,
 		jsonContext: ctx,
 		newResource: *resourceUnstructured}
-	er := Validate(policyContext)
+	er := Validate(registryclient.NewOrDie(), policyContext)
 
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
 	assert.Equal(t, er.PolicyResponse.Rules[0].Message,
@@ -1921,7 +1922,7 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing.
 		policy:      &policy,
 		jsonContext: ctx,
 		newResource: *resourceUnstructured}
-	er := Validate(policyContext)
+	er := Validate(registryclient.NewOrDie(), 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.")
 }
@@ -1974,7 +1975,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
 			policy:      &policy,
 			jsonContext: ctx,
 			newResource: *resourceUnstructured}
-		er := Validate(policyContext)
+		er := Validate(registryclient.NewOrDie(), policyContext)
 
 		for i, rule := range er.PolicyResponse.Rules {
 			assert.Equal(t, er.PolicyResponse.Rules[i].Status, test.expectedResults[i], "\ntest %s failed\nexpected: %s\nactual: %s", test.name, test.expectedResults[i].String(), er.PolicyResponse.Rules[i].Status.String())
@@ -2140,7 +2141,7 @@ func executeTest(t *testing.T, test testCase) {
 		jsonContext:   ctx,
 	}
 
-	resp := Validate(pc)
+	resp := Validate(registryclient.NewOrDie(), pc)
 	if resp.IsSuccessful() && test.requestDenied {
 		t.Errorf("Testcase has failed, policy: %v", policy.Name)
 	}
@@ -2239,7 +2240,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(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()})
 	for index, r := range er.PolicyResponse.Rules {
 		assert.Equal(t, r.Message, msgs[index])
 	}
@@ -2328,7 +2329,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(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx})
 	assert.Assert(t, !er.IsSuccessful())
 }
 
@@ -2417,7 +2418,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(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx})
 	assert.Assert(t, er.IsSuccessful())
 }
 
@@ -3004,7 +3005,7 @@ func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string,
 		policy:      &policy,
 		jsonContext: ctx,
 		newResource: *resourceUnstructured}
-	er := Validate(policyContext)
+	er := Validate(registryclient.NewOrDie(), policyContext)
 
 	assert.Equal(t, er.PolicyResponse.Rules[0].Status, status)
 	if msg != "" {
@@ -3068,7 +3069,7 @@ func Test_delete_ignore_pattern(t *testing.T) {
 		policy:      &policy,
 		jsonContext: ctx,
 		newResource: *resourceUnstructured}
-	engineResponseCreate := Validate(policyContextCreate)
+	engineResponseCreate := Validate(registryclient.NewOrDie(), policyContextCreate)
 	assert.Equal(t, len(engineResponseCreate.PolicyResponse.Rules), 1)
 	assert.Equal(t, engineResponseCreate.PolicyResponse.Rules[0].Status, response.RuleStatusFail)
 
@@ -3076,7 +3077,7 @@ func Test_delete_ignore_pattern(t *testing.T) {
 		policy:      &policy,
 		jsonContext: ctx,
 		oldResource: *resourceUnstructured}
-	engineResponseDelete := Validate(policyContextDelete)
+	engineResponseDelete := Validate(registryclient.NewOrDie(), policyContextDelete)
 	assert.Equal(t, len(engineResponseDelete.PolicyResponse.Rules), 0)
 }
 
@@ -3135,7 +3136,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(registryclient.NewOrDie(), &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 2e1e5db47a..346f173a9d 100644
--- a/pkg/policy/apply.go
+++ b/pkg/policy/apply.go
@@ -13,14 +13,20 @@ import (
 	"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/registryclient"
 	jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
 
 // applyPolicy applies policy on a resource
-func applyPolicy(policy kyvernov1.PolicyInterface, resource unstructured.Unstructured,
-	logger logr.Logger, excludeGroupRole []string,
-	client dclient.Interface, namespaceLabels map[string]string,
+func applyPolicy(
+	policy kyvernov1.PolicyInterface,
+	resource unstructured.Unstructured,
+	logger logr.Logger,
+	excludeGroupRole []string,
+	client dclient.Interface,
+	rclient registryclient.Client,
+	namespaceLabels map[string]string,
 ) (responses []*response.EngineResponse) {
 	startTime := time.Now()
 	defer func() {
@@ -56,7 +62,7 @@ func applyPolicy(policy kyvernov1.PolicyInterface, resource unstructured.Unstruc
 		logger.Error(err, "unable to set operation in context")
 	}
 
-	engineResponseMutation, err = mutation(policy, resource, logger, ctx, namespaceLabels)
+	engineResponseMutation, err = mutation(policy, resource, logger, ctx, rclient, namespaceLabels)
 	if err != nil {
 		logger.Error(err, "failed to process mutation rule")
 	}
@@ -68,19 +74,26 @@ func applyPolicy(policy kyvernov1.PolicyInterface, resource unstructured.Unstruc
 		WithClient(client).
 		WithExcludeGroupRole(excludeGroupRole...)
 
-	engineResponseValidation = engine.Validate(policyCtx)
+	engineResponseValidation = engine.Validate(rclient, policyCtx)
 	engineResponses = append(engineResponses, mergeRuleRespose(engineResponseMutation, engineResponseValidation))
 
 	return engineResponses
 }
 
-func mutation(policy kyvernov1.PolicyInterface, resource unstructured.Unstructured, log logr.Logger, jsonContext context.Interface, namespaceLabels map[string]string) (*response.EngineResponse, error) {
+func mutation(
+	policy kyvernov1.PolicyInterface,
+	resource unstructured.Unstructured,
+	log logr.Logger,
+	jsonContext context.Interface,
+	rclient registryclient.Client,
+	namespaceLabels map[string]string,
+) (*response.EngineResponse, error) {
 	policyContext := engine.NewPolicyContextWithJsonContext(jsonContext).
 		WithPolicy(policy).
 		WithNamespaceLabels(namespaceLabels).
 		WithNewResource(resource)
 
-	engineResponse := engine.Mutate(policyContext)
+	engineResponse := engine.Mutate(rclient, policyContext)
 	if !engineResponse.IsSuccessful() {
 		log.V(4).Info("failed to apply mutation rules; reporting them")
 		return engineResponse, nil
diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go
index 4b67e55e3c..e59c2cc2f3 100644
--- a/pkg/policy/existing.go
+++ b/pkg/policy/existing.go
@@ -81,7 +81,7 @@ func (pc *PolicyController) applyPolicy(policy kyvernov1.PolicyInterface, resour
 	}
 
 	namespaceLabels := common.GetNamespaceSelectorsFromNamespaceLister(resource.GetKind(), resource.GetNamespace(), pc.nsLister, logger)
-	engineResponse := applyPolicy(policy, resource, logger, pc.configHandler.GetExcludeGroupRole(), pc.client, namespaceLabels)
+	engineResponse := applyPolicy(policy, resource, logger, pc.configHandler.GetExcludeGroupRole(), pc.client, pc.rclient, namespaceLabels)
 	engineResponses = append(engineResponses, engineResponse...)
 
 	// post-processing, register the resource as processed
diff --git a/pkg/policy/policy_controller.go b/pkg/policy/policy_controller.go
index 9aa37f8cb0..ac8c886102 100644
--- a/pkg/policy/policy_controller.go
+++ b/pkg/policy/policy_controller.go
@@ -24,6 +24,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/config"
 	"github.com/kyverno/kyverno/pkg/event"
 	"github.com/kyverno/kyverno/pkg/metrics"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
 	"golang.org/x/exp/slices"
 	corev1 "k8s.io/api/core/v1"
@@ -53,8 +54,10 @@ const (
 type PolicyController struct {
 	client        dclient.Interface
 	kyvernoClient versioned.Interface
-	pInformer     kyvernov1informers.ClusterPolicyInformer
-	npInformer    kyvernov1informers.PolicyInformer
+	rclient       registryclient.Client
+
+	pInformer  kyvernov1informers.ClusterPolicyInformer
+	npInformer kyvernov1informers.PolicyInformer
 
 	eventGen      event.Interface
 	eventRecorder record.EventRecorder
@@ -93,6 +96,7 @@ type PolicyController struct {
 func NewPolicyController(
 	kyvernoClient versioned.Interface,
 	client dclient.Interface,
+	rclient registryclient.Client,
 	pInformer kyvernov1informers.ClusterPolicyInformer,
 	npInformer kyvernov1informers.PolicyInformer,
 	urInformer kyvernov1beta1informers.UpdateRequestInformer,
@@ -112,6 +116,7 @@ func NewPolicyController(
 	pc := PolicyController{
 		client:          client,
 		kyvernoClient:   kyvernoClient,
+		rclient:         rclient,
 		pInformer:       pInformer,
 		npInformer:      npInformer,
 		eventGen:        eventGen,
diff --git a/pkg/policy/updaterequest.go b/pkg/policy/updaterequest.go
index 5bc2ff6e8d..ab44c6ed4e 100644
--- a/pkg/policy/updaterequest.go
+++ b/pkg/policy/updaterequest.go
@@ -105,7 +105,7 @@ func (pc *PolicyController) handleUpdateRequest(ur *kyvernov1beta1.UpdateRequest
 		return false, errors.Wrapf(err, "failed to build policy context for rule %s", rule.Name)
 	}
 
-	engineResponse := engine.ApplyBackgroundChecks(policyContext)
+	engineResponse := engine.ApplyBackgroundChecks(pc.rclient, policyContext)
 	if len(engineResponse.PolicyResponse.Rules) == 0 {
 		return true, nil
 	}
diff --git a/pkg/registryclient/client.go b/pkg/registryclient/client.go
index 7e0369cb96..207114c3ea 100644
--- a/pkg/registryclient/client.go
+++ b/pkg/registryclient/client.go
@@ -11,51 +11,55 @@ import (
 	"github.com/chrismellard/docker-credential-acr-env/pkg/credhelper"
 	"github.com/google/go-containerregistry/pkg/authn"
 	"github.com/google/go-containerregistry/pkg/authn/github"
-	kauth "github.com/google/go-containerregistry/pkg/authn/kubernetes"
 	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/google/go-containerregistry/pkg/v1/google"
 	gcrremote "github.com/google/go-containerregistry/pkg/v1/remote"
-	"github.com/pkg/errors"
 	"github.com/sigstore/cosign/pkg/oci/remote"
 	"k8s.io/client-go/kubernetes"
 )
 
-// DefaultClient is default registry client.
-var DefaultClient, _ = InitClient()
+var baseKeychain = authn.NewMultiKeychain(
+	authn.DefaultKeychain,
+	google.Keychain,
+	authn.NewKeychainFromHelper(ecr.NewECRHelper(ecr.WithLogger(io.Discard))),
+	authn.NewKeychainFromHelper(credhelper.NewACRCredentialsHelper()),
+	github.Keychain,
+)
 
 // Client provides registry related objects.
 type Client interface {
-	// Keychain provides keychain object.
-	Keychain() authn.Keychain
+	// getKeychain provides keychain object.
+	getKeychain() authn.Keychain
 
-	// Transport provides transport object.
-	Transport() *http.Transport
+	// getTransport provides transport object.
+	getTransport() http.RoundTripper
 
 	// FetchImageDescriptor fetches Descriptor from registry with given imageRef
 	// and provides access to metadata about remote artifact.
-	FetchImageDescriptor(imageRef string) (*gcrremote.Descriptor, error)
+	FetchImageDescriptor(context.Context, string) (*gcrremote.Descriptor, error)
 
-	// UseLocalKeychain updates keychain with the default local keychain.
-	UseLocalKeychain()
+	// // RefreshKeychainPullSecrets loads fresh data from pull secrets and updates Keychain.
+	// // If pull secrets are empty - returns.
+	RefreshKeychainPullSecrets(context.Context) error
 
-	// RefreshKeychainPullSecrets loads fresh data from pull secrets and updates Keychain.
-	// If pull secrets are empty - returns.
-	RefreshKeychainPullSecrets() error
+	// BuildRemoteOption builds remote.Option based on client.
+	BuildRemoteOption() remote.Option
 }
 
-// InitClient initialize registry client with given options.
-func InitClient(options ...Option) (Client, error) {
-	baseKeychain := authn.NewMultiKeychain(
-		authn.DefaultKeychain,
-		google.Keychain,
-		authn.NewKeychainFromHelper(ecr.NewECRHelper(ecr.WithLogger(io.Discard))),
-		authn.NewKeychainFromHelper(credhelper.NewACRCredentialsHelper()),
-		github.Keychain,
-	)
+type client struct {
+	keychain            authn.Keychain
+	transport           *http.Transport
+	pullSecretRefresher func(context.Context, *client) error
+}
+
+// Option is an option to initialize registry client.
+type Option = func(*client) error
+
+// New creates a new Client with options
+func New(options ...Option) (Client, error) {
 	c := &client{
-		keychain:     baseKeychain,
-		baseKeychain: baseKeychain,
-		transport:    gcrremote.DefaultTransport.(*http.Transport),
+		keychain:  baseKeychain,
+		transport: gcrremote.DefaultTransport.(*http.Transport),
 	}
 	for _, opt := range options {
 		if err := opt(c); err != nil {
@@ -65,28 +69,30 @@ func InitClient(options ...Option) (Client, error) {
 	return c, nil
 }
 
-// Option is an option to initialize registry client.
-type Option func(*client) error
+// New creates a new Client with options
+func NewOrDie(options ...Option) Client {
+	c, err := New(options...)
+	if err != nil {
+		panic(err)
+	}
+	return c
+}
 
 // WithKeychainPullSecrets provides initialize registry client option that allows to use pull secrets.
-func WithKeychainPullSecrets(kubClient kubernetes.Interface, namespace, serviceAccount string, imagePullSecrets []string) Option {
+func WithKeychainPullSecrets(ctx context.Context, kubClient kubernetes.Interface, namespace, serviceAccount string, imagePullSecrets ...string) Option {
 	return func(c *client) error {
-		refresher := func(c *client) error {
-			freshKeychain, err := generateKeychainForPullSecrets(kubClient, namespace, serviceAccount, imagePullSecrets)
+		c.pullSecretRefresher = func(ctx context.Context, c *client) error {
+			freshKeychain, err := generateKeychainForPullSecrets(ctx, kubClient, namespace, serviceAccount, imagePullSecrets...)
 			if err != nil {
 				return err
 			}
-
 			c.keychain = authn.NewMultiKeychain(
-				c.baseKeychain,
+				baseKeychain,
 				freshKeychain,
 			)
-
 			return nil
 		}
-
-		c.pullSecretRefresher = refresher
-		return refresher(c)
+		return c.pullSecretRefresher(ctx, c)
 	}
 }
 
@@ -98,79 +104,50 @@ func WithAllowInsecureRegistry() Option {
 	}
 }
 
-type client struct {
-	keychain  authn.Keychain
-	transport *http.Transport
-
-	baseKeychain        authn.Keychain
-	pullSecretRefresher func(*client) error
-}
-
-// Keychain provides keychain object.
-func (c *client) Keychain() authn.Keychain {
-	return c.keychain
-}
-
-// Transport provides transport object.
-func (c *client) Transport() *http.Transport {
-	return c.transport
-}
-
-// UseLocalKeychain updates keychain with the default local keychain.
-func (c *client) UseLocalKeychain() {
-	c.keychain = authn.DefaultKeychain
-	c.baseKeychain = authn.DefaultKeychain
-}
-
-// FetchImageDescriptor fetches Descriptor from registry with given imageRef
-// and provides access to metadata about remote artifact.
-func (c *client) FetchImageDescriptor(imageRef string) (*gcrremote.Descriptor, error) {
-	parsedRef, err := name.ParseReference(imageRef)
-	if err != nil {
-		return nil, fmt.Errorf("failed to parse image reference: %s, error: %v", imageRef, err)
+// WithLocalKeychain provides initialize keychain with the default local keychain.
+func WithLocalKeychain() Option {
+	return func(c *client) error {
+		c.pullSecretRefresher = nil
+		c.keychain = authn.DefaultKeychain
+		return nil
 	}
-
-	desc, err := gcrremote.Get(parsedRef, gcrremote.WithAuthFromKeychain(c.keychain))
-	if err != nil {
-		return nil, fmt.Errorf("failed to fetch image reference: %s, error: %v", imageRef, err)
-	}
-
-	return desc, nil
 }
 
 // RefreshKeychainPullSecrets loads fresh data from pull secrets and updates Keychain.
 // If pull secrets are empty - returns.
-func (c *client) RefreshKeychainPullSecrets() error {
+func (c *client) RefreshKeychainPullSecrets(ctx context.Context) error {
 	if c.pullSecretRefresher == nil {
 		return nil
 	}
-
-	return c.pullSecretRefresher(c)
-}
-
-// generateKeychainForPullSecrets generates keychain by fetching secrets data from imagePullSecrets.
-func generateKeychainForPullSecrets(
-	client kubernetes.Interface,
-	namespace, serviceAccount string,
-	imagePullSecrets []string,
-) (authn.Keychain, error) {
-	kcOpts := kauth.Options{
-		Namespace:          namespace,
-		ServiceAccountName: serviceAccount,
-		ImagePullSecrets:   imagePullSecrets,
-	}
-
-	kc, err := kauth.New(context.Background(), client, kcOpts) // uses k8s client to fetch secrets data
-	if err != nil {
-		return nil, errors.Wrap(err, "failed to initialize registry keychain")
-	}
-	return kc, err
+	return c.pullSecretRefresher(ctx, c)
 }
 
 // BuildRemoteOption builds remote.Option based on client.
-func BuildRemoteOption(c Client) remote.Option {
+func (c *client) BuildRemoteOption() remote.Option {
 	return remote.WithRemoteOptions(
-		gcrremote.WithAuthFromKeychain(c.Keychain()),
-		gcrremote.WithTransport(c.Transport()),
+		gcrremote.WithAuthFromKeychain(c.keychain),
+		gcrremote.WithTransport(c.transport),
 	)
 }
+
+// FetchImageDescriptor fetches Descriptor from registry with given imageRef
+// and provides access to metadata about remote artifact.
+func (c *client) FetchImageDescriptor(ctx context.Context, imageRef string) (*gcrremote.Descriptor, error) {
+	parsedRef, err := name.ParseReference(imageRef)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse image reference: %s, error: %v", imageRef, err)
+	}
+	desc, err := gcrremote.Get(parsedRef, gcrremote.WithAuthFromKeychain(c.keychain), gcrremote.WithContext(ctx))
+	if err != nil {
+		return nil, fmt.Errorf("failed to fetch image reference: %s, error: %v", imageRef, err)
+	}
+	return desc, nil
+}
+
+func (c *client) getKeychain() authn.Keychain {
+	return c.keychain
+}
+
+func (c *client) getTransport() http.RoundTripper {
+	return c.transport
+}
diff --git a/pkg/registryclient/client_test.go b/pkg/registryclient/client_test.go
index 194f2ae587..2b816277a3 100644
--- a/pkg/registryclient/client_test.go
+++ b/pkg/registryclient/client_test.go
@@ -16,22 +16,22 @@ func TestInitClientWithEmptyOptions(t *testing.T) {
 	expClient := &client{
 		transport: remote.DefaultTransport.(*http.Transport),
 	}
-	c, err := InitClient()
+	c, err := New()
 	assert.NilError(t, err)
-	assert.Assert(t, expClient.transport == c.Transport())
-	assert.Assert(t, c.Keychain() != nil)
+	assert.Assert(t, expClient.transport == c.getTransport())
+	assert.Assert(t, c.getKeychain() != nil)
 }
 
 func TestInitClientWithInsecureRegistryOption(t *testing.T) {
 	expClient := &client{
 		transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}},
 	}
-	c, err := InitClient(WithAllowInsecureRegistry())
+	c, err := New(WithAllowInsecureRegistry())
 
 	expInsecureSkipVerify := expClient.transport.TLSClientConfig.InsecureSkipVerify
-	gotInsecureSkipVerify := c.Transport().TLSClientConfig.InsecureSkipVerify
+	gotInsecureSkipVerify := c.getTransport().(*http.Transport).TLSClientConfig.InsecureSkipVerify
 
 	assert.NilError(t, err)
 	assert.Assert(t, expInsecureSkipVerify == gotInsecureSkipVerify)
-	assert.Assert(t, c.Keychain() != nil)
+	assert.Assert(t, c.getKeychain() != nil)
 }
diff --git a/pkg/registryclient/utils.go b/pkg/registryclient/utils.go
new file mode 100644
index 0000000000..46cc520295
--- /dev/null
+++ b/pkg/registryclient/utils.go
@@ -0,0 +1,24 @@
+package registryclient
+
+import (
+	"context"
+
+	"github.com/google/go-containerregistry/pkg/authn"
+	kauth "github.com/google/go-containerregistry/pkg/authn/kubernetes"
+	"github.com/pkg/errors"
+	"k8s.io/client-go/kubernetes"
+)
+
+// generateKeychainForPullSecrets generates keychain by fetching secrets data from imagePullSecrets.
+func generateKeychainForPullSecrets(ctx context.Context, client kubernetes.Interface, namespace, serviceAccount string, imagePullSecrets ...string) (authn.Keychain, error) {
+	kcOpts := kauth.Options{
+		Namespace:          namespace,
+		ServiceAccountName: serviceAccount,
+		ImagePullSecrets:   imagePullSecrets,
+	}
+	kc, err := kauth.New(ctx, client, kcOpts) // uses k8s client to fetch secrets data
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to initialize registry keychain")
+	}
+	return kc, err
+}
diff --git a/pkg/testrunner/scenario.go b/pkg/testrunner/scenario.go
index 085ecc9eef..22e442b6e8 100644
--- a/pkg/testrunner/scenario.go
+++ b/pkg/testrunner/scenario.go
@@ -15,6 +15,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/clients/dclient"
 	"github.com/kyverno/kyverno/pkg/engine"
 	"github.com/kyverno/kyverno/pkg/engine/response"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	"github.com/stretchr/testify/assert"
 	"gopkg.in/yaml.v3"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -145,7 +146,7 @@ func runTestCase(t *testing.T, tc TestCase) bool {
 
 	policyContext := engine.NewPolicyContext().WithPolicy(policy).WithNewResource(*resource)
 
-	er := engine.Mutate(policyContext)
+	er := engine.Mutate(registryclient.NewOrDie(), policyContext)
 	t.Log("---Mutation---")
 	validateResource(t, er.PatchedResource, tc.Expected.Mutation.PatchedResource)
 	validateResponse(t, er.PolicyResponse, tc.Expected.Mutation.PolicyResponse)
@@ -157,7 +158,7 @@ func runTestCase(t *testing.T, tc TestCase) bool {
 
 	policyContext = policyContext.WithNewResource(*resource)
 
-	er = engine.Validate(policyContext)
+	er = engine.Validate(registryclient.NewOrDie(), policyContext)
 	t.Log("---Validation---")
 	validateResponse(t, er.PolicyResponse, tc.Expected.Validation.PolicyResponse)
 
@@ -173,7 +174,7 @@ func runTestCase(t *testing.T, tc TestCase) bool {
 		} else {
 			policyContext := policyContext.WithClient(client)
 
-			er = engine.ApplyBackgroundChecks(policyContext)
+			er = engine.ApplyBackgroundChecks(registryclient.NewOrDie(), policyContext)
 			t.Log(("---Generation---"))
 			validateResponse(t, er.PolicyResponse, tc.Expected.Generation.PolicyResponse)
 			// Expected generate resource will be in same namespaces as resource
diff --git a/pkg/webhooks/resource/generation/generation.go b/pkg/webhooks/resource/generation/generation.go
index 1d0ad7660b..215f774ba0 100644
--- a/pkg/webhooks/resource/generation/generation.go
+++ b/pkg/webhooks/resource/generation/generation.go
@@ -21,6 +21,7 @@ import (
 	enginutils "github.com/kyverno/kyverno/pkg/engine/utils"
 	"github.com/kyverno/kyverno/pkg/event"
 	"github.com/kyverno/kyverno/pkg/metrics"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	webhookgenerate "github.com/kyverno/kyverno/pkg/webhooks/updaterequest"
 	webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils"
 	admissionv1 "k8s.io/api/admission/v1"
@@ -45,6 +46,7 @@ func NewGenerationHandler(
 	log logr.Logger,
 	client dclient.Interface,
 	kyvernoClient versioned.Interface,
+	rclient registryclient.Client,
 	nsLister corev1listers.NamespaceLister,
 	urLister kyvernov1beta1listers.UpdateRequestNamespaceLister,
 	urGenerator webhookgenerate.Generator,
@@ -55,6 +57,7 @@ func NewGenerationHandler(
 		log:           log,
 		client:        client,
 		kyvernoClient: kyvernoClient,
+		rclient:       rclient,
 		nsLister:      nsLister,
 		urLister:      urLister,
 		urGenerator:   urGenerator,
@@ -67,6 +70,7 @@ type generationHandler struct {
 	log           logr.Logger
 	client        dclient.Interface
 	kyvernoClient versioned.Interface
+	rclient       registryclient.Client
 	nsLister      corev1listers.NamespaceLister
 	urLister      kyvernov1beta1listers.UpdateRequestNamespaceLister
 	urGenerator   webhookgenerate.Generator
@@ -92,7 +96,7 @@ func (h *generationHandler) Handle(
 			if request.Kind.Kind != "Namespace" && request.Namespace != "" {
 				policyContext = policyContext.WithNamespaceLabels(common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log))
 			}
-			engineResponse := engine.ApplyBackgroundChecks(policyContext)
+			engineResponse := engine.ApplyBackgroundChecks(h.rclient, policyContext)
 			for _, rule := range engineResponse.PolicyResponse.Rules {
 				if rule.Status != response.RuleStatusPass {
 					h.deleteGR(engineResponse)
diff --git a/pkg/webhooks/resource/handlers.go b/pkg/webhooks/resource/handlers.go
index c86853a7d5..1e10cc25d6 100644
--- a/pkg/webhooks/resource/handlers.go
+++ b/pkg/webhooks/resource/handlers.go
@@ -20,6 +20,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/metrics"
 	"github.com/kyverno/kyverno/pkg/openapi"
 	"github.com/kyverno/kyverno/pkg/policycache"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
 	jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
 	"github.com/kyverno/kyverno/pkg/webhooks"
@@ -38,6 +39,7 @@ type handlers struct {
 	// clients
 	client        dclient.Interface
 	kyvernoClient versioned.Interface
+	rclient       registryclient.Client
 
 	// config
 	configuration config.Configuration
@@ -64,6 +66,7 @@ type handlers struct {
 func NewHandlers(
 	client dclient.Interface,
 	kyvernoClient versioned.Interface,
+	rclient registryclient.Client,
 	configuration config.Configuration,
 	metricsConfig metrics.MetricsConfigManager,
 	pCache policycache.Cache,
@@ -80,6 +83,7 @@ func NewHandlers(
 	return &handlers{
 		client:           client,
 		kyvernoClient:    kyvernoClient,
+		rclient:          rclient,
 		configuration:    configuration,
 		metricsConfig:    metricsConfig,
 		pCache:           pCache,
@@ -113,7 +117,7 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
 	}
 	if len(generatePolicies) == 0 && request.Operation == admissionv1.Update {
 		// handle generate source resource updates
-		gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen)
+		gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen)
 		go gh.HandleUpdatesForGenerateRules(request, []kyvernov1.PolicyInterface{})
 	}
 
@@ -129,7 +133,7 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad
 		namespaceLabels = common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, logger)
 	}
 
-	vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports)
+	vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.rclient, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports)
 
 	ok, msg, warnings := vh.HandleValidation(h.metricsConfig, request, policies, policyContext, namespaceLabels, startTime)
 	if !ok {
@@ -163,7 +167,7 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi
 	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)
+	mh := mutation.NewMutationHandler(logger, h.rclient, h.eventGen, h.openApiManager, h.nsLister)
 	mutatePatches, mutateWarnings, err := mh.HandleMutation(h.metricsConfig, request, mutatePolicies, policyContext, startTime)
 	if err != nil {
 		logger.Error(err, "mutation failed")
diff --git a/pkg/webhooks/resource/imageverification/handler.go b/pkg/webhooks/resource/imageverification/handler.go
index 21199dbd0f..a8f4e0e60e 100644
--- a/pkg/webhooks/resource/imageverification/handler.go
+++ b/pkg/webhooks/resource/imageverification/handler.go
@@ -12,6 +12,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/engine/response"
 	"github.com/kyverno/kyverno/pkg/event"
 	"github.com/kyverno/kyverno/pkg/metrics"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
 	jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
 	reportutils "github.com/kyverno/kyverno/pkg/utils/report"
@@ -76,7 +77,7 @@ func (h *imageVerificationHandler) handleVerifyImages(logger logr.Logger, reques
 	verifiedImageData := &engine.ImageVerificationMetadata{}
 	for _, p := range policies {
 		policyContext := policyContext.WithPolicy(p)
-		resp, ivm := engine.VerifyAndPatchImages(policyContext)
+		resp, ivm := engine.VerifyAndPatchImages(registryclient.NewOrDie(), policyContext)
 
 		engineResponses = append(engineResponses, resp)
 		patches = append(patches, resp.GetPatches()...)
diff --git a/pkg/webhooks/resource/mutation/mutation.go b/pkg/webhooks/resource/mutation/mutation.go
index 9c09e7ac3b..f4ffe65a99 100644
--- a/pkg/webhooks/resource/mutation/mutation.go
+++ b/pkg/webhooks/resource/mutation/mutation.go
@@ -14,6 +14,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/event"
 	"github.com/kyverno/kyverno/pkg/metrics"
 	"github.com/kyverno/kyverno/pkg/openapi"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	"github.com/kyverno/kyverno/pkg/utils"
 	engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
 	jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
@@ -41,12 +42,14 @@ type MutationHandler interface {
 
 func NewMutationHandler(
 	log logr.Logger,
+	rclient registryclient.Client,
 	eventGen event.Interface,
 	openApiManager openapi.ValidateInterface,
 	nsLister corev1listers.NamespaceLister,
 ) MutationHandler {
 	return &mutationHandler{
 		log:            log,
+		rclient:        rclient,
 		eventGen:       eventGen,
 		openApiManager: openApiManager,
 		nsLister:       nsLister,
@@ -55,6 +58,7 @@ func NewMutationHandler(
 
 type mutationHandler struct {
 	log            logr.Logger
+	rclient        registryclient.Client
 	eventGen       event.Interface
 	openApiManager openapi.ValidateInterface
 	nsLister       corev1listers.NamespaceLister
@@ -144,7 +148,7 @@ func (h *mutationHandler) applyMutation(request *admissionv1.AdmissionRequest, p
 		policyContext = policyContext.WithNamespaceLabels(common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log))
 	}
 
-	engineResponse := engine.Mutate(policyContext)
+	engineResponse := engine.Mutate(h.rclient, policyContext)
 	policyPatches := engineResponse.GetPatches()
 
 	if !engineResponse.IsSuccessful() {
diff --git a/pkg/webhooks/resource/updaterequest.go b/pkg/webhooks/resource/updaterequest.go
index 418b254876..85d9154dd0 100644
--- a/pkg/webhooks/resource/updaterequest.go
+++ b/pkg/webhooks/resource/updaterequest.go
@@ -18,7 +18,7 @@ import (
 
 // createUpdateRequests applies generate and mutateExisting policies, and creates update requests for background reconcile
 func (h *handlers) createUpdateRequests(logger logr.Logger, request *admissionv1.AdmissionRequest, policyContext *engine.PolicyContext, generatePolicies, mutatePolicies []kyvernov1.PolicyInterface, ts time.Time) {
-	gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen)
+	gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen)
 	go h.handleMutateExisting(logger, request, mutatePolicies, policyContext, ts)
 	go gh.Handle(h.metricsConfig, request, generatePolicies, policyContext, ts)
 }
@@ -43,7 +43,7 @@ func (h *handlers) handleMutateExisting(logger logr.Logger, request *admissionv1
 
 		var rules []response.RuleResponse
 		policyContext := policyContext.WithPolicy(policy)
-		engineResponse := engine.ApplyBackgroundChecks(policyContext)
+		engineResponse := engine.ApplyBackgroundChecks(h.rclient, policyContext)
 
 		for _, rule := range engineResponse.PolicyResponse.Rules {
 			if rule.Status == response.RuleStatusPass {
diff --git a/pkg/webhooks/resource/validation/validation.go b/pkg/webhooks/resource/validation/validation.go
index 63e4cbfb90..6b42d6d0b9 100644
--- a/pkg/webhooks/resource/validation/validation.go
+++ b/pkg/webhooks/resource/validation/validation.go
@@ -13,6 +13,7 @@ import (
 	"github.com/kyverno/kyverno/pkg/event"
 	"github.com/kyverno/kyverno/pkg/metrics"
 	"github.com/kyverno/kyverno/pkg/policycache"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 	admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
 	controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
 	reportutils "github.com/kyverno/kyverno/pkg/utils/report"
@@ -33,6 +34,7 @@ type ValidationHandler interface {
 func NewValidationHandler(
 	log logr.Logger,
 	kyvernoClient versioned.Interface,
+	rclient registryclient.Client,
 	pCache policycache.Cache,
 	pcBuilder webhookutils.PolicyContextBuilder,
 	eventGen event.Interface,
@@ -41,6 +43,7 @@ func NewValidationHandler(
 	return &validationHandler{
 		log:              log,
 		kyvernoClient:    kyvernoClient,
+		rclient:          rclient,
 		pCache:           pCache,
 		pcBuilder:        pcBuilder,
 		eventGen:         eventGen,
@@ -51,6 +54,7 @@ func NewValidationHandler(
 type validationHandler struct {
 	log              logr.Logger
 	kyvernoClient    versioned.Interface
+	rclient          registryclient.Client
 	pCache           policycache.Cache
 	pcBuilder        webhookutils.PolicyContextBuilder
 	eventGen         event.Interface
@@ -95,7 +99,7 @@ func (v *validationHandler) HandleValidation(
 			failurePolicy = kyvernov1.Fail
 		}
 
-		engineResponse := engine.Validate(policyContext)
+		engineResponse := engine.Validate(v.rclient, policyContext)
 		if engineResponse.IsNil() {
 			// we get an empty response if old and new resources created the same response
 			// allow updates if resource update doesnt change the policy evaluation
@@ -142,7 +146,7 @@ func (v *validationHandler) buildAuditResponses(resource unstructured.Unstructur
 	var responses []*response.EngineResponse
 	for _, policy := range policies {
 		policyContext := policyContext.WithPolicy(policy).WithNamespaceLabels(namespaceLabels)
-		responses = append(responses, engine.Validate(policyContext))
+		responses = append(responses, engine.Validate(v.rclient, policyContext))
 	}
 	return responses, nil
 }
diff --git a/pkg/webhooks/resource/validation_test.go b/pkg/webhooks/resource/validation_test.go
index dd167dc71a..85cb946802 100644
--- a/pkg/webhooks/resource/validation_test.go
+++ b/pkg/webhooks/resource/validation_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	log "github.com/kyverno/kyverno/pkg/logging"
+	"github.com/kyverno/kyverno/pkg/registryclient"
 
 	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
 	"github.com/kyverno/kyverno/pkg/engine"
@@ -531,6 +532,7 @@ func TestValidate_failure_action_overrides(t *testing.T) {
 			assert.NilError(t, err)
 
 			er := engine.Validate(
+				registryclient.NewOrDie(),
 				engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured),
 			)
 			if tc.blocked && tc.messages != nil {
@@ -592,7 +594,7 @@ func Test_RuleSelector(t *testing.T) {
 
 	ctx := engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured)
 
-	resp := engine.Validate(ctx)
+	resp := engine.Validate(registryclient.NewOrDie(), ctx)
 	assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 2)
 	assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0)
 
@@ -603,7 +605,7 @@ func Test_RuleSelector(t *testing.T) {
 	applyOne := kyvernov1.ApplyOne
 	policy.Spec.ApplyRules = &applyOne
 
-	resp = engine.Validate(ctx)
+	resp = engine.Validate(registryclient.NewOrDie(), ctx)
 	assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 1)
 	assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0)