Repository: Azure/draft
Pull request #247 (2024-02-13 14:35:36)

Rehauling Directories, Path Logic, and Test Logic to Conform to Automated Payload (/lib)
# Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change.

Fixes # (issue)
Feature # (details)

## Type of change

Please delete options that are not relevant.

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update

# How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. Is it a  breaking change which will impact consuming tool(s).

- TestValidateDeployment_ContainerAllowedImages [x]
- TestValidateDeployment_ContainerEnforceProbes [x]


# Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged and published in downstream modules



squillace: @bacongobbler, what is the user's experience of this? Is this an internal thing, or will they see some change as a result? 
bacongobbler: No user experience change, just less code duplication.

Patch:
diff --git a/cmd/validate.go b/cmd/validate.go
index 40837e9..5a72d62 100644
--- a/cmd/validate.go
+++ b/cmd/validate.go
@@ -22,8 +22,8 @@ func newValidateCmd() *cobra.Command {
 
 	var cmd = &cobra.Command{
 		Use:   "validate",
-		Short: "Validates deployment manifests against AKS best practices",
-		Long:  `This command validates deployment manifests against several AKS best practices.`,
+		Short: "Validates manifests against AKS best practices",
+		Long:  `This command validates manifests against several AKS best practices.`,
 		RunE: func(cmd *cobra.Command, args []string) error {
 			if err := vc.run(); err != nil {
 				return err
@@ -35,8 +35,8 @@ func newValidateCmd() *cobra.Command {
 	f := cmd.Flags()
 
 	// TODO: add validation to the path
-	f.StringVarP(&vc.manifestPath, "manifest", "m", "", "'manifest' asks for the path to the deployment manifest")
-	f.BoolVarP(&vc.safeguardsOnly, "safeguards-only", "sg", false, "'safeguards-only' asserts whether or not validate will only run against safeguards constraints")
+	f.StringVarP(&vc.manifestPath, "manifest", "m", "", "'manifest' asks for the path to the manifest")
+	f.BoolVarP(&vc.safeguardsOnly, "safeguards-only", "s", false, "'safeguards-only' asserts whether or not validate will only run against safeguards constraints")
 
 	return cmd
 }
@@ -44,8 +44,8 @@ func newValidateCmd() *cobra.Command {
 func (vc *validateCmd) run() error {
 	ctx := context.Background()
 
-	log.Debugf("validating deployment manifest")
-	err := safeguards.ValidateDeployment(ctx, vc.manifestPath)
+	log.Debugf("validating manifest")
+	err := safeguards.ValidateManifest(ctx, vc.manifestPath)
 	if err != nil {
 		log.Errorf("validating safeguards: %s", err)
 	}
diff --git a/go.mod b/go.mod
index b6a49c4..37b11c7 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/Azure/draft
 go 1.21
 
 require (
+	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armpolicy v0.9.0
 	github.com/briandowns/spinner v1.23.0
 	github.com/cenkalti/backoff/v4 v4.2.1
 	github.com/fatih/color v1.16.0
@@ -18,6 +19,7 @@ require (
 	github.com/spf13/cobra v1.8.0
 	github.com/stretchr/testify v1.8.4
 	golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3
+	golang.org/x/mod v0.14.0
 	gopkg.in/yaml.v3 v3.0.1
 	helm.sh/helm/v3 v3.14.0
 	k8s.io/api v0.29.1
@@ -30,6 +32,8 @@ require (
 
 require (
 	github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
+	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 // indirect
+	github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 // indirect
 	github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
 	github.com/Masterminds/semver/v3 v3.2.1 // indirect
 	github.com/Microsoft/hcsshim v0.11.4 // indirect
diff --git a/go.sum b/go.sum
index 8924b36..6fbaa6f 100644
--- a/go.sum
+++ b/go.sum
@@ -13,8 +13,22 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 h1:fb8kj/Dh4CSwgsOzHeZY4Xh68cFVbzXx+ONXGMY//4w=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 h1:d81/ng9rET2YqdVkVwkb6EXeRrLJIwyGnJcAlAWKwhs=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armpolicy v0.9.0 h1:YA31g14FJRqNW6nsG/L1OTr4K238uR1yB9QS/rfpLUQ=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armpolicy v0.9.0/go.mod h1:oV/CiaEI6/PiHdtOBhAov1Gdk9dt32WsFpj+3NSL8SI=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 h1:7CBQ+Ei8SP2c6ydQTGCCrS35bDxgTMfoP2miAwK++OU=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1/go.mod h1:c/wcGeGx5FUPbM/JltUYHZcKmigwyVLJlDq+4HdtXaw=
 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
@@ -114,6 +128,8 @@ github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aB
 github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI=
 github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
 github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
+github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
+github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
 github.com/docker/cli v25.0.3+incompatible h1:KLeNs7zws74oFuVhgZQ5ONGZiXUUdgsdy6/EsX/6284=
 github.com/docker/cli v25.0.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
@@ -184,6 +200,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
+github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
 github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
@@ -308,6 +326,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
 github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -374,6 +394,8 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
+github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
+github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
diff --git a/pkg/safeguards/constraints/NoUnauthenticatedPulls/constraint/constraint.yaml b/pkg/safeguards/constraints/NoUnauthenticatedPulls/constraint/constraint.yaml
deleted file mode 100644
index 4786627..0000000
--- a/pkg/safeguards/constraints/NoUnauthenticatedPulls/constraint/constraint.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-apiVersion: constraints.gatekeeper.sh/v1beta1
-kind: K8sAzureV1ContainerRestrictedImagePulls
-metadata:
-  name: v1-container-restricted-image-pulls
-spec:
-  match:
-    kinds:
-      - apiGroups: ["apps"]
-        kinds: ["Deployment"]
-  parameters:
-    excludedImages: []
\ No newline at end of file
diff --git a/pkg/safeguards/constraints/NoUnauthenticatedPulls/template/no-unauthenticated-pulls.yaml b/pkg/safeguards/constraints/NoUnauthenticatedPulls/template/no-unauthenticated-pulls.yaml
deleted file mode 100644
index 14e638b..0000000
--- a/pkg/safeguards/constraints/NoUnauthenticatedPulls/template/no-unauthenticated-pulls.yaml
+++ /dev/null
@@ -1,64 +0,0 @@
-apiVersion: templates.gatekeeper.sh/v1beta1
-kind: ConstraintTemplate
-metadata:
-  name: k8sazurev1containerrestrictedimagepulls
-spec:
-  crd:
-    spec:
-      names:
-        kind: K8sAzureV1ContainerRestrictedImagePulls
-      validation:
-        # Schema for the `parameters` field
-        openAPIV3Schema:
-          properties:
-            excludedImages:
-              type: array
-              items:
-                type: string
-  targets:
-    - target: admission.k8s.gatekeeper.sh
-      rego: |
-        package k8sazurev1containerrestrictedimagepulls
-
-        has_key(object, key) {
-          _ = object[key]
-        }
-
-        violation[{"msg": msg}] {
-          container := input_containers[_]
-          not is_excluded(container)
-          not has_key(input.review.object.spec, "imagePullSecrets")
-          namespace := input.review.namespace
-          pod := input.review.object.metadata.name
-          msg := sprintf("%s in %s does not have imagePullSecrets. Unauthenticated image pulls are not recommended.", [pod, namespace])
-        }
-
-        input_containers[c] {
-          c := input.review.object.spec.containers[_]
-        }
-
-        input_containers[c] {
-          c := input.review.object.spec.initContainers[_]
-        }
-
-        input_containers[c] {
-          c := input.review.object.spec.ephemeralContainers[_]
-        }
-
-        is_excluded(container) {
-            exclude_images := object.get(object.get(input, "parameters", {}), "excludedImages", [])
-            img := container.image
-            exclusion := exclude_images[_]
-            matches_exclusion(img, exclusion)
-        }
-
-        matches_exclusion(img, exclusion) {
-            not endswith(exclusion, "*")
-            exclusion == img
-        }
-
-        matches_exclusion(img, exclusion) {
-            endswith(exclusion, "*")
-            prefix := trim_suffix(exclusion, "*")
-            startswith(img, prefix)
-        }
diff --git a/pkg/safeguards/constraints/PodDisruptionBudgets/constraint/constraint.yaml b/pkg/safeguards/constraints/PodDisruptionBudgets/constraint/constraint.yaml
deleted file mode 100644
index b7cdf7d..0000000
--- a/pkg/safeguards/constraints/PodDisruptionBudgets/constraint/constraint.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-apiVersion: constraints.gatekeeper.sh/v1beta1
-kind: K8sAzureV1DisallowedBadPodDisruptionBudgets
-metadata:
-  name: v1-disallowed-bad-pod-disruption-budgets
-spec:
-  match:
-    kinds:
-      - apiGroups: ["apps"]
-        kinds: ["Deployment", "ReplicaSet", "StatefulSet"]
-      - apiGroups: ["policy"]
-        kinds: ["PodDisruptionBudget"]
\ No newline at end of file
diff --git a/pkg/safeguards/constraints/PodDisruptionBudgets/template/pod-disruption-budgets.yaml b/pkg/safeguards/constraints/PodDisruptionBudgets/template/pod-disruption-budgets.yaml
deleted file mode 100644
index 57be9db..0000000
--- a/pkg/safeguards/constraints/PodDisruptionBudgets/template/pod-disruption-budgets.yaml
+++ /dev/null
@@ -1,111 +0,0 @@
-apiVersion: templates.gatekeeper.sh/v1beta1
-kind: ConstraintTemplate
-metadata:
-  name: k8sazurev1disallowedbadpoddisruptionbudgets
-  annotations:
-    metadata.gatekeeper.sh/title: "Pod Disruption Budget"
-    metadata.gatekeeper.sh/version: 1.0.0
-    metadata.gatekeeper.sh/requires-sync-data: |
-      "[
-        [
-          {
-            "groups": ["policy"],
-            "versions": ["v1"],
-            "kinds": ["PodDisruptionBudget"]
-          },
-          {
-            "groups": ["apps"],
-            "versions": ["v1"],
-            "kinds": ["StatefulSet", "ReplicaSet", "Deployment"]
-          }
-        ]
-      ]"
-    description: Prevents customers from applying bad Pod Disruption Budgets
-spec:
-  crd:
-    spec:
-      names:
-        kind: K8sAzureV1DisallowedBadPodDisruptionBudgets
-  targets:
-    - target: admission.k8s.gatekeeper.sh
-      rego: |
-        package k8sazurev1disallowedbadpoddisruptionbudgets
-
-        violation[{"msg": msg}] {
-          input.review.kind.kind == "PodDisruptionBudget"
-          pdb := input.review.object
-          not valid_pdb_max_unavailable(pdb)
-          msg := sprintf(
-            "PodDisruptionBudget <%s> has maxUnavailable of 0, only positive integers are allowed for maxUnavailable",
-            [pdb.metadata.name]
-          )
-        }
-
-        violation[{"msg": msg}] {
-          obj := input.review.object
-          pdb := data.inventory.namespace[obj.metadata.namespace]["policy/v1"].PodDisruptionBudget[_]
-          obj.spec.selector.matchLabels == pdb.spec.selector.matchLabels
-          not valid_pdb_max_unavailable(pdb)
-          msg := sprintf(
-            "%s <%s> has been selected by PodDisruptionBudget <%s> but has maxUnavailable of 0, only positive integers are allowed for maxUnavailable",
-            [obj.kind, obj.metadata.name, pdb.metadata.name]
-          )
-        }
-
-        violation[{"msg": msg}] {
-          obj := input.review.object
-          pdb := data.inventory.namespace[obj.metadata.namespace]["policy/v1"].PodDisruptionBudget[_]
-          obj.spec.selector.matchLabels == pdb.spec.selector.matchLabels
-          not valid_pdb_min_available(obj, pdb)
-          msg := sprintf("%s <%s> has %d replica(s) but PodDisruptionBudget <%s> has minAvailable of %d, only positive integers less than %d are allowed for minAvailable",
-            [obj.kind, obj.metadata.name, obj.spec.replicas, pdb.metadata.name, pdb.spec.minAvailable, obj.spec.replicas])
-        }
-
-        violation[{"msg":msg}] {
-          input.review.kind.kind == "PodDisruptionBudget"
-            pdb := input.review.object
-
-            matchingDeploys := {x | x.spec.selector.matchLabels == pdb.spec.selector.matchLabels; x = data.inventory.namespace[pdb.metadata.namespace]["apps/v1"].Deployment[_]}
-            deploy := matchingDeploys[_]
-            not valid_pdb_min_available(deploy, pdb)
-
-            msg := sprintf("PodDisruptionBudget %s specifies minAvailable of %d, but matching Deployment %s has %d replicas. minAvailable should be less than %d",[pdb.metadata.name,pdb.spec.minAvailable,deploy.metadata.name,deploy.spec.replicas,deploy.spec.replicas])
-        }
-
-        violation[{"msg":msg}] {
-          input.review.kind.kind == "PodDisruptionBudget"
-            pdb := input.review.object
-
-            matchingSS := {x | x.spec.selector.matchLabels == pdb.spec.selector.matchLabels; x = data.inventory.namespace[pdb.metadata.namespace]["apps/v1"].StatefulSet[_]}
-            ss := matchingSS[_]
-            not valid_pdb_min_available(ss, pdb)
-
-            msg := sprintf("PodDisruptionBudget %s specifies minAvailable of %d, but matching StatefulSet %s has %d replicas. minAvailable should be less than %d",[pdb.metadata.name,pdb.spec.minAvailable,ss.metadata.name,ss.spec.replicas,ss.spec.replicas])
-        }
-
-        violation[{"msg":msg}] {
-          input.review.kind.kind == "PodDisruptionBudget"
-            pdb := input.review.object
-
-            matchingRS := {x | x.spec.selector.matchLabels == pdb.spec.selector.matchLabels; x = data.inventory.namespace[pdb.metadata.namespace]["apps/v1"].ReplicaSet[_]}
-            rs := matchingRS[_]
-            not valid_pdb_min_available(rs, pdb)
-
-            msg := sprintf("PodDisruptionBudget %s specifies minAvailable of %d, but matching ReplicaSet %s has %d replicas. minAvailable should be less than %d",[pdb.metadata.name,pdb.spec.minAvailable,rs.metadata.name,rs.spec.replicas,rs.spec.replicas])
-        }
-
-        valid_pdb_min_available(obj, pdb) {
-          # default to -1 if minAvailable is not set so valid_pdb_min_available is always true
-          # for objects with >= 0 replicas. If minAvailable defaults to >= 0, objects with
-          # replicas field might violate this constraint if they are equal to the default set here
-          min_available := object.get(pdb.spec, "minAvailable", -1)
-          obj.spec.replicas > min_available
-        }
-
-        valid_pdb_max_unavailable(pdb) {
-          # default to 1 if maxUnavailable is not set so valid_pdb_max_unavailable always returns true.
-          # If maxUnavailable defaults to 0, it violates this constraint because all pods needs to be
-          # available and no pods can be evicted voluntarily
-          max_unavailable := object.get(pdb.spec, "maxUnavailable", 1)
-          max_unavailable > 0
-        }
\ No newline at end of file
diff --git a/pkg/safeguards/constraints/PodEnforceAntiaffinity/constraint/constraint.yaml b/pkg/safeguards/constraints/PodEnforceAntiaffinity/constraint/constraint.yaml
deleted file mode 100644
index a2e037d..0000000
--- a/pkg/safeguards/constraints/PodEnforceAntiaffinity/constraint/constraint.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-apiVersion: constraints.gatekeeper.sh/v1beta1
-kind: K8sAzureV1AntiAffinityRules
-metadata:
-  name: v1-multiple-replicas-need-anti-affinity
-spec:
-  match:
-    kinds:
-      - apiGroups: ["apps"]
-        kinds: ["Deployment","StatefulSet","ReplicationController","ReplicaSet"]
\ No newline at end of file
diff --git a/pkg/safeguards/constraints/PodEnforceAntiaffinity/template/pod-enforce-antiaffinity.yaml b/pkg/safeguards/constraints/PodEnforceAntiaffinity/template/pod-enforce-antiaffinity.yaml
deleted file mode 100644
index 4875678..0000000
--- a/pkg/safeguards/constraints/PodEnforceAntiaffinity/template/pod-enforce-antiaffinity.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-apiVersion: templates.gatekeeper.sh/v1beta1
-kind: ConstraintTemplate
-metadata:
-  name: k8sazurev1antiaffinityrules
-  annotations:
-    description: Requires deployments with multiple replicas have pod anti affinity rules
-spec:
-  crd:
-    spec:
-      names:
-        kind: K8sAzureV1AntiAffinityRules
-  targets:
-    - target: admission.k8s.gatekeeper.sh
-      rego: |
-        package k8sazurev1antiaffinityrules
-
-        missing_affinity(obj) {
-          not obj.affinity.podAntiAffinity
-        }
-
-        violation[{"msg": msg}] {
-          input.review.object.spec.replicas > 1
-          missing_affinity(input.review.object.spec.template.spec)
-          msg := sprintf("%s with %d replicas should have pod anti-affinity rules set to avoid disruptions due to nodes crashing", [input.review.kind.kind, input.review.object.spec.replicas])
-        }
\ No newline at end of file
diff --git a/pkg/safeguards/constraints/RestrictedTaints/constraint/constraint.yaml b/pkg/safeguards/constraints/RestrictedTaints/constraint/constraint.yaml
deleted file mode 100644
index e04ac51..0000000
--- a/pkg/safeguards/constraints/RestrictedTaints/constraint/constraint.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-apiVersion: constraints.gatekeeper.sh/v1beta1
-kind: K8sAzureV1ReservedTaints
-metadata:
-  name: v1-system-reserved-taints
-spec:
-  match:
-    kinds:
-      - apiGroups: ["apps"]
-        kinds: ["Deployment"]
-  parameters:
-    reservedTaints: ["CriticalAddonsOnly"]
\ No newline at end of file
diff --git a/pkg/safeguards/constraints/RestrictedTaints/template/restricted-taints.yaml b/pkg/safeguards/constraints/RestrictedTaints/template/restricted-taints.yaml
deleted file mode 100644
index 92dd00d..0000000
--- a/pkg/safeguards/constraints/RestrictedTaints/template/restricted-taints.yaml
+++ /dev/null
@@ -1,43 +0,0 @@
-apiVersion: templates.gatekeeper.sh/v1beta1
-kind: ConstraintTemplate
-metadata:
-  name: k8sazurev1reservedtaints
-  annotations:
-    description: Restricts the CriticalAddonsOnly taint to just the system pool
-spec:
-  crd:
-    spec:
-      names:
-        kind: K8sAzureV1ReservedTaints
-      validation:
-        openAPIV3Schema:
-          type: object
-          properties:
-            reservedTaints:
-              type: array
-              items:
-                type: string
-  targets:
-    - target: admission.k8s.gatekeeper.sh
-      rego: |
-        package k8sazurev1reservedtaints
-
-        is_system_pool(node) {
-          node.metadata.labels["kubernetes.azure.com/mode"] == "system"
-        }
-
-        is_system_pool(node) {
-          node.metadata.labels["kubernetes.azure.com/mode"] == "System"
-        }
-
-        violation[{"msg": msg}] {
-          node := input.review.object
-          # did the customer try to add a taint with key "CriticalAddonsOnly" to a non-system pool?
-          taints := {x | x = node.spec.taints[_].key}
-          not is_system_pool(node)
-          taint := taints[_]
-          restrictedTaint := input.parameters.reservedTaints[_]
-          re_match(restrictedTaint,taint)
-
-          msg := sprintf("Taint with key <%s> is reserved for the system pool only",[taint])
-        }      
\ No newline at end of file
diff --git a/pkg/safeguards/constraints/UniqueServiceSelectors/constraint/constraint.yaml b/pkg/safeguards/constraints/UniqueServiceSelectors/constraint/constraint.yaml
deleted file mode 100644
index 648df5d..0000000
--- a/pkg/safeguards/constraints/UniqueServiceSelectors/constraint/constraint.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-apiVersion: constraints.gatekeeper.sh/v1beta1
-kind: K8sAzureV1UniqueServiceSelector
-metadata:
-  name: v1-unique-service-selector
-spec:
-  match:
-    kinds:
-      - apiGroups: ["apps"]
-        kinds: ["Deployment"]
\ No newline at end of file
diff --git a/pkg/safeguards/constraints/UniqueServiceSelectors/template/unique-service-selectors.yaml b/pkg/safeguards/constraints/UniqueServiceSelectors/template/unique-service-selectors.yaml
deleted file mode 100644
index 9a97ac5..0000000
--- a/pkg/safeguards/constraints/UniqueServiceSelectors/template/unique-service-selectors.yaml
+++ /dev/null
@@ -1,70 +0,0 @@
-apiVersion: templates.gatekeeper.sh/v1beta1
-kind: ConstraintTemplate
-metadata:
-  name: k8sazurev1uniqueserviceselector
-  annotations:
-    metadata.gatekeeper.sh/title: "Unique Service Selectors"
-    metadata.gatekeeper.sh/version: 1.0.0
-    metadata.gatekeeper.sh/requires-sync-data: |
-      "[
-        [
-          {
-            "groups": [""],
-            "versions": ["v1"],
-            "kinds": ["Service"]
-          }
-        ]
-      ]"
-    description: >-
-      Requires Services to have unique selectors within a namespace.
-            Selectors are considered the same if they have identical keys and values.
-            Selectors may share a key/value pair so long as there is at least one
-            distinct key/value pair between them.
-
-            https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service
-spec:
-  crd:
-    spec:
-      names:
-        kind: K8sAzureV1UniqueServiceSelector
-  targets:
-    - target: admission.k8s.gatekeeper.sh
-      rego: |
-        package k8sazurev1uniqueserviceselector
-
-        make_apiversion(kind) = apiVersion {
-          g := kind.group
-          v := kind.version
-          g != ""
-          apiVersion = sprintf("%s/%s", [g, v])
-        }
-
-        make_apiversion(kind) = apiVersion {
-          kind.group == ""
-          apiVersion = kind.version
-        }
-
-        identical(obj, review) {
-          obj.metadata.namespace == review.object.metadata.namespace
-          obj.metadata.name == review.object.metadata.name
-          obj.kind == review.kind.kind
-          obj.apiVersion == make_apiversion(review.kind)
-        }
-
-        flatten_selector(obj) = flattened {
-          selectors := [s | s = concat(":", [key, val]); val = obj.spec.selector[key]]
-          flattened := concat(",", sort(selectors))
-        }
-
-        violation[{"msg": msg}] {
-          input.review.kind.kind == "Service"
-          input.review.kind.version == "v1"
-          input.review.kind.group == ""
-          input_namespace := input.review.object.metadata.namespace
-          input_selector := flatten_selector(input.review.object)
-          other := data.inventory.namespace[input_namespace]["v1"].Service[_]
-          not identical(other, input.review)
-          other_selector := flatten_selector(other)
-          input_selector == other_selector
-          msg := sprintf("same selector as service <%s> in namespace <%s>", [other.metadata.name, input_namespace])
-        }
\ No newline at end of file
diff --git a/pkg/safeguards/constraints/ContainerAllowedImages/constraint/constraint.yaml b/pkg/safeguards/lib/v1.0.0/container-allowed-images/constraint.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerAllowedImages/constraint/constraint.yaml
rename to pkg/safeguards/lib/v1.0.0/container-allowed-images/constraint.yaml
diff --git a/pkg/safeguards/constraints/ContainerAllowedImages/template/container-allowed-images.yaml b/pkg/safeguards/lib/v1.0.0/container-allowed-images/template.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerAllowedImages/template/container-allowed-images.yaml
rename to pkg/safeguards/lib/v1.0.0/container-allowed-images/template.yaml
diff --git a/pkg/safeguards/constraints/ContainerEnforceProbes/constraint/constraint.yaml b/pkg/safeguards/lib/v1.0.0/container-enforce-probes/constraint.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerEnforceProbes/constraint/constraint.yaml
rename to pkg/safeguards/lib/v1.0.0/container-enforce-probes/constraint.yaml
diff --git a/pkg/safeguards/constraints/ContainerEnforceProbes/template/container-enforce-probes.yaml b/pkg/safeguards/lib/v1.0.0/container-enforce-probes/template.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerEnforceProbes/template/container-enforce-probes.yaml
rename to pkg/safeguards/lib/v1.0.0/container-enforce-probes/template.yaml
diff --git a/pkg/safeguards/constraints/ContainerResourceLimits/constraint/constraint.yaml b/pkg/safeguards/lib/v1.0.0/container-limits/constraint.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerResourceLimits/constraint/constraint.yaml
rename to pkg/safeguards/lib/v1.0.0/container-limits/constraint.yaml
diff --git a/pkg/safeguards/constraints/ContainerResourceLimits/template/container-resource-limits.yaml b/pkg/safeguards/lib/v1.0.0/container-limits/template.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerResourceLimits/template/container-resource-limits.yaml
rename to pkg/safeguards/lib/v1.0.0/container-limits/template.yaml
diff --git a/pkg/safeguards/lib/v1.0.0/parameters/BUILD.bazel b/pkg/safeguards/lib/v1.0.0/parameters/BUILD.bazel
deleted file mode 100644
index 683b3dc..0000000
--- a/pkg/safeguards/lib/v1.0.0/parameters/BUILD.bazel
+++ /dev/null
@@ -1,9 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-
-go_library(
-    name = "parameters",
-    srcs = ["map.go"],
-    importpath = "go.goms.io/aks/rp/guardrails/server/lib/v1.0.0/parameters",
-    visibility = ["//visibility:public"],
-    deps = ["@com_github_azure_azure_sdk_for_go_sdk_resourcemanager_resources_armpolicy//:armpolicy"],
-)
diff --git a/pkg/safeguards/lib/v1.0.0/restricted-labels/constraint.yaml b/pkg/safeguards/lib/v1.0.0/restricted-labels/constraint.yaml
deleted file mode 100644
index 8acb03d..0000000
--- a/pkg/safeguards/lib/v1.0.0/restricted-labels/constraint.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-apiVersion: constraints.gatekeeper.sh/v1beta1
-kind: K8sAzureV1RestrictedLabels
-metadata:
-  name: v1-no-aks-specific-labels
-spec:
-  match:
-    kinds:
-      - apiGroups: [""]
-        kinds: ["Pod","Node", "Service"]
-      - apiGroups: ["apps"]
-        kinds: ["Deployment", "StatefulSet", "Replicaset"]
-  parameters:
-    labels: {{ .Values.labels }}
-    allowedUsers: {{ .Values.allowedUsers }}
-    allowedGroups: {{ .Values.allowedGroups }}
\ No newline at end of file
diff --git a/pkg/safeguards/lib/v1.0.0/restricted-labels/template.yaml b/pkg/safeguards/lib/v1.0.0/restricted-labels/template.yaml
deleted file mode 100644
index 8f8dcfd..0000000
--- a/pkg/safeguards/lib/v1.0.0/restricted-labels/template.yaml
+++ /dev/null
@@ -1,65 +0,0 @@
-apiVersion: templates.gatekeeper.sh/v1beta1
-kind: ConstraintTemplate
-metadata:
-  name: k8sazurev1restrictedlabels
-  annotations:
-    description: Prevents customers from applying AKS specific labels
-spec:
-  crd:
-    spec:
-      names:
-        kind: K8sAzureV1RestrictedLabels
-      validation:
-        openAPIV3Schema:
-          type: object
-          properties:
-            labels:
-              type: array
-              items:
-                type: string
-            allowedUsers:
-              type: array
-              items:
-                type: string
-            allowedGroups:
-              type: array
-              items:
-                type: string
-  targets:
-    - target: admission.k8s.gatekeeper.sh
-      rego: |
-        package k8sazurev1restrictedlabels
-
-        is_system_account(userinfo) {
-          user := input.parameters.allowedUsers[_]
-          userinfo.username == user
-        }
-
-        is_system_account(userinfo) {
-          userGroups := {x | x = userinfo.groups[_]}
-          allowedGroups := {x | x = input.parameters.allowedGroups[_]}
-          diffList := userGroups - allowedGroups
-          count(diffList) < count(userGroups)
-        }
-
-        to_set(arr) = newSet {
-          newSet = {x | x := arr[_]}
-        }
-
-        is_reserved_label(x, k) {
-          _ = x[k]
-        }
-
-        flatten_map(obj) = labels {
-          keyVals := [s | s = key; val = obj[key]]
-          keysStr := concat(",", keyVals)
-          labels := split(keysStr, ",")
-        }
-
-        violation[{"msg": msg}] {
-          providedLabel := flatten_map(input.review.object.metadata.labels)[_]
-          reservedLabel := to_set(input.parameters.labels)
-          is_reserved_label(reservedLabel, providedLabel)
-          not is_system_account(input.review.userInfo)
-          msg := sprintf("Label <%s> is reserved for AKS use only", [reservedLabel])
-        }
diff --git a/pkg/safeguards/lib/v1.0.0/restricted-node-edits/constraint.yaml b/pkg/safeguards/lib/v1.0.0/restricted-node-edits/constraint.yaml
deleted file mode 100644
index f413958..0000000
--- a/pkg/safeguards/lib/v1.0.0/restricted-node-edits/constraint.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: constraints.gatekeeper.sh/v1beta1
-kind: K8sAzureV1RestrictedNodeEdits
-metadata:
-  name: v1-cannot-edit-individual-nodes
-spec:
-  match:
-    kinds:
-      - apiGroups: [""]
-        kinds: ["Node"]
-  parameters:
-    allowedUsers: {{ .Values.allowedUsers }}
-    allowedGroups: {{ .Values.allowedGroups }}
\ No newline at end of file
diff --git a/pkg/safeguards/lib/v1.0.0/restricted-node-edits/template.yaml b/pkg/safeguards/lib/v1.0.0/restricted-node-edits/template.yaml
deleted file mode 100644
index 42c98be..0000000
--- a/pkg/safeguards/lib/v1.0.0/restricted-node-edits/template.yaml
+++ /dev/null
@@ -1,67 +0,0 @@
-apiVersion: templates.gatekeeper.sh/v1beta1
-kind: ConstraintTemplate
-metadata:
-  name: k8sazurev1restrictednodeedits
-spec:
-  crd:
-    spec:
-      names:
-        kind: K8sAzureV1RestrictedNodeEdits
-      validation:
-        openAPIV3Schema:
-          type: object
-          properties:
-            allowedUsers:
-              type: array
-              items:
-                type: string
-            allowedGroups:
-              type: array
-              items:
-                type: string
-  targets:
-    - target: admission.k8s.gatekeeper.sh
-      rego: |
-        package k8sazurev1restrictednodeedits
-
-        is_system_account(userinfo) {
-          user := input.parameters.allowedUsers[_]
-          userinfo.username == user
-        }
-
-        is_system_account(userinfo) {
-          userGroups := {x | x = userinfo.groups[_]}
-          allowedGroups := {x | x = input.parameters.allowedGroups[_]}
-          diffList := userGroups - allowedGroups
-          count(diffList) < count(userGroups)
-        }
-
-        flatten_map(obj) = labels {
-          keyVals := [s | s = concat(":",[key,val]); val = obj[key]]
-            str := concat(",", keyVals)
-            labels := split(str,",")
-        }
-
-        unchanged_taints_or_labels(node, oldNode) {
-            taints := {x | x = flatten_map(taint); taint = node.spec.taints[_]}
-            oldTaints := {x | x = flatten_map(taint); taint = oldNode.spec.taints[_]}
-            addedTaints := taints - oldTaints
-            deletedTaints := oldTaints - taints
-            count(addedTaints) == count(deletedTaints)
-            count(addedTaints) == 0
-            labels := {x | x = flatten_map(node.metadata.labels)[_]}
-            oldLabels := {x | x = flatten_map(oldNode.metadata.labels)[_]}
-            addedLabels := labels - oldLabels
-            deletedLabels := oldLabels - labels
-            count(addedLabels) == count(deletedLabels)
-            count(addedLabels) == 0
-        }
-
-
-        violation[{"msg":msg}]{
-            input.review.oldObject != null
-            input.review.userInfo != null
-            not unchanged_taints_or_labels(input.review.object, input.review.oldObject)
-            not is_system_account(input.review.userInfo)
-            msg := "Tainting or labelling individual nodes is not recommended. Please use AZ cli to taint/label nodepools instead"
-        }
\ No newline at end of file
diff --git a/pkg/safeguards/safeguards.go b/pkg/safeguards/safeguards.go
index 7ee6ac4..17d60a6 100644
--- a/pkg/safeguards/safeguards.go
+++ b/pkg/safeguards/safeguards.go
@@ -21,14 +21,17 @@ func init() {
 	_ = clientgoscheme.AddToScheme(s)
 	_ = api.AddToScheme(s)
 
+	selectedVersion = getLatestSafeguardsVersion()
+	updateSafeguardPaths()
+
 	fc = FileCrawler{
 		Safeguards: safeguards,
 	}
 }
 
-// ValidateDeployment is what will be called by `draft validate` to validate the user's deployment manifest
+// ValidatemManifest is what will be called by `draft validate` to validate the user's manifest
 // against each safeguards constraint
-func ValidateDeployment(ctx context.Context, deploymentPath string) error {
+func ValidateManifest(ctx context.Context, manifestPath string) error {
 	// constraint client instantiation
 	c, err := getConstraintClient()
 	if err != nil {
@@ -44,7 +47,7 @@ func ValidateDeployment(ctx context.Context, deploymentPath string) error {
 	if err != nil {
 		return err
 	}
-	deployment, err := fc.ReadDeployment(deploymentPath)
+	manifest, err := fc.ReadManifest(manifestPath)
 	if err != nil {
 		return err
 	}
@@ -60,5 +63,5 @@ func ValidateDeployment(ctx context.Context, deploymentPath string) error {
 	}
 
 	// validation of deployment manifest with constraints, templates loaded
-	return validateDeployment(ctx, c, deployment)
+	return validateManifest(ctx, c, manifest)
 }
diff --git a/pkg/safeguards/safeguards_constants.go b/pkg/safeguards/safeguards_constants.go
index 870970c..1201a3e 100644
--- a/pkg/safeguards/safeguards_constants.go
+++ b/pkg/safeguards/safeguards_constants.go
@@ -1,14 +1,16 @@
 package safeguards
 
+import "fmt"
+
 const (
-	Constraint_CAI = "container-allowed-images"
-	Constraint_CEP = "container-enforce-probes"
-	Constraint_CRL = "container-resource-limits"
-	Constraint_NUP = "no-unauthenticated-pulls"
-	Constraint_PDB = "pod-disruption-budgets"
-	Constraint_PEA = "pod-enforce-antiaffinity"
-	Constraint_RT  = "restricted-taints"
-	Constraint_USS = "unique-service-selectors"
+	Constraint_CAI   = "container-allowed-images"
+	Constraint_CEP   = "container-enforce-probes"
+	Constraint_CL    = "container-limits"
+	Constraint_CRIP  = "container-restricted-image-pulls"
+	Constraint_DBPDB = "disallowed-bad-pod-disruption-budgets"
+	Constraint_PEA   = "pod-enforce-antiaffinity"
+	Constraint_RT    = "restricted-taints"
+	Constraint_USS   = "unique-service-selectors"
 )
 
 type FileCrawler struct {
@@ -21,45 +23,54 @@ type Safeguard struct {
 	constraintPath string
 }
 
+var selectedVersion = "v1.0.0"
+
+// TODO: consider getting this from a text file we can bump
+var supportedVersions = []string{selectedVersion}
+
+const safeguardDirectory = "lib"
+const templateFileName = "template.yaml"
+const constraintFileName = "constraint.yaml"
+
 var safeguards = []Safeguard{
 	{
 		name:           Constraint_CAI,
-		templatePath:   "constraints/ContainerAllowedImages/template/container-allowed-images.yaml",
-		constraintPath: "constraints/ContainerAllowedImages/constraint/constraint.yaml",
+		templatePath:   fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_CAI, templateFileName),
+		constraintPath: fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_CAI, constraintFileName),
 	},
 	{
 		name:           Constraint_CEP,
-		templatePath:   "constraints/ContainerEnforceProbes/template/container-enforce-probes.yaml",
-		constraintPath: "constraints/ContainerEnforceProbes/constraint/constraint.yaml",
+		templatePath:   fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_CEP, templateFileName),
+		constraintPath: fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_CEP, constraintFileName),
 	},
 	{
-		name:           Constraint_CRL,
-		templatePath:   "constraints/ContainerResourceLimits/template/container-resource-limits.yaml",
-		constraintPath: "constraints/ContainerResourceLimits/constraint/constraint.yaml",
+		name:           Constraint_CL,
+		templatePath:   fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_CL, templateFileName),
+		constraintPath: fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_CL, constraintFileName),
 	},
 	{
-		name:           Constraint_NUP,
-		templatePath:   "constraints/NoUnauthenticatedPulls/template/no-unauthenticated-pulls.yaml",
-		constraintPath: "constraints/NoUnauthenticatedPulls/constraint/constraint.yaml",
+		name:           Constraint_CRIP,
+		templatePath:   fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_CRIP, templateFileName),
+		constraintPath: fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_CRIP, constraintFileName),
 	},
 	{
-		name:           Constraint_PDB,
-		templatePath:   "constraints/PodDisruptionBudgets/template/pod-disruption-budgets.yaml",
-		constraintPath: "constraints/PodDisruptionBudgets/constraint/constraint.yaml",
+		name:           Constraint_DBPDB,
+		templatePath:   fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_DBPDB, templateFileName),
+		constraintPath: fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_DBPDB, constraintFileName),
 	},
 	{
 		name:           Constraint_PEA,
-		templatePath:   "constraints/PodEnforceAntiaffinity/template/pod-enforce-antiaffinity.yaml",
-		constraintPath: "constraints/PodEnforceAntiaffinity/constraint/constraint.yaml",
+		templatePath:   fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_PEA, templateFileName),
+		constraintPath: fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_PEA, constraintFileName),
 	},
 	{
 		name:           Constraint_RT,
-		templatePath:   "constraints/RestrictedTaints/template/restricted-taints.yaml",
-		constraintPath: "constraints/RestrictedTaints/constraint/constraint.yaml",
+		templatePath:   fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_RT, templateFileName),
+		constraintPath: fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_RT, constraintFileName),
 	},
 	{
 		name:           Constraint_USS,
-		templatePath:   "constraints/UniqueServiceSelectors/template/unique-service-selectors.yaml",
-		constraintPath: "constraints/UniqueServiceSelectors/constraint/constraint.yaml",
+		templatePath:   fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_USS, templateFileName),
+		constraintPath: fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, Constraint_USS, constraintFileName),
 	},
 }
