Repository: plexsystems/konstraint
Pull request #29 (2020-07-24 22:04:50)

Split k8s library
This splits the large k8s library into multiple smaller ones. This is just a starting point, the libraries can be improved over time.

It also adds a check to ensure we are adding the correct number of libraries to a given ConstraintTemplate so we don't accidentally send Gatekeeper invalid resources.

Edit: Addresses #9 

jalseth: Weird. The tests pass locally for me. I'll look into it more later.
jpreese: Woo! This is awesome. For an initial merge, my stance is that if it works, I'm OK with it. I have a couple thoughts-- but they might be better served in future PRs to limit the Christmas celebration that's already going on.

1. I think we could get rid of `split_images` in `workloads.rego`. That function came from https://github.com/instrumenta/policies/tree/master/kubernetes (as well as a decent chunk of the other functions. couldn't get any movement on maintainership there).

Its only use is to see if a container has a latest tag, but we just use the Rego primitive `ends_with` to do that.

2. I'm on the fence about `container_images` (in workloads.rego). This is the same as `containers[_].image`. It feels a little odd to call out this property specifically. Looking through our policies in house, we just use `.image`.

Since these rules get copied into each policy, I would err on the side of cutting ruthlessly if we're sure it doesn't provide a lot of value. Move more policy-specific rules to the policy themselves.


jalseth: @jpreese Agreed, I pulled those functions out. I'm debating the value of the `security` package, but I think we can merge this and improve these as time goes along.
jpreese: +1 we'll iterate as things pop up. Templates look much smaller! :tada:

Patch:
diff --git a/Makefile b/Makefile
index 2c5fded..d5cc886 100644
--- a/Makefile
+++ b/Makefile
@@ -15,3 +15,10 @@ release:
 	GOOS=darwin GOARCH=amd64 go build -o build/konstraint-darwin-amd64
 	GOOS=windows GOARCH=amd64 go build -o build/konstraint-windows-amd64
 	GOOS=linux GOARCH=amd64 go build -o build/konstraint-linux-amd64
+
+.PHONY: update-static
+update-static: build
+	./build/konstraint create examples
+	./build/konstraint create examples --output test/create
+	./build/konstraint doc examples --output examples/policies.md
+	./build/konstraint doc examples --output test/doc/expected.md
diff --git a/README.md b/README.md
index c38b343..73af204 100644
--- a/README.md
+++ b/README.md
@@ -28,11 +28,13 @@ This creates a scenario where the policy needs to be written differently dependi
 
 - Auto-generate both `ConstraintTemplates` and the Constraints themselves. The `.rego` files are the source of truth and all development should happen in those files.
 
-- Enable the same policy to be used with Gatekeeper `AdmissionReviews` and Conftest `yaml` files. This is accomplished with the _Kubernetes library_.
+- Auto-generate the documentation for the policies, using the `.rego` files as the source of truth.
 
-### Kubernetes library
+- Enable the same policy to be used with Gatekeeper `AdmissionReviews` and Conftest `yaml` files. This is accomplished with the provided libraries.
 
-In the [examples/lib](examples/lib) directory, there is a `kubernetes.rego` file that enables policies to be written for both Conftest and Gatekeeper.
+### Kubernetes Libraries
+
+In the [examples/lib](examples/lib) directory, there are multiple libraries that enable policies to easily be written for both Conftest and Gatekeeper. You can include as little or as many of these libraries into your policies as desired.
 
 #### Purpose
 
@@ -55,3 +57,7 @@ To create the Gatekeeper resources, use `konstraint create <policy_dir>`. To gen
 **Konstraint ran without error, but I don't see any new files.**
 
 This typically means no policies were found, or the policies did not have any `violation[]` rules so they are not compatible with Gatekeeper. For more information, see [How Constraints are Created](docs/constraint_creation.md).
