diff --git a/api/kyverno/v1/clusterpolicy_types.go b/api/kyverno/v1/clusterpolicy_types.go
index 31a651edc7..29c4f7d34c 100644
--- a/api/kyverno/v1/clusterpolicy_types.go
+++ b/api/kyverno/v1/clusterpolicy_types.go
@@ -77,8 +77,8 @@ func (p *ClusterPolicy) BackgroundProcessingEnabled() bool {
 }
 
 // GetSpec returns the policy spec
-func (p *ClusterPolicy) GetSpec() Spec {
-	return p.Spec
+func (p *ClusterPolicy) GetSpec() *Spec {
+	return &p.Spec
 }
 
 // IsNamespaced indicates if the policy is namespace scoped
diff --git a/api/kyverno/v1/policy_interface.go b/api/kyverno/v1/policy_interface.go
new file mode 100644
index 0000000000..25f50e089b
--- /dev/null
+++ b/api/kyverno/v1/policy_interface.go
@@ -0,0 +1,12 @@
+package v1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// PolicyInterface abstracts the concrete policy type (Policy vs ClusterPolicy)
+// +kubebuilder:object:generate=false
+type PolicyInterface interface {
+	metav1.Object
+	GetSpec() *Spec
+}
diff --git a/api/kyverno/v1/policy_types.go b/api/kyverno/v1/policy_types.go
index b6d5f34160..661a763284 100755
--- a/api/kyverno/v1/policy_types.go
+++ b/api/kyverno/v1/policy_types.go
@@ -78,8 +78,8 @@ func (p *Policy) BackgroundProcessingEnabled() bool {
 }
 
 // GetSpec returns the policy spec
-func (p *Policy) GetSpec() Spec {
-	return p.Spec
+func (p *Policy) GetSpec() *Spec {
+	return &p.Spec
 }
 
 // IsNamespaced indicates if the policy is namespace scoped
diff --git a/docs/crd/v1/index.html b/docs/crd/v1/index.html
index 91be6477f6..f52623e62e 100644
--- a/docs/crd/v1/index.html
+++ b/docs/crd/v1/index.html
@@ -1736,6 +1736,11 @@ Deprecated. Policy metrics are available via the metrics endpoint</p>
 </tbody>
 </table>
 <hr />
+<h3 id="kyverno.io/v1.PolicyInterface">PolicyInterface
+</h3>
+<p>
+<p>PolicyInterface abstracts the concrete policy type (Policy vs ClusterPolicy)</p>
+</p>
 <h3 id="kyverno.io/v1.PolicyStatus">PolicyStatus
 </h3>
 <p>
diff --git a/pkg/autogen/autogen.go b/pkg/autogen/autogen.go
index a577765edc..b7ed2864a8 100644
--- a/pkg/autogen/autogen.go
+++ b/pkg/autogen/autogen.go
@@ -243,11 +243,6 @@ func GenerateRulePatches(spec *kyverno.Spec, controllers string, log logr.Logger
 	return
 }
 
-type Policy interface {
-	GetAnnotations() map[string]string
-	GetSpec() kyverno.Spec
-}
-
 // podControllersKey annotation could be:
 // scenario A: not exist, set default to "all", which generates on all pod controllers
 //               - if name / selector exist in resource description -> skip
@@ -311,12 +306,12 @@ func convertRule(rule kyvernoRule, kind string) (*kyverno.Rule, error) {
 	return &out, nil
 }
 
-func ComputeRules(p Policy) []kyverno.Rule {
+func ComputeRules(p kyverno.PolicyInterface) []kyverno.Rule {
 	spec := p.GetSpec()
 	if !toggle.AutogenInternals() {
 		return spec.Rules
 	}
-	applyAutoGen, desiredControllers := CanAutoGen(&spec, log.Log)
+	applyAutoGen, desiredControllers := CanAutoGen(spec, log.Log)
 
 	if !applyAutoGen {
 		desiredControllers = "none"
diff --git a/pkg/webhookconfig/configmanager.go b/pkg/webhookconfig/configmanager.go
index 5af9484cc1..775f26fb20 100644
--- a/pkg/webhookconfig/configmanager.go
+++ b/pkg/webhookconfig/configmanager.go
@@ -36,12 +36,6 @@ import (
 
 var DefaultWebhookTimeout int64 = 10
 
-// policy abstracts the concrete policy type (Policy vs ClusterPolicy)
-type policy interface {
-	metav1.Object
-	GetSpec() kyverno.Spec
-}
-
 // webhookConfigManager manges the webhook configuration dynamically
 // it is NOT multi-thread safe
 type webhookConfigManager struct {
@@ -372,7 +366,7 @@ func (m *webhookConfigManager) reconcileWebhook(namespace, name string) error {
 	return nil
 }
 
-func (m *webhookConfigManager) getPolicy(namespace, name string) (policy, error) {
+func (m *webhookConfigManager) getPolicy(namespace, name string) (kyverno.PolicyInterface, error) {
 	if namespace == "" {
 		return m.pLister.Get(name)
 	} else {
@@ -380,8 +374,8 @@ func (m *webhookConfigManager) getPolicy(namespace, name string) (policy, error)
 	}
 }
 
-func (m *webhookConfigManager) listAllPolicies() ([]policy, error) {
-	policies := []policy{}
+func (m *webhookConfigManager) listAllPolicies() ([]kyverno.PolicyInterface, error) {
+	policies := []kyverno.PolicyInterface{}
 	polList, err := m.npLister.Policies(metav1.NamespaceAll).List(labels.Everything())
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to list Policy")
@@ -734,7 +728,7 @@ func (m *webhookConfigManager) updateStatus(namespace, name string, ready bool)
 }
 
 // mergeWebhook merges the matching kinds of the policy to webhook.rule
-func (m *webhookConfigManager) mergeWebhook(dst *webhook, policy policy, updateValidate bool) {
+func (m *webhookConfigManager) mergeWebhook(dst *webhook, policy kyverno.PolicyInterface, updateValidate bool) {
 	matchedGVK := make([]string, 0)
 	for _, rule := range autogen.ComputeRules(policy) {
 		// matching kinds in generate policies need to be added to both webhook