diff --git a/pkg/safeguards/safeguards_helpers.go b/pkg/safeguards/safeguards_helpers.go
index 8f4d8c5..9cbae98 100644
--- a/pkg/safeguards/safeguards_helpers.go
+++ b/pkg/safeguards/safeguards_helpers.go
@@ -10,6 +10,7 @@ import (
 	"github.com/open-policy-agent/gatekeeper/v3/pkg/gator/reader"
 	"github.com/open-policy-agent/gatekeeper/v3/pkg/target"
 
+	"golang.org/x/mod/semver"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
 
@@ -28,11 +29,24 @@ func getConstraintClient() (*constraintclient.Client, error) {
 	return c, nil
 }
 
-// methods for retrieval of deployment manifest, constraint templates, and constraints
-func (fc FileCrawler) ReadDeployment(path string) (*unstructured.Unstructured, error) {
+// sorts the list of supported safeguards versions and returns the last item in the list
+func getLatestSafeguardsVersion() string {
+	semver.Sort(supportedVersions)
+	return supportedVersions[len(supportedVersions)-1]
+}
+
+func updateSafeguardPaths() {
+	for _, sg := range safeguards {
+		sg.templatePath = fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, sg.name, templateFileName)
+		sg.constraintPath = fmt.Sprintf("%s/%s/%s/%s", safeguardDirectory, selectedVersion, sg.name, constraintFileName)
+	}
+}
+
+// methods for retrieval of manifest, constraint templates, and constraints
+func (fc FileCrawler) ReadManifest(path string) (*unstructured.Unstructured, error) {
 	deployment, err := reader.ReadObject(f, path)
 	if err != nil {
-		return nil, fmt.Errorf("could not read deployment: %w", err)
+		return nil, fmt.Errorf("could not read manifest: %w", err)
 	}
 
 	return deployment, nil
@@ -135,20 +149,20 @@ func loadConstraints(ctx context.Context, c *constraintclient.Client, constraint
 	return nil
 }
 
-// does validation on deployment based on loaded constraint templates, constraints
-func validateDeployment(ctx context.Context, c *constraintclient.Client, deployment *unstructured.Unstructured) error {
+// does validation on manifest based on loaded constraint templates, constraints
+func validateManifest(ctx context.Context, c *constraintclient.Client, manifest *unstructured.Unstructured) error {
 	// Review makes sure the provided object satisfies all stored constraints.
 	// On error, the responses return value will still be populated so that
 	// partial results can be analyzed.
-	res, err := c.Review(ctx, deployment)
+	res, err := c.Review(ctx, manifest)
 	if err != nil {
-		return fmt.Errorf("could not review deployment: %w", err)
+		return fmt.Errorf("could not review manifest: %w", err)
 	}
 
 	for _, v := range res.ByTarget {
 		for _, result := range v.Results {
 			if result.Msg != "" {
-				return fmt.Errorf("deployment error: %s", result.Msg)
+				return fmt.Errorf("manifest error: %s", result.Msg)
 			}
 		}
 	}


Test patch:
diff --git a/pkg/safeguards/safeguards_test.go b/pkg/safeguards/safeguards_test.go
index 4f4e5b4..d3d24c9 100644
--- a/pkg/safeguards/safeguards_test.go
+++ b/pkg/safeguards/safeguards_test.go
@@ -14,6 +14,9 @@ var ctx = context.Background()
 var testFc FileCrawler
 
 func init() {
+	selectedVersion = getLatestSafeguardsVersion()
+	updateSafeguardPaths()
+
 	testFc = FileCrawler{
 		Safeguards: safeguards,
 	}
@@ -26,13 +29,9 @@ func TestValidateDeployment_ContainerAllowedImages(t *testing.T) {
 	assert.Nil(t, err)
 
 	// retrieving template, constraint, and deployments
-	constraintTemplate, err := testFc.ReadConstraintTemplate(testDeployment_CAI.Name)
-	assert.Nil(t, err)
-	constraint, err := testFc.ReadConstraint(testDeployment_CAI.Name)
+	constraintTemplate, err := testFc.ReadConstraintTemplate(testManifest_CAI.Name)
 	assert.Nil(t, err)
-	errDeployment, err := testFc.ReadDeployment(testDeployment_CAI.ErrorPath)
-	assert.Nil(t, err)
-	successDeployment, err := testFc.ReadDeployment(testDeployment_CAI.SuccessPath)
+	constraint, err := testFc.ReadConstraint(testManifest_CAI.Name)
 	assert.Nil(t, err)
 
 	// load template, constraint into constraint client
@@ -42,12 +41,8 @@ func TestValidateDeployment_ContainerAllowedImages(t *testing.T) {
 	assert.Nil(t, err)
 
 	// validating deployment manifests
-	// error case - should throw error
-	err = validateDeployment(ctx, c, errDeployment)
-	assert.NotNil(t, err)
-	// success case - should not throw error
-	err = validateDeployment(ctx, c, successDeployment)
-	assert.Nil(t, err)
+	validateTestManifests_Error(ctx, t, c, testFc, testManifest_CAI.ErrorPaths)
+	validateTestManifests_Success(ctx, t, c, testFc, testManifest_CAI.SuccessPaths)
 }
 
 // TODO: rich description here
@@ -57,13 +52,9 @@ func TestValidateDeployment_ContainerEnforceProbes(t *testing.T) {
 	assert.Nil(t, err)
 
 	// retrieving template, constraint, and deployments
-	constraintTemplate, err := testFc.ReadConstraintTemplate(testDeployment_CEP.Name)
+	constraintTemplate, err := testFc.ReadConstraintTemplate(testManifest_CEP.Name)
 	assert.Nil(t, err)
-	constraint, err := testFc.ReadConstraint(testDeployment_CEP.Name)
-	assert.Nil(t, err)
-	errDeployment, err := testFc.ReadDeployment(testDeployment_CEP.ErrorPath)
-	assert.Nil(t, err)
-	successDeployment, err := testFc.ReadDeployment(testDeployment_CEP.SuccessPath)
+	constraint, err := testFc.ReadConstraint(testManifest_CEP.Name)
 	assert.Nil(t, err)
 
 	// load template, constraint into constraint client
@@ -73,10 +64,175 @@ func TestValidateDeployment_ContainerEnforceProbes(t *testing.T) {
 	assert.Nil(t, err)
 
 	// validating deployment manifests
-	// error case - should throw error
-	err = validateDeployment(ctx, c, errDeployment)
-	assert.NotNil(t, err)
-	// success case - should not throw error
-	err = validateDeployment(ctx, c, successDeployment)
-	assert.Nil(t, err)
+	validateTestManifests_Error(ctx, t, c, testFc, testManifest_CEP.ErrorPaths)
+	validateTestManifests_Success(ctx, t, c, testFc, testManifest_CEP.SuccessPaths)
 }
+
+//// TODO: rich description here
+//func TestValidateDeployment_ContainerLimits(t *testing.T) {
+//	// instantiate constraint client
+//	c, err := getConstraintClient()
+//	assert.Nil(t, err)
+//
+//	// retrieving template, constraint, and deployments
+//	constraintTemplate, err := testFc.ReadConstraintTemplate(testManifest_CL.Name)
+//	assert.Nil(t, err)
+//	constraint, err := testFc.ReadConstraint(testManifest_CL.Name)
+//	assert.Nil(t, err)
+//
+//	// load template, constraint into constraint client
+//	err = loadConstraintTemplates(ctx, c, []*templates.ConstraintTemplate{constraintTemplate})
+//	assert.Nil(t, err)
+//	err = loadConstraints(ctx, c, []*unstructured.Unstructured{constraint})
+//	assert.Nil(t, err)
+//
+//	// validating deployment manifests
+//	validateTestManifests_Error(ctx, t, c, testFc, testManifest_CL.ErrorPaths)
+//	validateTestManifests_Success(ctx, t, c, testFc, testManifest_CL.SuccessPaths)
+//}
+//
+//// TODO: rich description here
+//func TestValidateDeployment_ContainerRestrictedImagePulls(t *testing.T) {
+//	// instantiate constraint client
+//	c, err := getConstraintClient()
+//	assert.Nil(t, err)
+//
+//	// retrieving template, constraint, and deployments
+//	constraintTemplate, err := testFc.ReadConstraintTemplate(testManifest_CRIP.Name)
+//	assert.Nil(t, err)
+//	constraint, err := testFc.ReadConstraint(testManifest_CRIP.Name)
+//	assert.Nil(t, err)
+//
+//	// load template, constraint into constraint client
+//	err = loadConstraintTemplates(ctx, c, []*templates.ConstraintTemplate{constraintTemplate})
+//	assert.Nil(t, err)
+//	err = loadConstraints(ctx, c, []*unstructured.Unstructured{constraint})
+//	assert.Nil(t, err)
+//
+//	// validating deployment manifests
+//	validateTestManifests_Error(ctx, t, c, testFc, testManifest_CRIP.ErrorPaths)
+//	validateTestManifests_Success(ctx, t, c, testFc, testManifest_CRIP.SuccessPaths)
+//}
+//
+//// TODO: rich description here
+//func TestValidateDeployment_DisallowedBadPodDisruptionBudget(t *testing.T) {
+//	// instantiate constraint client
+//	c, err := getConstraintClient()
+//	assert.Nil(t, err)
+//
+//	// retrieving template, constraint, and deployments
+//	constraintTemplate, err := testFc.ReadConstraintTemplate(testManifest_DBPDB.Name)
+//	assert.Nil(t, err)
+//	constraint, err := testFc.ReadConstraint(testManifest_DBPDB.Name)
+//	assert.Nil(t, err)
+//
+//	// load template, constraint into constraint client
+//	err = loadConstraintTemplates(ctx, c, []*templates.ConstraintTemplate{constraintTemplate})
+//	assert.Nil(t, err)
+//	err = loadConstraints(ctx, c, []*unstructured.Unstructured{constraint})
+//	assert.Nil(t, err)
+//
+//	// validating deployment manifests
+//	validateTestManifests_Error(ctx, t, c, testFc, testManifest_DBPDB.ErrorPaths)
+//	validateTestManifests_Success(ctx, t, c, testFc, testManifest_DBPDB.SuccessPaths)
+//}
+//
+//// TODO: rich description here
+//func TestValidateDeployment_PodEnforceAntiaffinity(t *testing.T) {
+//	// instantiate constraint client
+//	c, err := getConstraintClient()
+//	assert.Nil(t, err)
+//
+//	// retrieving template, constraint, and deployments
+//	constraintTemplate, err := testFc.ReadConstraintTemplate(testManifest_PEA.Name)
+//	assert.Nil(t, err)
+//	constraint, err := testFc.ReadConstraint(testManifest_PEA.Name)
+//	assert.Nil(t, err)
+//
+//	// load template, constraint into constraint client
+//	err = loadConstraintTemplates(ctx, c, []*templates.ConstraintTemplate{constraintTemplate})
+//	assert.Nil(t, err)
+//	err = loadConstraints(ctx, c, []*unstructured.Unstructured{constraint})
+//	assert.Nil(t, err)
+//
+//	// validating deployment manifests
+//	validateTestManifests_Error(ctx, t, c, testFc, testManifest_PEA.ErrorPaths)
+//	validateTestManifests_Success(ctx, t, c, testFc, testManifest_PEA.SuccessPaths)
+//}
+//
+//// TODO: rich description here
+//func TestValidateDeployment_RestrictedTaints(t *testing.T) {
+//	// instantiate constraint client
+//	c, err := getConstraintClient()
+//	assert.Nil(t, err)
+//
+//	// retrieving template, constraint, and deployments
+//	constraintTemplate, err := testFc.ReadConstraintTemplate(testManifest_RT.Name)
+//	assert.Nil(t, err)
+//	constraint, err := testFc.ReadConstraint(testManifest_RT.Name)
+//	assert.Nil(t, err)
+//
+//	// load template, constraint into constraint client
+//	err = loadConstraintTemplates(ctx, c, []*templates.ConstraintTemplate{constraintTemplate})
+//	assert.Nil(t, err)
+//	err = loadConstraints(ctx, c, []*unstructured.Unstructured{constraint})
+//	assert.Nil(t, err)
+//
+//	// validating deployment manifests
+//	validateTestManifests_Error(ctx, t, c, testFc, testManifest_RT.ErrorPaths)
+//	validateTestManifests_Success(ctx, t, c, testFc, testManifest_RT.SuccessPaths)
+//}
+//
+//// TODO: rich description here
+//func TestValidateDeployment_UniqueServiceSelectors(t *testing.T) {
+//	// instantiate constraint client
+//	c, err := getConstraintClient()
+//	assert.Nil(t, err)
+//
+//	// retrieving template, constraint, and deployments
+//	constraintTemplate, err := testFc.ReadConstraintTemplate(testManifest_USS.Name)
+//	assert.Nil(t, err)
+//	constraint, err := testFc.ReadConstraint(testManifest_USS.Name)
+//	assert.Nil(t, err)
+//
+//	// load template, constraint into constraint client
+//	err = loadConstraintTemplates(ctx, c, []*templates.ConstraintTemplate{constraintTemplate})
+//	assert.Nil(t, err)
+//	err = loadConstraints(ctx, c, []*unstructured.Unstructured{constraint})
+//	assert.Nil(t, err)
+//
+//	// validating deployment manifests
+//	validateTestManifests_Error(ctx, t, c, testFc, testManifest_USS.ErrorPaths)
+//	validateTestManifests_Success(ctx, t, c, testFc, testManifest_USS.SuccessPaths)
+//}
+//
+//// TODO: rich description here
+//func TestValidateDeployment_All(t *testing.T) {
+//	// instantiate constraint client
+//	c, err := getConstraintClient()
+//	assert.Nil(t, err)
+//
+//	var testTemplates []*templates.ConstraintTemplate
+//	var testConstraints []*unstructured.Unstructured
+//	for _, sg := range safeguards {
+//		// retrieving template, constraint, and deployments
+//		constraintTemplate, err := testFc.ReadConstraintTemplate(sg.name)
+//		assert.Nil(t, err)
+//		testTemplates = append(testTemplates, constraintTemplate)
+//
+//		constraint, err := testFc.ReadConstraint(sg.name)
+//		assert.Nil(t, err)
+//		testConstraints = append(testConstraints, constraint)
+//
+//	}
+//
+//	// load template, constraint into constraint client
+//	err = loadConstraintTemplates(ctx, c, testTemplates)
+//	assert.Nil(t, err)
+//	err = loadConstraints(ctx, c, testConstraints)
+//	assert.Nil(t, err)
+//
+//	// validating deployment manifests
+//	validateTestManifests_Error(ctx, t, c, testFc, testManifest_all.ErrorPaths)
+//	validateTestManifests_Success(ctx, t, c, testFc, testManifest_all.SuccessPaths)
+//}
diff --git a/pkg/safeguards/safeguards_test_constants.go b/pkg/safeguards/safeguards_test_constants.go
index be796ef..13d9f6b 100644
--- a/pkg/safeguards/safeguards_test_constants.go
+++ b/pkg/safeguards/safeguards_test_constants.go
@@ -1,55 +1,112 @@
 package safeguards
 
-type testDeployment struct {
-	Name        string
-	SuccessPath string
-	ErrorPath   string
+import "fmt"
+
+type TestManifest struct {
+	Name         string
+	SuccessPaths []string
+	ErrorPaths   []string
 }
 
-var testDeployment_CAI = testDeployment{
-	Name:        Constraint_CAI,
-	SuccessPath: "constraints/ContainerAllowedImages/deployments/CAI_Success_Manifest.yaml",
-	ErrorPath:   "constraints/ContainerAllowedImages/deployments/CAI_Error_Manifest.yaml",
+const testManifestDirectory = "tests"
+
+var testDeployments = []TestManifest{
+	testManifest_CAI,
+	testManifest_CEP,
+	testManifest_CL,
+	testManifest_CRIP,
+	testManifest_DBPDB,
+	testManifest_PEA,
+	testManifest_RT,
+	testManifest_USS,
+	testManifest_all,
 }
 
-var testDeployment_CEP = testDeployment{
-	Name:        Constraint_CEP,
-	SuccessPath: "constraints/ContainerEnforceProbes/deployments/CEP_Success_Manifest.yaml",
-	ErrorPath:   "constraints/ContainerEnforceProbes/deployments/CEP_Error_Manifest.yaml",
+var testError_CAI_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_CAI, "CAI-error-manifest.yaml")
+
+var testSuccess_CAI_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_CAI, "CAI-success-manifest.yaml")
+
+var testManifest_CAI = TestManifest{
+	Name:         Constraint_CAI,
+	SuccessPaths: []string{testSuccess_CAI_Standard},
+	ErrorPaths:   []string{testError_CAI_Standard},
 }
 
-var testDeployment_CRL = testDeployment{
-	Name:        Constraint_CRL,
-	SuccessPath: "constraints/ContainerResourceLimits/deployments/CRL_Success_Manifest.yaml",
-	ErrorPath:   "constraints/ContainerResourceLimits/deployments/CRL_Error_Manifest.yaml",
+var testError_CEP_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_CEP, "CEP-error-manifest.yaml")
+
+var testSuccess_CEP_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_CEP, "CEP-success-manifest.yaml")
+
+var testManifest_CEP = TestManifest{
+	Name:         Constraint_CEP,
+	SuccessPaths: []string{testSuccess_CEP_Standard},
+	ErrorPaths:   []string{testError_CEP_Standard},
 }
 
-var testDeployment_NUP = testDeployment{
-	Name:        Constraint_NUP,
-	SuccessPath: "constraints/NoUnauthenticatedPulls/deployments/NUP_Success_Manifest.yaml",
-	ErrorPath:   "constraints/NoUnauthenticatedPulls/deployments/NUP_Error_Manifest.yaml",
+var testError_CL_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_CL, "CL-error-manifest.yaml")
+
+var testSuccess_CL_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_CL, "CL-success-manifest.yaml")
+
+var testManifest_CL = TestManifest{
+	Name:         Constraint_CL,
+	SuccessPaths: []string{testSuccess_CL_Standard},
+	ErrorPaths:   []string{testError_CL_Standard},
 }
 
-var testDeployment_PDB = testDeployment{
-	Name:        Constraint_PDB,
-	SuccessPath: "constraints/PodDisruptionBudgets/deployments/PDB_Success_Manifest.yaml",
-	ErrorPath:   "constraints/PodDisruptionBudgets/deployments/PDB_Error_Manifest.yaml",
+var testError_CRIP_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_CRIP, "CRIP-error-manifest.yaml")
+
+var testSuccess_CRIP_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_CRIP, "CRIP-success-manifest.yaml")
+
+var testManifest_CRIP = TestManifest{
+	Name:         Constraint_CRIP,
+	SuccessPaths: []string{testSuccess_CRIP_Standard},
+	ErrorPaths:   []string{testError_CRIP_Standard},
 }
 
-var testDeployment_PEA = testDeployment{
-	Name:        Constraint_PEA,
-	SuccessPath: "constraints/PodEnforceAntiaffinity/deployments/PEA_Success_Manifest.yaml",
-	ErrorPath:   "constraints/PodEnforceAntiaffinity/deployments/PEA_Error_Manifest.yaml",
+var testError_DBPDB_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_DBPDB, "DBPDB-error-manifest.yaml")
+
+var testSuccess_DBPDB_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_DBPDB, "DBPDB-success-manifest.yaml")
+
+var testManifest_DBPDB = TestManifest{
+	Name:         Constraint_DBPDB,
+	SuccessPaths: []string{testSuccess_DBPDB_Standard},
+	ErrorPaths:   []string{testError_DBPDB_Standard},
 }
 
-var testDeployment_RT = testDeployment{
-	Name:        Constraint_RT,
-	SuccessPath: "constraints/RestrictedTaints/deployments/RT_Success_Manifest.yaml",
-	ErrorPath:   "constraints/RestrictedTaints/deployments/RT_Error_Manifest.yaml",
+var testError_PEA_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_PEA, "PEA-error-manifest.yaml")
+
+var testSuccess_PEA_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_PEA, "PEA-success-manifest.yaml")
+
+var testManifest_PEA = TestManifest{
+	Name:         Constraint_PEA,
+	SuccessPaths: []string{testSuccess_PEA_Standard},
+	ErrorPaths:   []string{testError_PEA_Standard},
+}
+
+var testError_RT_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_RT, "RT-error-manifest.yaml")
+
+var testSuccess_RT_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_RT, "RT-success-manifest.yaml")
+
+var testManifest_RT = TestManifest{
+	Name:         Constraint_RT,
+	SuccessPaths: []string{testSuccess_RT_Standard},
+	ErrorPaths:   []string{testError_RT_Standard},
 }
 
-var testDeployment_USS = testDeployment{
-	Name:        Constraint_USS,
-	SuccessPath: "constraints/UniqueServiceSelectors/deployments/USS_Success_Manifest.yaml",
-	ErrorPath:   "constraints/UniqueServiceSelectors/deployments/USS_Error_Manifest.yaml",
+var testError_USS_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_USS, "USS-error-manifest.yaml")
+
+var testSuccess_USS_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_USS, "USS-success-manifest.yaml")
+
+var testManifest_USS = TestManifest{
+	Name:         Constraint_USS,
+	SuccessPaths: []string{testSuccess_USS_Standard},
+	ErrorPaths:   []string{testError_USS_Standard},
+}
+
+var testError_all_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_USS, "all-error-manifest.yaml")
+var testSuccess_all_Standard = fmt.Sprintf("%s/%s/%s", testManifestDirectory, Constraint_USS, "all-error-manifest.yaml")
+
+var testManifest_all = TestManifest{
+	Name:         "all",
+	SuccessPaths: []string{testSuccess_all_Standard},
+	ErrorPaths:   []string{testError_all_Standard},
 }