+
+**I imported `lib.XYZ` but Gatekeeper cannot locate `lib.core`?**
+
+At this time, Konstraint only parses the imports from the policy. As many of the libraries rely on `lib.core`, if this is not imported in your policy it will not be included in the generated `ConstraintTemplate` which means not all of the necessary libraries have been included. This issue is being tracked in [https://github.com/plexsystems/konstraint/issues/30](https://github.com/plexsystems/konstraint/issues/30).
diff --git a/examples/containers-resource-constraints/template.yaml b/examples/containers-resource-constraints/template.yaml
deleted file mode 100644
index 3052d37..0000000
--- a/examples/containers-resource-constraints/template.yaml
+++ /dev/null
@@ -1,302 +0,0 @@
-apiVersion: templates.gatekeeper.sh/v1beta1
-kind: ConstraintTemplate
-metadata:
-  creationTimestamp: null
-  name: containersresourceconstraints
-spec:
-  crd:
-    spec:
-      names:
-        kind: ContainersResourceConstraints
-  targets:
-  - libs:
-    - |
-      package lib.k8s
-
-      default is_gatekeeper = false
-
-      is_gatekeeper {
-        has_field(input, "review")
-        has_field(input.review, "object")
-      }
-
-      object = input {
-        not is_gatekeeper
-      }
-
-      object = input.review.object {
-        is_gatekeeper
-      }
-
-      format(msg) = gatekeeper_format {
-        is_gatekeeper
-        gatekeeper_format = {"msg": msg}
-      }
-
-      format(msg) = msg {
-        not is_gatekeeper
-      }
-
-      name = object.metadata.name
-
-      kind = object.kind
-
-      has_field(obj, field) {
-        obj[field]
-      }
-
-      missing_field(obj, field) = true {
-        obj[field] == ""
-      }
-
-      missing_field(obj, field) = true {
-        not has_field(obj, field)
-      }
-
-      is_service {
-        lower(kind) == "service"
-      }
-
-      is_statefulset {
-        lower(kind) == "statefulset"
-      }
-
-      is_daemonset {
-        lower(kind) == "daemonset"
-      }
-
-      is_deployment {
-        lower(kind) == "deployment"
-      }
-
-      is_pod {
-        lower(kind) == "pod"
-      }
-
-      is_namespace {
-        lower(kind) == "namespace"
-      }
-
-      is_workload {
-        containers[_]
-      }
-
-      pod_containers(pod) = all_containers {
-        keys = {"containers", "initContainers"}
-        all_containers = [c | keys[k]; c = pod.spec[k][_]]
-      }
-
-      containers[container] {
-        pods[pod]
-        all_containers = pod_containers(pod)
-        container = all_containers[_]
-      }
-
-      containers[container] {
-        all_containers = pod_containers(object)
-        container = all_containers[_]
-      }
-
-      container_images[image] {
-        containers[container]
-        image = container.image
-      }
-
-      container_images[image] {
-        image = object.spec.image
-      }
-
-      split_image(image) = [image, "latest"] {
-        not contains(image, ":")
-      }
-
-      split_image(image) = [image_name, tag] {
-        [image_name, tag] = split(image, ":")
-      }
-
-      pods[pod] {
-        is_statefulset
-        pod = object.spec.template
-      }
-
-      pods[pod] {
-        is_daemonset
-        pod = object.spec.template
-      }
-
-      pods[pod] {
-        is_deployment
-        pod = object.spec.template
-      }
-
-      pods[pod] {
-        is_pod
-        pod = object
-      }
-
-      volumes[volume] {
-        pods[pod]
-        volume = pod.spec.volumes[_]
-      }
-
-      mem_multiple("E") = 1000000000000000000000 { true }
-
-      mem_multiple("P") = 1000000000000000000 { true }
-
-      mem_multiple("T") = 1000000000000000 { true }
-
-      mem_multiple("G") = 1000000000000 { true }
-
-      mem_multiple("M") = 1000000000 { true }
-
-      mem_multiple("k") = 1000000 { true }
-
-      mem_multiple("") = 1000 { true }
-
-      mem_multiple("m") = 1 { true }
-
-      mem_multiple("Ki") = 1024000 { true }
-
-      mem_multiple("Mi") = 1048576000 { true }
-
-      mem_multiple("Gi") = 1073741824000 { true }
-
-      mem_multiple("Ti") = 1099511627776000 { true }
-
-      mem_multiple("Pi") = 1125899906842624000 { true }
-
-      mem_multiple("Ei") = 1152921504606846976000 { true }
-
-      get_suffix(mem) = suffix {
-        not is_string(mem)
-        suffix := ""
-      }
-
-      get_suffix(mem) = suffix {
-        is_string(mem)
-        count(mem) > 0
-        suffix := substring(mem, count(mem) - 1, -1)
-        mem_multiple(suffix)
-      }
-
-      get_suffix(mem) = suffix {
-        is_string(mem)
-        count(mem) > 1
-        suffix := substring(mem, count(mem) - 2, -1)
-        mem_multiple(suffix)
-      }
-
-      get_suffix(mem) = suffix {
-        is_string(mem)
-        count(mem) > 1
-        not mem_multiple(substring(mem, count(mem) - 1, -1))
-        not mem_multiple(substring(mem, count(mem) - 2, -1))
-        suffix := ""
-      }
-
-      get_suffix(mem) = suffix {
-        is_string(mem)
-        count(mem) == 1
-        not mem_multiple(substring(mem, count(mem) - 1, -1))
-        suffix := ""
-      }
-
-      get_suffix(mem) = suffix {
-        is_string(mem)
-        count(mem) == 0
-        suffix := ""
-      }
-
-      canonify_mem(orig) = new {
-        is_number(orig)
-        new := orig * 1000
-      }
-
-      canonify_mem(orig) = new {
-        not is_number(orig)
-        suffix := get_suffix(orig)
-        raw := replace(orig, suffix, "")
-        re_match("^[0-9]+$", raw)
-        new := to_number(raw) * mem_multiple(suffix)
-      }
-
-      canonify_storage(orig) = new {
-        is_number(orig)
-        new := orig
-      }
-
-      canonify_storage(orig) = new {
-        not is_number(orig)
-        suffix := get_suffix(orig)
-        raw := replace(orig, suffix, "")
-        re_match("^[0-9]+$", raw)
-        new := to_number(raw) * mem_multiple(suffix)
-      }
-
-      canonify_cpu(orig) = new {
-        is_number(orig)
-        new := orig * 1000
-      }
-
-      canonify_cpu(orig) = new {
-        not is_number(orig)
-        endswith(orig, "m")
-        new := to_number(replace(orig, "m", ""))
-      }
-
-      canonify_cpu(orig) = new {
-        not is_number(orig)
-        not endswith(orig, "m")
-        re_match("^[0-9]+$", orig)
-        new := to_number(orig) * 1000
-      }
-
-      dropped_capability(container, cap) {
-        container.securityContext.capabilities.drop[_] == cap
-      }
-
-      added_capability(container, cap) {
-        container.securityContext.capabilities.add[_] == cap
-      }
-
-      no_read_only_filesystem(c) {
-        not has_field(c, "securityContext")
-      }
-
-      no_read_only_filesystem(c) {
-        has_field(c, "securityContext")
-        not has_field(c.securityContext, "readOnlyRootFilesystem")
-      }
-
-      priviledge_escalation_allowed(c) {
-        not has_field(c, "securityContext")
-      }
-
-      priviledge_escalation_allowed(c) {
-        has_field(c, "securityContext")
-        has_field(c.securityContext, "allowPrivilegeEscalation")
-      }
-    rego: |-
-      package policy
-
-      import data.lib.k8s
-
-      violation[msg] {
-        containers_resource_constraints_required
-
-        msg := k8s.format(sprintf("(%s) %s: Container resource constraints must be specified", [k8s.kind, k8s.name]))
-      }
-
-      containers_resource_constraints_required {
-        k8s.is_workload
-        not container_resources_provided
-      }
-
-      container_resources_provided {
-        k8s.containers[_].resources.requests.cpu
-        k8s.containers[_].resources.requests.memory
-        k8s.containers[_].resources.limits.cpu
-        k8s.containers[_].resources.limits.memory
-      }
-    target: admission.k8s.gatekeeper.sh
-status: {}
diff --git a/examples/containers-resource-constraints/constraint.yaml b/examples/deny-privileged-containers/constraint.yaml
old mode 100644
new mode 100755
similarity index 73%
rename from examples/containers-resource-constraints/constraint.yaml
rename to examples/deny-privileged-containers/constraint.yaml
index 5f1d5fa..66a7de0
--- a/examples/containers-resource-constraints/constraint.yaml
+++ b/examples/deny-privileged-containers/constraint.yaml
@@ -1,7 +1,7 @@
 apiVersion: constraints.gatekeeper.sh/v1beta1
-kind: ContainersResourceConstraints
+kind: DenyPrivilegedContainers
 metadata:
-  name: containersresourceconstraints
+  name: denyprivilegedcontainers
 spec:
   match:
     kinds:
diff --git a/examples/deny-privileged-containers/src.rego b/examples/deny-privileged-containers/src.rego
new file mode 100644
index 0000000..0887e19
--- /dev/null
+++ b/examples/deny-privileged-containers/src.rego
@@ -0,0 +1,28 @@
+package deny_privileged_containers
+
+import data.lib.core
+import data.lib.workloads
+import data.lib.security
+
+# @title Containers must not run as privileged
+#
+# Privileged containers can easily escalate to root privileges on the node. As
+# such containers running as privileged or with sufficient capabilities granted
+# to obtain the same effect are not allowed.
+#
+# @kinds apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod
+
+violation[msg] {
+  workloads.containers[container]
+  is_privileged(container)
+
+  msg = core.format(sprintf("(%s) %s: Containers are not allowed to run as privileged", [core.kind, core.name]))
+}
+
+is_privileged(container) {
+  container.securityContext.privileged
+}
+
+is_privileged(container) {
+  security.added_capability(container, "CAP_SYS_ADMIN")
+}
diff --git a/examples/deny-privileged-containers/template.yaml b/examples/deny-privileged-containers/template.yaml
new file mode 100755
index 0000000..56e68dc
--- /dev/null
+++ b/examples/deny-privileged-containers/template.yaml
@@ -0,0 +1,163 @@
+apiVersion: templates.gatekeeper.sh/v1beta1
+kind: ConstraintTemplate
+metadata:
+  creationTimestamp: null
+  name: denyprivilegedcontainers
+spec:
+  crd:
+    spec:
+      names:
+        kind: DenyPrivilegedContainers
+  targets:
+  - libs:
+    - |
+      package lib.core
+
+      default is_gatekeeper = false
+
+      is_gatekeeper {
+        has_field(input, "review")
+        has_field(input.review, "object")
+      }
+
+      object = input {
+        not is_gatekeeper
+      }
+
+      object = input.review.object {
+        is_gatekeeper
+      }
+
+      format(msg) = gatekeeper_format {
+        is_gatekeeper
+        gatekeeper_format = {"msg": msg}
+      }
+
+      format(msg) = msg {
+        not is_gatekeeper
+      }
+
+      name = object.metadata.name
+
+      kind = object.kind
+
+      api_version = object.apiVersion
+
+      has_field(obj, field) {
+        obj[field]
+      }
+
+      missing_field(obj, field) = true {
+        obj[field] == ""
+      }
+
+      missing_field(obj, field) = true {
+        not has_field(obj, field)
+      }
+    - |
+      package lib.workloads
+
+      import data.lib.core
+
+      is_statefulset {
+        lower(core.kind) == "statefulset"
+      }
+
+      is_daemonset {
+        lower(core.kind) == "daemonset"
+      }
+
+      is_deployment {
+        lower(core.kind) == "deployment"
+      }
+
+      is_pod {
+        lower(core.kind) == "pod"
+      }
+
+      is_workload {
+        containers[_]
+      }
+
+      pods[pod] {
+        is_statefulset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_daemonset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_deployment
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_pod
+        pod = core.object
+      }
+
+      volumes[volume] {
+        pods[pod]
+        volume = pod.spec.volumes[_]
+      }
+
+      pod_containers(pod) = all_containers {
+        keys = {"containers", "initContainers"}
+        all_containers = [c | keys[k]; c = pod.spec[k][_]]
+      }
+
+      containers[container] {
+        pods[pod]
+        all_containers = pod_containers(pod)
+        container = all_containers[_]
+      }
+
+      containers[container] {
+        all_containers = pod_containers(core.object)
+        container = all_containers[_]
+      }
+    - |
+      package lib.security
+
+      dropped_capability(container, cap) {
+        container.securityContext.capabilities.drop[_] == cap
+      }
+
+      added_capability(container, cap) {
+        container.securityContext.capabilities.add[_] == cap
+      }
+
+      dropped_capability(psp, cap) {
+        psp.spec.capabilities.drop[_] == cap
+      }
+
+      added_capability(psp, cap) {
+        psp.spec.capabilities.add[_] == cap
+      }
+    rego: |
+      package deny_privileged_containers
+
+      import data.lib.core
+      import data.lib.workloads
+      import data.lib.security
+
+
+      violation[msg] {
+        workloads.containers[container]
+        is_privileged(container)
+
+        msg = core.format(sprintf("(%s) %s: Containers are not allowed to run as privileged", [core.kind, core.name]))
+      }
+
+      is_privileged(container) {
+        container.securityContext.privileged
+      }
+
+      is_privileged(container) {
+        security.added_capability(container, "CAP_SYS_ADMIN")
+      }
+    target: admission.k8s.gatekeeper.sh
+status: {}
diff --git a/examples/lib/core.rego b/examples/lib/core.rego
new file mode 100644
index 0000000..0828163
--- /dev/null
+++ b/examples/lib/core.rego
@@ -0,0 +1,43 @@
+package lib.core
+
+default is_gatekeeper = false
+
+is_gatekeeper {
+  has_field(input, "review")
+  has_field(input.review, "object")
+}
+
+object = input {
+  not is_gatekeeper
+}
+
+object = input.review.object {
+  is_gatekeeper
+}
+
+format(msg) = gatekeeper_format {
+  is_gatekeeper
+  gatekeeper_format = {"msg": msg}
+}
+
+format(msg) = msg {
+  not is_gatekeeper
+}
+
+name = object.metadata.name
+
+kind = object.kind
+
+api_version = object.apiVersion
+
+has_field(obj, field) {
+  obj[field]
+}
+
+missing_field(obj, field) = true {
+  obj[field] == ""
+}
+
+missing_field(obj, field) = true {
+  not has_field(obj, field)
+}
diff --git a/examples/lib/kubernetes.rego b/examples/lib/measurements.rego
similarity index 51%
rename from examples/lib/kubernetes.rego
rename to examples/lib/measurements.rego
index cbb339a..e96a8f7 100644
--- a/examples/lib/kubernetes.rego
+++ b/examples/lib/measurements.rego
@@ -1,130 +1,4 @@
-package lib.k8s
-
-default is_gatekeeper = false
-
-is_gatekeeper {
-  has_field(input, "review")
-  has_field(input.review, "object")
-}
-
-object = input {
-  not is_gatekeeper
-}
-
-object = input.review.object {
-  is_gatekeeper
-}
-
-format(msg) = gatekeeper_format {
-  is_gatekeeper
-  gatekeeper_format = {"msg": msg}
-}
-
-format(msg) = msg {
-  not is_gatekeeper
-}
-
-name = object.metadata.name
-
-kind = object.kind
-
-has_field(obj, field) {
-  obj[field]
-}
-
-missing_field(obj, field) = true {
-  obj[field] == ""
-}
-
-missing_field(obj, field) = true {
-  not has_field(obj, field)
-}
-
-is_service {
-  lower(kind) == "service"
-}
-
-is_statefulset {
-  lower(kind) == "statefulset"
-}
-
-is_daemonset {
-  lower(kind) == "daemonset"
-}
-
-is_deployment {
-  lower(kind) == "deployment"
-}
-
-is_pod {
-  lower(kind) == "pod"
-}
-
-is_namespace {
-  lower(kind) == "namespace"
-}
-
-is_workload {
-  containers[_]
-}
-
-pod_containers(pod) = all_containers {
-  keys = {"containers", "initContainers"}
-  all_containers = [c | keys[k]; c = pod.spec[k][_]]
-}
-
-containers[container] {
-  pods[pod]
-  all_containers = pod_containers(pod)
-  container = all_containers[_]
-}
-
-containers[container] {
-  all_containers = pod_containers(object)
-  container = all_containers[_]
-}
-
-container_images[image] {
-  containers[container]
-  image = container.image
-}
-
-container_images[image] {
-  image = object.spec.image
-}
-
-split_image(image) = [image, "latest"] {
-  not contains(image, ":")
-}
-
-split_image(image) = [image_name, tag] {
-  [image_name, tag] = split(image, ":")
-}
-
-pods[pod] {
-  is_statefulset
-  pod = object.spec.template
-}
-
-pods[pod] {
-  is_daemonset
-  pod = object.spec.template
-}
-
-pods[pod] {
-  is_deployment
-  pod = object.spec.template
-}
-
-pods[pod] {
-  is_pod
-  pod = object
-}
-
-volumes[volume] {
-  pods[pod]
-  volume = pod.spec.volumes[_]
-}
+package lib.measurements
 
 # 10 ** 21
 mem_multiple("E") = 1000000000000000000000 { true }
@@ -253,29 +127,3 @@ canonify_cpu(orig) = new {
   re_match("^[0-9]+$", orig)
   new := to_number(orig) * 1000
 }
-
-dropped_capability(container, cap) {
-  container.securityContext.capabilities.drop[_] == cap
-}
-
-added_capability(container, cap) {
-  container.securityContext.capabilities.add[_] == cap
-}
-
-no_read_only_filesystem(c) {
-  not has_field(c, "securityContext")
-}
-
-no_read_only_filesystem(c) {
-  has_field(c, "securityContext")
-  not has_field(c.securityContext, "readOnlyRootFilesystem")
-}
-
-priviledge_escalation_allowed(c) {
-  not has_field(c, "securityContext")
-}
-
-priviledge_escalation_allowed(c) {
-  has_field(c, "securityContext")
-  has_field(c.securityContext, "allowPrivilegeEscalation")
-}
diff --git a/examples/lib/security.rego b/examples/lib/security.rego
new file mode 100644
index 0000000..6931e59
--- /dev/null
+++ b/examples/lib/security.rego
@@ -0,0 +1,17 @@
+package lib.security
+
+dropped_capability(container, cap) {
+  container.securityContext.capabilities.drop[_] == cap
+}
+
+added_capability(container, cap) {
+  container.securityContext.capabilities.add[_] == cap
+}
+
+dropped_capability(psp, cap) {
+  psp.spec.capabilities.drop[_] == cap
+}
+
+added_capability(psp, cap) {
+  psp.spec.capabilities.add[_] == cap
+}
diff --git a/examples/lib/workloads.rego b/examples/lib/workloads.rego
new file mode 100644
index 0000000..f69f344
--- /dev/null
+++ b/examples/lib/workloads.rego
@@ -0,0 +1,64 @@
+package lib.workloads
+
+import data.lib.core
+
+is_statefulset {
+  lower(core.kind) == "statefulset"
+}
+
+is_daemonset {
+  lower(core.kind) == "daemonset"
+}
+
+is_deployment {
+  lower(core.kind) == "deployment"
+}
+
+is_pod {
+  lower(core.kind) == "pod"
+}
+
+is_workload {
+  containers[_]
+}
+
+pods[pod] {
+  is_statefulset
+  pod = core.object.spec.template
+}
+
+pods[pod] {
+  is_daemonset
+  pod = core.object.spec.template
+}
+
+pods[pod] {
+  is_deployment
+  pod = core.object.spec.template
+}
+
+pods[pod] {
+  is_pod
+  pod = core.object
+}
+
+volumes[volume] {
+  pods[pod]
+  volume = pod.spec.volumes[_]
+}
+
+pod_containers(pod) = all_containers {
+  keys = {"containers", "initContainers"}
+  all_containers = [c | keys[k]; c = pod.spec[k][_]]
+}
+
+containers[container] {
+  pods[pod]
+  all_containers = pod_containers(pod)
+  container = all_containers[_]
+}
+
+containers[container] {
+  all_containers = pod_containers(core.object)
+  container = all_containers[_]
+}
diff --git a/examples/policies.md b/examples/policies.md
index 8927719..132e0d4 100755
--- a/examples/policies.md
+++ b/examples/policies.md
@@ -3,6 +3,7 @@
 ## Violations
 
 * [Images must not use the latest tag](#Images-must-not-use-the-latest-tag)
+* [Containers must not run as privileged](#Containers-must-not-run-as-privileged)
 * [Containers must define resource constraints](#Containers-must-define-resource-constraints)
 
 ## Warnings
@@ -22,26 +23,67 @@ we can have higher confidence that our applications are immutable and do not cha
 ### Rego
 
 ```rego
-package main
+package container_latest_tag
 
-import data.lib.k8s
+import data.lib.core
+import data.lib.workloads
 
 violation[msg] {
-  has_latest_tag
+  workloads.containers[container]
+  has_latest_tag(container)
 
-  msg := k8s.format(sprintf("(%s) %s: Images must not use the latest tag", [k8s.kind, k8s.name]))
+  msg := core.format(sprintf("(%s) %s: Images must not use the latest tag", [core.kind, core.name]))
 }
 
-has_latest_tag {
-  endswith(k8s.container_images[_], ":latest")
+has_latest_tag(c) {
+  endswith(c.image, ":latest")
 }
 
-has_latest_tag {
-  contains(k8s.container_images[_], ":") == false
+has_latest_tag(c) {
+  contains(c.image, ":") == false
 }
 
 ```
-_source: [containers-latest-tag](containers-latest-tag)_
+_source: [deny-containers-latest-tag](deny-containers-latest-tag)_
+
+## Containers must not run as privileged
+
+**Severity:** violation
+
+**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod
+
+
+Privileged containers can easily escalate to root privileges on the node. As
+such containers running as privileged or with sufficient capabilities granted
+to obtain the same effect are not allowed.
+
+### Rego
+
+```rego
+package deny_privileged_containers
+
+import data.lib.core
+import data.lib.workloads
+import data.lib.security
+
+
+violation[msg] {
+  workloads.containers[container]
+  is_privileged(container)
+
+  msg = core.format(sprintf("(%s) %s: Containers are not allowed to run as privileged", [core.kind, core.name]))
+}
+
+is_privileged(container) {
+  container.securityContext.privileged
+}
+
+is_privileged(container) {
+  security.added_capability(container, "CAP_SYS_ADMIN")
+}
+
+```
+_source: [deny-privileged-containers](deny-privileged-containers)_
 
 ## Containers must define resource constraints
 
@@ -56,29 +98,31 @@ and potentially starve other applications that need to run.
 ### Rego
 
 ```rego
-package policy
+package container_resource_constraints
 
-import data.lib.k8s
+import data.lib.core
+import data.lib.workloads
 
 violation[msg] {
   containers_resource_constraints_required
 
-  msg := k8s.format(sprintf("(%s) %s: Container resource constraints must be specified", [k8s.kind, k8s.name]))
+  msg := core.format(sprintf("(%s) %s: Container resource constraints must be specified", [core.kind, core.name]))
 }
 
 containers_resource_constraints_required {
-  k8s.is_workload
+  workloads.is_workload
   not container_resources_provided
 }
 
 container_resources_provided {
-  k8s.containers[_].resources.requests.cpu
-  k8s.containers[_].resources.requests.memory
-  k8s.containers[_].resources.limits.cpu
-  k8s.containers[_].resources.limits.memory
+  workloads.containers[_].resources.requests.cpu
+  workloads.containers[_].resources.requests.memory
+  workloads.containers[_].resources.limits.cpu
+  workloads.containers[_].resources.limits.memory
 }
+
 ```
-_source: [containers-resource-constraints](containers-resource-constraints)_
+_source: [require-containers-resource-constraints](require-containers-resource-constraints)_
 
 ## Deprecated Deployment and DaemonSet API
 
@@ -94,17 +138,15 @@ the version for both of these resources must be `apps/v1`.
 ### Rego
 
 ```rego
-package main
-
-import data.lib.k8s
+package warn_deprecated_api_versions
 
 warn[msg] {
   resources := ["DaemonSet", "Deployment"]
   input.apiVersion == "extensions/v1beta1"
   input.kind == resources[_]
 
-  msg := k8s.format(sprintf("%s/%s: API extensions/v1beta1 for %s has been deprecated, use apps/v1 instead.", [input.kind, input.metadata.name, input.kind]))
+  msg := sprintf("%s/%s: API extensions/v1beta1 for %s has been deprecated, use apps/v1 instead.", [input.kind, input.metadata.name, input.kind])
 }
 
 ```
-_source: [warnings](warnings)_
+_source: [warn-deprecated-api-versions](warn-deprecated-api-versions)_
diff --git a/examples/require-containers-resource-constraints/constraint.yaml b/examples/require-containers-resource-constraints/constraint.yaml
new file mode 100644
index 0000000..74e23d7
--- /dev/null
+++ b/examples/require-containers-resource-constraints/constraint.yaml
@@ -0,0 +1,15 @@
+apiVersion: constraints.gatekeeper.sh/v1beta1
+kind: RequireContainersResourceConstraints
+metadata:
+  name: requirecontainersresourceconstraints
+spec:
+  match:
+    kinds:
+    - apiGroups:
+      - apps
+      - ""
+      kinds:
+      - DaemonSet
+      - Deployment
+      - StatefulSet
+      - Pod
diff --git a/examples/containers-resource-constraints/kustomization.yaml b/examples/require-containers-resource-constraints/kustomization.yaml
similarity index 100%
rename from examples/containers-resource-constraints/kustomization.yaml
rename to examples/require-containers-resource-constraints/kustomization.yaml
diff --git a/examples/containers-resource-constraints/src.rego b/examples/require-containers-resource-constraints/src.rego
similarity index 52%
rename from examples/containers-resource-constraints/src.rego
rename to examples/require-containers-resource-constraints/src.rego
index 27b6546..21d496e 100644
--- a/examples/containers-resource-constraints/src.rego
+++ b/examples/require-containers-resource-constraints/src.rego
@@ -1,6 +1,7 @@
-package policy
+package container_resource_constraints
 
-import data.lib.k8s
+import data.lib.core
+import data.lib.workloads
 
 # @title Containers must define resource constraints
 #
@@ -11,17 +12,17 @@ import data.lib.k8s
 violation[msg] {
   containers_resource_constraints_required
 
-  msg := k8s.format(sprintf("(%s) %s: Container resource constraints must be specified", [k8s.kind, k8s.name]))
+  msg := core.format(sprintf("(%s) %s: Container resource constraints must be specified", [core.kind, core.name]))
 }
 
 containers_resource_constraints_required {
-  k8s.is_workload
+  workloads.is_workload
   not container_resources_provided
 }
 
 container_resources_provided {
-  k8s.containers[_].resources.requests.cpu
-  k8s.containers[_].resources.requests.memory
-  k8s.containers[_].resources.limits.cpu
-  k8s.containers[_].resources.limits.memory
-}
\ No newline at end of file
+  workloads.containers[_].resources.requests.cpu
+  workloads.containers[_].resources.requests.memory
+  workloads.containers[_].resources.limits.cpu
+  workloads.containers[_].resources.limits.memory
+}
diff --git a/examples/require-containers-resource-constraints/template.yaml b/examples/require-containers-resource-constraints/template.yaml
new file mode 100644
index 0000000..f2156e8
--- /dev/null
+++ b/examples/require-containers-resource-constraints/template.yaml
@@ -0,0 +1,146 @@
+apiVersion: templates.gatekeeper.sh/v1beta1
+kind: ConstraintTemplate
+metadata:
+  creationTimestamp: null
+  name: requirecontainersresourceconstraints
+spec:
+  crd:
+    spec:
+      names:
+        kind: RequireContainersResourceConstraints
+  targets:
+  - libs:
+    - |
+      package lib.core
+
+      default is_gatekeeper = false
+
+      is_gatekeeper {
+        has_field(input, "review")
+        has_field(input.review, "object")
+      }
+
+      object = input {
+        not is_gatekeeper
+      }
+
+      object = input.review.object {
+        is_gatekeeper
+      }
+
+      format(msg) = gatekeeper_format {
+        is_gatekeeper
+        gatekeeper_format = {"msg": msg}
+      }
+
+      format(msg) = msg {
+        not is_gatekeeper
+      }
+
+      name = object.metadata.name
+
+      kind = object.kind
+
+      api_version = object.apiVersion
+
+      has_field(obj, field) {
+        obj[field]
+      }
+
+      missing_field(obj, field) = true {
+        obj[field] == ""
+      }
+
+      missing_field(obj, field) = true {
+        not has_field(obj, field)
+      }
+    - |
+      package lib.workloads
+
+      import data.lib.core
+
+      is_statefulset {
+        lower(core.kind) == "statefulset"
+      }
+
+      is_daemonset {
+        lower(core.kind) == "daemonset"
+      }
+
+      is_deployment {
+        lower(core.kind) == "deployment"
+      }
+
+      is_pod {
+        lower(core.kind) == "pod"
+      }
+
+      is_workload {
+        containers[_]
+      }
+
+      pods[pod] {
+        is_statefulset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_daemonset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_deployment
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_pod
+        pod = core.object
+      }
+
+      volumes[volume] {
+        pods[pod]
+        volume = pod.spec.volumes[_]
+      }
+
+      pod_containers(pod) = all_containers {
+        keys = {"containers", "initContainers"}
+        all_containers = [c | keys[k]; c = pod.spec[k][_]]
+      }
+
+      containers[container] {
+        pods[pod]
+        all_containers = pod_containers(pod)
+        container = all_containers[_]
+      }
+
+      containers[container] {
+        all_containers = pod_containers(core.object)
+        container = all_containers[_]
+      }
+    rego: |
+      package container_resource_constraints
+
+      import data.lib.core
+      import data.lib.workloads
+
+      violation[msg] {
+        containers_resource_constraints_required
+
+        msg := core.format(sprintf("(%s) %s: Container resource constraints must be specified", [core.kind, core.name]))
+      }
+
+      containers_resource_constraints_required {
+        workloads.is_workload
+        not container_resources_provided
+      }
+
+      container_resources_provided {
+        workloads.containers[_].resources.requests.cpu
+        workloads.containers[_].resources.requests.memory
+        workloads.containers[_].resources.limits.cpu
+        workloads.containers[_].resources.limits.memory
+      }
+    target: admission.k8s.gatekeeper.sh
+status: {}
diff --git a/examples/warnings/src.rego b/examples/warn-deprecated-api-versions/src.rego
similarity index 72%
rename from examples/warnings/src.rego
rename to examples/warn-deprecated-api-versions/src.rego
index e4e2dc7..e2d085e 100644
--- a/examples/warnings/src.rego
+++ b/examples/warn-deprecated-api-versions/src.rego
@@ -1,6 +1,4 @@
-package main
-
-import data.lib.k8s
+package warn_deprecated_api_versions
 
 # @title Deprecated Deployment and DaemonSet API
 #
@@ -14,5 +12,5 @@ warn[msg] {
   input.apiVersion == "extensions/v1beta1"
   input.kind == resources[_]
 
-  msg := k8s.format(sprintf("%s/%s: API extensions/v1beta1 for %s has been deprecated, use apps/v1 instead.", [input.kind, input.metadata.name, input.kind]))
+  msg := sprintf("%s/%s: API extensions/v1beta1 for %s has been deprecated, use apps/v1 instead.", [input.kind, input.metadata.name, input.kind])
 }
diff --git a/internal/commands/create.go b/internal/commands/create.go
index 838bd23..b503181 100644
--- a/internal/commands/create.go
+++ b/internal/commands/create.go
@@ -94,6 +94,11 @@ func runCreateCommand(path string) error {
 			constraintFileName = fmt.Sprintf("constraint_%s.yaml", GetKindFromPath(policy.FilePath))
 		}
 
+		matchingLibraries := getMatchingLibraries(policy, libraries)
+		if len(matchingLibraries) != len(policy.ImportPackages) {
+			return fmt.Errorf("missing imported libraries")
+		}
+
 		if _, err := os.Stat(outputDir); os.IsNotExist(err) {
 			err := os.MkdirAll(outputDir, os.ModePerm)
 			if err != nil {
@@ -101,7 +106,7 @@ func runCreateCommand(path string) error {
 			}
 		}
 
-		constraintTemplate := getConstraintTemplate(policy, libraries)
+		constraintTemplate := getConstraintTemplate(policy, matchingLibraries)
 		constraintTemplateBytes, err := yaml.Marshal(&constraintTemplate)
 		if err != nil {
 			return fmt.Errorf("marshal constrainttemplate: %w", err)
@@ -131,16 +136,7 @@ func runCreateCommand(path string) error {
 	return nil
 }
 
-func getConstraintTemplate(policy rego.File, libraries []rego.File) v1beta1.ConstraintTemplate {
-	var libs []string
-	for _, importPackage := range policy.ImportPackages {
-		for _, library := range libraries {
-			if importPackage == library.PackageName {
-				libs = append(libs, library.Contents)
-			}
-		}
-	}
-
+func getConstraintTemplate(policy rego.File, libraries []string) v1beta1.ConstraintTemplate {
 	kind := GetKindFromPath(policy.FilePath)
 
 	constraintTemplate := v1beta1.ConstraintTemplate{
@@ -162,7 +158,7 @@ func getConstraintTemplate(policy rego.File, libraries []rego.File) v1beta1.Cons
 			Targets: []v1beta1.Target{
 				{
 					Target: "admission.k8s.gatekeeper.sh",
-					Libs:   libs,
+					Libs:   libraries,
 					Rego:   getRegoWithoutComments(policy.Contents),
 				},
 			},
@@ -251,3 +247,16 @@ func getLibraryPath(path string) (string, error) {
 
 	return libraryPath, nil
 }
+
+func getMatchingLibraries(policy rego.File, libraries []rego.File) []string {
+	var libs []string
+	for _, importPackage := range policy.ImportPackages {
+		for _, library := range libraries {
+			if importPackage == library.PackageName {
+				libs = append(libs, library.Contents)
+			}
+		}
+	}
+
+	return libs
+}


Test patch:
diff --git a/examples/containers-latest-tag/template.yaml b/examples/containers-latest-tag/template.yaml
deleted file mode 100644
index d34e682..0000000
--- a/examples/containers-latest-tag/template.yaml
+++ /dev/null
@@ -1,298 +0,0 @@
-apiVersion: templates.gatekeeper.sh/v1beta1
-kind: ConstraintTemplate
-metadata:
-  creationTimestamp: null
-  name: containerslatesttag
-spec:
-  crd:
-    spec:
-      names:
-        kind: ContainersLatestTag
-  targets:
-  - libs:
-    - |
-      package lib.k8s
-
-      default is_gatekeeper = false
-
-      is_gatekeeper {
-        has_field(input, "review")
-        has_field(input.review, "object")
-      }
-
-      object = input {
-        not is_gatekeeper
-      }
-
-      object = input.review.object {
-        is_gatekeeper
-      }
-
-      format(msg) = gatekeeper_format {
-        is_gatekeeper
-        gatekeeper_format = {"msg": msg}
-      }
-
-      format(msg) = msg {
-        not is_gatekeeper
-      }
-
-      name = object.metadata.name
-
-      kind = object.kind
-
-      has_field(obj, field) {
-        obj[field]
-      }
-
-      missing_field(obj, field) = true {
-        obj[field] == ""
-      }
-
-      missing_field(obj, field) = true {
-        not has_field(obj, field)
-      }
-
-      is_service {
-        lower(kind) == "service"
-      }
-
-      is_statefulset {
-        lower(kind) == "statefulset"
-      }
-
-      is_daemonset {
-        lower(kind) == "daemonset"
-      }
-
-      is_deployment {
-        lower(kind) == "deployment"
-      }
-
-      is_pod {
-        lower(kind) == "pod"
-      }
-
-      is_namespace {
-        lower(kind) == "namespace"
-      }
-
-      is_workload {
-        containers[_]
-      }
-
-      pod_containers(pod) = all_containers {
-        keys = {"containers", "initContainers"}
-        all_containers = [c | keys[k]; c = pod.spec[k][_]]
-      }
-
-      containers[container] {
-        pods[pod]
-        all_containers = pod_containers(pod)
-        container = all_containers[_]
-      }
-
-      containers[container] {
-        all_containers = pod_containers(object)
-        container = all_containers[_]
-      }
-
-      container_images[image] {
-        containers[container]
-        image = container.image
-      }
-
-      container_images[image] {
-        image = object.spec.image
-      }
-
-      split_image(image) = [image, "latest"] {
-        not contains(image, ":")
-      }
-
-      split_image(image) = [image_name, tag] {
-        [image_name, tag] = split(image, ":")
-      }
-
-      pods[pod] {
-        is_statefulset
-        pod = object.spec.template
-      }
-
-      pods[pod] {
-        is_daemonset
-        pod = object.spec.template
-      }
-
-      pods[pod] {
-        is_deployment
-        pod = object.spec.template
-      }
-
-      pods[pod] {
-        is_pod
-        pod = object
-      }
-
-      volumes[volume] {
-        pods[pod]
-        volume = pod.spec.volumes[_]
-      }
-
-      mem_multiple("E") = 1000000000000000000000 { true }
-
-      mem_multiple("P") = 1000000000000000000 { true }
-
-      mem_multiple("T") = 1000000000000000 { true }
-
-      mem_multiple("G") = 1000000000000 { true }
-
-      mem_multiple("M") = 1000000000 { true }
-
-      mem_multiple("k") = 1000000 { true }
-
-      mem_multiple("") = 1000 { true }
-
-      mem_multiple("m") = 1 { true }
-
-      mem_multiple("Ki") = 1024000 { true }
-
-      mem_multiple("Mi") = 1048576000 { true }
-
-      mem_multiple("Gi") = 1073741824000 { true }
-
-      mem_multiple("Ti") = 1099511627776000 { true }
-
-      mem_multiple("Pi") = 1125899906842624000 { true }
-
-      mem_multiple("Ei") = 1152921504606846976000 { true }
-
-      get_suffix(mem) = suffix {
-        not is_string(mem)
-        suffix := ""
-      }
-
-      get_suffix(mem) = suffix {
-        is_string(mem)
-        count(mem) > 0
-        suffix := substring(mem, count(mem) - 1, -1)
-        mem_multiple(suffix)
-      }
-
-      get_suffix(mem) = suffix {
-        is_string(mem)
-        count(mem) > 1
-        suffix := substring(mem, count(mem) - 2, -1)
-        mem_multiple(suffix)
-      }
-
-      get_suffix(mem) = suffix {
-        is_string(mem)
-        count(mem) > 1
-        not mem_multiple(substring(mem, count(mem) - 1, -1))
-        not mem_multiple(substring(mem, count(mem) - 2, -1))
-        suffix := ""
-      }
-
-      get_suffix(mem) = suffix {
-        is_string(mem)
-        count(mem) == 1
-        not mem_multiple(substring(mem, count(mem) - 1, -1))
-        suffix := ""
-      }
-
-      get_suffix(mem) = suffix {
-        is_string(mem)
-        count(mem) == 0
-        suffix := ""
-      }
-
-      canonify_mem(orig) = new {
-        is_number(orig)
-        new := orig * 1000
-      }
-
-      canonify_mem(orig) = new {
-        not is_number(orig)
-        suffix := get_suffix(orig)
-        raw := replace(orig, suffix, "")
-        re_match("^[0-9]+$", raw)
-        new := to_number(raw) * mem_multiple(suffix)
-      }
-
-      canonify_storage(orig) = new {
-        is_number(orig)
-        new := orig
-      }
-
-      canonify_storage(orig) = new {
-        not is_number(orig)
-        suffix := get_suffix(orig)
-        raw := replace(orig, suffix, "")
-        re_match("^[0-9]+$", raw)
-        new := to_number(raw) * mem_multiple(suffix)
-      }
-
-      canonify_cpu(orig) = new {
-        is_number(orig)
-        new := orig * 1000
-      }
-
-      canonify_cpu(orig) = new {
-        not is_number(orig)
-        endswith(orig, "m")
-        new := to_number(replace(orig, "m", ""))
-      }
-
-      canonify_cpu(orig) = new {
-        not is_number(orig)
-        not endswith(orig, "m")
-        re_match("^[0-9]+$", orig)
-        new := to_number(orig) * 1000
-      }
-
-      dropped_capability(container, cap) {
-        container.securityContext.capabilities.drop[_] == cap
-      }
-
-      added_capability(container, cap) {
-        container.securityContext.capabilities.add[_] == cap
-      }
-
-      no_read_only_filesystem(c) {
-        not has_field(c, "securityContext")
-      }
-
-      no_read_only_filesystem(c) {
-        has_field(c, "securityContext")
-        not has_field(c.securityContext, "readOnlyRootFilesystem")
-      }
-
-      priviledge_escalation_allowed(c) {
-        not has_field(c, "securityContext")
-      }
-
-      priviledge_escalation_allowed(c) {
-        has_field(c, "securityContext")
-        has_field(c.securityContext, "allowPrivilegeEscalation")
-      }
-    rego: |
-      package main
-
-      import data.lib.k8s
-
-      violation[msg] {
-        has_latest_tag
-
-        msg := k8s.format(sprintf("(%s) %s: Images must not use the latest tag", [k8s.kind, k8s.name]))
-      }
-
-      has_latest_tag {
-        endswith(k8s.container_images[_], ":latest")
-      }
-
-      has_latest_tag {
-        contains(k8s.container_images[_], ":") == false
-      }
-    target: admission.k8s.gatekeeper.sh
-status: {}
diff --git a/examples/containers-latest-tag/constraint.yaml b/examples/deny-containers-latest-tag/constraint.yaml
similarity index 76%
rename from examples/containers-latest-tag/constraint.yaml
rename to examples/deny-containers-latest-tag/constraint.yaml
index c8b04c7..47d1e75 100755
--- a/examples/containers-latest-tag/constraint.yaml
+++ b/examples/deny-containers-latest-tag/constraint.yaml
@@ -1,7 +1,7 @@
 apiVersion: constraints.gatekeeper.sh/v1beta1
-kind: ContainersLatestTag
+kind: DenyContainersLatestTag
 metadata:
-  name: containerslatesttag
+  name: denycontainerslatesttag
 spec:
   match:
     kinds:
diff --git a/examples/containers-latest-tag/src.rego b/examples/deny-containers-latest-tag/src.rego
similarity index 50%
rename from examples/containers-latest-tag/src.rego
rename to examples/deny-containers-latest-tag/src.rego
index 94adb78..b58b4dc 100644
--- a/examples/containers-latest-tag/src.rego
+++ b/examples/deny-containers-latest-tag/src.rego
@@ -1,6 +1,7 @@
-package main
+package container_latest_tag
 
-import data.lib.k8s
+import data.lib.core
+import data.lib.workloads
 
 # @title Images must not use the latest tag
 #
@@ -9,15 +10,16 @@ import data.lib.k8s
 #
 # @kinds apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod
 violation[msg] {
-  has_latest_tag
+  workloads.containers[container]
+  has_latest_tag(container)
 
-  msg := k8s.format(sprintf("(%s) %s: Images must not use the latest tag", [k8s.kind, k8s.name]))
+  msg := core.format(sprintf("(%s) %s: Images must not use the latest tag", [core.kind, core.name]))
 }
 
-has_latest_tag {
-  endswith(k8s.container_images[_], ":latest")
+has_latest_tag(c) {
+  endswith(c.image, ":latest")
 }
 
-has_latest_tag {
-  contains(k8s.container_images[_], ":") == false
+has_latest_tag(c) {
+  contains(c.image, ":") == false
 }
diff --git a/examples/containers-latest-tag/src_test.rego b/examples/deny-containers-latest-tag/src_test.rego
similarity index 55%
rename from examples/containers-latest-tag/src_test.rego
rename to examples/deny-containers-latest-tag/src_test.rego
index f3e344b..d974884 100644
--- a/examples/containers-latest-tag/src_test.rego
+++ b/examples/deny-containers-latest-tag/src_test.rego
@@ -1,34 +1,40 @@
-package main
+package container_latest_tag
 
 test_input_as_image_without_latest_tag {
   input := {
     "kind": "Pod",
+    "metadata": {"name": "test"},
     "spec": {
       "containers": [{"image": "image:1.0.0"}]
     }
   }
 
-  not has_latest_tag with input as input
+  violations := violation with input as input
+  count(violations) == 0
 }
 
 test_input_as_image_with_latest_tag {
   input := {
     "kind": "Pod",
+    "metadata": {"name": "test"},
     "spec": {
       "containers": [{"image": "image:latest"}]
     }
   }
 
-  has_latest_tag with input as input
+  violations := violation with input as input
+  count(violations) == 1
 }
 
 test_input_as_image_with_no_tag {
   input := {
     "kind": "Pod",
+    "metadata": {"name": "test"},
     "spec": {
       "containers": [{"image": "image"}]
     }
   }
 
-  has_latest_tag with input as input
+  violations := violation with input as input
+  count(violations) == 1
 }
diff --git a/examples/deny-containers-latest-tag/template.yaml b/examples/deny-containers-latest-tag/template.yaml
new file mode 100644
index 0000000..831e37a
--- /dev/null
+++ b/examples/deny-containers-latest-tag/template.yaml
@@ -0,0 +1,143 @@
+apiVersion: templates.gatekeeper.sh/v1beta1
+kind: ConstraintTemplate
+metadata:
+  creationTimestamp: null
+  name: denycontainerslatesttag
+spec:
+  crd:
+    spec:
+      names:
+        kind: DenyContainersLatestTag
+  targets:
+  - libs:
+    - |
+      package lib.core
+
+      default is_gatekeeper = false
+
+      is_gatekeeper {
+        has_field(input, "review")
+        has_field(input.review, "object")
+      }
+
+      object = input {
+        not is_gatekeeper
+      }
+
+      object = input.review.object {
+        is_gatekeeper
+      }
+
+      format(msg) = gatekeeper_format {
+        is_gatekeeper
+        gatekeeper_format = {"msg": msg}
+      }
+
+      format(msg) = msg {
+        not is_gatekeeper
+      }
+
+      name = object.metadata.name
+
+      kind = object.kind
+
+      api_version = object.apiVersion
+
+      has_field(obj, field) {
+        obj[field]
+      }
+
+      missing_field(obj, field) = true {
+        obj[field] == ""
+      }
+
+      missing_field(obj, field) = true {
+        not has_field(obj, field)
+      }
+    - |
+      package lib.workloads
+
+      import data.lib.core
+
+      is_statefulset {
+        lower(core.kind) == "statefulset"
+      }
+
+      is_daemonset {
+        lower(core.kind) == "daemonset"
+      }
+
+      is_deployment {
+        lower(core.kind) == "deployment"
+      }
+
+      is_pod {
+        lower(core.kind) == "pod"
+      }
+
+      is_workload {
+        containers[_]
+      }
+
+      pods[pod] {
+        is_statefulset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_daemonset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_deployment
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_pod
+        pod = core.object
+      }
+
+      volumes[volume] {
+        pods[pod]
+        volume = pod.spec.volumes[_]
+      }
+
+      pod_containers(pod) = all_containers {
+        keys = {"containers", "initContainers"}
+        all_containers = [c | keys[k]; c = pod.spec[k][_]]
+      }
+
+      containers[container] {
+        pods[pod]
+        all_containers = pod_containers(pod)
+        container = all_containers[_]
+      }
+
+      containers[container] {
+        all_containers = pod_containers(core.object)
+        container = all_containers[_]
+      }
+    rego: |
+      package container_latest_tag
+
+      import data.lib.core
+      import data.lib.workloads
+
+      violation[msg] {
+        workloads.containers[container]
+        has_latest_tag(container)
+
+        msg := core.format(sprintf("(%s) %s: Images must not use the latest tag", [core.kind, core.name]))
+      }
+
+      has_latest_tag(c) {
+        endswith(c.image, ":latest")
+      }
+
+      has_latest_tag(c) {
+        contains(c.image, ":") == false
+      }
+    target: admission.k8s.gatekeeper.sh
+status: {}
diff --git a/examples/deny-privileged-containers/src_test.rego b/examples/deny-privileged-containers/src_test.rego
new file mode 100644
index 0000000..cb0c503
--- /dev/null
+++ b/examples/deny-privileged-containers/src_test.rego
@@ -0,0 +1,64 @@
+package deny_privileged_containers
+
+test_privileged {
+  input := {
+    "kind": "Pod",
+    "metadata": {"name": "test"},
+    "spec": {
+      "containers": [{
+        "securityContext": {
+          "privileged": true
+        }
+      }]
+    }
+  }
+
+  violations := violation with input as input
+  count(violations) == 1
+}
+
+test_not_privileged {
+  input := {
+    "kind": "Pod",
+    "metadata": {"name": "test"},
+    "spec": {
+      "containers": [{
+        "securityContext": {
+          "privileged": false
+        }
+      }]
+    }
+  }
+
+  violations := violation with input as input
+  count(violations) == 0
+}
+
+test_added_capability {
+  input := {
+    "kind": "Pod",
+    "metadata": {"name": "test"},
+    "spec": {
+      "containers": [{
+        "securityContext": {
+          "capabilities": {
+            "add": ["CAP_SYS_ADMIN"]
+          }
+        }
+      }]
+    }
+  }
+
+  violations := violation with input as input
+  count(violations) == 1
+}
+
+test_null {
+  input := {
+    "kind": "Pod",
+    "metadata": {"name": "test"},
+  }
+
+  violations := violation with input as input
+  count(violations) == 0
+}
diff --git a/examples/lib/core_test.rego b/examples/lib/core_test.rego
new file mode 100644
index 0000000..938ea80
--- /dev/null
+++ b/examples/lib/core_test.rego
@@ -0,0 +1,37 @@
+package lib.core
+
+test_not_gk {
+  input := {
+    "kind": "test"
+  }
+
+  not is_gatekeeper with input as input
+}
+
+test_is_gk {
+  input := {
+    "review": {
+      "object": {
+        "kind": "test"
+      }
+    }
+  }
+
+  is_gatekeeper with input as input
+}
+
+test_has_field_pos {
+  obj := {
+    "kind": "test"
+  }
+
+  has_field(obj, "kind")
+}
+
+test_has_field_neg {
+  obj := {
+    "kind": "test"
+  }
+  
+  not has_field(obj, "abc")
+}
diff --git a/examples/lib/security_test.rego b/examples/lib/security_test.rego
new file mode 100644
index 0000000..82f12c0
--- /dev/null
+++ b/examples/lib/security_test.rego
@@ -0,0 +1,97 @@
+package lib.security
+
+test_added_capabilities_container_match {
+  input := {
+    "securityContext": {
+      "capabilities": {
+        "add": ["CAP_SYS_ADMIN"]
+      }
+    }
+  }
+
+  added_capability(input, "CAP_SYS_ADMIN")
+}
+
+test_added_capabilities_container_nomatch {
+  input := {
+    "securityContext": {
+      "capabilities": {
+        "add": ["CAP_SYS_ADMIN"]
+      }
+    }
+  }
+
+  not added_capability(input, "test")
+}
+
+test_added_capabilities_psp_match {
+  input := {
+    "spec": {
+      "capabilities": {
+        "add": ["CAP_SYS_ADMIN"]
+      }
+    }
+  }
+
+  added_capability(input, "CAP_SYS_ADMIN")
+}
+
+test_added_capabilities_psp_nomatch {
+  input := {
+    "spec": {
+      "capabilities": {
+        "add": ["CAP_SYS_ADMIN"]
+      }
+    }
+  }
+
+  not added_capability(input, "test")
+}
+
+test_dropped_capabilities_container_match {
+  input := {
+    "securityContext": {
+      "capabilities": {
+        "drop": ["CAP_SYS_ADMIN"]
+      }
+    }
+  }
+
+  dropped_capability(input, "CAP_SYS_ADMIN")
+}
+
+test_dropped_capabilities_container_nomatch {
+  input := {
+    "securityContext": {
+      "capabilities": {
+        "drop": ["CAP_SYS_ADMIN"]
+      }
+    }
+  }
+
+  not dropped_capability(input, "test")
+}
+
+test_dropped_capabilities_psp_match {
+  input := {
+    "spec": {
+      "capabilities": {
+        "drop": ["CAP_SYS_ADMIN"]
+      }
+    }
+  }
+
+  dropped_capability(input, "CAP_SYS_ADMIN")
+}
+
+test_dropped_capabilities_psp_nomatch {
+  input := {
+    "spec": {
+      "capabilities": {
+        "drop": ["CAP_SYS_ADMIN"]
+      }
+    }
+  }
+
+  not dropped_capability(input, "test")
+}
diff --git a/examples/containers-resource-constraints/src_test.rego b/examples/require-containers-resource-constraints/src_test.rego
similarity index 96%
rename from examples/containers-resource-constraints/src_test.rego
rename to examples/require-containers-resource-constraints/src_test.rego
index bda2c08..a108880 100644
--- a/examples/containers-resource-constraints/src_test.rego
+++ b/examples/require-containers-resource-constraints/src_test.rego
@@ -1,4 +1,4 @@
-package policy
+package container_resource_constraints
 
 test_input_as_container_missing_resources {
   input := {
diff --git a/examples/containers-resource-constraints/test.yaml b/examples/require-containers-resource-constraints/test.yaml
similarity index 79%
rename from examples/containers-resource-constraints/test.yaml
rename to examples/require-containers-resource-constraints/test.yaml
index bf3d4f6..91b4e9d 100644
--- a/examples/containers-resource-constraints/test.yaml
+++ b/examples/require-containers-resource-constraints/test.yaml
@@ -5,4 +5,4 @@ metadata:
 spec:
   containers:
   - name: containers-resource-constraints-required-test
-    image: plexdev.azurecr.io/pcp/test:1.0
\ No newline at end of file
+    image: plexdev.azurecr.io/pcp/test:1.0
diff --git a/examples/warn-deprecated-api-versions/src_test.rego b/examples/warn-deprecated-api-versions/src_test.rego
new file mode 100644
index 0000000..6808f44
--- /dev/null
+++ b/examples/warn-deprecated-api-versions/src_test.rego
@@ -0,0 +1,34 @@
+package warn_deprecated_api_versions
+
+test_matching {
+  input := {
+    "kind": "Deployment",
+    "metadata": {"name": "test"},
+    "apiVersion": "extensions/v1beta1"
+  }
+
+  warns := warn with input as input
+  count(warns) == 1
+}
+
+test_different_kind {
+  input := {
+    "kind": "test",
+    "metadata": {"name": "test"},
+    "apiVersion": "extensions/v1beta1"
+  }
+
+  warns := warn with input as input
+  count(warns) == 0
+}
+
+test_different_apiversion {
+  input := {
+    "kind": "Deployment",
+    "metadata": {"name": "test"},
+    "apiVersion": "test"
+  }
+
+  warns := warn with input as input
+  count(warns) == 0
+}
diff --git a/internal/commands/create_test.go b/internal/commands/create_test.go
index 84b7ef0..5b0861a 100644
--- a/internal/commands/create_test.go
+++ b/internal/commands/create_test.go
@@ -74,26 +74,25 @@ func TestGetConstraintTemplate_CorrectLibrariesImported(t *testing.T) {
 import data.lib.foo
 rule[msg] { msg = true }`
 
-	libraryRegos := []string{`package lib.foo`, `package lib.bar`}
-
 	policyFile, err := rego.NewFile("/foo/test-kind/src.rego", policyImportsFoo)
 	if err != nil {
 		t.Fatal("new rego file:", err)
 	}
 
+	libraryRegos := []string{`package lib.foo`, `package lib.bar`}
 	var libraries []rego.File
-	for key, library := range libraryRegos {
-		libraryPath := fmt.Sprintf("/foo/lib/library-%v.rego", key)
-
-		libraryFile, err := rego.NewFile(libraryPath, library)
+	for i, libraryRego := range libraryRegos {
+		lib, err := rego.NewFile(fmt.Sprintf("lib.%d.rego", i), libraryRego)
 		if err != nil {
-			t.Fatal("new rego file:", err)
+			t.Fatal("create library from rego")
 		}
 
-		libraries = append(libraries, libraryFile)
+		libraries = append(libraries, lib)
 	}
 
-	actual := getConstraintTemplate(policyFile, libraries)
+	matchingLibraries := getMatchingLibraries(policyFile, libraries)
+
+	actual := getConstraintTemplate(policyFile, matchingLibraries)
 	if err != nil {
 		t.Fatal("get constraint:", err)
 	}
diff --git a/test/create/constraint_DenyContainersLatestTag.yaml b/test/create/constraint_DenyContainersLatestTag.yaml
new file mode 100755
index 0000000..47d1e75
--- /dev/null
+++ b/test/create/constraint_DenyContainersLatestTag.yaml
@@ -0,0 +1,15 @@
+apiVersion: constraints.gatekeeper.sh/v1beta1
+kind: DenyContainersLatestTag
+metadata:
+  name: denycontainerslatesttag
+spec:
+  match:
+    kinds:
+    - apiGroups:
+      - apps
+      - ""
+      kinds:
+      - DaemonSet
+      - Deployment
+      - StatefulSet
+      - Pod
diff --git a/test/create/constraint_DenyPrivilegedContainers.yaml b/test/create/constraint_DenyPrivilegedContainers.yaml
new file mode 100755
index 0000000..66a7de0
--- /dev/null
+++ b/test/create/constraint_DenyPrivilegedContainers.yaml
@@ -0,0 +1,15 @@
+apiVersion: constraints.gatekeeper.sh/v1beta1
+kind: DenyPrivilegedContainers
+metadata:
+  name: denyprivilegedcontainers
+spec:
+  match:
+    kinds:
+    - apiGroups:
+      - apps
+      - ""
+      kinds:
+      - DaemonSet
+      - Deployment
+      - StatefulSet
+      - Pod
diff --git a/test/create/constraint_RequireContainersResourceConstraints.yaml b/test/create/constraint_RequireContainersResourceConstraints.yaml
new file mode 100755
index 0000000..74e23d7
--- /dev/null
+++ b/test/create/constraint_RequireContainersResourceConstraints.yaml
@@ -0,0 +1,15 @@
+apiVersion: constraints.gatekeeper.sh/v1beta1
+kind: RequireContainersResourceConstraints
+metadata:
+  name: requirecontainersresourceconstraints
+spec:
+  match:
+    kinds:
+    - apiGroups:
+      - apps
+      - ""
+      kinds:
+      - DaemonSet
+      - Deployment
+      - StatefulSet
+      - Pod
diff --git a/test/create/template_DenyContainersLatestTag.yaml b/test/create/template_DenyContainersLatestTag.yaml
new file mode 100755
index 0000000..831e37a
--- /dev/null
+++ b/test/create/template_DenyContainersLatestTag.yaml
@@ -0,0 +1,143 @@
+apiVersion: templates.gatekeeper.sh/v1beta1
+kind: ConstraintTemplate
+metadata:
+  creationTimestamp: null
+  name: denycontainerslatesttag
+spec:
+  crd:
+    spec:
+      names:
+        kind: DenyContainersLatestTag
+  targets:
+  - libs:
+    - |
+      package lib.core
+
+      default is_gatekeeper = false
+
+      is_gatekeeper {
+        has_field(input, "review")
+        has_field(input.review, "object")
+      }
+
+      object = input {
+        not is_gatekeeper
+      }
+
+      object = input.review.object {
+        is_gatekeeper
+      }
+
+      format(msg) = gatekeeper_format {
+        is_gatekeeper
+        gatekeeper_format = {"msg": msg}
+      }
+
+      format(msg) = msg {
+        not is_gatekeeper
+      }
+
+      name = object.metadata.name
+
+      kind = object.kind
+
+      api_version = object.apiVersion
+
+      has_field(obj, field) {
+        obj[field]
+      }
+
+      missing_field(obj, field) = true {
+        obj[field] == ""
+      }
+
+      missing_field(obj, field) = true {
+        not has_field(obj, field)
+      }
+    - |
+      package lib.workloads
+
+      import data.lib.core
+
+      is_statefulset {
+        lower(core.kind) == "statefulset"
+      }
+
+      is_daemonset {
+        lower(core.kind) == "daemonset"
+      }
+
+      is_deployment {
+        lower(core.kind) == "deployment"
+      }
+
+      is_pod {
+        lower(core.kind) == "pod"
+      }
+
+      is_workload {
+        containers[_]
+      }
+
+      pods[pod] {
+        is_statefulset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_daemonset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_deployment
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_pod
+        pod = core.object
+      }
+
+      volumes[volume] {
+        pods[pod]
+        volume = pod.spec.volumes[_]
+      }
+
+      pod_containers(pod) = all_containers {
+        keys = {"containers", "initContainers"}
+        all_containers = [c | keys[k]; c = pod.spec[k][_]]
+      }
+
+      containers[container] {
+        pods[pod]
+        all_containers = pod_containers(pod)
+        container = all_containers[_]
+      }
+
+      containers[container] {
+        all_containers = pod_containers(core.object)
+        container = all_containers[_]
+      }
+    rego: |
+      package container_latest_tag
+
+      import data.lib.core
+      import data.lib.workloads
+
+      violation[msg] {
+        workloads.containers[container]
+        has_latest_tag(container)
+
+        msg := core.format(sprintf("(%s) %s: Images must not use the latest tag", [core.kind, core.name]))
+      }
+
+      has_latest_tag(c) {
+        endswith(c.image, ":latest")
+      }
+
+      has_latest_tag(c) {
+        contains(c.image, ":") == false
+      }
+    target: admission.k8s.gatekeeper.sh
+status: {}
diff --git a/test/create/template_DenyPrivilegedContainers.yaml b/test/create/template_DenyPrivilegedContainers.yaml
new file mode 100755
index 0000000..56e68dc
--- /dev/null
+++ b/test/create/template_DenyPrivilegedContainers.yaml
@@ -0,0 +1,163 @@
+apiVersion: templates.gatekeeper.sh/v1beta1
+kind: ConstraintTemplate
+metadata:
+  creationTimestamp: null
+  name: denyprivilegedcontainers
+spec:
+  crd:
+    spec:
+      names:
+        kind: DenyPrivilegedContainers
+  targets:
+  - libs:
+    - |
+      package lib.core
+
+      default is_gatekeeper = false
+
+      is_gatekeeper {
+        has_field(input, "review")
+        has_field(input.review, "object")
+      }
+
+      object = input {
+        not is_gatekeeper
+      }
+
+      object = input.review.object {
+        is_gatekeeper
+      }
+
+      format(msg) = gatekeeper_format {
+        is_gatekeeper
+        gatekeeper_format = {"msg": msg}
+      }
+
+      format(msg) = msg {
+        not is_gatekeeper
+      }
+
+      name = object.metadata.name
+
+      kind = object.kind
+
+      api_version = object.apiVersion
+
+      has_field(obj, field) {
+        obj[field]
+      }
+
+      missing_field(obj, field) = true {
+        obj[field] == ""
+      }
+
+      missing_field(obj, field) = true {
+        not has_field(obj, field)
+      }
+    - |
+      package lib.workloads
+
+      import data.lib.core
+
+      is_statefulset {
+        lower(core.kind) == "statefulset"
+      }
+
+      is_daemonset {
+        lower(core.kind) == "daemonset"
+      }
+
+      is_deployment {
+        lower(core.kind) == "deployment"
+      }
+
+      is_pod {
+        lower(core.kind) == "pod"
+      }
+
+      is_workload {
+        containers[_]
+      }
+
+      pods[pod] {
+        is_statefulset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_daemonset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_deployment
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_pod
+        pod = core.object
+      }
+
+      volumes[volume] {
+        pods[pod]
+        volume = pod.spec.volumes[_]
+      }
+
+      pod_containers(pod) = all_containers {
+        keys = {"containers", "initContainers"}
+        all_containers = [c | keys[k]; c = pod.spec[k][_]]
+      }
+
+      containers[container] {
+        pods[pod]
+        all_containers = pod_containers(pod)
+        container = all_containers[_]
+      }
+
+      containers[container] {
+        all_containers = pod_containers(core.object)
+        container = all_containers[_]
+      }
+    - |
+      package lib.security
+
+      dropped_capability(container, cap) {
+        container.securityContext.capabilities.drop[_] == cap
+      }
+
+      added_capability(container, cap) {
+        container.securityContext.capabilities.add[_] == cap
+      }
+
+      dropped_capability(psp, cap) {
+        psp.spec.capabilities.drop[_] == cap
+      }
+
+      added_capability(psp, cap) {
+        psp.spec.capabilities.add[_] == cap
+      }
+    rego: |
+      package deny_privileged_containers
+
+      import data.lib.core
+      import data.lib.workloads
+      import data.lib.security
+
+
+      violation[msg] {
+        workloads.containers[container]
+        is_privileged(container)
+
+        msg = core.format(sprintf("(%s) %s: Containers are not allowed to run as privileged", [core.kind, core.name]))
+      }
+
+      is_privileged(container) {
+        container.securityContext.privileged
+      }
+
+      is_privileged(container) {
+        security.added_capability(container, "CAP_SYS_ADMIN")
+      }
+    target: admission.k8s.gatekeeper.sh
+status: {}
diff --git a/test/create/template_RequireContainersResourceConstraints.yaml b/test/create/template_RequireContainersResourceConstraints.yaml
new file mode 100755
index 0000000..f2156e8
--- /dev/null
+++ b/test/create/template_RequireContainersResourceConstraints.yaml
@@ -0,0 +1,146 @@
+apiVersion: templates.gatekeeper.sh/v1beta1
+kind: ConstraintTemplate
+metadata:
+  creationTimestamp: null
+  name: requirecontainersresourceconstraints
+spec:
+  crd:
+    spec:
+      names:
+        kind: RequireContainersResourceConstraints
+  targets:
+  - libs:
+    - |
+      package lib.core
+
+      default is_gatekeeper = false
+
+      is_gatekeeper {
+        has_field(input, "review")
+        has_field(input.review, "object")
+      }
+
+      object = input {
+        not is_gatekeeper
+      }
+
+      object = input.review.object {
+        is_gatekeeper
+      }
+
+      format(msg) = gatekeeper_format {
+        is_gatekeeper
+        gatekeeper_format = {"msg": msg}
+      }
+
+      format(msg) = msg {
+        not is_gatekeeper
+      }
+
+      name = object.metadata.name
+
+      kind = object.kind
+
+      api_version = object.apiVersion
+
+      has_field(obj, field) {
+        obj[field]
+      }
+
+      missing_field(obj, field) = true {
+        obj[field] == ""
+      }
+
+      missing_field(obj, field) = true {
+        not has_field(obj, field)
+      }
+    - |
+      package lib.workloads
+
+      import data.lib.core
+
+      is_statefulset {
+        lower(core.kind) == "statefulset"
+      }
+
+      is_daemonset {
+        lower(core.kind) == "daemonset"
+      }
+
+      is_deployment {
+        lower(core.kind) == "deployment"
+      }
+
+      is_pod {
+        lower(core.kind) == "pod"
+      }
+
+      is_workload {
+        containers[_]
+      }
+
+      pods[pod] {
+        is_statefulset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_daemonset
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_deployment
+        pod = core.object.spec.template
+      }
+
+      pods[pod] {
+        is_pod
+        pod = core.object
+      }
+
+      volumes[volume] {
+        pods[pod]
+        volume = pod.spec.volumes[_]
+      }
+
+      pod_containers(pod) = all_containers {
+        keys = {"containers", "initContainers"}
+        all_containers = [c | keys[k]; c = pod.spec[k][_]]
+      }
+
+      containers[container] {
+        pods[pod]
+        all_containers = pod_containers(pod)
+        container = all_containers[_]
+      }
+
+      containers[container] {
+        all_containers = pod_containers(core.object)
+        container = all_containers[_]
+      }
+    rego: |
+      package container_resource_constraints
+
+      import data.lib.core
+      import data.lib.workloads
+
+      violation[msg] {
+        containers_resource_constraints_required
+
+        msg := core.format(sprintf("(%s) %s: Container resource constraints must be specified", [core.kind, core.name]))
+      }
+
+      containers_resource_constraints_required {
+        workloads.is_workload
+        not container_resources_provided
+      }
+
+      container_resources_provided {
+        workloads.containers[_].resources.requests.cpu
+        workloads.containers[_].resources.requests.memory
+        workloads.containers[_].resources.limits.cpu
+        workloads.containers[_].resources.limits.memory
+      }
+    target: admission.k8s.gatekeeper.sh
+status: {}
diff --git a/test/doc/expected.md b/test/doc/expected.md
index 8b841ea..c46c2f5 100644
--- a/test/doc/expected.md
+++ b/test/doc/expected.md
@@ -3,6 +3,7 @@
 ## Violations
 
 * [Images must not use the latest tag](#Images-must-not-use-the-latest-tag)
+* [Containers must not run as privileged](#Containers-must-not-run-as-privileged)
 * [Containers must define resource constraints](#Containers-must-define-resource-constraints)
 
 ## Warnings
@@ -22,26 +23,67 @@ we can have higher confidence that our applications are immutable and do not cha
 ### Rego
 
 ```rego
-package main
+package container_latest_tag
 
-import data.lib.k8s
+import data.lib.core
+import data.lib.workloads
 
 violation[msg] {
-  has_latest_tag
+  workloads.containers[container]
+  has_latest_tag(container)
 
-  msg := k8s.format(sprintf("(%s) %s: Images must not use the latest tag", [k8s.kind, k8s.name]))
+  msg := core.format(sprintf("(%s) %s: Images must not use the latest tag", [core.kind, core.name]))
 }
 
-has_latest_tag {
-  endswith(k8s.container_images[_], ":latest")
+has_latest_tag(c) {
+  endswith(c.image, ":latest")
 }
 
-has_latest_tag {
-  contains(k8s.container_images[_], ":") == false
+has_latest_tag(c) {
+  contains(c.image, ":") == false
 }
 
 ```
-_source: [../../examples/containers-latest-tag](../../examples/containers-latest-tag)_
+_source: [../../examples/deny-containers-latest-tag](../../examples/deny-containers-latest-tag)_
+
+## Containers must not run as privileged
+
+**Severity:** violation
+
+**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod
+
+
+Privileged containers can easily escalate to root privileges on the node. As
+such containers running as privileged or with sufficient capabilities granted
+to obtain the same effect are not allowed.
+
+### Rego
+
+```rego
+package deny_privileged_containers
+
+import data.lib.core
+import data.lib.workloads
+import data.lib.security
+
+
+violation[msg] {
+  workloads.containers[container]
+  is_privileged(container)
+
+  msg = core.format(sprintf("(%s) %s: Containers are not allowed to run as privileged", [core.kind, core.name]))
+}
+
+is_privileged(container) {
+  container.securityContext.privileged
+}
+
+is_privileged(container) {
+  security.added_capability(container, "CAP_SYS_ADMIN")
+}
+
+```
+_source: [../../examples/deny-privileged-containers](../../examples/deny-privileged-containers)_
 
 ## Containers must define resource constraints
 
@@ -56,29 +98,31 @@ and potentially starve other applications that need to run.
 ### Rego
 
 ```rego
-package policy
+package container_resource_constraints
 
-import data.lib.k8s
+import data.lib.core
+import data.lib.workloads
 
 violation[msg] {
   containers_resource_constraints_required
 
-  msg := k8s.format(sprintf("(%s) %s: Container resource constraints must be specified", [k8s.kind, k8s.name]))
+  msg := core.format(sprintf("(%s) %s: Container resource constraints must be specified", [core.kind, core.name]))
 }
 
 containers_resource_constraints_required {
-  k8s.is_workload
+  workloads.is_workload
   not container_resources_provided
 }
 
 container_resources_provided {
-  k8s.containers[_].resources.requests.cpu
-  k8s.containers[_].resources.requests.memory
-  k8s.containers[_].resources.limits.cpu
-  k8s.containers[_].resources.limits.memory
+  workloads.containers[_].resources.requests.cpu
+  workloads.containers[_].resources.requests.memory
+  workloads.containers[_].resources.limits.cpu
+  workloads.containers[_].resources.limits.memory
 }
+
 ```
-_source: [../../examples/containers-resource-constraints](../../examples/containers-resource-constraints)_
+_source: [../../examples/require-containers-resource-constraints](../../examples/require-containers-resource-constraints)_
 
 ## Deprecated Deployment and DaemonSet API
 
@@ -94,17 +138,15 @@ the version for both of these resources must be `apps/v1`.
 ### Rego
 
 ```rego
-package main
-
-import data.lib.k8s
+package warn_deprecated_api_versions
 
 warn[msg] {
   resources := ["DaemonSet", "Deployment"]
   input.apiVersion == "extensions/v1beta1"
   input.kind == resources[_]
 
-  msg := k8s.format(sprintf("%s/%s: API extensions/v1beta1 for %s has been deprecated, use apps/v1 instead.", [input.kind, input.metadata.name, input.kind]))
+  msg := sprintf("%s/%s: API extensions/v1beta1 for %s has been deprecated, use apps/v1 instead.", [input.kind, input.metadata.name, input.kind])
 }
 
 ```
-_source: [../../examples/warnings](../../examples/warnings)_
+_source: [../../examples/warn-deprecated-api-versions](../../examples/warn-deprecated-api-versions)_