From 5ce80c4e687f0487f3bfbc5f0cbe08849eb5bca5 Mon Sep 17 00:00:00 2001
From: shuting <shuting@nirmata.com>
Date: Tue, 13 Jun 2023 18:26:56 +0800
Subject: [PATCH] fix: target scope validation for the generate rule (#7479)

* fix target scope validation for generate

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* add kuttl tests

Signed-off-by: ShutingZhao <shuting@nirmata.com>

---------

Signed-off-by: ShutingZhao <shuting@nirmata.com>
---
 api/kyverno/v1/common_types.go                |  39 ++-
 api/kyverno/v1/rule_test.go                   |   2 +-
 api/kyverno/v1/rule_types.go                  |   6 +-
 api/kyverno/v2beta1/clusterpolicy_types.go    |   2 +-
 api/kyverno/v2beta1/policy_types.go           |   2 +-
 api/kyverno/v2beta1/rule_test.go              |   8 +-
 api/kyverno/v2beta1/rule_types.go             |   8 +-
 api/kyverno/v2beta1/spec_test.go              |   2 +-
 api/kyverno/v2beta1/spec_types.go             |   8 +-
 pkg/validation/cleanuppolicy/validate.go      |   1 +
 pkg/validation/policy/generate.go             |  59 -----
 pkg/validation/policy/validate.go             |   9 +-
 pkg/validation/policy/validate_test.go        | 131 ----------
 .../create-default-pdb/policy.yaml            |  22 +-
 .../target-namespace-scope/01-crd.yaml        | 234 ++++++++++++++++++
 .../01-update-clusterrole.yaml                |  20 ++
 .../{01-fail.yaml => 02-check.yaml}           |   4 +-
 .../target-namespace-scope/03-delete.yaml     |   6 +
 .../target-namespace-scope/04-delete.yaml     |   6 +
 .../target-namespace-scope/README.md          |   3 +-
 .../target-namespace-scope/policy-pass.yaml   |  59 +++++
 .../policy/immutable-clonelist/policy.yaml    |   2 +-
 .../immutable-clonelist/update-kinds.yaml     |   2 +-
 .../policy/immutable-clonelist/update-ns.yaml |   2 +-
 .../immutable-clonelist/update-selector.yaml  |   2 +-
 .../policy/target-namespace-scope/01-crd.yaml | 234 ++++++++++++++++++
 .../target-namespace-scope/01-fail.yaml       |   5 -
 .../target-namespace-scope/02-check.yaml      |  13 +
 .../target-namespace-scope/03-delete.yaml     |   6 +
 ...espaced-target.yaml => policy-fail-0.yaml} |   0
 .../target-namespace-scope/policy-fail-1.yaml |  31 +++
 .../target-namespace-scope/policy-fail-2.yaml |  31 +++
 .../target-namespace-scope/policy-fail-3.yaml |  31 +++
 .../target-namespace-scope/policy-pass.yaml   |  30 +++
 34 files changed, 777 insertions(+), 243 deletions(-)
 create mode 100644 test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/01-crd.yaml
 create mode 100644 test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/01-update-clusterrole.yaml
 rename test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/{01-fail.yaml => 02-check.yaml} (69%)
 create mode 100644 test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/03-delete.yaml
 create mode 100644 test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/04-delete.yaml
 create mode 100644 test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/policy-pass.yaml
 create mode 100644 test/conformance/kuttl/generate/validation/policy/target-namespace-scope/01-crd.yaml
 delete mode 100644 test/conformance/kuttl/generate/validation/policy/target-namespace-scope/01-fail.yaml
 create mode 100644 test/conformance/kuttl/generate/validation/policy/target-namespace-scope/02-check.yaml
 create mode 100644 test/conformance/kuttl/generate/validation/policy/target-namespace-scope/03-delete.yaml
 rename test/conformance/kuttl/generate/validation/policy/target-namespace-scope/{policy-namespaced-target.yaml => policy-fail-0.yaml} (100%)
 create mode 100644 test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-1.yaml
 create mode 100644 test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-2.yaml
 create mode 100644 test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-3.yaml
 create mode 100644 test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-pass.yaml

diff --git a/api/kyverno/v1/common_types.go b/api/kyverno/v1/common_types.go
index e7f177feba..c74e9535ab 100644
--- a/api/kyverno/v1/common_types.go
+++ b/api/kyverno/v1/common_types.go
@@ -620,9 +620,15 @@ type CloneList struct {
 	Selector *metav1.LabelSelector `json:"selector,omitempty" yaml:"selector,omitempty"`
 }
 