diff --git a/pkg/safeguards/safeguards_test_helpers.go b/pkg/safeguards/safeguards_test_helpers.go
new file mode 100644
index 0000000..5d8d880
--- /dev/null
+++ b/pkg/safeguards/safeguards_test_helpers.go
@@ -0,0 +1,31 @@
+package safeguards
+
+import (
+	"context"
+	"testing"
+
+	constraintclient "github.com/open-policy-agent/frameworks/constraint/pkg/client"
+	"github.com/stretchr/testify/assert"
+)
+
+func validateTestManifests_Error(ctx context.Context, t *testing.T, c *constraintclient.Client, testFc FileCrawler, testManifestPaths []string) {
+	for _, path := range testManifestPaths {
+		errManifest, err := testFc.ReadManifest(path)
+		assert.Nil(t, err)
+
+		// error case - should throw error
+		err = validateManifest(ctx, c, errManifest)
+		assert.NotNil(t, err)
+	}
+}
+
+func validateTestManifests_Success(ctx context.Context, t *testing.T, c *constraintclient.Client, testFc FileCrawler, testManifestPaths []string) {
+	for _, path := range testManifestPaths {
+		successManifest, err := testFc.ReadManifest(path)
+		assert.Nil(t, err)
+
+		// success case - should not throw error
+		err = validateManifest(ctx, c, successManifest)
+		assert.Nil(t, err)
+	}
+}
diff --git a/pkg/safeguards/constraints/ContainerAllowedImages/deployments/CAI_Error_Manifest.yaml b/pkg/safeguards/tests/all/all-error-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerAllowedImages/deployments/CAI_Error_Manifest.yaml
rename to pkg/safeguards/tests/all/all-error-manifest.yaml
diff --git a/pkg/safeguards/constraints/ContainerAllowedImages/deployments/CAI_Success_Manifest.yaml b/pkg/safeguards/tests/all/all-success-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerAllowedImages/deployments/CAI_Success_Manifest.yaml
rename to pkg/safeguards/tests/all/all-success-manifest.yaml
diff --git a/pkg/safeguards/tests/container-allowed-images/CAI-error-manifest.yaml b/pkg/safeguards/tests/container-allowed-images/CAI-error-manifest.yaml
new file mode 100644
index 0000000..2e04317
--- /dev/null
+++ b/pkg/safeguards/tests/container-allowed-images/CAI-error-manifest.yaml
@@ -0,0 +1,21 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: my-deployment
+  labels:
+    app: my-app
+    environment: production
+    testLabel3: randomlabel
+spec:
+  replicas: 3
+  selector:
+    matchLabels:
+      app: my-app
+  template:
+    metadata:
+      labels:
+        app: my-app
+    spec:
+      containers:
+        - name: badcontainer
+          image: badimage
\ No newline at end of file
diff --git a/pkg/safeguards/constraints/ContainerResourceLimits/deployments/CRL_Error_Manifest.yaml b/pkg/safeguards/tests/container-allowed-images/CAI-success-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerResourceLimits/deployments/CRL_Error_Manifest.yaml
rename to pkg/safeguards/tests/container-allowed-images/CAI-success-manifest.yaml
diff --git a/pkg/safeguards/constraints/ContainerEnforceProbes/deployments/CEP_Error_Manifest.yaml b/pkg/safeguards/tests/container-enforce-probes/CEP-error-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerEnforceProbes/deployments/CEP_Error_Manifest.yaml
rename to pkg/safeguards/tests/container-enforce-probes/CEP-error-manifest.yaml
diff --git a/pkg/safeguards/constraints/ContainerEnforceProbes/deployments/CEP_Success_Manifest.yaml b/pkg/safeguards/tests/container-enforce-probes/CEP-success-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerEnforceProbes/deployments/CEP_Success_Manifest.yaml
rename to pkg/safeguards/tests/container-enforce-probes/CEP-success-manifest.yaml
diff --git a/pkg/safeguards/constraints/ContainerResourceLimits/deployments/CRL_Success_Manifest.yaml b/pkg/safeguards/tests/container-limits/CL-error-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/ContainerResourceLimits/deployments/CRL_Success_Manifest.yaml
rename to pkg/safeguards/tests/container-limits/CL-error-manifest.yaml
diff --git a/pkg/safeguards/constraints/PodDisruptionBudgets/deployments/PDB_Error_Manifest.yaml b/pkg/safeguards/tests/container-limits/CL-success-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/PodDisruptionBudgets/deployments/PDB_Error_Manifest.yaml
rename to pkg/safeguards/tests/container-limits/CL-success-manifest.yaml
diff --git a/pkg/safeguards/constraints/NoUnauthenticatedPulls/deployments/NUP_Error_Manifest.yaml b/pkg/safeguards/tests/container-restricted-image-pulls/CRIP-error-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/NoUnauthenticatedPulls/deployments/NUP_Error_Manifest.yaml
rename to pkg/safeguards/tests/container-restricted-image-pulls/CRIP-error-manifest.yaml
diff --git a/pkg/safeguards/constraints/NoUnauthenticatedPulls/deployments/NUP_Success_Manifest.yaml b/pkg/safeguards/tests/container-restricted-image-pulls/CRIP-success-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/NoUnauthenticatedPulls/deployments/NUP_Success_Manifest.yaml
rename to pkg/safeguards/tests/container-restricted-image-pulls/CRIP-success-manifest.yaml
diff --git a/pkg/safeguards/constraints/PodDisruptionBudgets/deployments/PDB_Success_Manifest.yaml b/pkg/safeguards/tests/disallowed-bad-pod-disruption-budget/DBPDB-error-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/PodDisruptionBudgets/deployments/PDB_Success_Manifest.yaml
rename to pkg/safeguards/tests/disallowed-bad-pod-disruption-budget/DBPDB-error-manifest.yaml
diff --git a/pkg/safeguards/constraints/PodEnforceAntiaffinity/deployments/PEA_Error_Manifest.yaml b/pkg/safeguards/tests/disallowed-bad-pod-disruption-budget/DBPDB-success-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/PodEnforceAntiaffinity/deployments/PEA_Error_Manifest.yaml
rename to pkg/safeguards/tests/disallowed-bad-pod-disruption-budget/DBPDB-success-manifest.yaml
diff --git a/pkg/safeguards/constraints/PodEnforceAntiaffinity/deployments/PEA_Success_Manifest.yaml b/pkg/safeguards/tests/pod-enforce-antiaffinity/PEA-error-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/PodEnforceAntiaffinity/deployments/PEA_Success_Manifest.yaml
rename to pkg/safeguards/tests/pod-enforce-antiaffinity/PEA-error-manifest.yaml
diff --git a/pkg/safeguards/constraints/RestrictedTaints/deployments/RT_Error_Manifest.yaml b/pkg/safeguards/tests/pod-enforce-antiaffinity/PEA-success-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/RestrictedTaints/deployments/RT_Error_Manifest.yaml
rename to pkg/safeguards/tests/pod-enforce-antiaffinity/PEA-success-manifest.yaml
diff --git a/pkg/safeguards/constraints/RestrictedTaints/deployments/RT_Success_Manifest.yaml b/pkg/safeguards/tests/restricted-taints/RT-error-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/RestrictedTaints/deployments/RT_Success_Manifest.yaml
rename to pkg/safeguards/tests/restricted-taints/RT-error-manifest.yaml
diff --git a/pkg/safeguards/constraints/UniqueServiceSelectors/deployments/USS_Error_Manifest.yaml b/pkg/safeguards/tests/restricted-taints/RT-success-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/UniqueServiceSelectors/deployments/USS_Error_Manifest.yaml
rename to pkg/safeguards/tests/restricted-taints/RT-success-manifest.yaml
diff --git a/pkg/safeguards/constraints/UniqueServiceSelectors/deployments/USS_Success_Manifest.yaml b/pkg/safeguards/tests/unique-service-selectors/USS-error-manifest.yaml
similarity index 100%
rename from pkg/safeguards/constraints/UniqueServiceSelectors/deployments/USS_Success_Manifest.yaml
rename to pkg/safeguards/tests/unique-service-selectors/USS-error-manifest.yaml
diff --git a/pkg/safeguards/tests/unique-service-selectors/USS-success-manifest.yaml b/pkg/safeguards/tests/unique-service-selectors/USS-success-manifest.yaml
new file mode 100644
index 0000000..b6f9e8b
--- /dev/null
+++ b/pkg/safeguards/tests/unique-service-selectors/USS-success-manifest.yaml
@@ -0,0 +1,21 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: my-deployment
+  labels:
+    app: my-app
+    environment: production
+    testLabel3: randomlabel
+spec:
+  replicas: 3
+  selector:
+    matchLabels:
+      app: my-app
+  template:
+    metadata:
+      labels:
+        app: my-app
+    spec:
+      containers:
+        - name: my-container
+          image: nginx:latest
\ No newline at end of file