-func (g *Generation) Validate(path *field.Path, clusterResources sets.Set[string]) (errs field.ErrorList) {
-	if err := g.validateTargetsScope(clusterResources); err != nil {
-		errs = append(errs, field.Forbidden(path.Child("generate").Child("namespace"), fmt.Sprintf("target resource scope mismatched: %v ", err)))
+func (g *Generation) Validate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
+	if namespaced {
+		if err := g.validateTargetsScope(clusterResources, policyNamespace); err != nil {
+			errs = append(errs, field.Forbidden(path.Child("generate").Child("namespace"), fmt.Sprintf("target resource scope mismatched: %v ", err)))
+		}
+	} else {
+		if g.GetNamespace() == "" && g.CloneList.Namespace == "" {
+			errs = append(errs, field.Forbidden(path.Child("generate"), "target namespace must be set in a clusterpolicy"))
+		}
 	}
 
 	generateType, _ := g.GetTypeAndSync()
@@ -662,15 +668,28 @@ func (g *Generation) SetData(in apiextensions.JSON) {
 	g.RawData = ToJSON(in)
 }
 
-func (g *Generation) validateTargetsScope(clusterResources sets.Set[string]) error {
+func (g *Generation) validateTargetsScope(clusterResources sets.Set[string], policyNamespace string) error {
 	target := g.ResourceSpec
-	if clusterResources.Has(target.GetKind()) {
-		if target.GetNamespace() != "" {
-			return fmt.Errorf("the target namespace must not be set for cluster-wide resource: %v", target.GetKind())
+	if clusterResources.Has(target.GetAPIVersion() + "/" + target.GetKind()) {
+		return fmt.Errorf("the target must be a namespaced resource: %v/%v", target.GetAPIVersion(), target.GetKind())
+	}
+
+	if g.GetNamespace() != policyNamespace {
+		return fmt.Errorf("a namespaced policy cannot generate resources in other namespaces, expected: %v, received: %v", policyNamespace, g.GetNamespace())
+	}
+
+	if g.Clone.Name != "" {
+		if g.Clone.Namespace != policyNamespace {
+			return fmt.Errorf("a namespaced policy cannot clone resources from other namespaces, expected: %v, received: %v", policyNamespace, g.Clone.Namespace)
 		}
-	} else {
-		if target.GetNamespace() == "" {
-			return fmt.Errorf("the target namespace must be set for namespaced resource: %v", target.GetKind())
+	}
+
+	for _, kind := range g.CloneList.Kinds {
+		if clusterResources.Has(kind) {
+			return fmt.Errorf("the source in cloneList must be a namespaced resource: %v/%v", target.GetAPIVersion(), target.GetKind())
+		}
+		if g.CloneList.Namespace != policyNamespace {
+			return fmt.Errorf("a namespaced policy cannot clone resources from other namespace, expected: %v, received: %v", policyNamespace, g.CloneList.Namespace)
 		}
 	}
 
diff --git a/api/kyverno/v1/rule_test.go b/api/kyverno/v1/rule_test.go
index b5ec8d475e..f90271a0cb 100644
--- a/api/kyverno/v1/rule_test.go
+++ b/api/kyverno/v1/rule_test.go
@@ -1133,7 +1133,7 @@ func Test_Validate_ClusterPolicy_Generate_Variables(t *testing.T) {
 		var rule *Rule
 		err := json.Unmarshal(testcase.rule, &rule)
 		assert.NilError(t, err, testcase.name)
-		errs := rule.ValidateGenerate(path, nil)
+		errs := rule.ValidateGenerate(path, false, "", nil)
 		assert.Equal(t, len(errs) != 0, testcase.shouldFail, testcase.name)
 	}
 }
diff --git a/api/kyverno/v1/rule_types.go b/api/kyverno/v1/rule_types.go
index 6ba9c4240f..31ed70a04e 100644
--- a/api/kyverno/v1/rule_types.go
+++ b/api/kyverno/v1/rule_types.go
@@ -404,12 +404,12 @@ func (r *Rule) ValidatePSaControlNames(path *field.Path) (errs field.ErrorList)
 	return errs
 }
 
-func (r *Rule) ValidateGenerate(path *field.Path, clusterResources sets.Set[string]) (errs field.ErrorList) {
+func (r *Rule) ValidateGenerate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
 	if !r.HasGenerate() {
 		return nil
 	}
 
-	return r.Generation.Validate(path, clusterResources)
+	return r.Generation.Validate(path, namespaced, policyNamespace, clusterResources)
 }
 
 // Validate implements programmatic validation
@@ -420,6 +420,6 @@ func (r *Rule) Validate(path *field.Path, namespaced bool, policyNamespace strin
 	errs = append(errs, r.ExcludeResources.Validate(path.Child("exclude"), namespaced, clusterResources)...)
 	errs = append(errs, r.ValidateMutationRuleTargetNamespace(path, namespaced, policyNamespace)...)
 	errs = append(errs, r.ValidatePSaControlNames(path)...)
-	errs = append(errs, r.ValidateGenerate(path, clusterResources)...)
+	errs = append(errs, r.ValidateGenerate(path, namespaced, policyNamespace, clusterResources)...)
 	return errs
 }
diff --git a/api/kyverno/v2beta1/clusterpolicy_types.go b/api/kyverno/v2beta1/clusterpolicy_types.go
index d2642f22f6..739e5a3f67 100644
--- a/api/kyverno/v2beta1/clusterpolicy_types.go
+++ b/api/kyverno/v2beta1/clusterpolicy_types.go
@@ -105,7 +105,7 @@ func (p *ClusterPolicy) IsReady() bool {
 func (p *ClusterPolicy) Validate(clusterResources sets.Set[string]) (errs field.ErrorList) {
 	errs = append(errs, kyvernov1.ValidateAutogenAnnotation(field.NewPath("metadata").Child("annotations"), p.GetAnnotations())...)
 	errs = append(errs, kyvernov1.ValidatePolicyName(field.NewPath("name"), p.Name)...)
-	errs = append(errs, p.Spec.Validate(field.NewPath("spec"), p.IsNamespaced(), clusterResources)...)
+	errs = append(errs, p.Spec.Validate(field.NewPath("spec"), p.IsNamespaced(), p.Namespace, clusterResources)...)
 	return errs
 }
 
diff --git a/api/kyverno/v2beta1/policy_types.go b/api/kyverno/v2beta1/policy_types.go
index 5fd55513e4..1227427ba6 100644
--- a/api/kyverno/v2beta1/policy_types.go
+++ b/api/kyverno/v2beta1/policy_types.go
@@ -105,7 +105,7 @@ func (p *Policy) IsReady() bool {
 func (p *Policy) Validate(clusterResources sets.Set[string]) (errs field.ErrorList) {
 	errs = append(errs, kyvernov1.ValidateAutogenAnnotation(field.NewPath("metadata").Child("annotations"), p.GetAnnotations())...)
 	errs = append(errs, kyvernov1.ValidatePolicyName(field.NewPath("name"), p.Name)...)
-	errs = append(errs, p.Spec.Validate(field.NewPath("spec"), p.IsNamespaced(), clusterResources)...)
+	errs = append(errs, p.Spec.Validate(field.NewPath("spec"), p.IsNamespaced(), p.Namespace, clusterResources)...)
 	return errs
 }
 
diff --git a/api/kyverno/v2beta1/rule_test.go b/api/kyverno/v2beta1/rule_test.go
index cb9acbe988..1a00c058ea 100644
--- a/api/kyverno/v2beta1/rule_test.go
+++ b/api/kyverno/v2beta1/rule_test.go
@@ -13,7 +13,7 @@ func Test_Validate_RuleType_EmptyRule(t *testing.T) {
 		Name: "validate-user-privilege",
 	}
 	path := field.NewPath("dummy")
-	errs := subject.Validate(path, false, nil)
+	errs := subject.Validate(path, false, "", nil)
 	assert.Equal(t, len(errs), 1)
 	assert.Equal(t, errs[0].Field, "dummy")
 	assert.Equal(t, errs[0].Type, field.ErrorTypeInvalid)
@@ -94,7 +94,7 @@ func Test_Validate_RuleType_MultipleRule(t *testing.T) {
 	assert.NilError(t, err)
 	for _, rule := range policy.Spec.Rules {
 		path := field.NewPath("dummy")
-		errs := rule.Validate(path, false, nil)
+		errs := rule.Validate(path, false, "", nil)
 		assert.Assert(t, len(errs) != 0)
 	}
 }
@@ -153,7 +153,7 @@ func Test_Validate_RuleType_SingleRule(t *testing.T) {
 	assert.NilError(t, err)
 	for _, rule := range policy.Spec.Rules {
 		path := field.NewPath("dummy")
-		errs := rule.Validate(path, false, nil)
+		errs := rule.Validate(path, false, "", nil)
 		assert.Assert(t, len(errs) == 0)
 	}
 }
@@ -534,7 +534,7 @@ func Test_Validate_ClusterPolicy_Generate_Variables(t *testing.T) {
 		var rule *Rule
 		err := json.Unmarshal(testcase.rule, &rule)
 		assert.NilError(t, err, testcase.name)
-		errs := rule.ValidateGenerate(path, nil)
+		errs := rule.ValidateGenerate(path, false, "", nil)
 		assert.Equal(t, len(errs) != 0, testcase.shouldFail, testcase.name)
 	}
 }
diff --git a/api/kyverno/v2beta1/rule_types.go b/api/kyverno/v2beta1/rule_types.go
index a7ec3b5d15..cc643a24e9 100644
--- a/api/kyverno/v2beta1/rule_types.go
+++ b/api/kyverno/v2beta1/rule_types.go
@@ -183,20 +183,20 @@ func (r *Rule) ValidateMatchExcludeConflict(path *field.Path) (errs field.ErrorL
 	return append(errs, field.Invalid(path, r, "Rule is matching an empty set"))
 }
 
-func (r *Rule) ValidateGenerate(path *field.Path, clusterResources sets.Set[string]) (errs field.ErrorList) {
+func (r *Rule) ValidateGenerate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
 	if !r.HasGenerate() {
 		return nil
 	}
 
-	return r.Generation.Validate(path, clusterResources)
+	return r.Generation.Validate(path, namespaced, policyNamespace, clusterResources)
 }
 
 // Validate implements programmatic validation
-func (r *Rule) Validate(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) {
+func (r *Rule) Validate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
 	errs = append(errs, r.ValidateRuleType(path)...)
 	errs = append(errs, r.ValidateMatchExcludeConflict(path)...)
 	errs = append(errs, r.MatchResources.Validate(path.Child("match"), namespaced, clusterResources)...)
 	errs = append(errs, r.ExcludeResources.Validate(path.Child("exclude"), namespaced, clusterResources)...)
-	errs = append(errs, r.ValidateGenerate(path, clusterResources)...)
+	errs = append(errs, r.ValidateGenerate(path, namespaced, policyNamespace, clusterResources)...)
 	return errs
 }
diff --git a/api/kyverno/v2beta1/spec_test.go b/api/kyverno/v2beta1/spec_test.go
index 4575edcf73..ff3f131d65 100644
--- a/api/kyverno/v2beta1/spec_test.go
+++ b/api/kyverno/v2beta1/spec_test.go
@@ -47,7 +47,7 @@ func Test_Validate_UniqueRuleName(t *testing.T) {
 		}},
 	}
 	path := field.NewPath("dummy")
-	errs := subject.Validate(path, false, nil)
+	errs := subject.Validate(path, false, "", nil)
 	assert.Equal(t, len(errs), 1)
 	assert.Equal(t, errs[0].Field, "dummy.rules[1].name")
 	assert.Equal(t, errs[0].Type, field.ErrorTypeInvalid)
diff --git a/api/kyverno/v2beta1/spec_types.go b/api/kyverno/v2beta1/spec_types.go
index ae0f7bd71e..32009e79df 100644
--- a/api/kyverno/v2beta1/spec_types.go
+++ b/api/kyverno/v2beta1/spec_types.go
@@ -216,10 +216,10 @@ func (s *Spec) ValidateRuleNames(path *field.Path) (errs field.ErrorList) {
 }
 
 // ValidateRules implements programmatic validation of Rules
-func (s *Spec) ValidateRules(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) {
+func (s *Spec) ValidateRules(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
 	errs = append(errs, s.ValidateRuleNames(path)...)
 	for i, rule := range s.Rules {
-		errs = append(errs, rule.Validate(path.Index(i), namespaced, clusterResources)...)
+		errs = append(errs, rule.Validate(path.Index(i), namespaced, policyNamespace, clusterResources)...)
 	}
 	return errs
 }
@@ -232,14 +232,14 @@ func (s *Spec) ValidateDeprecatedFields(path *field.Path) (errs field.ErrorList)
 }
 
 // Validate implements programmatic validation
-func (s *Spec) Validate(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) {
+func (s *Spec) Validate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
 	if err := s.ValidateDeprecatedFields(path); err != nil {
 		errs = append(errs, err...)
 	}
 	if s.WebhookTimeoutSeconds != nil && (*s.WebhookTimeoutSeconds < 1 || *s.WebhookTimeoutSeconds > 30) {
 		errs = append(errs, field.Invalid(path.Child("webhookTimeoutSeconds"), s.WebhookTimeoutSeconds, "the timeout value must be between 1 and 30 seconds"))
 	}
-	errs = append(errs, s.ValidateRules(path.Child("rules"), namespaced, clusterResources)...)
+	errs = append(errs, s.ValidateRules(path.Child("rules"), namespaced, policyNamespace, clusterResources)...)
 	if namespaced && len(s.ValidationFailureActionOverrides) > 0 {
 		errs = append(errs, field.Forbidden(path.Child("validationFailureActionOverrides"), "Use of validationFailureActionOverrides is supported only with ClusterPolicy"))
 	}
diff --git a/pkg/validation/cleanuppolicy/validate.go b/pkg/validation/cleanuppolicy/validate.go
index 52811148d9..0d36640002 100644
--- a/pkg/validation/cleanuppolicy/validate.go
+++ b/pkg/validation/cleanuppolicy/validate.go
@@ -33,6 +33,7 @@ func FetchClusteredResources(logger logr.Logger, client dclient.Interface) (sets
 	for _, resList := range res {
 		for _, r := range resList.APIResources {
 			if !r.Namespaced {
+				clusterResources.Insert(resList.GroupVersion + "/" + r.Kind)
 				clusterResources.Insert(r.Kind)
 			}
 		}
diff --git a/pkg/validation/policy/generate.go b/pkg/validation/policy/generate.go
index 1ce8e2451f..7634a041e8 100644
--- a/pkg/validation/policy/generate.go
+++ b/pkg/validation/policy/generate.go
@@ -8,8 +8,6 @@ import (
 	"fmt"
 
 	kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
-	kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/sets"
 )
 
@@ -48,63 +46,6 @@ func immutableGenerateFields(new, old kyvernov1.PolicyInterface) error {
 	return nil
 }
 
-// checkClusterResourceInMatchAndExclude returns false if namespaced ClusterPolicy contains cluster wide resources in
-// Match and Exclude block
-func checkClusterResourceInMatchAndExclude(rule kyvernov1.Rule, clusterResources sets.Set[string], policyNamespace string, mock bool, res []*metav1.APIResourceList) error {
-	if !mock {
-		// Check for generate policy
-		// - if resource to be generated is namespaced resource then the namespace field
-		// should be mentioned
-		// - if resource to be generated is non namespaced resource then the namespace field
-		// should not be mentioned
-		if rule.HasGenerate() {
-			generateResourceKind := rule.Generation.Kind
-			for _, resList := range res {
-				for _, r := range resList.APIResources {
-					if r.Kind == generateResourceKind {
-						if r.Namespaced {
-							if rule.Generation.Namespace == "" {
-								return fmt.Errorf("path: spec.rules[%v]: please mention the namespace to generate a namespaced resource", rule.Name)
-							}
-							if rule.Generation.Namespace != policyNamespace {
-								return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate resources in other namespaces, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Namespace)
-							}
-							if rule.Generation.Clone.Name != "" {
-								if rule.Generation.Clone.Namespace != policyNamespace {
-									return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot clone resources to or from other namespaces, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Clone.Namespace)
-								}
-							}
-						} else {
-							if rule.Generation.Namespace != "" {
-								return fmt.Errorf("path: spec.rules[%v]: do not mention the namespace to generate a non namespaced resource", rule.Name)
-							}
-							if policyNamespace != "" {
-								return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate cluster-wide resources", rule.Name)
-							}
-						}
-					} else if len(rule.Generation.CloneList.Kinds) != 0 {
-						for _, kind := range rule.Generation.CloneList.Kinds {
-							_, splitkind := kubeutils.GetKindFromGVK(kind)
-							if r.Kind == splitkind {
-								if r.Namespaced {
-									if rule.Generation.CloneList.Namespace != policyNamespace {
-										return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot clone resource in other namespace, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Namespace)
-									}
-								} else {
-									if policyNamespace != "" {
-										return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate cluster-wide resources", rule.Name)
-									}
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-	return nil
-}
-
 func resetMutableFields(rule kyvernov1.Rule) *kyvernov1.Rule {
 	new := new(kyvernov1.Rule)
 	rule.DeepCopyInto(new)
diff --git a/pkg/validation/policy/validate.go b/pkg/validation/policy/validate.go
index 5e8dd0b818..26e6471f86 100644
--- a/pkg/validation/policy/validate.go
+++ b/pkg/validation/policy/validate.go
@@ -165,6 +165,7 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
 		for _, resList := range res {
 			for _, r := range resList.APIResources {
 				if !r.Namespaced {
+					clusterResources.Insert(resList.GroupVersion + "/" + r.Kind)
 					clusterResources.Insert(r.Kind)
 				}
 			}
@@ -277,14 +278,6 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
 			}
 		}
 
-		// validate Cluster Resources in namespaced policy
-		// For namespaced policy, ClusterResource type field and values are not allowed in match and exclude
-		if policy.IsNamespaced() {
-			if err := checkClusterResourceInMatchAndExclude(rule, clusterResources, policy.GetNamespace(), mock, res); err != nil {
-				return warnings, err
-			}
-		}
-
 		if err := validateActions(i, &rules[i], client, mock, username); err != nil {
 			return warnings, err
 		}
diff --git a/pkg/validation/policy/validate_test.go b/pkg/validation/policy/validate_test.go
index 6367018555..41c250b99b 100644
--- a/pkg/validation/policy/validate_test.go
+++ b/pkg/validation/policy/validate_test.go
@@ -13,7 +13,6 @@ import (
 	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
 	apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apimachinery/pkg/util/validation/field"
 )
 
@@ -1094,136 +1093,6 @@ func Test_Namespced_Policy(t *testing.T) {
 	assert.Assert(t, err != nil)
 }
 
-func Test_Namespaced_Generate_Policy(t *testing.T) {
-	testcases := []struct {
-		description     string
-		rule            []byte
-		policyNamespace string
-		expectedError   error
-	}{
-		{
-			description: "Only generate resource where the policy exists",
-			rule: []byte(`
-			{"name": "gen-zk",
-                "generate": {
-                    "synchronize": false,
-                    "apiVersion": "v1",
-                    "kind": "ConfigMap",
-                    "name": "zk",
-                    "namespace": "default",
-                    "data": {
-                        "kind": "ConfigMap",
-                        "metadata": {
-                            "labels": {
-                                "somekey": "somevalue"
-                            }
-                        },
-                        "data": {
-                            "ZK_ADDRESS": "192.168.10.10:2181",
-                            "KAFKA_ADDRESS": "192.168.10.13:9092"
-                        }
-                    }
-                }
-					}`),
-			policyNamespace: "poltest",
-			expectedError:   errors.New("path: spec.rules[gen-zk]: a namespaced policy cannot generate resources in other namespaces, expected: poltest, received: default"),
-		},
-		{
-			description: "Not allowed to clone resource outside the policy namespace",
-			rule: []byte(`
-        {
-            "name": "sync-image-pull-secret",
-            "generate": {
-                "apiVersion": "v1",
-                "kind": "Secret",
-                "name": "secret-basic-auth-gen",
-                "namespace": "poltest",
-                "synchronize": true,
-                "clone": {
-                    "namespace": "default",
-                    "name": "secret-basic-auth"
-                }
-            }
-        }`),
-			policyNamespace: "poltest",
-			expectedError:   errors.New("path: spec.rules[sync-image-pull-secret]: a namespaced policy cannot clone resources to or from other namespaces, expected: poltest, received: default"),
-		},
-		{
-			description: "Do not mention the namespace to generate cluster scoped resource",
-			rule: []byte(`
-        {
-            "name": "sync-clone",
-            "generate": {
-                "apiVersion": "storage.k8s.io/v1",
-                "kind": "StorageClass",
-                "name": "local-class",
-                "namespace": "poltest",
-                "synchronize": true,
-                "clone": {
-                    "name": "pv-class"
-                }
-            }
-        }`),
-			policyNamespace: "poltest",
-			expectedError:   errors.New("path: spec.rules[sync-clone]: do not mention the namespace to generate a non namespaced resource"),
-		},
-		{
-			description: "Not allowed to clone cluster scoped resource",
-			rule: []byte(`
-        {
-            "name": "sync-clone",
-            "generate": {
-                "apiVersion": "storage.k8s.io/v1",
-                "kind": "StorageClass",
-                "name": "local-class",
-                "synchronize": true,
-                "clone": {
-                    "name": "pv-class"
-                }
-            }
-        }`),
-			policyNamespace: "poltest",
-			expectedError:   errors.New("path: spec.rules[sync-clone]: a namespaced policy cannot generate cluster-wide resources"),
-		},
-		{
-			description: "Not allowed to multi clone cluster scoped resource",
-			rule: []byte(`
-    {
-        "name": "sync-multi-clone",
-        "generate": {
-            "namespace": "staging",
-            "synchronize": true,
-            "cloneList": {
-                "namespace": "staging",
-                "kinds": [
-                    "storage.k8s.io/v1/StorageClass"
-                ],
-                "selector": {
-                    "matchLabels": {
-                        "allowedToBeCloned": "true"
-                    }
-                }
-            }
-        }
-    }`),
-			policyNamespace: "staging",
-			expectedError:   errors.New("path: spec.rules[sync-multi-clone]: a namespaced policy cannot generate cluster-wide resources"),
-		},
-	}
-	for _, tc := range testcases {
-		t.Run(tc.description, func(t *testing.T) {
-			var rule kyverno.Rule
-			_ = json.Unmarshal(tc.rule, &rule)
-			err := checkClusterResourceInMatchAndExclude(rule, sets.New[string](), tc.policyNamespace, false, testResourceList())
-			if tc.expectedError != nil {
-				assert.Error(t, err, tc.expectedError.Error())
-			} else {
-				assert.NilError(t, err)
-			}
-		})
-	}
-}
-
 func Test_patchesJson6902_Policy(t *testing.T) {
 	rawPolicy := []byte(`
 	{
diff --git a/test/cli/test-generate/create-default-pdb/policy.yaml b/test/cli/test-generate/create-default-pdb/policy.yaml
index 3ae3fb037f..a96c53eb57 100644
--- a/test/cli/test-generate/create-default-pdb/policy.yaml
+++ b/test/cli/test-generate/create-default-pdb/policy.yaml
@@ -1,15 +1,27 @@
 apiVersion: kyverno.io/v1
-kind: Policy
+kind: ClusterPolicy
 metadata:
   name: create-default-pdb
-  namespace: hello-world
+  annotations:
+    policies.kyverno.io/title: Add Pod Disruption Budget
+    policies.kyverno.io/category: Sample
+    kyverno.io/kyverno-version: 1.6.2
+    policies.kyverno.io/minversion: 1.6.0
+    policies.kyverno.io/subject: Deployment
+    policies.kyverno.io/description: >-
+      A PodDisruptionBudget limits the number of Pods of a replicated application that
+      are down simultaneously from voluntary disruptions. For example, a quorum-based
+      application would like to ensure that the number of replicas running is never brought
+      below the number needed for a quorum. As an application owner, you can create a PodDisruptionBudget (PDB)
+      for each application. This policy will create a PDB resource whenever a new Deployment is created.
 spec:
   rules:
   - name: create-default-pdb
     match:
-      resources:
-        kinds:
-        - Deployment
+      any:
+      - resources:
+          kinds:
+          - Deployment
     generate:
       apiVersion: policy/v1
       kind: PodDisruptionBudget
diff --git a/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/01-crd.yaml b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/01-crd.yaml
new file mode 100644
index 0000000000..7b4fcae4f4
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/01-crd.yaml
@@ -0,0 +1,234 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  creationTimestamp: null
+  name: roles.iam.aws.crossplane.io
+spec:
+  group: iam.aws.crossplane.io
+  names:
+    categories:
+    - crossplane
+    - managed
+    - aws
+    kind: Role
+    listKind: RoleList
+    plural: roles
+    shortNames:
+    - iamrole
+    singular: role
+  scope: Cluster
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .status.conditions[?(@.type=='Ready')].status
+      name: READY
+      type: string
+    - jsonPath: .status.conditions[?(@.type=='Synced')].status
+      name: SYNCED
+      type: string
+    - jsonPath: .metadata.creationTimestamp
+      name: AGE
+      type: date
+    name: v1beta1
+    schema:
+      openAPIV3Schema:
+        description: An Role is a managed resource that represents an AWS IAM Role.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: An RoleSpec defines the desired state of an Role.
+            properties:
+              deletionPolicy:
+                default: Delete
+                description: DeletionPolicy specifies what will happen to the underlying
+                  external when this managed resource is deleted - either "Delete"
+                  or "Orphan" the external resource.
+                enum:
+                - Orphan
+                - Delete
+                type: string
+              forProvider:
+                description: RoleParameters define the desired state of an AWS IAM
+                  Role.
+                properties:
+                  assumeRolePolicyDocument:
+                    description: AssumeRolePolicyDocument is the the trust relationship
+                      policy document that grants an entity permission to assume the
+                      role.
+                    type: string
+                  description:
+                    description: Description is a description of the role.
+                    type: string
+                  maxSessionDuration:
+                    description: 'MaxSessionDuration is the duration (in seconds)
+                      that you want to set for the specified role. The default maximum
+                      of one hour is applied. This setting can have a value from 1
+                      hour to 12 hours. Default: 3600'
+                    format: int32
+                    type: integer
+                  path:
+                    description: 'Path is the path to the role. Default: /'
+                    type: string
+                  permissionsBoundary:
+                    description: PermissionsBoundary is the ARN of the policy that
+                      is used to set the permissions boundary for the role.
+                    type: string
+                  tags:
+                    description: Tags. For more information about tagging, see Tagging
+                      IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html)
+                      in the IAM User Guide.
+                    items:
+                      description: Tag represents user-provided metadata that can
+                        be associated with a IAM role. For more information about
+                        tagging, see Tagging IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html)
+                        in the IAM User Guide.
+                      properties:
+                        key:
+                          description: The key name that can be used to look up or
+                            retrieve the associated value. For example, Department
+                            or Cost Center are common choices.
+                          type: string
+                        value:
+                          description: "The value associated with this tag. For example,
+                            tags with a key name of Department could have values such
+                            as Human Resources, Accounting, and Support. Tags with
+                            a key name of Cost Center might have values that consist
+                            of the number associated with the different cost centers
+                            in your company. Typically, many resources have tags with
+                            the same key name but with different values. \n AWS always
+                            interprets the tag Value as a single string. If you need
+                            to store an array, you can store comma-separated values
+                            in the string. However, you must interpret the value in
+                            your code."
+                          type: string
+                      required:
+                      - key
+                      type: object
+                    type: array
+                required:
+                - assumeRolePolicyDocument
+                type: object
+              providerConfigRef:
+                default:
+                  name: default
+                description: ProviderConfigReference specifies how the provider that
+                  will be used to create, observe, update, and delete this managed
+                  resource should be configured.
+                properties:
+                  name:
+                    description: Name of the referenced object.
+                    type: string
+                required:
+                - name
+                type: object
+              providerRef:
+                description: 'ProviderReference specifies the provider that will be
+                  used to create, observe, update, and delete this managed resource.
+                  Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`'
+                properties:
+                  name:
+                    description: Name of the referenced object.
+                    type: string
+                required:
+                - name
+                type: object
+              writeConnectionSecretToRef:
+                description: WriteConnectionSecretToReference specifies the namespace
+                  and name of a Secret to which any connection details for this managed
+                  resource should be written. Connection details frequently include
+                  the endpoint, username, and password required to connect to the
+                  managed resource.
+                properties:
+                  name:
+                    description: Name of the secret.
+                    type: string
+                  namespace:
+                    description: Namespace of the secret.
+                    type: string
+                required:
+                - name
+                - namespace
+                type: object
+            required:
+            - forProvider
+            type: object
+          status:
+            description: An RoleStatus represents the observed state of an Role.
+            properties:
+              atProvider:
+                description: RoleExternalStatus keeps the state for the external resource
+                properties:
+                  arn:
+                    description: ARN is the Amazon Resource Name (ARN) specifying
+                      the role. For more information about ARNs and how to use them
+                      in policies, see IAM Identifiers (http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html)
+                      in the IAM User Guide guide.
+                    type: string
+                  roleID:
+                    description: RoleID is the stable and unique string identifying
+                      the role. For more information about IDs, see IAM Identifiers
+                      (http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html)
+                      in the Using IAM guide.
+                    type: string
+                required:
+                - arn
+                - roleID
+                type: object
+              conditions:
+                description: Conditions of the resource.
+                items:
+                  description: A Condition that may apply to a resource.
+                  properties:
+                    lastTransitionTime:
+                      description: LastTransitionTime is the last time this condition
+                        transitioned from one status to another.
+                      format: date-time
+                      type: string
+                    message:
+                      description: A Message containing details about this condition's
+                        last transition from one status to another, if any.
+                      type: string
+                    reason:
+                      description: A Reason for this condition's last transition from
+                        one status to another.
+                      type: string
+                    status:
+                      description: Status of this condition; is it currently True,
+                        False, or Unknown?
+                      type: string
+                    type:
+                      description: Type of this condition. At most one of each condition
+                        type may apply to a resource at any point in time.
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+            type: object
+        required:
+        - spec
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions:
+  - v1beta1
\ No newline at end of file
diff --git a/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/01-update-clusterrole.yaml b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/01-update-clusterrole.yaml
new file mode 100644
index 0000000000..864c0d1a7c
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/01-update-clusterrole.yaml
@@ -0,0 +1,20 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: kyverno:background-controller:additional
+  labels:
+    app.kubernetes.io/component: background-controller
+    app.kubernetes.io/instance: kyverno
+    app.kubernetes.io/part-of: kyverno
+rules:
+- apiGroups:
+  - '*'
+  resources:
+  - namespaces
+  verbs:
+  - create
+  - update
+  - patch
+  - delete
+  - get
+  - list
\ No newline at end of file
diff --git a/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/01-fail.yaml b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/02-check.yaml
similarity index 69%
rename from test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/01-fail.yaml
rename to test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/02-check.yaml
index 64a5c70397..571d6bc6ce 100644
--- a/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/01-fail.yaml
+++ b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/02-check.yaml
@@ -4,4 +4,6 @@ apply:
 - file: policy-namespaced-target.yaml
   shouldFail: true
 - file: policy-cluster-target.yaml
-  shouldFail: true
\ No newline at end of file
+  shouldFail: false
+- file: policy-pass.yaml
+  shouldFail: false
\ No newline at end of file
diff --git a/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/03-delete.yaml b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/03-delete.yaml
new file mode 100644
index 0000000000..a728371431
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/03-delete.yaml
@@ -0,0 +1,6 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+delete:
+- apiVersion: apiextensions.k8s.io/v1
+  kind: CustomResourceDefinition
+  name: roles.iam.aws.crossplane.io
diff --git a/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/04-delete.yaml b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/04-delete.yaml
new file mode 100644
index 0000000000..67d9ba2b9b
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/04-delete.yaml
@@ -0,0 +1,6 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+delete:
+- apiVersion: rbac.authorization.k8s.io/v1
+  kind: ClusterRole
+  name: kyverno:background-controller:additional
diff --git a/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/README.md b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/README.md
index 587390c88e..cd668aec99 100644
--- a/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/README.md
+++ b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/README.md
@@ -9,4 +9,5 @@ The test fails if the policy creation is allowed, otherwise passes.
 
 ## Reference Issue(s)
 
-https://github.com/kyverno/kyverno/issues/7038
\ No newline at end of file
+https://github.com/kyverno/kyverno/issues/7038
+https://github.com/kyverno/kyverno/issues/7470
\ No newline at end of file
diff --git a/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/policy-pass.yaml b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/policy-pass.yaml
new file mode 100644
index 0000000000..050476b48e
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/clusterpolicy/target-namespace-scope/policy-pass.yaml
@@ -0,0 +1,59 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+  name: target-namespace-scope-pass-1
+spec:
+  generateExistingOnPolicyUpdate: true
+  rules:
+    - generate:
+        apiVersion: iam.aws.crossplane.io/v1beta1
+        data:
+          rules:
+            - verbs:
+                - "*"
+              apiGroups:
+                - "*"
+              resources:
+                - "*"
+        kind: Role
+        name: superuser
+        namespace: "{{request.object.metadata.name}}"
+        synchronize: true
+      match:
+        any:
+          - resources:
+              kinds:
+                - Namespace
+              names:
+                - dev-*
+      name: role-per-namespace
+---
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+  name: user-per-namespace-pass-2
+spec:
+  generateExistingOnPolicyUpdate: true
+  rules:
+    - generate:
+        apiVersion: rbac.authorization.k8s.io/v1
+        data:
+          rules:
+            - verbs:
+                - "*"
+              apiGroups:
+                - "*"
+              resources:
+                - "*"
+        kind: Role
+        name: superuser
+        namespace: "{{request.object.metadata.name}}"
+        synchronize: true
+      match:
+        any:
+          - resources:
+              kinds:
+                - Namespace
+              names:
+                - dev-*
+      name: role-per-namespace
\ No newline at end of file
diff --git a/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/policy.yaml b/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/policy.yaml
index 347351737d..e5b4787831 100644
--- a/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/policy.yaml
+++ b/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/policy.yaml
@@ -13,7 +13,7 @@ spec:
           kinds:
           - ConfigMap
     generate:
-      namespace: "{{request.object.metadata.name}}"
+      namespace: default
       synchronize : true
       cloneList:
         namespace: default
diff --git a/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-kinds.yaml b/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-kinds.yaml
index 9d3c5acbfb..62bc618921 100644
--- a/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-kinds.yaml
+++ b/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-kinds.yaml
@@ -13,7 +13,7 @@ spec:
           kinds:
           - ConfigMap
     generate:
-      namespace: "{{request.object.metadata.name}}"
+      namespace: default
       synchronize : true
       cloneList:
         namespace: default
diff --git a/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-ns.yaml b/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-ns.yaml
index 8ca170c663..8681d01c05 100644
--- a/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-ns.yaml
+++ b/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-ns.yaml
@@ -13,7 +13,7 @@ spec:
           kinds:
           - ConfigMap
     generate:
-      namespace: "{{request.object.metadata.name}}"
+      namespace: default
       synchronize : true
       cloneList:
         namespace: update-clonelist-ns
diff --git a/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-selector.yaml b/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-selector.yaml
index c954047483..8ef1934da1 100644
--- a/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-selector.yaml
+++ b/test/conformance/kuttl/generate/validation/policy/immutable-clonelist/update-selector.yaml
@@ -13,7 +13,7 @@ spec:
           kinds:
           - 
     generate:
-      namespace: "{{request.object.metadata.name}}"
+      namespace: default
       synchronize : true
       cloneList:
         namespace: default
diff --git a/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/01-crd.yaml b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/01-crd.yaml
new file mode 100644
index 0000000000..7b4fcae4f4
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/01-crd.yaml
@@ -0,0 +1,234 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  creationTimestamp: null
+  name: roles.iam.aws.crossplane.io
+spec:
+  group: iam.aws.crossplane.io
+  names:
+    categories:
+    - crossplane
+    - managed
+    - aws
+    kind: Role
+    listKind: RoleList
+    plural: roles
+    shortNames:
+    - iamrole
+    singular: role
+  scope: Cluster
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .status.conditions[?(@.type=='Ready')].status
+      name: READY
+      type: string
+    - jsonPath: .status.conditions[?(@.type=='Synced')].status
+      name: SYNCED
+      type: string
+    - jsonPath: .metadata.creationTimestamp
+      name: AGE
+      type: date
+    name: v1beta1
+    schema:
+      openAPIV3Schema:
+        description: An Role is a managed resource that represents an AWS IAM Role.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: An RoleSpec defines the desired state of an Role.
+            properties:
+              deletionPolicy:
+                default: Delete
+                description: DeletionPolicy specifies what will happen to the underlying
+                  external when this managed resource is deleted - either "Delete"
+                  or "Orphan" the external resource.
+                enum:
+                - Orphan
+                - Delete
+                type: string
+              forProvider:
+                description: RoleParameters define the desired state of an AWS IAM
+                  Role.
+                properties:
+                  assumeRolePolicyDocument:
+                    description: AssumeRolePolicyDocument is the the trust relationship
+                      policy document that grants an entity permission to assume the
+                      role.
+                    type: string
+                  description:
+                    description: Description is a description of the role.
+                    type: string
+                  maxSessionDuration:
+                    description: 'MaxSessionDuration is the duration (in seconds)
+                      that you want to set for the specified role. The default maximum
+                      of one hour is applied. This setting can have a value from 1
+                      hour to 12 hours. Default: 3600'
+                    format: int32
+                    type: integer
+                  path:
+                    description: 'Path is the path to the role. Default: /'
+                    type: string
+                  permissionsBoundary:
+                    description: PermissionsBoundary is the ARN of the policy that
+                      is used to set the permissions boundary for the role.
+                    type: string
+                  tags:
+                    description: Tags. For more information about tagging, see Tagging
+                      IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html)
+                      in the IAM User Guide.
+                    items:
+                      description: Tag represents user-provided metadata that can
+                        be associated with a IAM role. For more information about
+                        tagging, see Tagging IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html)
+                        in the IAM User Guide.
+                      properties:
+                        key:
+                          description: The key name that can be used to look up or
+                            retrieve the associated value. For example, Department
+                            or Cost Center are common choices.
+                          type: string
+                        value:
+                          description: "The value associated with this tag. For example,
+                            tags with a key name of Department could have values such
+                            as Human Resources, Accounting, and Support. Tags with
+                            a key name of Cost Center might have values that consist
+                            of the number associated with the different cost centers
+                            in your company. Typically, many resources have tags with
+                            the same key name but with different values. \n AWS always
+                            interprets the tag Value as a single string. If you need
+                            to store an array, you can store comma-separated values
+                            in the string. However, you must interpret the value in
+                            your code."
+                          type: string
+                      required:
+                      - key
+                      type: object
+                    type: array
+                required:
+                - assumeRolePolicyDocument
+                type: object
+              providerConfigRef:
+                default:
+                  name: default
+                description: ProviderConfigReference specifies how the provider that
+                  will be used to create, observe, update, and delete this managed
+                  resource should be configured.
+                properties:
+                  name:
+                    description: Name of the referenced object.
+                    type: string
+                required:
+                - name
+                type: object
+              providerRef:
+                description: 'ProviderReference specifies the provider that will be
+                  used to create, observe, update, and delete this managed resource.
+                  Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`'
+                properties:
+                  name:
+                    description: Name of the referenced object.
+                    type: string
+                required:
+                - name
+                type: object
+              writeConnectionSecretToRef:
+                description: WriteConnectionSecretToReference specifies the namespace
+                  and name of a Secret to which any connection details for this managed
+                  resource should be written. Connection details frequently include
+                  the endpoint, username, and password required to connect to the
+                  managed resource.
+                properties:
+                  name:
+                    description: Name of the secret.
+                    type: string
+                  namespace:
+                    description: Namespace of the secret.
+                    type: string
+                required:
+                - name
+                - namespace
+                type: object
+            required:
+            - forProvider
+            type: object
+          status:
+            description: An RoleStatus represents the observed state of an Role.
+            properties:
+              atProvider:
+                description: RoleExternalStatus keeps the state for the external resource
+                properties:
+                  arn:
+                    description: ARN is the Amazon Resource Name (ARN) specifying
+                      the role. For more information about ARNs and how to use them
+                      in policies, see IAM Identifiers (http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html)
+                      in the IAM User Guide guide.
+                    type: string
+                  roleID:
+                    description: RoleID is the stable and unique string identifying
+                      the role. For more information about IDs, see IAM Identifiers
+                      (http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html)
+                      in the Using IAM guide.
+                    type: string
+                required:
+                - arn
+                - roleID
+                type: object
+              conditions:
+                description: Conditions of the resource.
+                items:
+                  description: A Condition that may apply to a resource.
+                  properties:
+                    lastTransitionTime:
+                      description: LastTransitionTime is the last time this condition
+                        transitioned from one status to another.
+                      format: date-time
+                      type: string
+                    message:
+                      description: A Message containing details about this condition's
+                        last transition from one status to another, if any.
+                      type: string
+                    reason:
+                      description: A Reason for this condition's last transition from
+                        one status to another.
+                      type: string
+                    status:
+                      description: Status of this condition; is it currently True,
+                        False, or Unknown?
+                      type: string
+                    type:
+                      description: Type of this condition. At most one of each condition
+                        type may apply to a resource at any point in time.
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+            type: object
+        required:
+        - spec
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions:
+  - v1beta1
\ No newline at end of file
diff --git a/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/01-fail.yaml b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/01-fail.yaml
deleted file mode 100644
index f50ac6fc25..0000000000
--- a/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/01-fail.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-apiVersion: kuttl.dev/v1beta1
-kind: TestStep
-apply:
-- file: policy-namespaced-target.yaml
-  shouldFail: true
\ No newline at end of file
diff --git a/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/02-check.yaml b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/02-check.yaml
new file mode 100644
index 0000000000..a8a18b0534
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/02-check.yaml
@@ -0,0 +1,13 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+apply:
+- file: policy-pass.yaml
+  shouldFail: false
+- file: policy-fail-0.yaml
+  shouldFail: true
+- file: policy-fail-1.yaml
+  shouldFail: true
+- file: policy-fail-2.yaml
+  shouldFail: true
+- file: policy-fail-3.yaml
+  shouldFail: true
\ No newline at end of file
diff --git a/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/03-delete.yaml b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/03-delete.yaml
new file mode 100644
index 0000000000..a728371431
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/03-delete.yaml
@@ -0,0 +1,6 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+delete:
+- apiVersion: apiextensions.k8s.io/v1
+  kind: CustomResourceDefinition
+  name: roles.iam.aws.crossplane.io
diff --git a/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-namespaced-target.yaml b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-0.yaml
similarity index 100%
rename from test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-namespaced-target.yaml
rename to test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-0.yaml
diff --git a/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-1.yaml b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-1.yaml
new file mode 100644
index 0000000000..2291bdd1aa
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-1.yaml
@@ -0,0 +1,31 @@
+---
+apiVersion: kyverno.io/v1
+kind: Policy
+metadata:
+  name: pol-target-namespace-scope-fail-1
+  namespace: default
+spec:
+  generateExistingOnPolicyUpdate: true
+  rules:
+    - generate:
+        apiVersion: iam.aws.crossplane.io/v1beta1
+        data:
+          rules:
+            - verbs:
+                - "*"
+              apiGroups:
+                - "*"
+              resources:
+                - "*"
+        kind: Role
+        name: superuser
+        namespace: default
+        synchronize: true
+      match:
+        any:
+          - resources:
+              kinds:
+                - Secret
+              names:
+                - dev-*
+      name: role-per-namespace
diff --git a/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-2.yaml b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-2.yaml
new file mode 100644
index 0000000000..81d76143de
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-2.yaml
@@ -0,0 +1,31 @@
+---
+apiVersion: kyverno.io/v1
+kind: Policy
+metadata:
+  name: pol-target-namespace-scope-fail-2
+  namespace: default
+spec:
+  generateExistingOnPolicyUpdate: true
+  rules:
+    - generate:
+        apiVersion: rbac.authorization.k8s.io/v1
+        data:
+          rules:
+            - verbs:
+                - "*"
+              apiGroups:
+                - "*"
+              resources:
+                - "*"
+        kind: Role
+        name: superuser
+        namespace: "{{request.object.metadata.name}}"
+        synchronize: true
+      match:
+        any:
+          - resources:
+              kinds:
+                - Secret
+              names:
+                - dev-*
+      name: role-per-namespace
diff --git a/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-3.yaml b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-3.yaml
new file mode 100644
index 0000000000..41c369ce2d
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-fail-3.yaml
@@ -0,0 +1,31 @@
+---
+apiVersion: kyverno.io/v1
+kind: Policy
+metadata:
+  name: pol-target-namespace-scope-fail-3
+  namespace: default
+spec:
+  generateExistingOnPolicyUpdate: true
+  rules:
+    - generate:
+        apiVersion: rbac.authorization.k8s.io/v1
+        data:
+          rules:
+            - verbs:
+                - "*"
+              apiGroups:
+                - "*"
+              resources:
+                - "*"
+        kind: Role
+        name: superuser
+        namespace: test
+        synchronize: true
+      match:
+        any:
+          - resources:
+              kinds:
+                - Secret
+              names:
+                - dev-*
+      name: role-per-namespace
\ No newline at end of file
diff --git a/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-pass.yaml b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-pass.yaml
new file mode 100644
index 0000000000..ec0d97e11c
--- /dev/null
+++ b/test/conformance/kuttl/generate/validation/policy/target-namespace-scope/policy-pass.yaml
@@ -0,0 +1,30 @@
+apiVersion: kyverno.io/v1
+kind: Policy
+metadata:
+  name: user-per-namespace-pass
+  namespace: default
+spec:
+  generateExistingOnPolicyUpdate: true
+  rules:
+    - generate:
+        apiVersion: rbac.authorization.k8s.io/v1
+        data:
+          rules:
+            - verbs:
+                - "*"
+              apiGroups:
+                - "*"
+              resources:
+                - "*"
+        kind: Role
+        name: superuser
+        namespace: default
+        synchronize: true
+      match:
+        any:
+          - resources:
+              kinds:
+                - Secret
+              names:
+                - dev-*
+      name: role-per-namespace