Repository: microsoft/regorus (language: python)

Issue:
Lockdown kata containers policies with tests
Kata containers are an important usecase. They were one of the core reasons that regorus was developed. 
Recently, kata-containers and the confidential version (CoCo) have switch to using Regorus.

@danmihai1 has provided a bunch of policies as test data to be locked down.


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index dcc42572..14ae72fa 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -30,6 +30,8 @@ jobs:
       run: cargo test -r --verbose
     - name: Run tests (ACI)
       run: cargo test -r --test aci
+    - name: Run tests (KATA)
+      run: cargo test -r --test kata
     - name: Run tests (OPA Conformance)
       run: >-
         cargo test -r --test opa --features opa-testutil,serde_json/arbitrary_precision  -- $(tr '\n' ' ' < tests/opa.passing)
@@ -39,6 +41,8 @@ jobs:
       run: cargo test -r --verbose --target x86_64-unknown-linux-musl
     - name: Run tests (MUSL ACI)
       run: cargo test -r --test aci --target x86_64-unknown-linux-musl
+    - name: Run tests (KATA ACI)
+      run: cargo test -r --test kata --target x86_64-unknown-linux-musl
     - name: Run tests (MUSL OPA Conformance)
       run: >-
         cargo test -r --test opa --features opa-testutil,serde_json/arbitrary_precision --target x86_64-unknown-linux-musl  -- $(tr '\n' ' ' < tests/opa.passing)
diff --git a/Cargo.toml b/Cargo.toml
index 351d79c6..05345fd2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -124,6 +124,11 @@ name="aci"
 harness=false
 test=false
 
+[[test]]
+name="kata"
+harness=false
+test=false
+
 [package.metadata.docs.rs]
 # To build locally:
 # RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps
diff --git a/scripts/pre-push b/scripts/pre-push
index 5e36118d..edb9ef67 100755
--- a/scripts/pre-push
+++ b/scripts/pre-push
@@ -15,6 +15,7 @@ if [ -f Cargo.toml ]; then
    # Ensure that all tests pass
    cargo test -r
    cargo test -r --test aci
+   cargo test -r --test kata
 
    # Ensure that OPA conformance tests don't regress.
    cargo test -r --features opa-testutil,serde_json/arbitrary_precision --test opa -- $(tr '\n' ' ' < tests/opa.passing)


Test patch:
diff --git a/tests/kata/data/k8s-policy-job/inputs.txt b/tests/kata/data/k8s-policy-job/inputs.txt
new file mode 100644
index 00000000..7dbafe42
--- /dev/null
+++ b/tests/kata/data/k8s-policy-job/inputs.txt
@@ -0,0 +1,34 @@
+["ep":"AllowRequestsFailingPolicy",{}],
+
+["ep":"UpdateInterfaceRequest",{"interface":{"device":"eth0","name":"eth0","IPAddresses":[{"family":0,"address":"10.244.2.67","mask":"24"},{"family":1,"address":"fe80::e0a5:89ff:feb9:9950","mask":"64"}],"mtu":1500,"hwAddr":"e2:a5:89:b9:99:50","pciPath":"","type_":"","raw_flags":0}}],
+
+["ep":"UpdateRoutesRequest",{"routes":{"Routes":[{"dest":"","gateway":"10.244.2.1","device":"eth0","source":"","scope":0,"family":0}]}}],
+
+["ep":"CreateSandboxRequest",{"hostname":"policy-job-dx6cb","dns":["search kata-containers-k8s-tests.svc.cluster.local svc.cluster.local cluster.local iete1orsbdmu5ia1kq2du43hqe.bx.internal.cloudapp.net","nameserver 10.0.0.10","options ndots:5",""],"storages":[{"driver":"virtio-fs","driver_options":[],"source":"kataShared","fstype":"virtiofs","options":[],"mount_point":"/run/kata-containers/shared/containers/","fs_group":null},{"driver":"ephemeral","driver_options":[],"source":"shm","fstype":"tmpfs","options":["noexec","nosuid","nodev","mode=1777","size=67108864"],"mount_point":"/run/kata-containers/sandbox/shm","fs_group":null}],"sandbox_pidns":false,"sandbox_id":"d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4","guest_hook_path":"","kernel_modules":[]}],
+
+["ep":"GuestDetailsRequest",{"mem_block_size":true,"mem_hotplug_probe":true}],
+
+["ep":"CreateContainerRequest",{"container_id":"d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4","exec_id":"d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4","string_user":null,"devices":[],"storages":[],"OCI":{"Version":"1.1.0","Process":{"Terminal":false,"ConsoleSize":null,"User":{"UID":65535,"GID":65535,"AdditionalGids":[65535],"Username":""},"Args":["/pause"],"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cwd":"/","Capabilities":{"Bounding":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Effective":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Inheritable":[],"Permitted":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Ambient":[]},"Rlimits":[],"NoNewPrivileges":true,"ApparmorProfile":"","OOMScoreAdj":-998,"SelinuxLabel":""},"Root":{"Path":"/run/kata-containers/shared/containers/d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4/rootfs","Readonly":true},"Hostname":"policy-job-dx6cb","Mounts":[{"destination":"/proc","source":"proc","type_":"proc","options":["nosuid","noexec","nodev"]},{"destination":"/dev","source":"tmpfs","type_":"tmpfs","options":["nosuid","strictatime","mode=755","size=65536k"]},{"destination":"/dev/pts","source":"devpts","type_":"devpts","options":["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620","gid=5"]},{"destination":"/dev/mqueue","source":"mqueue","type_":"mqueue","options":["nosuid","noexec","nodev"]},{"destination":"/sys","source":"sysfs","type_":"sysfs","options":["nosuid","noexec","nodev","ro"]},{"destination":"/dev/shm","source":"/run/kata-containers/sandbox/shm","type_":"bind","options":["rbind"]},{"destination":"/etc/resolv.conf","source":"/run/kata-containers/shared/containers/d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4-c45e5b9869248a78-resolv.conf","type_":"bind","options":["rbind","ro","nosuid","nodev","noexec"]}],"Hooks":null,"Annotations":{"io.katacontainers.pkg.oci.bundle_path":"/run/containerd/io.containerd.runtime.v2.task/k8s.io/d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4","io.kubernetes.cri.container-type":"sandbox","io.kubernetes.cri.sandbox-cpu-shares":"2","nerdctl/network-namespace":"/var/run/netns/cni-92275a86-e0ea-c2c4-9e0c-4ac1ebe428eb","io.katacontainers.pkg.oci.container_type":"pod_sandbox","io.kubernetes.cri.sandbox-uid":"392939d5-72a0-4421-9cd7-1e89c3f86256","io.kubernetes.cri.sandbox-name":"policy-job-dx6cb","io.kubernetes.cri.sandbox-log-directory":"/var/log/pods/kata-containers-k8s-tests_policy-job-dx6cb_392939d5-72a0-4421-9cd7-1e89c3f86256","io.kubernetes.cri.sandbox-cpu-period":"100000","io.kubernetes.cri.sandbox-memory":"0","io.kubernetes.cri.sandbox-namespace":"kata-containers-k8s-tests","io.kubernetes.cri.sandbox-cpu-quota":"0","io.kubernetes.cri.sandbox-id":"d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4"},"Linux":{"UIDMappings":[],"GIDMappings":[],"Sysctl":{},"Resources":{"Devices":[],"Memory":null,"CPU":{"Shares":2,"Quota":0,"Period":0,"RealtimeRuntime":0,"RealtimePeriod":0,"Cpus":"","Mems":""},"Pids":null,"BlockIO":null,"HugepageLimits":[],"Network":null},"CgroupsPath":"kubepods-besteffort-pod392939d5_72a0_4421_9cd7_1e89c3f86256.slice:cri-containerd:d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4","Namespaces":[{"Type":"ipc","Path":""},{"Type":"uts","Path":""},{"Type":"mount","Path":""}],"Devices":[],"Seccomp":null,"RootfsPropagation":"","MaskedPaths":["/proc/acpi","/proc/asound","/proc/kcore","/proc/keys","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug","/sys/firmware","/sys/devices/virtual/powercap","/proc/scsi"],"ReadonlyPaths":["/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"],"MountLabel":"","IntelRdt":null},"Solaris":null,"Windows":null},"sandbox_pidns":false,"shared_mounts":[],"stdin_port":0,"stdout_port":0,"stderr_port":0}],
+
+["ep":"OnlineCPUMemRequest",{"wait":false,"nb_cpus":0,"cpu_only":false}],
+
+["ep":"UpdateEphemeralMountsRequest",{"storages":[]}],
+
+["ep":"StartContainerRequest",{"container_id":"d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4"}],
+
+["ep":"WaitProcessRequest",{"container_id":"d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4","exec_id":"d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4"}],
+
+["ep":"GetOOMEventRequest",{}],
+
+["ep":"CreateContainerRequest",{"container_id":"92fe9b0523a4ef0e39cdd2a509bd6a13639571f50d9d7df74f6dbab37cae23fa","exec_id":"92fe9b0523a4ef0e39cdd2a509bd6a13639571f50d9d7df74f6dbab37cae23fa","string_user":null,"devices":[],"storages":[],"OCI":{"Version":"1.1.0","Process":{"Terminal":false,"ConsoleSize":null,"User":{"UID":0,"GID":0,"AdditionalGids":[0,10],"Username":""},"Args":["/bin/sh","-c","sleep 120"],"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","HOSTNAME=policy-job-dx6cb","var1=val1","KUBERNETES_PORT_443_TCP_PROTO=tcp","KUBERNETES_PORT_443_TCP_PORT=443","KUBERNETES_PORT_443_TCP_ADDR=10.0.0.1","KUBERNETES_SERVICE_HOST=10.0.0.1","KUBERNETES_SERVICE_PORT=443","KUBERNETES_SERVICE_PORT_HTTPS=443","KUBERNETES_PORT=tcp://10.0.0.1:443","KUBERNETES_PORT_443_TCP=tcp://10.0.0.1:443"],"Cwd":"/","Capabilities":{"Bounding":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Effective":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Inheritable":[],"Permitted":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Ambient":[]},"Rlimits":[],"NoNewPrivileges":false,"ApparmorProfile":"cri-containerd.apparmor.d","OOMScoreAdj":1000,"SelinuxLabel":""},"Root":{"Path":"/run/kata-containers/shared/containers/92fe9b0523a4ef0e39cdd2a509bd6a13639571f50d9d7df74f6dbab37cae23fa/rootfs","Readonly":true},"Hostname":"","Mounts":[{"destination":"/proc","source":"proc","type_":"proc","options":["nosuid","noexec","nodev"]},{"destination":"/dev","source":"tmpfs","type_":"tmpfs","options":["nosuid","strictatime","mode=755","size=65536k"]},{"destination":"/dev/pts","source":"devpts","type_":"devpts","options":["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620","gid=5"]},{"destination":"/dev/mqueue","source":"mqueue","type_":"mqueue","options":["nosuid","noexec","nodev"]},{"destination":"/sys","source":"sysfs","type_":"sysfs","options":["nosuid","noexec","nodev","ro"]},{"destination":"/sys/fs/cgroup","source":"cgroup","type_":"cgroup","options":["nosuid","noexec","nodev","relatime","ro"]},{"destination":"/etc/hosts","source":"/run/kata-containers/shared/containers/92fe9b0523a4ef0e39cdd2a509bd6a13639571f50d9d7df74f6dbab37cae23fa-4195c832f3a3ed66-hosts","type_":"bind","options":["rbind","rprivate","rw"]},{"destination":"/dev/termination-log","source":"/run/kata-containers/shared/containers/92fe9b0523a4ef0e39cdd2a509bd6a13639571f50d9d7df74f6dbab37cae23fa-0f5544bf9955d14d-termination-log","type_":"bind","options":["rbind","rprivate","rw"]},{"destination":"/etc/hostname","source":"/run/kata-containers/shared/containers/92fe9b0523a4ef0e39cdd2a509bd6a13639571f50d9d7df74f6dbab37cae23fa-2f1945da6e7bade6-hostname","type_":"bind","options":["rbind","rprivate","ro"]},{"destination":"/etc/resolv.conf","source":"/run/kata-containers/shared/containers/92fe9b0523a4ef0e39cdd2a509bd6a13639571f50d9d7df74f6dbab37cae23fa-22d6d391f26cb161-resolv.conf","type_":"bind","options":["rbind","rprivate","ro"]},{"destination":"/dev/shm","source":"/run/kata-containers/sandbox/shm","type_":"bind","options":["rbind"]},{"destination":"/var/run/secrets/kubernetes.io/serviceaccount","source":"/run/kata-containers/shared/containers/92fe9b0523a4ef0e39cdd2a509bd6a13639571f50d9d7df74f6dbab37cae23fa-aa0738a33eb59dbf-serviceaccount","type_":"bind","options":["rbind","rprivate","ro"]}],"Hooks":null,"Annotations":{"io.kubernetes.cri.container-name":"hello","io.katacontainers.pkg.oci.bundle_path":"/run/containerd/io.containerd.runtime.v2.task/k8s.io/92fe9b0523a4ef0e39cdd2a509bd6a13639571f50d9d7df74f6dbab37cae23fa","io.kubernetes.cri.container-type":"container","io.kubernetes.cri.sandbox-name":"policy-job-dx6cb","io.kubernetes.cri.image-name":"quay.io/prometheus/busybox:latest","io.kubernetes.cri.sandbox-uid":"392939d5-72a0-4421-9cd7-1e89c3f86256","io.katacontainers.pkg.oci.container_type":"pod_container","io.kubernetes.cri.sandbox-namespace":"kata-containers-k8s-tests","io.kubernetes.cri.sandbox-id":"d2bc5590b33176f997388b25f600080d2276cbd08ad134ace2d254260ac864e4"},"Linux":{"UIDMappings":[],"GIDMappings":[],"Sysctl":{},"Resources":{"Devices":[],"Memory":{"Limit":0,"Reservation":0,"Swap":0,"Kernel":0,"KernelTCP":0,"Swappiness":0,"DisableOOMKiller":false},"CPU":{"Shares":2,"Quota":0,"Period":100000,"RealtimeRuntime":0,"RealtimePeriod":0,"Cpus":"","Mems":""},"Pids":null,"BlockIO":null,"HugepageLimits":[],"Network":null},"CgroupsPath":"kubepods-besteffort-pod392939d5_72a0_4421_9cd7_1e89c3f86256.slice:cri-containerd:92fe9b0523a4ef0e39cdd2a509bd6a13639571f50d9d7df74f6dbab37cae23fa","Namespaces":[{"Type":"ipc","Path":""},{"Type":"uts","Path":""},{"Type":"mount","Path":""}],"Devices":[],"Seccomp":null,"RootfsPropagation":"","MaskedPaths":["/proc/asound","/proc/acpi","/proc/kcore","/proc/keys","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug","/proc/scsi","/sys/firmware"],"ReadonlyPaths":["/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"],"MountLabel":"","IntelRdt":null},"Solaris":null,"Windows":null},"sandbox_pidns":false,"shared_mounts":[],"stdin_port":0,"stdout_port":0,"stderr_port":0}],
+
+["ep":"OnlineCPUMemRequest",{"wait":false,"nb_cpus":0,"cpu_only":false}],
+
+["ep":"UpdateEphemeralMountsRequest",{"storages":[]}],
+
+["ep":"StartContainerRequest",{"container_id":"92fe9b0523a4ef0e39cdd2a509bd6a13639571f50d9d7df74f6dbab37cae23fa"}],
+
+["ep":"OnlineCPUMemRequest",{"wait":false,"nb_cpus":0,"cpu_only":false}],
+
+["ep":"UpdateEphemeralMountsRequest",{"storages":[]}],
+
diff --git a/tests/kata/data/k8s-policy-job/output.json b/tests/kata/data/k8s-policy-job/output.json
new file mode 100644
index 00000000..c0f5c42d
--- /dev/null
+++ b/tests/kata/data/k8s-policy-job/output.json
@@ -0,0 +1,19 @@
+[
+  false,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true
+]
\ No newline at end of file
diff --git a/tests/kata/data/k8s-policy-job/policy.rego b/tests/kata/data/k8s-policy-job/policy.rego
new file mode 100644
index 00000000..b131baa6
--- /dev/null
+++ b/tests/kata/data/k8s-policy-job/policy.rego
@@ -0,0 +1,1652 @@
+# Copyright (c) 2023 Microsoft Corporation
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+package agent_policy
+
+import future.keywords.in
+import future.keywords.every
+
+# Default values, returned by OPA when rules cannot be evaluated to true.
+default AddARPNeighborsRequest := false
+default AddSwapRequest := false
+default CloseStdinRequest := false
+default CopyFileRequest := false
+default CreateContainerRequest := false
+default CreateSandboxRequest := true
+default DestroySandboxRequest := true
+default ExecProcessRequest := false
+default GetOOMEventRequest := true
+default GuestDetailsRequest := true
+default ListInterfacesRequest := false
+default ListRoutesRequest := false
+default MemHotplugByProbeRequest := false
+default OnlineCPUMemRequest := true
+default PauseContainerRequest := false
+default ReadStreamRequest := false
+default RemoveContainerRequest := true
+default RemoveStaleVirtiofsShareMountsRequest := true
+default ReseedRandomDevRequest := false
+default ResumeContainerRequest := false
+default SetGuestDateTimeRequest := false
+default SetPolicyRequest := false
+default SignalProcessRequest := true
+default StartContainerRequest := true
+default StartTracingRequest := false
+default StatsContainerRequest := true
+default StopTracingRequest := false
+default TtyWinResizeRequest := true
+default UpdateContainerRequest := false
+default UpdateEphemeralMountsRequest := true
+default UpdateInterfaceRequest := true
+default UpdateRoutesRequest := true
+default WaitProcessRequest := true
+default WriteStreamRequest := false
+
+# AllowRequestsFailingPolicy := true configures the Agent to *allow any
+# requests causing a policy failure*. This is an unsecure configuration
+# but is useful for allowing unsecure pods to start, then connect to
+# them and inspect OPA logs for the root cause of a failure.
+default AllowRequestsFailingPolicy := false
+
+CreateContainerRequest {
+    i_oci := input.OCI
+    i_storages := input.storages
+
+    some p_container in policy_data.containers
+    print("======== CreateContainerRequest: trying next policy container")
+
+    p_pidns := p_container.sandbox_pidns
+    i_pidns := input.sandbox_pidns
+    print("CreateContainerRequest: p_pidns =", p_pidns, "i_pidns =", i_pidns)
+    p_pidns == i_pidns
+
+    p_oci := p_container.OCI
+
+    print("CreateContainerRequest: p Version =", p_oci.Version, "i Version =", i_oci.Version)
+    p_oci.Version == i_oci.Version
+
+    print("CreateContainerRequest: p Readonly =", p_oci.Root.Readonly, "i Readonly =", i_oci.Root.Readonly)
+    p_oci.Root.Readonly == i_oci.Root.Readonly
+
+    allow_anno(p_oci, i_oci)
+
+    p_storages := p_container.storages
+    allow_by_anno(p_oci, i_oci, p_storages, i_storages)
+
+    allow_linux(p_oci, i_oci)
+
+    print("CreateContainerRequest: true")
+}
+
+# Reject unexpected annotations.
+allow_anno(p_oci, i_oci) {
+    print("allow_anno 1: start")
+
+    not i_oci.Annotations
+
+    print("allow_anno 1: true")
+}
+allow_anno(p_oci, i_oci) {
+    print("allow_anno 2: p Annotations =", p_oci.Annotations)
+    print("allow_anno 2: i Annotations =", i_oci.Annotations)
+
+    i_keys := object.keys(i_oci.Annotations)
+    print("allow_anno 2: i keys =", i_keys)
+
+    every i_key in i_keys {
+        allow_anno_key(i_key, p_oci)
+    }
+
+    print("allow_anno 2: true")
+}
+
+allow_anno_key(i_key, p_oci) {
+    print("allow_anno_key 1: i key =", i_key)
+
+    startswith(i_key, "io.kubernetes.cri.")
+
+    print("allow_anno_key 1: true")
+}
+allow_anno_key(i_key, p_oci) {
+    print("allow_anno_key 2: i key =", i_key)
+
+    some p_key, _ in p_oci.Annotations
+    p_key == i_key
+
+    print("allow_anno_key 2: true")
+}
+
+# Get the value of the "io.kubernetes.cri.sandbox-name" annotation and
+# correlate it with other annotations and process fields.
+allow_by_anno(p_oci, i_oci, p_storages, i_storages) {
+    print("allow_by_anno 1: start")
+
+    s_name := "io.kubernetes.cri.sandbox-name"
+
+    not p_oci.Annotations[s_name]
+
+    i_s_name := i_oci.Annotations[s_name]
+    print("allow_by_anno 1: i_s_name =", i_s_name)
+
+    allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, i_s_name)
+
+    print("allow_by_anno 1: true")
+}
+allow_by_anno(p_oci, i_oci, p_storages, i_storages) {
+    print("allow_by_anno 2: start")
+
+    s_name := "io.kubernetes.cri.sandbox-name"
+
+    p_s_name := p_oci.Annotations[s_name]
+    i_s_name := i_oci.Annotations[s_name]
+    print("allow_by_anno 2: i_s_name =", i_s_name, "p_s_name =", p_s_name)
+
+    allow_sandbox_name(p_s_name, i_s_name)
+    allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, i_s_name)
+
+    print("allow_by_anno 2: true")
+}
+
+allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, s_name) {
+    print("allow_by_sandbox_name: start")
+
+    s_namespace := "io.kubernetes.cri.sandbox-namespace"
+
+    p_namespace := p_oci.Annotations[s_namespace]
+    i_namespace := i_oci.Annotations[s_namespace]
+    print("allow_by_sandbox_name: p_namespace =", p_namespace, "i_namespace =", i_namespace)
+    p_namespace == i_namespace
+
+    allow_by_container_types(p_oci, i_oci, s_name, p_namespace)
+    allow_by_bundle_or_sandbox_id(p_oci, i_oci, p_storages, i_storages)
+    allow_process(p_oci, i_oci, s_name)
+
+    print("allow_by_sandbox_name: true")
+}
+
+allow_sandbox_name(p_s_name, i_s_name) {
+    print("allow_sandbox_name 1: start")
+
+    p_s_name == i_s_name
+
+    print("allow_sandbox_name 1: true")
+}
+allow_sandbox_name(p_s_name, i_s_name) {
+    print("allow_sandbox_name 2: start")
+
+    # TODO: should generated names be handled differently?
+    contains(p_s_name, "$(generated-name)")
+
+    print("allow_sandbox_name 2: true")
+}
+
+# Check that the "io.kubernetes.cri.container-type" and
+# "io.katacontainers.pkg.oci.container_type" annotations designate the
+# expected type - either a "sandbox" or a "container". Then, validate
+# other annotations based on the actual "sandbox" or "container" value
+# from the input container.
+allow_by_container_types(p_oci, i_oci, s_name, s_namespace) {
+    print("allow_by_container_types: checking io.kubernetes.cri.container-type")
+
+    c_type := "io.kubernetes.cri.container-type"
+    
+    p_cri_type := p_oci.Annotations[c_type]
+    i_cri_type := i_oci.Annotations[c_type]
+    print("allow_by_container_types: p_cri_type =", p_cri_type, "i_cri_type =", i_cri_type)
+    p_cri_type == i_cri_type
+
+    allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace)
+
+    print("allow_by_container_types: true")
+}
+
+allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace) {
+    print("allow_by_container_type 1: i_cri_type =", i_cri_type)
+    i_cri_type == "sandbox"
+
+    i_kata_type := i_oci.Annotations["io.katacontainers.pkg.oci.container_type"]
+    print("allow_by_container_type 1: i_kata_type =", i_kata_type)
+    i_kata_type == "pod_sandbox"
+
+    allow_sandbox_container_name(p_oci, i_oci)
+    allow_sandbox_net_namespace(p_oci, i_oci)
+    allow_sandbox_log_directory(p_oci, i_oci, s_name, s_namespace)
+
+    print("allow_by_container_type 1: true")
+}
+
+allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace) {
+    print("allow_by_container_type 2: i_cri_type =", i_cri_type)
+    i_cri_type == "container"
+
+    i_kata_type := i_oci.Annotations["io.katacontainers.pkg.oci.container_type"]
+    print("allow_by_container_type 2: i_kata_type =", i_kata_type)
+    i_kata_type == "pod_container"
+
+    allow_container_name(p_oci, i_oci)
+    allow_net_namespace(p_oci, i_oci)
+    allow_log_directory(p_oci, i_oci)
+
+    print("allow_by_container_type 2: true")
+}
+
+# "io.kubernetes.cri.container-name" annotation
+allow_sandbox_container_name(p_oci, i_oci) {
+    print("allow_sandbox_container_name: start")
+
+    container_annotation_missing(p_oci, i_oci, "io.kubernetes.cri.container-name")
+
+    print("allow_sandbox_container_name: true")
+}
+
+allow_container_name(p_oci, i_oci) {
+    print("allow_container_name: start")
+
+    allow_container_annotation(p_oci, i_oci, "io.kubernetes.cri.container-name")
+
+    print("allow_container_name: true")
+}
+
+container_annotation_missing(p_oci, i_oci, key) {
+    print("container_annotation_missing:", key)
+
+    not p_oci.Annotations[key]
+    not i_oci.Annotations[key]
+
+    print("container_annotation_missing: true")
+}
+
+allow_container_annotation(p_oci, i_oci, key) {
+    print("allow_container_annotation: key =", key)
+
+    p_value := p_oci.Annotations[key]
+    i_value := i_oci.Annotations[key]
+    print("allow_container_annotation: p_value =", p_value, "i_value =", i_value)
+
+    p_value == i_value
+
+    print("allow_container_annotation: true")
+}
+
+# "nerdctl/network-namespace" annotation
+allow_sandbox_net_namespace(p_oci, i_oci) {
+    print("allow_sandbox_net_namespace: start")
+
+    key := "nerdctl/network-namespace"
+
+    p_namespace := p_oci.Annotations[key]
+    i_namespace := i_oci.Annotations[key]
+    print("allow_sandbox_net_namespace: p_namespace =", p_namespace, "i_namespace =", i_namespace)
+
+    regex.match(p_namespace, i_namespace)
+
+    print("allow_sandbox_net_namespace: true")
+}
+
+allow_net_namespace(p_oci, i_oci) {
+    print("allow_net_namespace: start")
+
+    key := "nerdctl/network-namespace"
+
+    not p_oci.Annotations[key]
+    not i_oci.Annotations[key]
+
+    print("allow_net_namespace: true")
+}
+
+# "io.kubernetes.cri.sandbox-log-directory" annotation
+allow_sandbox_log_directory(p_oci, i_oci, s_name, s_namespace) {
+    print("allow_sandbox_log_directory: start")
+
+    key := "io.kubernetes.cri.sandbox-log-directory"
+
+    p_dir := p_oci.Annotations[key]
+    regex1 := replace(p_dir, "$(sandbox-name)", s_name)
+    regex2 := replace(regex1, "$(sandbox-namespace)", s_namespace)
+    print("allow_sandbox_log_directory: regex2 =", regex2)
+
+    i_dir := i_oci.Annotations[key]
+    print("allow_sandbox_log_directory: i_dir =", i_dir)
+
+    regex.match(regex2, i_dir)
+
+    print("allow_sandbox_log_directory: true")
+}
+
+allow_log_directory(p_oci, i_oci) {
+    print("allow_log_directory: start")
+
+    key := "io.kubernetes.cri.sandbox-log-directory"
+
+    not p_oci.Annotations[key]
+    not i_oci.Annotations[key]
+
+    print("allow_log_directory: true")
+}
+
+allow_linux(p_oci, i_oci) {
+    p_namespaces := p_oci.Linux.Namespaces
+    print("allow_linux: p namespaces =", p_namespaces)
+
+    i_namespaces := i_oci.Linux.Namespaces
+    print("allow_linux: i namespaces =", i_namespaces)
+
+    p_namespaces == i_namespaces
+
+    allow_masked_paths(p_oci, i_oci)
+    allow_readonly_paths(p_oci, i_oci)
+
+    print("allow_linux: true")
+}
+
+allow_masked_paths(p_oci, i_oci) {
+    p_paths := p_oci.Linux.MaskedPaths
+    print("allow_masked_paths 1: p_paths =", p_paths)
+
+    i_paths := i_oci.Linux.MaskedPaths
+    print("allow_masked_paths 1: i_paths =", i_paths)
+
+    allow_masked_paths_array(p_paths, i_paths)
+
+    print("allow_masked_paths 1: true")
+}
+allow_masked_paths(p_oci, i_oci) {
+    print("allow_masked_paths 2: start")
+
+    not p_oci.Linux.MaskedPaths
+    not i_oci.Linux.MaskedPaths
+
+    print("allow_masked_paths 2: true")
+}
+
+# All the policy masked paths must be masked in the input data too.
+# Input is allowed to have more masked paths than the policy.
+allow_masked_paths_array(p_array, i_array) {
+    every p_elem in p_array {
+        allow_masked_path(p_elem, i_array)
+    }
+}
+
+allow_masked_path(p_elem, i_array) {
+    print("allow_masked_path: p_elem =", p_elem)
+
+    some i_elem in i_array
+    p_elem == i_elem
+
+    print("allow_masked_path: true")
+}
+
+allow_readonly_paths(p_oci, i_oci) {
+    p_paths := p_oci.Linux.ReadonlyPaths
+    print("allow_readonly_paths 1: p_paths =", p_paths)
+
+    i_paths := i_oci.Linux.ReadonlyPaths
+    print("allow_readonly_paths 1: i_paths =", i_paths)
+
+    allow_readonly_paths_array(p_paths, i_paths, i_oci.Linux.MaskedPaths)
+
+    print("allow_readonly_paths 1: true")
+}
+allow_readonly_paths(p_oci, i_oci) {
+    print("allow_readonly_paths 2: start")
+
+    not p_oci.Linux.ReadonlyPaths
+    not i_oci.Linux.ReadonlyPaths
+
+    print("allow_readonly_paths 2: true")
+}
+
+# All the policy readonly paths must be either:
+# - Present in the input readonly paths, or
+# - Present in the input masked paths.
+# Input is allowed to have more readonly paths than the policy.
+allow_readonly_paths_array(p_array, i_array, masked_paths) {
+    every p_elem in p_array {
+        allow_readonly_path(p_elem, i_array, masked_paths)
+    }
+}
+
+allow_readonly_path(p_elem, i_array, masked_paths) {
+    print("allow_readonly_path 1: p_elem =", p_elem)
+
+    some i_elem in i_array
+    p_elem == i_elem
+
+    print("allow_readonly_path 1: true")
+}
+allow_readonly_path(p_elem, i_array, masked_paths) {
+    print("allow_readonly_path 2: p_elem =", p_elem)
+
+    some i_masked in masked_paths
+    p_elem == i_masked
+
+    print("allow_readonly_path 2: true")
+}
+
+# Check the consistency of the input "io.katacontainers.pkg.oci.bundle_path"
+# and io.kubernetes.cri.sandbox-id" values with other fields.
+allow_by_bundle_or_sandbox_id(p_oci, i_oci, p_storages, i_storages) {
+    print("allow_by_bundle_or_sandbox_id: start")
+
+    bundle_path := i_oci.Annotations["io.katacontainers.pkg.oci.bundle_path"]
+    bundle_id := replace(bundle_path, "/run/containerd/io.containerd.runtime.v2.task/k8s.io/", "")
+
+    key := "io.kubernetes.cri.sandbox-id"
+
+    p_regex := p_oci.Annotations[key]
+    sandbox_id := i_oci.Annotations[key]
+
+    print("allow_by_bundle_or_sandbox_id: sandbox_id =", sandbox_id, "regex =", p_regex)
+    regex.match(p_regex, sandbox_id)
+
+    allow_root_path(p_oci, i_oci, bundle_id)
+
+    every i_mount in input.OCI.Mounts {
+        allow_mount(p_oci, i_mount, bundle_id, sandbox_id)
+    }
+
+    # TODO: enable allow_storages() after fixing https://github.com/kata-containers/kata-containers/issues/8833
+    # allow_storages(p_storages, i_storages, bundle_id, sandbox_id)
+
+    print("allow_by_bundle_or_sandbox_id: true")
+}
+
+allow_process(p_oci, i_oci, s_name) {
+    p_process := p_oci.Process
+    i_process := i_oci.Process
+
+    print("allow_process: i terminal =", i_process.Terminal, "p terminal =", p_process.Terminal)
+    p_process.Terminal == i_process.Terminal
+
+    print("allow_process: i cwd =", i_process.Cwd, "i cwd =", p_process.Cwd)
+    p_process.Cwd == i_process.Cwd
+
+    print("allow_process: i noNewPrivileges =", i_process.NoNewPrivileges, "p noNewPrivileges =", p_process.NoNewPrivileges)
+    p_process.NoNewPrivileges == i_process.NoNewPrivileges
+
+    allow_caps(p_process.Capabilities, i_process.Capabilities)
+    allow_user(p_process, i_process)
+    allow_args(p_process, i_process, s_name)
+    allow_env(p_process, i_process, s_name)
+
+    print("allow_process: true")
+}
+
+allow_user(p_process, i_process) {
+    p_user := p_process.User
+    i_user := i_process.User
+
+    # TODO: track down the reason for mcr.microsoft.com/oss/bitnami/redis:6.0.8 being
+    #       executed with uid = 0 despite having "User": "1001" in its container image
+    #       config.
+    #print("allow_user: input uid =", i_user.UID, "policy uid =", p_user.UID)
+    #p_user.UID == i_user.UID
+
+    # TODO: track down the reason for registry.k8s.io/pause:3.9 being
+    #       executed with gid = 0 despite having "65535:65535" in its container image
+    #       config.
+    #print("allow_user: input gid =", i_user.GID, "policy gid =", p_user.GID)
+    #p_user.GID == i_user.GID
+
+    # TODO: compare the additionalGids field too after computing its value
+    # based on /etc/passwd and /etc/group from the container image.
+}
+
+allow_args(p_process, i_process, s_name) {
+    print("allow_args 1: no args")
+
+    not p_process.Args
+    not i_process.Args
+
+    print("allow_args 1: true")
+}
+allow_args(p_process, i_process, s_name) {
+    print("allow_args 2: policy args =", p_process.Args)
+    print("allow_args 2: input args =", i_process.Args)
+
+    count(p_process.Args) == count(i_process.Args)
+
+    every i, i_arg in i_process.Args {
+        allow_arg(i, i_arg, p_process, s_name)
+    }
+
+    print("allow_args 2: true")
+}
+allow_arg(i, i_arg, p_process, s_name) {
+    p_arg := p_process.Args[i]
+    print("allow_arg 1: i =", i, "i_arg =", i_arg, "p_arg =", p_arg)
+
+    p_arg2 := replace(p_arg, "$$", "$")
+    p_arg2 == i_arg
+
+    print("allow_arg 1: true")
+}
+allow_arg(i, i_arg, p_process, s_name) {
+    p_arg := p_process.Args[i]
+    print("allow_arg 2: i =", i, "i_arg =", i_arg, "p_arg =", p_arg)
+
+    # TODO: can $(node-name) be handled better?
+    contains(p_arg, "$(node-name)")
+
+    print("allow_arg 2: true")
+}
+allow_arg(i, i_arg, p_process, s_name) {
+    p_arg := p_process.Args[i]
+    print("allow_arg 3: i =", i, "i_arg =", i_arg, "p_arg =", p_arg)
+
+    p_arg2 := replace(p_arg, "$$", "$")
+    p_arg3 := replace(p_arg2, "$(sandbox-name)", s_name)
+    print("allow_arg 3: p_arg3 =", p_arg3)
+    p_arg3 == i_arg
+
+    print("allow_arg 3: true")
+}
+
+# OCI process.Env field
+allow_env(p_process, i_process, s_name) {
+    print("allow_env: p env =", p_process.Env)
+    print("allow_env: i env =", i_process.Env)
+
+    every i_var in i_process.Env {
+        print("allow_env: i_var =", i_var)
+        allow_var(p_process, i_process, i_var, s_name)
+    }
+
+    print("allow_env: true")
+}
+
+# Allow input env variables that are present in the policy data too.
+allow_var(p_process, i_process, i_var, s_name) {
+    some p_var in p_process.Env
+    p_var == i_var
+    print("allow_var 1: true")
+}
+
+# Match input with one of the policy variables, after substituting $(sandbox-name).
+allow_var(p_process, i_process, i_var, s_name) {
+    some p_var in p_process.Env
+    p_var2 := replace(p_var, "$(sandbox-name)", s_name)
+
+    print("allow_var 2: p_var2 =", p_var2)
+    p_var2 == i_var
+
+    print("allow_var 2: true")
+}
+
+# Allow input env variables that match with a request_defaults regex.
+allow_var(p_process, i_process, i_var, s_name) {
+    some p_regex1 in policy_data.request_defaults.CreateContainerRequest.allow_env_regex
+    p_regex2 := replace(p_regex1, "$(ipv4_a)", policy_data.common.ipv4_a)
+    p_regex3 := replace(p_regex2, "$(ip_p)", policy_data.common.ip_p)
+    p_regex4 := replace(p_regex3, "$(svc_name)", policy_data.common.svc_name)
+    p_regex5 := replace(p_regex4, "$(dns_label)", policy_data.common.dns_label)
+
+    print("allow_var 3: p_regex5 =", p_regex5)
+    regex.match(p_regex5, i_var)
+
+    print("allow_var 3: true")
+}
+
+# Allow fieldRef "fieldPath: status.podIP" values.
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+    is_ip(name_value[1])
+
+    some p_var in p_process.Env
+    allow_pod_ip_var(name_value[0], p_var)
+
+    print("allow_var 4: true")
+}
+
+# Allow common fieldRef variables.
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+
+    some p_var in p_process.Env
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == name_value[0]
+
+    # TODO: should these be handled in a different way?
+    always_allowed := ["$(host-name)", "$(node-name)", "$(pod-uid)"]
+    some allowed in always_allowed
+    contains(p_name_value[1], allowed)
+
+    print("allow_var 5: true")
+}
+
+# Allow fieldRef "fieldPath: status.hostIP" values.
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+    is_ip(name_value[1])
+
+    some p_var in p_process.Env
+    allow_host_ip_var(name_value[0], p_var)
+
+    print("allow_var 6: true")
+}
+
+# Allow resourceFieldRef values (e.g., "limits.cpu").
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+
+    some p_var in p_process.Env
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == name_value[0]
+
+    # TODO: should these be handled in a different way?
+    always_allowed = ["$(resource-field)", "$(todo-annotation)"]
+    some allowed in always_allowed
+    contains(p_name_value[1], allowed)
+
+    print("allow_var 7: true")
+}
+
+allow_pod_ip_var(var_name, p_var) {
+    print("allow_pod_ip_var: var_name =", var_name, "p_var =", p_var)
+
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == var_name
+    p_name_value[1] == "$(pod-ip)"
+
+    print("allow_pod_ip_var: true")
+}
+
+allow_host_ip_var(var_name, p_var) {
+    print("allow_host_ip_var: var_name =", var_name, "p_var =", p_var)
+
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == var_name
+    p_name_value[1] == "$(host-ip)"
+
+    print("allow_host_ip_var: true")
+}
+
+is_ip(value) {
+    bytes = split(value, ".")
+    count(bytes) == 4
+
+    is_ip_first_byte(bytes[0])
+    is_ip_other_byte(bytes[1])
+    is_ip_other_byte(bytes[2])
+    is_ip_other_byte(bytes[3])
+}
+is_ip_first_byte(component) {
+    number = to_number(component)
+    number >= 1
+    number <= 255
+}
+is_ip_other_byte(component) {
+    number = to_number(component)
+    number >= 0
+    number <= 255
+}
+
+# OCI root.Path
+allow_root_path(p_oci, i_oci, bundle_id) {
+    i_path := i_oci.Root.Path
+    p_path1 := p_oci.Root.Path
+    print("allow_root_path: i_path =", i_path, "p_path1 =", p_path1)
+
+    p_path2 := replace(p_path1, "$(cpath)", policy_data.common.cpath)
+    print("allow_root_path: p_path2 =", p_path2)
+
+    p_path3 := replace(p_path2, "$(bundle-id)", bundle_id)
+    print("allow_root_path: p_path3 =", p_path3)
+
+    p_path3 == i_path
+
+    print("allow_root_path: true")
+}
+
+# device mounts
+allow_mount(p_oci, i_mount, bundle_id, sandbox_id) {
+    print("allow_mount: i_mount =", i_mount)
+
+    some p_mount in p_oci.Mounts
+    print("allow_mount: p_mount =", p_mount)
+    check_mount(p_mount, i_mount, bundle_id, sandbox_id)
+
+    # TODO: are there any other required policy checks for mounts - e.g.,
+    #       multiple mounts with same source or destination?
+
+    print("allow_mount: true")
+}
+
+check_mount(p_mount, i_mount, bundle_id, sandbox_id) {
+    p_mount == i_mount
+    print("check_mount 1: true")
+}
+check_mount(p_mount, i_mount, bundle_id, sandbox_id) {
+    p_mount.destination == i_mount.destination
+    p_mount.type_ == i_mount.type_
+    p_mount.options == i_mount.options
+
+    mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id)
+
+    print("check_mount 2: true")
+}
+
+mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id) {
+    regex1 := p_mount.source
+    regex2 := replace(regex1, "$(sfprefix)", policy_data.common.sfprefix)
+    regex3 := replace(regex2, "$(cpath)", policy_data.common.cpath)
+    regex4 := replace(regex3, "$(bundle-id)", bundle_id)
+
+    print("mount_source_allows 1: regex4 =", regex4)
+    regex.match(regex4, i_mount.source)
+
+    print("mount_source_allows 1: true")
+}
+mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id) {
+    regex1 := p_mount.source
+    regex2 := replace(regex1, "$(sfprefix)", policy_data.common.sfprefix)
+    regex3 := replace(regex2, "$(cpath)", policy_data.common.cpath)
+    regex4 := replace(regex3, "$(sandbox-id)", sandbox_id)
+
+    print("mount_source_allows 2: regex4 =", regex4)
+    regex.match(regex4, i_mount.source)
+
+    print("mount_source_allows 2: true")
+}
+
+######################################################################
+# Storages
+
+allow_storages(p_storages, i_storages, bundle_id, sandbox_id) {
+    p_count := count(p_storages)
+    i_count := count(i_storages)
+    print("allow_storages: p_count =", p_count, "i_count =", i_count)
+
+    p_count == i_count
+
+    # Get the container image layer IDs and verity root hashes, from the "overlayfs" storage.
+    some overlay_storage in p_storages
+    overlay_storage.driver == "overlayfs"
+    print("allow_storages: overlay_storage =", overlay_storage)
+    count(overlay_storage.options) == 2
+
+    layer_ids := split(overlay_storage.options[0], ":")
+    print("allow_storages: layer_ids =", layer_ids)
+
+    root_hashes := split(overlay_storage.options[1], ":")
+    print("allow_storages: root_hashes =", root_hashes)
+
+    every i_storage in i_storages {
+        allow_storage(p_storages, i_storage, bundle_id, sandbox_id, layer_ids, root_hashes)
+    }
+
+    print("allow_storages: true")
+}
+
+allow_storage(p_storages, i_storage, bundle_id, sandbox_id, layer_ids, root_hashes) {
+    some p_storage in p_storages
+
+    print("allow_storage: p_storage =", p_storage)
+    print("allow_storage: i_storage =", i_storage)
+
+    p_storage.driver           == i_storage.driver
+    p_storage.driver_options   == i_storage.driver_options
+    p_storage.fs_group         == i_storage.fs_group
+
+    allow_storage_options(p_storage, i_storage, layer_ids, root_hashes)
+    allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids)
+
+    # TODO: validate the source field too.
+
+    print("allow_storage: true")
+}
+
+allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) {
+    print("allow_storage_options 1: start")
+
+    p_storage.driver != "blk"
+    p_storage.driver != "overlayfs"
+    p_storage.options == i_storage.options
+
+    print("allow_storage_options 1: true")
+}
+allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) {
+    print("allow_storage_options 2: start")
+
+    p_storage.driver == "overlayfs"
+    count(p_storage.options) == 2
+
+    policy_ids := split(p_storage.options[0], ":")
+    print("allow_storage_options 2: policy_ids =", policy_ids)
+    policy_ids == layer_ids
+
+    policy_hashes := split(p_storage.options[1], ":")
+    print("allow_storage_options 2: policy_hashes =", policy_hashes)
+
+    p_count := count(policy_ids)
+    print("allow_storage_options 2: p_count =", p_count)
+    p_count >= 1
+    p_count == count(policy_hashes)
+
+    i_count := count(i_storage.options)
+    print("allow_storage_options 2: i_count =", i_count)
+    i_count == p_count + 3
+
+    print("allow_storage_options 2: i_storage.options[0] =", i_storage.options[0])
+    i_storage.options[0] == "io.katacontainers.fs-opt.layer-src-prefix=/var/lib/containerd/io.containerd.snapshotter.v1.tardev/layers"
+
+    print("allow_storage_options 2: i_storage.options[i_count - 2] =", i_storage.options[i_count - 2])
+    i_storage.options[i_count - 2] == "io.katacontainers.fs-opt.overlay-rw"
+
+    lowerdir := concat("=", ["lowerdir", p_storage.options[0]])
+    print("allow_storage_options 2: lowerdir =", lowerdir)
+
+    i_storage.options[i_count - 1] == lowerdir
+    print("allow_storage_options 2: i_storage.options[i_count - 1] =", i_storage.options[i_count - 1])
+
+    every i, policy_id in policy_ids {
+        allow_overlay_layer(policy_id, policy_hashes[i], i_storage.options[i + 1])
+    }
+
+    print("allow_storage_options 2: true")
+}
+allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) {
+    print("allow_storage_options 3: start")
+
+    p_storage.driver == "blk"
+    count(p_storage.options) == 1
+
+    startswith(p_storage.options[0], "$(hash")
+    hash_suffix := trim_left(p_storage.options[0], "$(hash")
+
+    endswith(hash_suffix, ")")
+    hash_index := trim_right(hash_suffix, ")")
+    i := to_number(hash_index)
+    print("allow_storage_options 3: i =", i)
+
+    hash_option := concat("=", ["io.katacontainers.fs-opt.root-hash", root_hashes[i]])
+    print("allow_storage_options 3: hash_option =", hash_option)
+
+    count(i_storage.options) == 4
+    i_storage.options[0] == "ro"
+    i_storage.options[1] == "io.katacontainers.fs-opt.block_device=file"
+    i_storage.options[2] == "io.katacontainers.fs-opt.is-layer"
+    i_storage.options[3] == hash_option
+
+    print("allow_storage_options 3: true")
+}
+
+allow_overlay_layer(policy_id, policy_hash, i_option) {
+    print("allow_overlay_layer: policy_id =", policy_id, "policy_hash =", policy_hash)
+    print("allow_overlay_layer: i_option =", i_option)
+
+    startswith(i_option, "io.katacontainers.fs-opt.layer=")
+    i_value := replace(i_option, "io.katacontainers.fs-opt.layer=", "")
+    i_value_decoded := base64.decode(i_value)
+    print("allow_overlay_layer: i_value_decoded =", i_value_decoded)
+
+    policy_suffix := concat("=", ["tar,ro,io.katacontainers.fs-opt.block_device=file,io.katacontainers.fs-opt.is-layer,io.katacontainers.fs-opt.root-hash", policy_hash])
+    p_value := concat(",", [policy_id, policy_suffix])
+    print("allow_overlay_layer: p_value =", p_value)
+
+    p_value == i_value_decoded
+
+    print("allow_overlay_layer: true")
+}
+
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "tar"
+
+    startswith(p_storage.mount_point, "$(layer")
+    mount_suffix := trim_left(p_storage.mount_point, "$(layer")
+
+    endswith(mount_suffix, ")")
+    layer_index := trim_right(mount_suffix, ")")
+    i := to_number(layer_index)
+    print("allow_mount_point 1: i =", i)
+
+    layer_id := layer_ids[i]
+    print("allow_mount_point 1: layer_id =", layer_id)
+
+    p_mount := concat("/", ["/run/kata-containers/sandbox/layers", layer_id])
+    print("allow_mount_point 1: p_mount =", p_mount)
+
+    p_mount == i_storage.mount_point
+
+    print("allow_mount_point 1: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "fuse3.kata-overlay"
+
+    mount1 := replace(p_storage.mount_point, "$(cpath)", policy_data.common.cpath)
+    mount2 := replace(mount1, "$(bundle-id)", bundle_id)
+    print("allow_mount_point 2: mount2 =", mount2)
+
+    mount2 == i_storage.mount_point
+
+    print("allow_mount_point 2: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "local"
+
+    mount1 := p_storage.mount_point
+    print("allow_mount_point 3: mount1 =", mount1)
+
+    mount2 := replace(mount1, "$(cpath)", policy_data.common.cpath)
+    print("allow_mount_point 3: mount2 =", mount2)
+
+    mount3 := replace(mount2, "$(sandbox-id)", sandbox_id)
+    print("allow_mount_point 3: mount3 =", mount3)
+
+    regex.match(mount3, i_storage.mount_point)
+
+    print("allow_mount_point 3: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "bind"
+
+    mount1 := p_storage.mount_point
+    print("allow_mount_point 4: mount1 =", mount1)
+
+    mount2 := replace(mount1, "$(cpath)", policy_data.common.cpath)
+    print("allow_mount_point 4: mount2 =", mount2)
+
+    mount3 := replace(mount2, "$(bundle-id)", bundle_id)
+    print("allow_mount_point 4: mount3 =", mount3)
+
+    regex.match(mount3, i_storage.mount_point)
+
+    print("allow_mount_point 4: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "tmpfs"
+
+    mount1 := p_storage.mount_point
+    print("allow_mount_point 5: mount1 =", mount1)
+
+    regex.match(mount1, i_storage.mount_point)
+
+    print("allow_mount_point 5: true")
+}
+
+# process.Capabilities
+allow_caps(p_caps, i_caps) {
+    print("allow_caps: policy Ambient =", p_caps.Ambient)
+    print("allow_caps: input Ambient =", i_caps.Ambient)
+    match_caps(p_caps.Ambient, i_caps.Ambient)
+
+    print("allow_caps: policy Bounding =", p_caps.Bounding)
+    print("allow_caps: input Bounding =", i_caps.Bounding)
+    match_caps(p_caps.Bounding, i_caps.Bounding)
+
+    print("allow_caps: policy Effective =", p_caps.Effective)
+    print("allow_caps: input Effective =", i_caps.Effective)
+    match_caps(p_caps.Effective, i_caps.Effective)
+
+    print("allow_caps: policy Inheritable =", p_caps.Inheritable)
+    print("allow_caps: input Inheritable =", i_caps.Inheritable)
+    match_caps(p_caps.Inheritable, i_caps.Inheritable)
+
+    print("allow_caps: policy Permitted =", p_caps.Permitted)
+    print("allow_caps: input Permitted =", i_caps.Permitted)
+    match_caps(p_caps.Permitted, i_caps.Permitted)
+}
+
+match_caps(p_caps, i_caps) {
+    print("match_caps 1: start")
+
+    p_caps == i_caps
+
+    print("match_caps 1: true")
+}
+match_caps(p_caps, i_caps) {
+    print("match_caps 2: start")
+
+    count(p_caps) == 1
+    p_caps[0] == "$(default_caps)"
+
+    print("match_caps 2: default_caps =", policy_data.common.default_caps)
+    policy_data.common.default_caps == i_caps
+
+    print("match_caps 2: true")
+}
+match_caps(p_caps, i_caps) {
+    print("match_caps 3: start")
+
+    count(p_caps) == 1
+    p_caps[0] == "$(privileged_caps)"
+
+    print("match_caps 3: privileged_caps =", policy_data.common.privileged_caps)
+    policy_data.common.privileged_caps == i_caps
+
+    print("match_caps 3: true")
+}
+
+######################################################################
+CopyFileRequest {
+    print("CopyFileRequest: input.path =", input.path)
+
+    some regex1 in policy_data.request_defaults.CopyFileRequest
+    regex2 := replace(regex1, "$(cpath)", policy_data.common.cpath)
+    regex.match(regex2, input.path)
+
+    print("CopyFileRequest: true")
+}
+
+CreateSandboxRequest {
+    i_pidns := input.sandbox_pidns
+    print("CreateSandboxRequest: i_pidns =", i_pidns)
+    i_pidns == false
+}
+
+ExecProcessRequest {
+    print("ExecProcessRequest 1: input =", input)
+
+    i_command = concat(" ", input.process.Args)
+    print("ExecProcessRequest 1: i_command =", i_command)
+
+    some p_command in policy_data.request_defaults.ExecProcessRequest.commands
+    print("ExecProcessRequest 1: p_command =", p_command)
+    p_command == i_command
+
+    print("ExecProcessRequest 1: true")
+}
+ExecProcessRequest {
+    print("ExecProcessRequest 2: input =", input)
+
+    # TODO: match input container ID with its corresponding container.exec_commands.
+    i_command = concat(" ", input.process.Args)
+    print("ExecProcessRequest 3: i_command =", i_command)
+
+    some container in policy_data.containers
+    some p_command in container.exec_commands
+    print("ExecProcessRequest 2: p_command =", p_command)
+
+    # TODO: should other input data fields be validated as well?
+    p_command == i_command
+
+    print("ExecProcessRequest 2: true")
+}
+ExecProcessRequest {
+    print("ExecProcessRequest 3: input =", input)
+
+    i_command = concat(" ", input.process.Args)
+    print("ExecProcessRequest 3: i_command =", i_command)
+
+    some p_regex in policy_data.request_defaults.ExecProcessRequest.regex
+    print("ExecProcessRequest 3: p_regex =", p_regex)
+
+    regex.match(p_regex, i_command)
+
+    print("ExecProcessRequest 3: true")
+}
+
+CloseStdinRequest {
+    policy_data.request_defaults.CloseStdinRequest == true
+}
+
+ReadStreamRequest {
+    policy_data.request_defaults.ReadStreamRequest == true
+}
+
+WriteStreamRequest {
+    policy_data.request_defaults.WriteStreamRequest == true
+}
+
+policy_data := {
+  "containers": [
+    {
+      "OCI": {
+        "Version": "1.1.0",
+        "Process": {
+          "Terminal": false,
+          "User": {
+            "UID": 65535,
+            "GID": 65535,
+            "AdditionalGids": [],
+            "Username": ""
+          },
+          "Args": [
+            "/pause"
+          ],
+          "Env": [
+            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+          ],
+          "Cwd": "/",
+          "Capabilities": {
+            "Ambient": [],
+            "Bounding": [
+              "$(default_caps)"
+            ],
+            "Effective": [
+              "$(default_caps)"
+            ],
+            "Inheritable": [],
+            "Permitted": [
+              "$(default_caps)"
+            ]
+          },
+          "NoNewPrivileges": true
+        },
+        "Root": {
+          "Path": "$(cpath)/$(bundle-id)/rootfs",
+          "Readonly": true
+        },
+        "Mounts": [
+          {
+            "destination": "/proc",
+            "source": "proc",
+            "type_": "proc",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/dev",
+            "source": "tmpfs",
+            "type_": "tmpfs",
+            "options": [
+              "nosuid",
+              "strictatime",
+              "mode=755",
+              "size=65536k"
+            ]
+          },
+          {
+            "destination": "/dev/pts",
+            "source": "devpts",
+            "type_": "devpts",
+            "options": [
+              "nosuid",
+              "noexec",
+              "newinstance",
+              "ptmxmode=0666",
+              "mode=0620",
+              "gid=5"
+            ]
+          },
+          {
+            "destination": "/dev/shm",
+            "source": "/run/kata-containers/sandbox/shm",
+            "type_": "bind",
+            "options": [
+              "rbind"
+            ]
+          },
+          {
+            "destination": "/dev/mqueue",
+            "source": "mqueue",
+            "type_": "mqueue",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/sys",
+            "source": "sysfs",
+            "type_": "sysfs",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/etc/resolv.conf",
+            "source": "$(sfprefix)resolv.conf$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "ro",
+              "nosuid",
+              "nodev",
+              "noexec"
+            ]
+          }
+        ],
+        "Annotations": {
+          "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/$(bundle-id)",
+          "io.katacontainers.pkg.oci.container_type": "pod_sandbox",
+          "io.kubernetes.cri.container-type": "sandbox",
+          "io.kubernetes.cri.sandbox-id": "^[a-z0-9]{64}$",
+          "io.kubernetes.cri.sandbox-log-directory": "^/var/log/pods/$(sandbox-namespace)_$(sandbox-name)_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
+          "io.kubernetes.cri.sandbox-namespace": "kata-containers-k8s-tests",
+          "nerdctl/network-namespace": "^/var/run/netns/cni-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
+        },
+        "Linux": {
+          "Namespaces": [
+            {
+              "Type": "ipc",
+              "Path": ""
+            },
+            {
+              "Type": "uts",
+              "Path": ""
+            },
+            {
+              "Type": "mount",
+              "Path": ""
+            }
+          ],
+          "MaskedPaths": [
+            "/proc/acpi",
+            "/proc/asound",
+            "/proc/kcore",
+            "/proc/keys",
+            "/proc/latency_stats",
+            "/proc/timer_list",
+            "/proc/timer_stats",
+            "/proc/sched_debug",
+            "/sys/firmware",
+            "/proc/scsi"
+          ],
+          "ReadonlyPaths": [
+            "/proc/bus",
+            "/proc/fs",
+            "/proc/irq",
+            "/proc/sys",
+            "/proc/sysrq-trigger"
+          ]
+        }
+      },
+      "storages": [
+        {
+          "driver": "blk",
+          "driver_options": [],
+          "source": "",
+          "fstype": "tar",
+          "options": [
+            "$(hash0)"
+          ],
+          "mount_point": "$(layer0)",
+          "fs_group": null
+        },
+        {
+          "driver": "overlayfs",
+          "driver_options": [],
+          "source": "",
+          "fstype": "fuse3.kata-overlay",
+          "options": [
+            "5a5aad80055ff20012a50dc25f8df7a29924474324d65f7d5306ee8ee27ff71d",
+            "817250f1a3e336da76f5bd3fa784e1b26d959b9c131876815ba2604048b70c18"
+          ],
+          "mount_point": "$(cpath)/$(bundle-id)/rootfs",
+          "fs_group": null
+        }
+      ],
+      "sandbox_pidns": false,
+      "exec_commands": []
+    },
+    {
+      "OCI": {
+        "Version": "1.1.0",
+        "Process": {
+          "Terminal": false,
+          "User": {
+            "UID": 0,
+            "GID": 0,
+            "AdditionalGids": [],
+            "Username": ""
+          },
+          "Args": [
+            "/bin/sh",
+            "-c",
+            "sleep 120"
+          ],
+          "Env": [
+            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+            "HOSTNAME=$(host-name)",
+            "var1=val1"
+          ],
+          "Cwd": "/",
+          "Capabilities": {
+            "Ambient": [],
+            "Bounding": [
+              "$(default_caps)"
+            ],
+            "Effective": [
+              "$(default_caps)"
+            ],
+            "Inheritable": [],
+            "Permitted": [
+              "$(default_caps)"
+            ]
+          },
+          "NoNewPrivileges": false
+        },
+        "Root": {
+          "Path": "$(cpath)/$(bundle-id)/rootfs",
+          "Readonly": true
+        },
+        "Mounts": [
+          {
+            "destination": "/proc",
+            "source": "proc",
+            "type_": "proc",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/dev",
+            "source": "tmpfs",
+            "type_": "tmpfs",
+            "options": [
+              "nosuid",
+              "strictatime",
+              "mode=755",
+              "size=65536k"
+            ]
+          },
+          {
+            "destination": "/dev/pts",
+            "source": "devpts",
+            "type_": "devpts",
+            "options": [
+              "nosuid",
+              "noexec",
+              "newinstance",
+              "ptmxmode=0666",
+              "mode=0620",
+              "gid=5"
+            ]
+          },
+          {
+            "destination": "/dev/shm",
+            "source": "/run/kata-containers/sandbox/shm",
+            "type_": "bind",
+            "options": [
+              "rbind"
+            ]
+          },
+          {
+            "destination": "/dev/mqueue",
+            "source": "mqueue",
+            "type_": "mqueue",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/sys",
+            "source": "sysfs",
+            "type_": "sysfs",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/sys/fs/cgroup",
+            "source": "cgroup",
+            "type_": "cgroup",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev",
+              "relatime",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/etc/hosts",
+            "source": "$(sfprefix)hosts$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "rw"
+            ]
+          },
+          {
+            "destination": "/dev/termination-log",
+            "source": "$(sfprefix)termination-log$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "rw"
+            ]
+          },
+          {
+            "destination": "/etc/hostname",
+            "source": "$(sfprefix)hostname$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/etc/resolv.conf",
+            "source": "$(sfprefix)resolv.conf$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/var/run/secrets/kubernetes.io/serviceaccount",
+            "source": "$(sfprefix)serviceaccount$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/var/run/secrets/azure/tokens",
+            "source": "$(sfprefix)tokens$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "ro"
+            ]
+          }
+        ],
+        "Annotations": {
+          "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/$(bundle-id)",
+          "io.katacontainers.pkg.oci.container_type": "pod_container",
+          "io.kubernetes.cri.container-name": "hello",
+          "io.kubernetes.cri.container-type": "container",
+          "io.kubernetes.cri.image-name": "quay.io/prometheus/busybox:latest",
+          "io.kubernetes.cri.sandbox-id": "^[a-z0-9]{64}$",
+          "io.kubernetes.cri.sandbox-namespace": "kata-containers-k8s-tests"
+        },
+        "Linux": {
+          "Namespaces": [
+            {
+              "Type": "ipc",
+              "Path": ""
+            },
+            {
+              "Type": "uts",
+              "Path": ""
+            },
+            {
+              "Type": "mount",
+              "Path": ""
+            }
+          ],
+          "MaskedPaths": [
+            "/proc/acpi",
+            "/proc/kcore",
+            "/proc/keys",
+            "/proc/latency_stats",
+            "/proc/timer_list",
+            "/proc/timer_stats",
+            "/proc/sched_debug",
+            "/proc/scsi",
+            "/sys/firmware"
+          ],
+          "ReadonlyPaths": [
+            "/proc/asound",
+            "/proc/bus",
+            "/proc/fs",
+            "/proc/irq",
+            "/proc/sys",
+            "/proc/sysrq-trigger"
+          ]
+        }
+      },
+      "storages": [
+        {
+          "driver": "blk",
+          "driver_options": [],
+          "source": "",
+          "fstype": "tar",
+          "options": [
+            "$(hash0)"
+          ],
+          "mount_point": "$(layer0)",
+          "fs_group": null
+        },
+        {
+          "driver": "blk",
+          "driver_options": [],
+          "source": "",
+          "fstype": "tar",
+          "options": [
+            "$(hash1)"
+          ],
+          "mount_point": "$(layer1)",
+          "fs_group": null
+        },
+        {
+          "driver": "overlayfs",
+          "driver_options": [],
+          "source": "",
+          "fstype": "fuse3.kata-overlay",
+          "options": [
+            "b1ef3d18661ad727a8ad01d462fffc63ec90420a938056bc0422822328f83ff7:a8650d4a279d4537d7fc43c582a264528a96f041b1327d41b503f368c301594f",
+            "75ed5e78d30374b9557d32c17ae6d2e1b4afdc5efd128812e6c99a0f60708dba:2e52d86498030b4b99318650826d3f121bdfac5fe7bbbea9d18c546d48003aa0"
+          ],
+          "mount_point": "$(cpath)/$(bundle-id)/rootfs",
+          "fs_group": null
+        }
+      ],
+      "sandbox_pidns": false,
+      "exec_commands": []
+    }
+  ],
+  "common": {
+    "cpath": "/run/kata-containers/shared/containers",
+    "sfprefix": "^$(cpath)/$(bundle-id)-[a-z0-9]{16}-",
+    "ipv4_a": "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])",
+    "ip_p": "[0-9]{1,5}",
+    "svc_name": "[A-Z_\\.\\-]+",
+    "dns_label": "[a-zA-Z0-9_\\.\\-]+",
+    "default_caps": [
+      "CAP_CHOWN",
+      "CAP_DAC_OVERRIDE",
+      "CAP_FSETID",
+      "CAP_FOWNER",
+      "CAP_MKNOD",
+      "CAP_NET_RAW",
+      "CAP_SETGID",
+      "CAP_SETUID",
+      "CAP_SETFCAP",
+      "CAP_SETPCAP",
+      "CAP_NET_BIND_SERVICE",
+      "CAP_SYS_CHROOT",
+      "CAP_KILL",
+      "CAP_AUDIT_WRITE"
+    ],
+    "privileged_caps": [
+      "CAP_CHOWN",
+      "CAP_DAC_OVERRIDE",
+      "CAP_DAC_READ_SEARCH",
+      "CAP_FOWNER",
+      "CAP_FSETID",
+      "CAP_KILL",
+      "CAP_SETGID",
+      "CAP_SETUID",
+      "CAP_SETPCAP",
+      "CAP_LINUX_IMMUTABLE",
+      "CAP_NET_BIND_SERVICE",
+      "CAP_NET_BROADCAST",
+      "CAP_NET_ADMIN",
+      "CAP_NET_RAW",
+      "CAP_IPC_LOCK",
+      "CAP_IPC_OWNER",
+      "CAP_SYS_MODULE",
+      "CAP_SYS_RAWIO",
+      "CAP_SYS_CHROOT",
+      "CAP_SYS_PTRACE",
+      "CAP_SYS_PACCT",
+      "CAP_SYS_ADMIN",
+      "CAP_SYS_BOOT",
+      "CAP_SYS_NICE",
+      "CAP_SYS_RESOURCE",
+      "CAP_SYS_TIME",
+      "CAP_SYS_TTY_CONFIG",
+      "CAP_MKNOD",
+      "CAP_LEASE",
+      "CAP_AUDIT_WRITE",
+      "CAP_AUDIT_CONTROL",
+      "CAP_SETFCAP",
+      "CAP_MAC_OVERRIDE",
+      "CAP_MAC_ADMIN",
+      "CAP_SYSLOG",
+      "CAP_WAKE_ALARM",
+      "CAP_BLOCK_SUSPEND",
+      "CAP_AUDIT_READ",
+      "CAP_PERFMON",
+      "CAP_BPF",
+      "CAP_CHECKPOINT_RESTORE"
+    ]
+  },
+  "request_defaults": {
+    "CreateContainerRequest": {
+      "allow_env_regex": [
+        "^HOSTNAME=$(dns_label)$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP=tcp://$(ipv4_a):$(ip_p)$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP_PROTO=tcp$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP_PORT=$(ip_p)$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP_ADDR=$(ipv4_a)$",
+        "^$(svc_name)_SERVICE_HOST=$(ipv4_a)$",
+        "^$(svc_name)_SERVICE_PORT=$(ip_p)$",
+        "^$(svc_name)_SERVICE_PORT_$(dns_label)=$(ip_p)$",
+        "^$(svc_name)_PORT=tcp://$(ipv4_a):$(ip_p)$",
+        "^AZURE_CLIENT_ID=[A-Fa-f0-9-]+$",
+        "^AZURE_TENANT_ID=[A-Fa-f0-9-]+$",
+        "^AZURE_FEDERATED_TOKEN_FILE=/var/run/secrets/azure/tokens/azure-identity-token$",
+        "^AZURE_AUTHORITY_HOST=https://login\\.microsoftonline\\.com/$"
+      ]
+    },
+    "CopyFileRequest": [
+      "^$(cpath)/"
+    ],
+    "ExecProcessRequest": {
+      "commands": [],
+      "regex": []
+    },
+    "CloseStdinRequest": false,
+    "ReadStreamRequest": true,
+    "WriteStreamRequest": false
+  }
+}
diff --git a/tests/kata/data/k8s-policy-pod/inputs.txt b/tests/kata/data/k8s-policy-pod/inputs.txt
new file mode 100644
index 00000000..ad4332e8
--- /dev/null
+++ b/tests/kata/data/k8s-policy-pod/inputs.txt
@@ -0,0 +1,34 @@
+["ep":"AllowRequestsFailingPolicy",{}],
+
+["ep":"UpdateInterfaceRequest",{"interface":{"device":"eth0","name":"eth0","IPAddresses":[{"family":0,"address":"10.244.2.63","mask":"24"},{"family":1,"address":"fe80::b076:e0ff:fee4:4483","mask":"64"}],"mtu":1500,"hwAddr":"b2:76:e0:e4:44:83","pciPath":"","type_":"","raw_flags":0}}],
+
+["ep":"UpdateRoutesRequest",{"routes":{"Routes":[{"dest":"","gateway":"10.244.2.1","device":"eth0","source":"","scope":0,"family":0}]}}],
+
+["ep":"CreateSandboxRequest",{"hostname":"policy-pod","dns":["search kata-containers-k8s-tests.svc.cluster.local svc.cluster.local cluster.local iete1orsbdmu5ia1kq2du43hqe.bx.internal.cloudapp.net","nameserver 10.0.0.10","options ndots:5",""],"storages":[{"driver":"virtio-fs","driver_options":[],"source":"kataShared","fstype":"virtiofs","options":[],"mount_point":"/run/kata-containers/shared/containers/","fs_group":null},{"driver":"ephemeral","driver_options":[],"source":"shm","fstype":"tmpfs","options":["noexec","nosuid","nodev","mode=1777","size=67108864"],"mount_point":"/run/kata-containers/sandbox/shm","fs_group":null}],"sandbox_pidns":false,"sandbox_id":"e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2","guest_hook_path":"","kernel_modules":[]}],
+
+["ep":"GuestDetailsRequest",{"mem_block_size":true,"mem_hotplug_probe":true}],
+
+["ep":"CreateContainerRequest",{"container_id":"e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2","exec_id":"e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2","string_user":null,"devices":[],"storages":[],"OCI":{"Version":"1.1.0","Process":{"Terminal":false,"ConsoleSize":null,"User":{"UID":65535,"GID":65535,"AdditionalGids":[65535],"Username":""},"Args":["/pause"],"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cwd":"/","Capabilities":{"Bounding":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Effective":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Inheritable":[],"Permitted":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Ambient":[]},"Rlimits":[],"NoNewPrivileges":true,"ApparmorProfile":"","OOMScoreAdj":-998,"SelinuxLabel":""},"Root":{"Path":"/run/kata-containers/shared/containers/e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2/rootfs","Readonly":true},"Hostname":"policy-pod","Mounts":[{"destination":"/proc","source":"proc","type_":"proc","options":["nosuid","noexec","nodev"]},{"destination":"/dev","source":"tmpfs","type_":"tmpfs","options":["nosuid","strictatime","mode=755","size=65536k"]},{"destination":"/dev/pts","source":"devpts","type_":"devpts","options":["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620","gid=5"]},{"destination":"/dev/mqueue","source":"mqueue","type_":"mqueue","options":["nosuid","noexec","nodev"]},{"destination":"/sys","source":"sysfs","type_":"sysfs","options":["nosuid","noexec","nodev","ro"]},{"destination":"/dev/shm","source":"/run/kata-containers/sandbox/shm","type_":"bind","options":["rbind"]},{"destination":"/etc/resolv.conf","source":"/run/kata-containers/shared/containers/e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2-c9c9b9dc84433468-resolv.conf","type_":"bind","options":["rbind","ro","nosuid","nodev","noexec"]}],"Hooks":null,"Annotations":{"io.kubernetes.cri.sandbox-name":"policy-pod","io.katacontainers.pkg.oci.bundle_path":"/run/containerd/io.containerd.runtime.v2.task/k8s.io/e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2","io.kubernetes.cri.container-type":"sandbox","io.kubernetes.cri.sandbox-namespace":"kata-containers-k8s-tests","nerdctl/network-namespace":"/var/run/netns/cni-3e413870-ed7b-d970-7440-cc55c7795613","io.kubernetes.cri.sandbox-memory":"0","io.kubernetes.cri.sandbox-uid":"f7641c61-8a63-4bc6-98b9-6a4a5b0d860a","io.kubernetes.cri.sandbox-cpu-period":"100000","io.kubernetes.cri.sandbox-log-directory":"/var/log/pods/kata-containers-k8s-tests_policy-pod_f7641c61-8a63-4bc6-98b9-6a4a5b0d860a","io.kubernetes.cri.sandbox-cpu-quota":"0","io.kubernetes.cri.sandbox-id":"e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2","io.katacontainers.pkg.oci.container_type":"pod_sandbox","io.kubernetes.cri.sandbox-cpu-shares":"2"},"Linux":{"UIDMappings":[],"GIDMappings":[],"Sysctl":{},"Resources":{"Devices":[],"Memory":null,"CPU":{"Shares":2,"Quota":0,"Period":0,"RealtimeRuntime":0,"RealtimePeriod":0,"Cpus":"","Mems":""},"Pids":null,"BlockIO":null,"HugepageLimits":[],"Network":null},"CgroupsPath":"kubepods-besteffort-podf7641c61_8a63_4bc6_98b9_6a4a5b0d860a.slice:cri-containerd:e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2","Namespaces":[{"Type":"ipc","Path":""},{"Type":"uts","Path":""},{"Type":"mount","Path":""}],"Devices":[],"Seccomp":null,"RootfsPropagation":"","MaskedPaths":["/proc/acpi","/proc/asound","/proc/kcore","/proc/keys","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug","/sys/firmware","/sys/devices/virtual/powercap","/proc/scsi"],"ReadonlyPaths":["/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"],"MountLabel":"","IntelRdt":null},"Solaris":null,"Windows":null},"sandbox_pidns":false,"shared_mounts":[],"stdin_port":0,"stdout_port":0,"stderr_port":0}],
+
+["ep":"OnlineCPUMemRequest",{"wait":false,"nb_cpus":0,"cpu_only":false}],
+
+["ep":"UpdateEphemeralMountsRequest",{"storages":[]}],
+
+["ep":"StartContainerRequest",{"container_id":"e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2"}],
+
+["ep":"WaitProcessRequest",{"container_id":"e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2","exec_id":"e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2"}],
+
+["ep":"GetOOMEventRequest",{}],
+
+["ep":"CreateContainerRequest",{"container_id":"151c95eb53118154e0574814181406187332a6569c6c82bcf29e3e89392282c9","exec_id":"151c95eb53118154e0574814181406187332a6569c6c82bcf29e3e89392282c9","string_user":null,"devices":[],"storages":[],"OCI":{"Version":"1.1.0","Process":{"Terminal":false,"ConsoleSize":null,"User":{"UID":0,"GID":0,"AdditionalGids":[0,10],"Username":""},"Args":["sh"],"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","HOSTNAME=policy-pod","KUBE_CONFIG_1=value-1","KUBE_CONFIG_2=value-2","KUBERNETES_PORT_443_TCP_PROTO=tcp","KUBERNETES_PORT_443_TCP_PORT=443","KUBERNETES_PORT_443_TCP_ADDR=10.0.0.1","KUBERNETES_SERVICE_HOST=10.0.0.1","KUBERNETES_SERVICE_PORT=443","KUBERNETES_SERVICE_PORT_HTTPS=443","KUBERNETES_PORT=tcp://10.0.0.1:443","KUBERNETES_PORT_443_TCP=tcp://10.0.0.1:443"],"Cwd":"/","Capabilities":{"Bounding":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Effective":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Inheritable":[],"Permitted":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Ambient":[]},"Rlimits":[],"NoNewPrivileges":false,"ApparmorProfile":"cri-containerd.apparmor.d","OOMScoreAdj":1000,"SelinuxLabel":""},"Root":{"Path":"/run/kata-containers/shared/containers/151c95eb53118154e0574814181406187332a6569c6c82bcf29e3e89392282c9/rootfs","Readonly":false},"Hostname":"","Mounts":[{"destination":"/proc","source":"proc","type_":"proc","options":["nosuid","noexec","nodev"]},{"destination":"/dev","source":"tmpfs","type_":"tmpfs","options":["nosuid","strictatime","mode=755","size=65536k"]},{"destination":"/dev/pts","source":"devpts","type_":"devpts","options":["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620","gid=5"]},{"destination":"/dev/mqueue","source":"mqueue","type_":"mqueue","options":["nosuid","noexec","nodev"]},{"destination":"/sys","source":"sysfs","type_":"sysfs","options":["nosuid","noexec","nodev","ro"]},{"destination":"/sys/fs/cgroup","source":"cgroup","type_":"cgroup","options":["nosuid","noexec","nodev","relatime","ro"]},{"destination":"/etc/hosts","source":"/run/kata-containers/shared/containers/151c95eb53118154e0574814181406187332a6569c6c82bcf29e3e89392282c9-b0241970194aa0f1-hosts","type_":"bind","options":["rbind","rprivate","rw"]},{"destination":"/dev/termination-log","source":"/run/kata-containers/shared/containers/151c95eb53118154e0574814181406187332a6569c6c82bcf29e3e89392282c9-70afdd32aae261c9-termination-log","type_":"bind","options":["rbind","rprivate","rw"]},{"destination":"/etc/hostname","source":"/run/kata-containers/shared/containers/151c95eb53118154e0574814181406187332a6569c6c82bcf29e3e89392282c9-cdfc9069abab6aa0-hostname","type_":"bind","options":["rbind","rprivate","rw"]},{"destination":"/etc/resolv.conf","source":"/run/kata-containers/shared/containers/151c95eb53118154e0574814181406187332a6569c6c82bcf29e3e89392282c9-a9ad9599a85707b3-resolv.conf","type_":"bind","options":["rbind","rprivate","rw"]},{"destination":"/dev/shm","source":"/run/kata-containers/sandbox/shm","type_":"bind","options":["rbind"]},{"destination":"/var/run/secrets/kubernetes.io/serviceaccount","source":"/run/kata-containers/shared/containers/151c95eb53118154e0574814181406187332a6569c6c82bcf29e3e89392282c9-5f0e5e3718c35792-serviceaccount","type_":"bind","options":["rbind","rprivate","ro"]}],"Hooks":null,"Annotations":{"io.katacontainers.pkg.oci.bundle_path":"/run/containerd/io.containerd.runtime.v2.task/k8s.io/151c95eb53118154e0574814181406187332a6569c6c82bcf29e3e89392282c9","io.kubernetes.cri.sandbox-id":"e4bd2a97acb81f9ea31d49e21ef2675bcd8bf6883d3cd4273d6d4fb2f6fcbba2","io.kubernetes.cri.container-type":"container","io.katacontainers.pkg.oci.container_type":"pod_container","io.kubernetes.cri.sandbox-name":"policy-pod","io.kubernetes.cri.container-name":"busybox","io.kubernetes.cri.sandbox-uid":"f7641c61-8a63-4bc6-98b9-6a4a5b0d860a","io.kubernetes.cri.sandbox-namespace":"kata-containers-k8s-tests","io.kubernetes.cri.image-name":"quay.io/prometheus/busybox:latest"},"Linux":{"UIDMappings":[],"GIDMappings":[],"Sysctl":{},"Resources":{"Devices":[],"Memory":{"Limit":0,"Reservation":0,"Swap":0,"Kernel":0,"KernelTCP":0,"Swappiness":0,"DisableOOMKiller":false},"CPU":{"Shares":2,"Quota":0,"Period":100000,"RealtimeRuntime":0,"RealtimePeriod":0,"Cpus":"","Mems":""},"Pids":null,"BlockIO":null,"HugepageLimits":[],"Network":null},"CgroupsPath":"kubepods-besteffort-podf7641c61_8a63_4bc6_98b9_6a4a5b0d860a.slice:cri-containerd:151c95eb53118154e0574814181406187332a6569c6c82bcf29e3e89392282c9","Namespaces":[{"Type":"ipc","Path":""},{"Type":"uts","Path":""},{"Type":"mount","Path":""}],"Devices":[],"Seccomp":null,"RootfsPropagation":"","MaskedPaths":["/proc/asound","/proc/acpi","/proc/kcore","/proc/keys","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug","/proc/scsi","/sys/firmware"],"ReadonlyPaths":["/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"],"MountLabel":"","IntelRdt":null},"Solaris":null,"Windows":null},"sandbox_pidns":false,"shared_mounts":[],"stdin_port":0,"stdout_port":0,"stderr_port":0}],
+
+["ep":"OnlineCPUMemRequest",{"wait":false,"nb_cpus":0,"cpu_only":false}],
+
+["ep":"UpdateEphemeralMountsRequest",{"storages":[]}],
+
+["ep":"StartContainerRequest",{"container_id":"151c95eb53118154e0574814181406187332a6569c6c82bcf29e3e89392282c9"}],
+
+["ep":"OnlineCPUMemRequest",{"wait":false,"nb_cpus":0,"cpu_only":false}],
+
+["ep":"UpdateEphemeralMountsRequest",{"storages":[]}],
+
diff --git a/tests/kata/data/k8s-policy-pod/output.json b/tests/kata/data/k8s-policy-pod/output.json
new file mode 100644
index 00000000..c0f5c42d
--- /dev/null
+++ b/tests/kata/data/k8s-policy-pod/output.json
@@ -0,0 +1,19 @@
+[
+  false,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true
+]
\ No newline at end of file
diff --git a/tests/kata/data/k8s-policy-pod/policy.rego b/tests/kata/data/k8s-policy-pod/policy.rego
new file mode 100644
index 00000000..9d503f77
--- /dev/null
+++ b/tests/kata/data/k8s-policy-pod/policy.rego
@@ -0,0 +1,1653 @@
+# Copyright (c) 2023 Microsoft Corporation
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+package agent_policy
+
+import future.keywords.in
+import future.keywords.every
+
+# Default values, returned by OPA when rules cannot be evaluated to true.
+default AddARPNeighborsRequest := false
+default AddSwapRequest := false
+default CloseStdinRequest := false
+default CopyFileRequest := false
+default CreateContainerRequest := false
+default CreateSandboxRequest := true
+default DestroySandboxRequest := true
+default ExecProcessRequest := false
+default GetOOMEventRequest := true
+default GuestDetailsRequest := true
+default ListInterfacesRequest := false
+default ListRoutesRequest := false
+default MemHotplugByProbeRequest := false
+default OnlineCPUMemRequest := true
+default PauseContainerRequest := false
+default ReadStreamRequest := false
+default RemoveContainerRequest := true
+default RemoveStaleVirtiofsShareMountsRequest := true
+default ReseedRandomDevRequest := false
+default ResumeContainerRequest := false
+default SetGuestDateTimeRequest := false
+default SetPolicyRequest := false
+default SignalProcessRequest := true
+default StartContainerRequest := true
+default StartTracingRequest := false
+default StatsContainerRequest := true
+default StopTracingRequest := false
+default TtyWinResizeRequest := true
+default UpdateContainerRequest := false
+default UpdateEphemeralMountsRequest := true
+default UpdateInterfaceRequest := true
+default UpdateRoutesRequest := true
+default WaitProcessRequest := true
+default WriteStreamRequest := false
+
+# AllowRequestsFailingPolicy := true configures the Agent to *allow any
+# requests causing a policy failure*. This is an unsecure configuration
+# but is useful for allowing unsecure pods to start, then connect to
+# them and inspect OPA logs for the root cause of a failure.
+default AllowRequestsFailingPolicy := false
+
+CreateContainerRequest {
+    i_oci := input.OCI
+    i_storages := input.storages
+
+    some p_container in policy_data.containers
+    print("======== CreateContainerRequest: trying next policy container")
+
+    p_pidns := p_container.sandbox_pidns
+    i_pidns := input.sandbox_pidns
+    print("CreateContainerRequest: p_pidns =", p_pidns, "i_pidns =", i_pidns)
+    p_pidns == i_pidns
+
+    p_oci := p_container.OCI
+
+    print("CreateContainerRequest: p Version =", p_oci.Version, "i Version =", i_oci.Version)
+    p_oci.Version == i_oci.Version
+
+    print("CreateContainerRequest: p Readonly =", p_oci.Root.Readonly, "i Readonly =", i_oci.Root.Readonly)
+    p_oci.Root.Readonly == i_oci.Root.Readonly
+
+    allow_anno(p_oci, i_oci)
+
+    p_storages := p_container.storages
+    allow_by_anno(p_oci, i_oci, p_storages, i_storages)
+
+    allow_linux(p_oci, i_oci)
+
+    print("CreateContainerRequest: true")
+}
+
+# Reject unexpected annotations.
+allow_anno(p_oci, i_oci) {
+    print("allow_anno 1: start")
+
+    not i_oci.Annotations
+
+    print("allow_anno 1: true")
+}
+allow_anno(p_oci, i_oci) {
+    print("allow_anno 2: p Annotations =", p_oci.Annotations)
+    print("allow_anno 2: i Annotations =", i_oci.Annotations)
+
+    i_keys := object.keys(i_oci.Annotations)
+    print("allow_anno 2: i keys =", i_keys)
+
+    every i_key in i_keys {
+        allow_anno_key(i_key, p_oci)
+    }
+
+    print("allow_anno 2: true")
+}
+
+allow_anno_key(i_key, p_oci) {
+    print("allow_anno_key 1: i key =", i_key)
+
+    startswith(i_key, "io.kubernetes.cri.")
+
+    print("allow_anno_key 1: true")
+}
+allow_anno_key(i_key, p_oci) {
+    print("allow_anno_key 2: i key =", i_key)
+
+    some p_key, _ in p_oci.Annotations
+    p_key == i_key
+
+    print("allow_anno_key 2: true")
+}
+
+# Get the value of the "io.kubernetes.cri.sandbox-name" annotation and
+# correlate it with other annotations and process fields.
+allow_by_anno(p_oci, i_oci, p_storages, i_storages) {
+    print("allow_by_anno 1: start")
+
+    s_name := "io.kubernetes.cri.sandbox-name"
+
+    not p_oci.Annotations[s_name]
+
+    i_s_name := i_oci.Annotations[s_name]
+    print("allow_by_anno 1: i_s_name =", i_s_name)
+
+    allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, i_s_name)
+
+    print("allow_by_anno 1: true")
+}
+allow_by_anno(p_oci, i_oci, p_storages, i_storages) {
+    print("allow_by_anno 2: start")
+
+    s_name := "io.kubernetes.cri.sandbox-name"
+
+    p_s_name := p_oci.Annotations[s_name]
+    i_s_name := i_oci.Annotations[s_name]
+    print("allow_by_anno 2: i_s_name =", i_s_name, "p_s_name =", p_s_name)
+
+    allow_sandbox_name(p_s_name, i_s_name)
+    allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, i_s_name)
+
+    print("allow_by_anno 2: true")
+}
+
+allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, s_name) {
+    print("allow_by_sandbox_name: start")
+
+    s_namespace := "io.kubernetes.cri.sandbox-namespace"
+
+    p_namespace := p_oci.Annotations[s_namespace]
+    i_namespace := i_oci.Annotations[s_namespace]
+    print("allow_by_sandbox_name: p_namespace =", p_namespace, "i_namespace =", i_namespace)
+    p_namespace == i_namespace
+
+    allow_by_container_types(p_oci, i_oci, s_name, p_namespace)
+    allow_by_bundle_or_sandbox_id(p_oci, i_oci, p_storages, i_storages)
+    allow_process(p_oci, i_oci, s_name)
+
+    print("allow_by_sandbox_name: true")
+}
+
+allow_sandbox_name(p_s_name, i_s_name) {
+    print("allow_sandbox_name 1: start")
+
+    p_s_name == i_s_name
+
+    print("allow_sandbox_name 1: true")
+}
+allow_sandbox_name(p_s_name, i_s_name) {
+    print("allow_sandbox_name 2: start")
+
+    # TODO: should generated names be handled differently?
+    contains(p_s_name, "$(generated-name)")
+
+    print("allow_sandbox_name 2: true")
+}
+
+# Check that the "io.kubernetes.cri.container-type" and
+# "io.katacontainers.pkg.oci.container_type" annotations designate the
+# expected type - either a "sandbox" or a "container". Then, validate
+# other annotations based on the actual "sandbox" or "container" value
+# from the input container.
+allow_by_container_types(p_oci, i_oci, s_name, s_namespace) {
+    print("allow_by_container_types: checking io.kubernetes.cri.container-type")
+
+    c_type := "io.kubernetes.cri.container-type"
+    
+    p_cri_type := p_oci.Annotations[c_type]
+    i_cri_type := i_oci.Annotations[c_type]
+    print("allow_by_container_types: p_cri_type =", p_cri_type, "i_cri_type =", i_cri_type)
+    p_cri_type == i_cri_type
+
+    allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace)
+
+    print("allow_by_container_types: true")
+}
+
+allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace) {
+    print("allow_by_container_type 1: i_cri_type =", i_cri_type)
+    i_cri_type == "sandbox"
+
+    i_kata_type := i_oci.Annotations["io.katacontainers.pkg.oci.container_type"]
+    print("allow_by_container_type 1: i_kata_type =", i_kata_type)
+    i_kata_type == "pod_sandbox"
+
+    allow_sandbox_container_name(p_oci, i_oci)
+    allow_sandbox_net_namespace(p_oci, i_oci)
+    allow_sandbox_log_directory(p_oci, i_oci, s_name, s_namespace)
+
+    print("allow_by_container_type 1: true")
+}
+
+allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace) {
+    print("allow_by_container_type 2: i_cri_type =", i_cri_type)
+    i_cri_type == "container"
+
+    i_kata_type := i_oci.Annotations["io.katacontainers.pkg.oci.container_type"]
+    print("allow_by_container_type 2: i_kata_type =", i_kata_type)
+    i_kata_type == "pod_container"
+
+    allow_container_name(p_oci, i_oci)
+    allow_net_namespace(p_oci, i_oci)
+    allow_log_directory(p_oci, i_oci)
+
+    print("allow_by_container_type 2: true")
+}
+
+# "io.kubernetes.cri.container-name" annotation
+allow_sandbox_container_name(p_oci, i_oci) {
+    print("allow_sandbox_container_name: start")
+
+    container_annotation_missing(p_oci, i_oci, "io.kubernetes.cri.container-name")
+
+    print("allow_sandbox_container_name: true")
+}
+
+allow_container_name(p_oci, i_oci) {
+    print("allow_container_name: start")
+
+    allow_container_annotation(p_oci, i_oci, "io.kubernetes.cri.container-name")
+
+    print("allow_container_name: true")
+}
+
+container_annotation_missing(p_oci, i_oci, key) {
+    print("container_annotation_missing:", key)
+
+    not p_oci.Annotations[key]
+    not i_oci.Annotations[key]
+
+    print("container_annotation_missing: true")
+}
+
+allow_container_annotation(p_oci, i_oci, key) {
+    print("allow_container_annotation: key =", key)
+
+    p_value := p_oci.Annotations[key]
+    i_value := i_oci.Annotations[key]
+    print("allow_container_annotation: p_value =", p_value, "i_value =", i_value)
+
+    p_value == i_value
+
+    print("allow_container_annotation: true")
+}
+
+# "nerdctl/network-namespace" annotation
+allow_sandbox_net_namespace(p_oci, i_oci) {
+    print("allow_sandbox_net_namespace: start")
+
+    key := "nerdctl/network-namespace"
+
+    p_namespace := p_oci.Annotations[key]
+    i_namespace := i_oci.Annotations[key]
+    print("allow_sandbox_net_namespace: p_namespace =", p_namespace, "i_namespace =", i_namespace)
+
+    regex.match(p_namespace, i_namespace)
+
+    print("allow_sandbox_net_namespace: true")
+}
+
+allow_net_namespace(p_oci, i_oci) {
+    print("allow_net_namespace: start")
+
+    key := "nerdctl/network-namespace"
+
+    not p_oci.Annotations[key]
+    not i_oci.Annotations[key]
+
+    print("allow_net_namespace: true")
+}
+
+# "io.kubernetes.cri.sandbox-log-directory" annotation
+allow_sandbox_log_directory(p_oci, i_oci, s_name, s_namespace) {
+    print("allow_sandbox_log_directory: start")
+
+    key := "io.kubernetes.cri.sandbox-log-directory"
+
+    p_dir := p_oci.Annotations[key]
+    regex1 := replace(p_dir, "$(sandbox-name)", s_name)
+    regex2 := replace(regex1, "$(sandbox-namespace)", s_namespace)
+    print("allow_sandbox_log_directory: regex2 =", regex2)
+
+    i_dir := i_oci.Annotations[key]
+    print("allow_sandbox_log_directory: i_dir =", i_dir)
+
+    regex.match(regex2, i_dir)
+
+    print("allow_sandbox_log_directory: true")
+}
+
+allow_log_directory(p_oci, i_oci) {
+    print("allow_log_directory: start")
+
+    key := "io.kubernetes.cri.sandbox-log-directory"
+
+    not p_oci.Annotations[key]
+    not i_oci.Annotations[key]
+
+    print("allow_log_directory: true")
+}
+
+allow_linux(p_oci, i_oci) {
+    p_namespaces := p_oci.Linux.Namespaces
+    print("allow_linux: p namespaces =", p_namespaces)
+
+    i_namespaces := i_oci.Linux.Namespaces
+    print("allow_linux: i namespaces =", i_namespaces)
+
+    p_namespaces == i_namespaces
+
+    allow_masked_paths(p_oci, i_oci)
+    allow_readonly_paths(p_oci, i_oci)
+
+    print("allow_linux: true")
+}
+
+allow_masked_paths(p_oci, i_oci) {
+    p_paths := p_oci.Linux.MaskedPaths
+    print("allow_masked_paths 1: p_paths =", p_paths)
+
+    i_paths := i_oci.Linux.MaskedPaths
+    print("allow_masked_paths 1: i_paths =", i_paths)
+
+    allow_masked_paths_array(p_paths, i_paths)
+
+    print("allow_masked_paths 1: true")
+}
+allow_masked_paths(p_oci, i_oci) {
+    print("allow_masked_paths 2: start")
+
+    not p_oci.Linux.MaskedPaths
+    not i_oci.Linux.MaskedPaths
+
+    print("allow_masked_paths 2: true")
+}
+
+# All the policy masked paths must be masked in the input data too.
+# Input is allowed to have more masked paths than the policy.
+allow_masked_paths_array(p_array, i_array) {
+    every p_elem in p_array {
+        allow_masked_path(p_elem, i_array)
+    }
+}
+
+allow_masked_path(p_elem, i_array) {
+    print("allow_masked_path: p_elem =", p_elem)
+
+    some i_elem in i_array
+    p_elem == i_elem
+
+    print("allow_masked_path: true")
+}
+
+allow_readonly_paths(p_oci, i_oci) {
+    p_paths := p_oci.Linux.ReadonlyPaths
+    print("allow_readonly_paths 1: p_paths =", p_paths)
+
+    i_paths := i_oci.Linux.ReadonlyPaths
+    print("allow_readonly_paths 1: i_paths =", i_paths)
+
+    allow_readonly_paths_array(p_paths, i_paths, i_oci.Linux.MaskedPaths)
+
+    print("allow_readonly_paths 1: true")
+}
+allow_readonly_paths(p_oci, i_oci) {
+    print("allow_readonly_paths 2: start")
+
+    not p_oci.Linux.ReadonlyPaths
+    not i_oci.Linux.ReadonlyPaths
+
+    print("allow_readonly_paths 2: true")
+}
+
+# All the policy readonly paths must be either:
+# - Present in the input readonly paths, or
+# - Present in the input masked paths.
+# Input is allowed to have more readonly paths than the policy.
+allow_readonly_paths_array(p_array, i_array, masked_paths) {
+    every p_elem in p_array {
+        allow_readonly_path(p_elem, i_array, masked_paths)
+    }
+}
+
+allow_readonly_path(p_elem, i_array, masked_paths) {
+    print("allow_readonly_path 1: p_elem =", p_elem)
+
+    some i_elem in i_array
+    p_elem == i_elem
+
+    print("allow_readonly_path 1: true")
+}
+allow_readonly_path(p_elem, i_array, masked_paths) {
+    print("allow_readonly_path 2: p_elem =", p_elem)
+
+    some i_masked in masked_paths
+    p_elem == i_masked
+
+    print("allow_readonly_path 2: true")
+}
+
+# Check the consistency of the input "io.katacontainers.pkg.oci.bundle_path"
+# and io.kubernetes.cri.sandbox-id" values with other fields.
+allow_by_bundle_or_sandbox_id(p_oci, i_oci, p_storages, i_storages) {
+    print("allow_by_bundle_or_sandbox_id: start")
+
+    bundle_path := i_oci.Annotations["io.katacontainers.pkg.oci.bundle_path"]
+    bundle_id := replace(bundle_path, "/run/containerd/io.containerd.runtime.v2.task/k8s.io/", "")
+
+    key := "io.kubernetes.cri.sandbox-id"
+
+    p_regex := p_oci.Annotations[key]
+    sandbox_id := i_oci.Annotations[key]
+
+    print("allow_by_bundle_or_sandbox_id: sandbox_id =", sandbox_id, "regex =", p_regex)
+    regex.match(p_regex, sandbox_id)
+
+    allow_root_path(p_oci, i_oci, bundle_id)
+
+    every i_mount in input.OCI.Mounts {
+        allow_mount(p_oci, i_mount, bundle_id, sandbox_id)
+    }
+
+    # TODO: enable allow_storages() after fixing https://github.com/kata-containers/kata-containers/issues/8833
+    # allow_storages(p_storages, i_storages, bundle_id, sandbox_id)
+
+    print("allow_by_bundle_or_sandbox_id: true")
+}
+
+allow_process(p_oci, i_oci, s_name) {
+    p_process := p_oci.Process
+    i_process := i_oci.Process
+
+    print("allow_process: i terminal =", i_process.Terminal, "p terminal =", p_process.Terminal)
+    p_process.Terminal == i_process.Terminal
+
+    print("allow_process: i cwd =", i_process.Cwd, "i cwd =", p_process.Cwd)
+    p_process.Cwd == i_process.Cwd
+
+    print("allow_process: i noNewPrivileges =", i_process.NoNewPrivileges, "p noNewPrivileges =", p_process.NoNewPrivileges)
+    p_process.NoNewPrivileges == i_process.NoNewPrivileges
+
+    allow_caps(p_process.Capabilities, i_process.Capabilities)
+    allow_user(p_process, i_process)
+    allow_args(p_process, i_process, s_name)
+    allow_env(p_process, i_process, s_name)
+
+    print("allow_process: true")
+}
+
+allow_user(p_process, i_process) {
+    p_user := p_process.User
+    i_user := i_process.User
+
+    # TODO: track down the reason for mcr.microsoft.com/oss/bitnami/redis:6.0.8 being
+    #       executed with uid = 0 despite having "User": "1001" in its container image
+    #       config.
+    #print("allow_user: input uid =", i_user.UID, "policy uid =", p_user.UID)
+    #p_user.UID == i_user.UID
+
+    # TODO: track down the reason for registry.k8s.io/pause:3.9 being
+    #       executed with gid = 0 despite having "65535:65535" in its container image
+    #       config.
+    #print("allow_user: input gid =", i_user.GID, "policy gid =", p_user.GID)
+    #p_user.GID == i_user.GID
+
+    # TODO: compare the additionalGids field too after computing its value
+    # based on /etc/passwd and /etc/group from the container image.
+}
+
+allow_args(p_process, i_process, s_name) {
+    print("allow_args 1: no args")
+
+    not p_process.Args
+    not i_process.Args
+
+    print("allow_args 1: true")
+}
+allow_args(p_process, i_process, s_name) {
+    print("allow_args 2: policy args =", p_process.Args)
+    print("allow_args 2: input args =", i_process.Args)
+
+    count(p_process.Args) == count(i_process.Args)
+
+    every i, i_arg in i_process.Args {
+        allow_arg(i, i_arg, p_process, s_name)
+    }
+
+    print("allow_args 2: true")
+}
+allow_arg(i, i_arg, p_process, s_name) {
+    p_arg := p_process.Args[i]
+    print("allow_arg 1: i =", i, "i_arg =", i_arg, "p_arg =", p_arg)
+
+    p_arg2 := replace(p_arg, "$$", "$")
+    p_arg2 == i_arg
+
+    print("allow_arg 1: true")
+}
+allow_arg(i, i_arg, p_process, s_name) {
+    p_arg := p_process.Args[i]
+    print("allow_arg 2: i =", i, "i_arg =", i_arg, "p_arg =", p_arg)
+
+    # TODO: can $(node-name) be handled better?
+    contains(p_arg, "$(node-name)")
+
+    print("allow_arg 2: true")
+}
+allow_arg(i, i_arg, p_process, s_name) {
+    p_arg := p_process.Args[i]
+    print("allow_arg 3: i =", i, "i_arg =", i_arg, "p_arg =", p_arg)
+
+    p_arg2 := replace(p_arg, "$$", "$")
+    p_arg3 := replace(p_arg2, "$(sandbox-name)", s_name)
+    print("allow_arg 3: p_arg3 =", p_arg3)
+    p_arg3 == i_arg
+
+    print("allow_arg 3: true")
+}
+
+# OCI process.Env field
+allow_env(p_process, i_process, s_name) {
+    print("allow_env: p env =", p_process.Env)
+    print("allow_env: i env =", i_process.Env)
+
+    every i_var in i_process.Env {
+        print("allow_env: i_var =", i_var)
+        allow_var(p_process, i_process, i_var, s_name)
+    }
+
+    print("allow_env: true")
+}
+
+# Allow input env variables that are present in the policy data too.
+allow_var(p_process, i_process, i_var, s_name) {
+    some p_var in p_process.Env
+    p_var == i_var
+    print("allow_var 1: true")
+}
+
+# Match input with one of the policy variables, after substituting $(sandbox-name).
+allow_var(p_process, i_process, i_var, s_name) {
+    some p_var in p_process.Env
+    p_var2 := replace(p_var, "$(sandbox-name)", s_name)
+
+    print("allow_var 2: p_var2 =", p_var2)
+    p_var2 == i_var
+
+    print("allow_var 2: true")
+}
+
+# Allow input env variables that match with a request_defaults regex.
+allow_var(p_process, i_process, i_var, s_name) {
+    some p_regex1 in policy_data.request_defaults.CreateContainerRequest.allow_env_regex
+    p_regex2 := replace(p_regex1, "$(ipv4_a)", policy_data.common.ipv4_a)
+    p_regex3 := replace(p_regex2, "$(ip_p)", policy_data.common.ip_p)
+    p_regex4 := replace(p_regex3, "$(svc_name)", policy_data.common.svc_name)
+    p_regex5 := replace(p_regex4, "$(dns_label)", policy_data.common.dns_label)
+
+    print("allow_var 3: p_regex5 =", p_regex5)
+    regex.match(p_regex5, i_var)
+
+    print("allow_var 3: true")
+}
+
+# Allow fieldRef "fieldPath: status.podIP" values.
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+    is_ip(name_value[1])
+
+    some p_var in p_process.Env
+    allow_pod_ip_var(name_value[0], p_var)
+
+    print("allow_var 4: true")
+}
+
+# Allow common fieldRef variables.
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+
+    some p_var in p_process.Env
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == name_value[0]
+
+    # TODO: should these be handled in a different way?
+    always_allowed := ["$(host-name)", "$(node-name)", "$(pod-uid)"]
+    some allowed in always_allowed
+    contains(p_name_value[1], allowed)
+
+    print("allow_var 5: true")
+}
+
+# Allow fieldRef "fieldPath: status.hostIP" values.
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+    is_ip(name_value[1])
+
+    some p_var in p_process.Env
+    allow_host_ip_var(name_value[0], p_var)
+
+    print("allow_var 6: true")
+}
+
+# Allow resourceFieldRef values (e.g., "limits.cpu").
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+
+    some p_var in p_process.Env
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == name_value[0]
+
+    # TODO: should these be handled in a different way?
+    always_allowed = ["$(resource-field)", "$(todo-annotation)"]
+    some allowed in always_allowed
+    contains(p_name_value[1], allowed)
+
+    print("allow_var 7: true")
+}
+
+allow_pod_ip_var(var_name, p_var) {
+    print("allow_pod_ip_var: var_name =", var_name, "p_var =", p_var)
+
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == var_name
+    p_name_value[1] == "$(pod-ip)"
+
+    print("allow_pod_ip_var: true")
+}
+
+allow_host_ip_var(var_name, p_var) {
+    print("allow_host_ip_var: var_name =", var_name, "p_var =", p_var)
+
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == var_name
+    p_name_value[1] == "$(host-ip)"
+
+    print("allow_host_ip_var: true")
+}
+
+is_ip(value) {
+    bytes = split(value, ".")
+    count(bytes) == 4
+
+    is_ip_first_byte(bytes[0])
+    is_ip_other_byte(bytes[1])
+    is_ip_other_byte(bytes[2])
+    is_ip_other_byte(bytes[3])
+}
+is_ip_first_byte(component) {
+    number = to_number(component)
+    number >= 1
+    number <= 255
+}
+is_ip_other_byte(component) {
+    number = to_number(component)
+    number >= 0
+    number <= 255
+}
+
+# OCI root.Path
+allow_root_path(p_oci, i_oci, bundle_id) {
+    i_path := i_oci.Root.Path
+    p_path1 := p_oci.Root.Path
+    print("allow_root_path: i_path =", i_path, "p_path1 =", p_path1)
+
+    p_path2 := replace(p_path1, "$(cpath)", policy_data.common.cpath)
+    print("allow_root_path: p_path2 =", p_path2)
+
+    p_path3 := replace(p_path2, "$(bundle-id)", bundle_id)
+    print("allow_root_path: p_path3 =", p_path3)
+
+    p_path3 == i_path
+
+    print("allow_root_path: true")
+}
+
+# device mounts
+allow_mount(p_oci, i_mount, bundle_id, sandbox_id) {
+    print("allow_mount: i_mount =", i_mount)
+
+    some p_mount in p_oci.Mounts
+    print("allow_mount: p_mount =", p_mount)
+    check_mount(p_mount, i_mount, bundle_id, sandbox_id)
+
+    # TODO: are there any other required policy checks for mounts - e.g.,
+    #       multiple mounts with same source or destination?
+
+    print("allow_mount: true")
+}
+
+check_mount(p_mount, i_mount, bundle_id, sandbox_id) {
+    p_mount == i_mount
+    print("check_mount 1: true")
+}
+check_mount(p_mount, i_mount, bundle_id, sandbox_id) {
+    p_mount.destination == i_mount.destination
+    p_mount.type_ == i_mount.type_
+    p_mount.options == i_mount.options
+
+    mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id)
+
+    print("check_mount 2: true")
+}
+
+mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id) {
+    regex1 := p_mount.source
+    regex2 := replace(regex1, "$(sfprefix)", policy_data.common.sfprefix)
+    regex3 := replace(regex2, "$(cpath)", policy_data.common.cpath)
+    regex4 := replace(regex3, "$(bundle-id)", bundle_id)
+
+    print("mount_source_allows 1: regex4 =", regex4)
+    regex.match(regex4, i_mount.source)
+
+    print("mount_source_allows 1: true")
+}
+mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id) {
+    regex1 := p_mount.source
+    regex2 := replace(regex1, "$(sfprefix)", policy_data.common.sfprefix)
+    regex3 := replace(regex2, "$(cpath)", policy_data.common.cpath)
+    regex4 := replace(regex3, "$(sandbox-id)", sandbox_id)
+
+    print("mount_source_allows 2: regex4 =", regex4)
+    regex.match(regex4, i_mount.source)
+
+    print("mount_source_allows 2: true")
+}
+
+######################################################################
+# Storages
+
+allow_storages(p_storages, i_storages, bundle_id, sandbox_id) {
+    p_count := count(p_storages)
+    i_count := count(i_storages)
+    print("allow_storages: p_count =", p_count, "i_count =", i_count)
+
+    p_count == i_count
+
+    # Get the container image layer IDs and verity root hashes, from the "overlayfs" storage.
+    some overlay_storage in p_storages
+    overlay_storage.driver == "overlayfs"
+    print("allow_storages: overlay_storage =", overlay_storage)
+    count(overlay_storage.options) == 2
+
+    layer_ids := split(overlay_storage.options[0], ":")
+    print("allow_storages: layer_ids =", layer_ids)
+
+    root_hashes := split(overlay_storage.options[1], ":")
+    print("allow_storages: root_hashes =", root_hashes)
+
+    every i_storage in i_storages {
+        allow_storage(p_storages, i_storage, bundle_id, sandbox_id, layer_ids, root_hashes)
+    }
+
+    print("allow_storages: true")
+}
+
+allow_storage(p_storages, i_storage, bundle_id, sandbox_id, layer_ids, root_hashes) {
+    some p_storage in p_storages
+
+    print("allow_storage: p_storage =", p_storage)
+    print("allow_storage: i_storage =", i_storage)
+
+    p_storage.driver           == i_storage.driver
+    p_storage.driver_options   == i_storage.driver_options
+    p_storage.fs_group         == i_storage.fs_group
+
+    allow_storage_options(p_storage, i_storage, layer_ids, root_hashes)
+    allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids)
+
+    # TODO: validate the source field too.
+
+    print("allow_storage: true")
+}
+
+allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) {
+    print("allow_storage_options 1: start")
+
+    p_storage.driver != "blk"
+    p_storage.driver != "overlayfs"
+    p_storage.options == i_storage.options
+
+    print("allow_storage_options 1: true")
+}
+allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) {
+    print("allow_storage_options 2: start")
+
+    p_storage.driver == "overlayfs"
+    count(p_storage.options) == 2
+
+    policy_ids := split(p_storage.options[0], ":")
+    print("allow_storage_options 2: policy_ids =", policy_ids)
+    policy_ids == layer_ids
+
+    policy_hashes := split(p_storage.options[1], ":")
+    print("allow_storage_options 2: policy_hashes =", policy_hashes)
+
+    p_count := count(policy_ids)
+    print("allow_storage_options 2: p_count =", p_count)
+    p_count >= 1
+    p_count == count(policy_hashes)
+
+    i_count := count(i_storage.options)
+    print("allow_storage_options 2: i_count =", i_count)
+    i_count == p_count + 3
+
+    print("allow_storage_options 2: i_storage.options[0] =", i_storage.options[0])
+    i_storage.options[0] == "io.katacontainers.fs-opt.layer-src-prefix=/var/lib/containerd/io.containerd.snapshotter.v1.tardev/layers"
+
+    print("allow_storage_options 2: i_storage.options[i_count - 2] =", i_storage.options[i_count - 2])
+    i_storage.options[i_count - 2] == "io.katacontainers.fs-opt.overlay-rw"
+
+    lowerdir := concat("=", ["lowerdir", p_storage.options[0]])
+    print("allow_storage_options 2: lowerdir =", lowerdir)
+
+    i_storage.options[i_count - 1] == lowerdir
+    print("allow_storage_options 2: i_storage.options[i_count - 1] =", i_storage.options[i_count - 1])
+
+    every i, policy_id in policy_ids {
+        allow_overlay_layer(policy_id, policy_hashes[i], i_storage.options[i + 1])
+    }
+
+    print("allow_storage_options 2: true")
+}
+allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) {
+    print("allow_storage_options 3: start")
+
+    p_storage.driver == "blk"
+    count(p_storage.options) == 1
+
+    startswith(p_storage.options[0], "$(hash")
+    hash_suffix := trim_left(p_storage.options[0], "$(hash")
+
+    endswith(hash_suffix, ")")
+    hash_index := trim_right(hash_suffix, ")")
+    i := to_number(hash_index)
+    print("allow_storage_options 3: i =", i)
+
+    hash_option := concat("=", ["io.katacontainers.fs-opt.root-hash", root_hashes[i]])
+    print("allow_storage_options 3: hash_option =", hash_option)
+
+    count(i_storage.options) == 4
+    i_storage.options[0] == "ro"
+    i_storage.options[1] == "io.katacontainers.fs-opt.block_device=file"
+    i_storage.options[2] == "io.katacontainers.fs-opt.is-layer"
+    i_storage.options[3] == hash_option
+
+    print("allow_storage_options 3: true")
+}
+
+allow_overlay_layer(policy_id, policy_hash, i_option) {
+    print("allow_overlay_layer: policy_id =", policy_id, "policy_hash =", policy_hash)
+    print("allow_overlay_layer: i_option =", i_option)
+
+    startswith(i_option, "io.katacontainers.fs-opt.layer=")
+    i_value := replace(i_option, "io.katacontainers.fs-opt.layer=", "")
+    i_value_decoded := base64.decode(i_value)
+    print("allow_overlay_layer: i_value_decoded =", i_value_decoded)
+
+    policy_suffix := concat("=", ["tar,ro,io.katacontainers.fs-opt.block_device=file,io.katacontainers.fs-opt.is-layer,io.katacontainers.fs-opt.root-hash", policy_hash])
+    p_value := concat(",", [policy_id, policy_suffix])
+    print("allow_overlay_layer: p_value =", p_value)
+
+    p_value == i_value_decoded
+
+    print("allow_overlay_layer: true")
+}
+
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "tar"
+
+    startswith(p_storage.mount_point, "$(layer")
+    mount_suffix := trim_left(p_storage.mount_point, "$(layer")
+
+    endswith(mount_suffix, ")")
+    layer_index := trim_right(mount_suffix, ")")
+    i := to_number(layer_index)
+    print("allow_mount_point 1: i =", i)
+
+    layer_id := layer_ids[i]
+    print("allow_mount_point 1: layer_id =", layer_id)
+
+    p_mount := concat("/", ["/run/kata-containers/sandbox/layers", layer_id])
+    print("allow_mount_point 1: p_mount =", p_mount)
+
+    p_mount == i_storage.mount_point
+
+    print("allow_mount_point 1: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "fuse3.kata-overlay"
+
+    mount1 := replace(p_storage.mount_point, "$(cpath)", policy_data.common.cpath)
+    mount2 := replace(mount1, "$(bundle-id)", bundle_id)
+    print("allow_mount_point 2: mount2 =", mount2)
+
+    mount2 == i_storage.mount_point
+
+    print("allow_mount_point 2: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "local"
+
+    mount1 := p_storage.mount_point
+    print("allow_mount_point 3: mount1 =", mount1)
+
+    mount2 := replace(mount1, "$(cpath)", policy_data.common.cpath)
+    print("allow_mount_point 3: mount2 =", mount2)
+
+    mount3 := replace(mount2, "$(sandbox-id)", sandbox_id)
+    print("allow_mount_point 3: mount3 =", mount3)
+
+    regex.match(mount3, i_storage.mount_point)
+
+    print("allow_mount_point 3: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "bind"
+
+    mount1 := p_storage.mount_point
+    print("allow_mount_point 4: mount1 =", mount1)
+
+    mount2 := replace(mount1, "$(cpath)", policy_data.common.cpath)
+    print("allow_mount_point 4: mount2 =", mount2)
+
+    mount3 := replace(mount2, "$(bundle-id)", bundle_id)
+    print("allow_mount_point 4: mount3 =", mount3)
+
+    regex.match(mount3, i_storage.mount_point)
+
+    print("allow_mount_point 4: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "tmpfs"
+
+    mount1 := p_storage.mount_point
+    print("allow_mount_point 5: mount1 =", mount1)
+
+    regex.match(mount1, i_storage.mount_point)
+
+    print("allow_mount_point 5: true")
+}
+
+# process.Capabilities
+allow_caps(p_caps, i_caps) {
+    print("allow_caps: policy Ambient =", p_caps.Ambient)
+    print("allow_caps: input Ambient =", i_caps.Ambient)
+    match_caps(p_caps.Ambient, i_caps.Ambient)
+
+    print("allow_caps: policy Bounding =", p_caps.Bounding)
+    print("allow_caps: input Bounding =", i_caps.Bounding)
+    match_caps(p_caps.Bounding, i_caps.Bounding)
+
+    print("allow_caps: policy Effective =", p_caps.Effective)
+    print("allow_caps: input Effective =", i_caps.Effective)
+    match_caps(p_caps.Effective, i_caps.Effective)
+
+    print("allow_caps: policy Inheritable =", p_caps.Inheritable)
+    print("allow_caps: input Inheritable =", i_caps.Inheritable)
+    match_caps(p_caps.Inheritable, i_caps.Inheritable)
+
+    print("allow_caps: policy Permitted =", p_caps.Permitted)
+    print("allow_caps: input Permitted =", i_caps.Permitted)
+    match_caps(p_caps.Permitted, i_caps.Permitted)
+}
+
+match_caps(p_caps, i_caps) {
+    print("match_caps 1: start")
+
+    p_caps == i_caps
+
+    print("match_caps 1: true")
+}
+match_caps(p_caps, i_caps) {
+    print("match_caps 2: start")
+
+    count(p_caps) == 1
+    p_caps[0] == "$(default_caps)"
+
+    print("match_caps 2: default_caps =", policy_data.common.default_caps)
+    policy_data.common.default_caps == i_caps
+
+    print("match_caps 2: true")
+}
+match_caps(p_caps, i_caps) {
+    print("match_caps 3: start")
+
+    count(p_caps) == 1
+    p_caps[0] == "$(privileged_caps)"
+
+    print("match_caps 3: privileged_caps =", policy_data.common.privileged_caps)
+    policy_data.common.privileged_caps == i_caps
+
+    print("match_caps 3: true")
+}
+
+######################################################################
+CopyFileRequest {
+    print("CopyFileRequest: input.path =", input.path)
+
+    some regex1 in policy_data.request_defaults.CopyFileRequest
+    regex2 := replace(regex1, "$(cpath)", policy_data.common.cpath)
+    regex.match(regex2, input.path)
+
+    print("CopyFileRequest: true")
+}
+
+CreateSandboxRequest {
+    i_pidns := input.sandbox_pidns
+    print("CreateSandboxRequest: i_pidns =", i_pidns)
+    i_pidns == false
+}
+
+ExecProcessRequest {
+    print("ExecProcessRequest 1: input =", input)
+
+    i_command = concat(" ", input.process.Args)
+    print("ExecProcessRequest 1: i_command =", i_command)
+
+    some p_command in policy_data.request_defaults.ExecProcessRequest.commands
+    print("ExecProcessRequest 1: p_command =", p_command)
+    p_command == i_command
+
+    print("ExecProcessRequest 1: true")
+}
+ExecProcessRequest {
+    print("ExecProcessRequest 2: input =", input)
+
+    # TODO: match input container ID with its corresponding container.exec_commands.
+    i_command = concat(" ", input.process.Args)
+    print("ExecProcessRequest 3: i_command =", i_command)
+
+    some container in policy_data.containers
+    some p_command in container.exec_commands
+    print("ExecProcessRequest 2: p_command =", p_command)
+
+    # TODO: should other input data fields be validated as well?
+    p_command == i_command
+
+    print("ExecProcessRequest 2: true")
+}
+ExecProcessRequest {
+    print("ExecProcessRequest 3: input =", input)
+
+    i_command = concat(" ", input.process.Args)
+    print("ExecProcessRequest 3: i_command =", i_command)
+
+    some p_regex in policy_data.request_defaults.ExecProcessRequest.regex
+    print("ExecProcessRequest 3: p_regex =", p_regex)
+
+    regex.match(p_regex, i_command)
+
+    print("ExecProcessRequest 3: true")
+}
+
+CloseStdinRequest {
+    policy_data.request_defaults.CloseStdinRequest == true
+}
+
+ReadStreamRequest {
+    policy_data.request_defaults.ReadStreamRequest == true
+}
+
+WriteStreamRequest {
+    policy_data.request_defaults.WriteStreamRequest == true
+}
+
+policy_data := {
+  "containers": [
+    {
+      "OCI": {
+        "Version": "1.1.0",
+        "Process": {
+          "Terminal": false,
+          "User": {
+            "UID": 65535,
+            "GID": 65535,
+            "AdditionalGids": [],
+            "Username": ""
+          },
+          "Args": [
+            "/pause"
+          ],
+          "Env": [
+            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+          ],
+          "Cwd": "/",
+          "Capabilities": {
+            "Ambient": [],
+            "Bounding": [
+              "$(default_caps)"
+            ],
+            "Effective": [
+              "$(default_caps)"
+            ],
+            "Inheritable": [],
+            "Permitted": [
+              "$(default_caps)"
+            ]
+          },
+          "NoNewPrivileges": true
+        },
+        "Root": {
+          "Path": "$(cpath)/$(bundle-id)/rootfs",
+          "Readonly": true
+        },
+        "Mounts": [
+          {
+            "destination": "/proc",
+            "source": "proc",
+            "type_": "proc",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/dev",
+            "source": "tmpfs",
+            "type_": "tmpfs",
+            "options": [
+              "nosuid",
+              "strictatime",
+              "mode=755",
+              "size=65536k"
+            ]
+          },
+          {
+            "destination": "/dev/pts",
+            "source": "devpts",
+            "type_": "devpts",
+            "options": [
+              "nosuid",
+              "noexec",
+              "newinstance",
+              "ptmxmode=0666",
+              "mode=0620",
+              "gid=5"
+            ]
+          },
+          {
+            "destination": "/dev/shm",
+            "source": "/run/kata-containers/sandbox/shm",
+            "type_": "bind",
+            "options": [
+              "rbind"
+            ]
+          },
+          {
+            "destination": "/dev/mqueue",
+            "source": "mqueue",
+            "type_": "mqueue",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/sys",
+            "source": "sysfs",
+            "type_": "sysfs",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/etc/resolv.conf",
+            "source": "$(sfprefix)resolv.conf$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "ro",
+              "nosuid",
+              "nodev",
+              "noexec"
+            ]
+          }
+        ],
+        "Annotations": {
+          "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/$(bundle-id)",
+          "io.katacontainers.pkg.oci.container_type": "pod_sandbox",
+          "io.kubernetes.cri.container-type": "sandbox",
+          "io.kubernetes.cri.sandbox-id": "^[a-z0-9]{64}$",
+          "io.kubernetes.cri.sandbox-log-directory": "^/var/log/pods/$(sandbox-namespace)_$(sandbox-name)_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
+          "io.kubernetes.cri.sandbox-name": "policy-pod",
+          "io.kubernetes.cri.sandbox-namespace": "kata-containers-k8s-tests",
+          "nerdctl/network-namespace": "^/var/run/netns/cni-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
+        },
+        "Linux": {
+          "Namespaces": [
+            {
+              "Type": "ipc",
+              "Path": ""
+            },
+            {
+              "Type": "uts",
+              "Path": ""
+            },
+            {
+              "Type": "mount",
+              "Path": ""
+            }
+          ],
+          "MaskedPaths": [
+            "/proc/acpi",
+            "/proc/asound",
+            "/proc/kcore",
+            "/proc/keys",
+            "/proc/latency_stats",
+            "/proc/timer_list",
+            "/proc/timer_stats",
+            "/proc/sched_debug",
+            "/sys/firmware",
+            "/proc/scsi"
+          ],
+          "ReadonlyPaths": [
+            "/proc/bus",
+            "/proc/fs",
+            "/proc/irq",
+            "/proc/sys",
+            "/proc/sysrq-trigger"
+          ]
+        }
+      },
+      "storages": [
+        {
+          "driver": "blk",
+          "driver_options": [],
+          "source": "",
+          "fstype": "tar",
+          "options": [
+            "$(hash0)"
+          ],
+          "mount_point": "$(layer0)",
+          "fs_group": null
+        },
+        {
+          "driver": "overlayfs",
+          "driver_options": [],
+          "source": "",
+          "fstype": "fuse3.kata-overlay",
+          "options": [
+            "5a5aad80055ff20012a50dc25f8df7a29924474324d65f7d5306ee8ee27ff71d",
+            "817250f1a3e336da76f5bd3fa784e1b26d959b9c131876815ba2604048b70c18"
+          ],
+          "mount_point": "$(cpath)/$(bundle-id)/rootfs",
+          "fs_group": null
+        }
+      ],
+      "sandbox_pidns": false,
+      "exec_commands": []
+    },
+    {
+      "OCI": {
+        "Version": "1.1.0",
+        "Process": {
+          "Terminal": false,
+          "User": {
+            "UID": 0,
+            "GID": 0,
+            "AdditionalGids": [],
+            "Username": ""
+          },
+          "Args": [
+            "sh"
+          ],
+          "Env": [
+            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+            "HOSTNAME=$(host-name)",
+            "KUBE_CONFIG_1=value-1",
+            "KUBE_CONFIG_2=value-2"
+          ],
+          "Cwd": "/",
+          "Capabilities": {
+            "Ambient": [],
+            "Bounding": [
+              "$(default_caps)"
+            ],
+            "Effective": [
+              "$(default_caps)"
+            ],
+            "Inheritable": [],
+            "Permitted": [
+              "$(default_caps)"
+            ]
+          },
+          "NoNewPrivileges": false
+        },
+        "Root": {
+          "Path": "$(cpath)/$(bundle-id)/rootfs",
+          "Readonly": false
+        },
+        "Mounts": [
+          {
+            "destination": "/proc",
+            "source": "proc",
+            "type_": "proc",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/dev",
+            "source": "tmpfs",
+            "type_": "tmpfs",
+            "options": [
+              "nosuid",
+              "strictatime",
+              "mode=755",
+              "size=65536k"
+            ]
+          },
+          {
+            "destination": "/dev/pts",
+            "source": "devpts",
+            "type_": "devpts",
+            "options": [
+              "nosuid",
+              "noexec",
+              "newinstance",
+              "ptmxmode=0666",
+              "mode=0620",
+              "gid=5"
+            ]
+          },
+          {
+            "destination": "/dev/shm",
+            "source": "/run/kata-containers/sandbox/shm",
+            "type_": "bind",
+            "options": [
+              "rbind"
+            ]
+          },
+          {
+            "destination": "/dev/mqueue",
+            "source": "mqueue",
+            "type_": "mqueue",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/sys",
+            "source": "sysfs",
+            "type_": "sysfs",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/sys/fs/cgroup",
+            "source": "cgroup",
+            "type_": "cgroup",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev",
+              "relatime",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/etc/hosts",
+            "source": "$(sfprefix)hosts$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "rw"
+            ]
+          },
+          {
+            "destination": "/dev/termination-log",
+            "source": "$(sfprefix)termination-log$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "rw"
+            ]
+          },
+          {
+            "destination": "/etc/hostname",
+            "source": "$(sfprefix)hostname$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "rw"
+            ]
+          },
+          {
+            "destination": "/etc/resolv.conf",
+            "source": "$(sfprefix)resolv.conf$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "rw"
+            ]
+          },
+          {
+            "destination": "/var/run/secrets/kubernetes.io/serviceaccount",
+            "source": "$(sfprefix)serviceaccount$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/var/run/secrets/azure/tokens",
+            "source": "$(sfprefix)tokens$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "ro"
+            ]
+          }
+        ],
+        "Annotations": {
+          "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/$(bundle-id)",
+          "io.katacontainers.pkg.oci.container_type": "pod_container",
+          "io.kubernetes.cri.container-name": "busybox",
+          "io.kubernetes.cri.container-type": "container",
+          "io.kubernetes.cri.image-name": "quay.io/prometheus/busybox:latest",
+          "io.kubernetes.cri.sandbox-id": "^[a-z0-9]{64}$",
+          "io.kubernetes.cri.sandbox-name": "policy-pod",
+          "io.kubernetes.cri.sandbox-namespace": "kata-containers-k8s-tests"
+        },
+        "Linux": {
+          "Namespaces": [
+            {
+              "Type": "ipc",
+              "Path": ""
+            },
+            {
+              "Type": "uts",
+              "Path": ""
+            },
+            {
+              "Type": "mount",
+              "Path": ""
+            }
+          ],
+          "MaskedPaths": [
+            "/proc/acpi",
+            "/proc/kcore",
+            "/proc/keys",
+            "/proc/latency_stats",
+            "/proc/timer_list",
+            "/proc/timer_stats",
+            "/proc/sched_debug",
+            "/proc/scsi",
+            "/sys/firmware"
+          ],
+          "ReadonlyPaths": [
+            "/proc/asound",
+            "/proc/bus",
+            "/proc/fs",
+            "/proc/irq",
+            "/proc/sys",
+            "/proc/sysrq-trigger"
+          ]
+        }
+      },
+      "storages": [
+        {
+          "driver": "blk",
+          "driver_options": [],
+          "source": "",
+          "fstype": "tar",
+          "options": [
+            "$(hash0)"
+          ],
+          "mount_point": "$(layer0)",
+          "fs_group": null
+        },
+        {
+          "driver": "blk",
+          "driver_options": [],
+          "source": "",
+          "fstype": "tar",
+          "options": [
+            "$(hash1)"
+          ],
+          "mount_point": "$(layer1)",
+          "fs_group": null
+        },
+        {
+          "driver": "overlayfs",
+          "driver_options": [],
+          "source": "",
+          "fstype": "fuse3.kata-overlay",
+          "options": [
+            "b1ef3d18661ad727a8ad01d462fffc63ec90420a938056bc0422822328f83ff7:a8650d4a279d4537d7fc43c582a264528a96f041b1327d41b503f368c301594f",
+            "75ed5e78d30374b9557d32c17ae6d2e1b4afdc5efd128812e6c99a0f60708dba:2e52d86498030b4b99318650826d3f121bdfac5fe7bbbea9d18c546d48003aa0"
+          ],
+          "mount_point": "$(cpath)/$(bundle-id)/rootfs",
+          "fs_group": null
+        }
+      ],
+      "sandbox_pidns": false,
+      "exec_commands": []
+    }
+  ],
+  "common": {
+    "cpath": "/run/kata-containers/shared/containers",
+    "sfprefix": "^$(cpath)/$(bundle-id)-[a-z0-9]{16}-",
+    "ipv4_a": "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])",
+    "ip_p": "[0-9]{1,5}",
+    "svc_name": "[A-Z_\\.\\-]+",
+    "dns_label": "[a-zA-Z0-9_\\.\\-]+",
+    "default_caps": [
+      "CAP_CHOWN",
+      "CAP_DAC_OVERRIDE",
+      "CAP_FSETID",
+      "CAP_FOWNER",
+      "CAP_MKNOD",
+      "CAP_NET_RAW",
+      "CAP_SETGID",
+      "CAP_SETUID",
+      "CAP_SETFCAP",
+      "CAP_SETPCAP",
+      "CAP_NET_BIND_SERVICE",
+      "CAP_SYS_CHROOT",
+      "CAP_KILL",
+      "CAP_AUDIT_WRITE"
+    ],
+    "privileged_caps": [
+      "CAP_CHOWN",
+      "CAP_DAC_OVERRIDE",
+      "CAP_DAC_READ_SEARCH",
+      "CAP_FOWNER",
+      "CAP_FSETID",
+      "CAP_KILL",
+      "CAP_SETGID",
+      "CAP_SETUID",
+      "CAP_SETPCAP",
+      "CAP_LINUX_IMMUTABLE",
+      "CAP_NET_BIND_SERVICE",
+      "CAP_NET_BROADCAST",
+      "CAP_NET_ADMIN",
+      "CAP_NET_RAW",
+      "CAP_IPC_LOCK",
+      "CAP_IPC_OWNER",
+      "CAP_SYS_MODULE",
+      "CAP_SYS_RAWIO",
+      "CAP_SYS_CHROOT",
+      "CAP_SYS_PTRACE",
+      "CAP_SYS_PACCT",
+      "CAP_SYS_ADMIN",
+      "CAP_SYS_BOOT",
+      "CAP_SYS_NICE",
+      "CAP_SYS_RESOURCE",
+      "CAP_SYS_TIME",
+      "CAP_SYS_TTY_CONFIG",
+      "CAP_MKNOD",
+      "CAP_LEASE",
+      "CAP_AUDIT_WRITE",
+      "CAP_AUDIT_CONTROL",
+      "CAP_SETFCAP",
+      "CAP_MAC_OVERRIDE",
+      "CAP_MAC_ADMIN",
+      "CAP_SYSLOG",
+      "CAP_WAKE_ALARM",
+      "CAP_BLOCK_SUSPEND",
+      "CAP_AUDIT_READ",
+      "CAP_PERFMON",
+      "CAP_BPF",
+      "CAP_CHECKPOINT_RESTORE"
+    ]
+  },
+  "request_defaults": {
+    "CreateContainerRequest": {
+      "allow_env_regex": [
+        "^HOSTNAME=$(dns_label)$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP=tcp://$(ipv4_a):$(ip_p)$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP_PROTO=tcp$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP_PORT=$(ip_p)$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP_ADDR=$(ipv4_a)$",
+        "^$(svc_name)_SERVICE_HOST=$(ipv4_a)$",
+        "^$(svc_name)_SERVICE_PORT=$(ip_p)$",
+        "^$(svc_name)_SERVICE_PORT_$(dns_label)=$(ip_p)$",
+        "^$(svc_name)_PORT=tcp://$(ipv4_a):$(ip_p)$",
+        "^AZURE_CLIENT_ID=[A-Fa-f0-9-]+$",
+        "^AZURE_TENANT_ID=[A-Fa-f0-9-]+$",
+        "^AZURE_FEDERATED_TOKEN_FILE=/var/run/secrets/azure/tokens/azure-identity-token$",
+        "^AZURE_AUTHORITY_HOST=https://login\\.microsoftonline\\.com/$"
+      ]
+    },
+    "CopyFileRequest": [
+      "^$(cpath)/"
+    ],
+    "ExecProcessRequest": {
+      "commands": [],
+      "regex": []
+    },
+    "CloseStdinRequest": false,
+    "ReadStreamRequest": false,
+    "WriteStreamRequest": false
+  }
+}
diff --git a/tests/kata/data/k8s-policy-rc/inputs.txt b/tests/kata/data/k8s-policy-rc/inputs.txt
new file mode 100644
index 00000000..101e789d
--- /dev/null
+++ b/tests/kata/data/k8s-policy-rc/inputs.txt
@@ -0,0 +1,33 @@
+["ep":"AllowRequestsFailingPolicy",{}],
+
+["ep":"UpdateInterfaceRequest",{"interface":{"device":"eth0","name":"eth0","IPAddresses":[{"family":0,"address":"10.244.2.68","mask":"24"},{"family":1,"address":"fe80::ec1f:81ff:fe86:255b","mask":"64"}],"mtu":1500,"hwAddr":"ee:1f:81:86:25:5b","pciPath":"","type_":"","raw_flags":0}}],
+
+["ep":"UpdateRoutesRequest",{"routes":{"Routes":[{"dest":"","gateway":"10.244.2.1","device":"eth0","source":"","scope":0,"family":0}]}}],
+
+["ep":"CreateSandboxRequest",{"hostname":"policy-rc-test-674df","dns":["search kata-containers-k8s-tests.svc.cluster.local svc.cluster.local cluster.local iete1orsbdmu5ia1kq2du43hqe.bx.internal.cloudapp.net","nameserver 10.0.0.10","options ndots:5",""],"storages":[{"driver":"virtio-fs","driver_options":[],"source":"kataShared","fstype":"virtiofs","options":[],"mount_point":"/run/kata-containers/shared/containers/","fs_group":null},{"driver":"ephemeral","driver_options":[],"source":"shm","fstype":"tmpfs","options":["noexec","nosuid","nodev","mode=1777","size=67108864"],"mount_point":"/run/kata-containers/sandbox/shm","fs_group":null}],"sandbox_pidns":false,"sandbox_id":"c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5","guest_hook_path":"","kernel_modules":[]}],
+
+["ep":"GuestDetailsRequest",{"mem_block_size":true,"mem_hotplug_probe":true}],
+
+["ep":"CreateContainerRequest",{"container_id":"c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5","exec_id":"c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5","string_user":null,"devices":[],"storages":[],"OCI":{"Version":"1.1.0","Process":{"Terminal":false,"ConsoleSize":null,"User":{"UID":65535,"GID":65535,"AdditionalGids":[65535],"Username":""},"Args":["/pause"],"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cwd":"/","Capabilities":{"Bounding":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Effective":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Inheritable":[],"Permitted":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Ambient":[]},"Rlimits":[],"NoNewPrivileges":true,"ApparmorProfile":"","OOMScoreAdj":-998,"SelinuxLabel":""},"Root":{"Path":"/run/kata-containers/shared/containers/c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5/rootfs","Readonly":true},"Hostname":"policy-rc-test-674df","Mounts":[{"destination":"/proc","source":"proc","type_":"proc","options":["nosuid","noexec","nodev"]},{"destination":"/dev","source":"tmpfs","type_":"tmpfs","options":["nosuid","strictatime","mode=755","size=65536k"]},{"destination":"/dev/pts","source":"devpts","type_":"devpts","options":["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620","gid=5"]},{"destination":"/dev/mqueue","source":"mqueue","type_":"mqueue","options":["nosuid","noexec","nodev"]},{"destination":"/sys","source":"sysfs","type_":"sysfs","options":["nosuid","noexec","nodev","ro"]},{"destination":"/dev/shm","source":"/run/kata-containers/sandbox/shm","type_":"bind","options":["rbind"]},{"destination":"/etc/resolv.conf","source":"/run/kata-containers/shared/containers/c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5-e1c85415dcc483c7-resolv.conf","type_":"bind","options":["rbind","ro","nosuid","nodev","noexec"]}],"Hooks":null,"Annotations":{"io.kubernetes.cri.sandbox-uid":"eeb60852-0dcd-4387-b0f9-eebd63ddb7a8","io.katacontainers.pkg.oci.bundle_path":"/run/containerd/io.containerd.runtime.v2.task/k8s.io/c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5","io.kubernetes.cri.sandbox-cpu-period":"100000","io.kubernetes.cri.sandbox-id":"c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5","io.kubernetes.cri.sandbox-name":"policy-rc-test-674df","io.katacontainers.pkg.oci.container_type":"pod_sandbox","io.kubernetes.cri.container-type":"sandbox","nerdctl/network-namespace":"/var/run/netns/cni-939f1f21-5cec-1599-6804-471950c9c843","io.kubernetes.cri.sandbox-log-directory":"/var/log/pods/kata-containers-k8s-tests_policy-rc-test-674df_eeb60852-0dcd-4387-b0f9-eebd63ddb7a8","io.kubernetes.cri.sandbox-cpu-quota":"0","io.kubernetes.cri.sandbox-cpu-shares":"2","io.kubernetes.cri.sandbox-namespace":"kata-containers-k8s-tests","io.kubernetes.cri.sandbox-memory":"0"},"Linux":{"UIDMappings":[],"GIDMappings":[],"Sysctl":{},"Resources":{"Devices":[],"Memory":null,"CPU":{"Shares":2,"Quota":0,"Period":0,"RealtimeRuntime":0,"RealtimePeriod":0,"Cpus":"","Mems":""},"Pids":null,"BlockIO":null,"HugepageLimits":[],"Network":null},"CgroupsPath":"kubepods-besteffort-podeeb60852_0dcd_4387_b0f9_eebd63ddb7a8.slice:cri-containerd:c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5","Namespaces":[{"Type":"ipc","Path":""},{"Type":"uts","Path":""},{"Type":"mount","Path":""}],"Devices":[],"Seccomp":null,"RootfsPropagation":"","MaskedPaths":["/proc/acpi","/proc/asound","/proc/kcore","/proc/keys","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug","/sys/firmware","/sys/devices/virtual/powercap","/proc/scsi"],"ReadonlyPaths":["/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"],"MountLabel":"","IntelRdt":null},"Solaris":null,"Windows":null},"sandbox_pidns":false,"shared_mounts":[],"stdin_port":0,"stdout_port":0,"stderr_port":0}],
+
+["ep":"OnlineCPUMemRequest",{"wait":false,"nb_cpus":0,"cpu_only":false}],
+
+["ep":"UpdateEphemeralMountsRequest",{"storages":[]}],
+
+["ep":"StartContainerRequest",{"container_id":"c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5"}],
+
+["ep":"WaitProcessRequest",{"container_id":"c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5","exec_id":"c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5"}],
+
+["ep":"GetOOMEventRequest",{}],
+
+["ep":"CreateContainerRequest",{"container_id":"432a988cdb7fd4798e112d1a27a272d86e665dbfbdc4fc6937af127120b3a03e","exec_id":"432a988cdb7fd4798e112d1a27a272d86e665dbfbdc4fc6937af127120b3a03e","string_user":null,"devices":[],"storages":[{"driver":"local","driver_options":[],"source":"local","fstype":"local","options":["mode=0777"],"mount_point":"/run/kata-containers/shared/containers/c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5/rootfs/local/host-empty-vol","fs_group":null}],"OCI":{"Version":"1.1.0","Process":{"Terminal":false,"ConsoleSize":null,"User":{"UID":0,"GID":0,"AdditionalGids":[0,1,2,3,4,6,10,11,20,26,27],"Username":""},"Args":["nginx","-g","daemon off;"],"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","HOSTNAME=policy-rc-test-674df","NGINX_VERSION=1.15.12","KUBERNETES_PORT_443_TCP=tcp://10.0.0.1:443","KUBERNETES_PORT_443_TCP_PROTO=tcp","KUBERNETES_PORT_443_TCP_PORT=443","KUBERNETES_PORT_443_TCP_ADDR=10.0.0.1","KUBERNETES_SERVICE_HOST=10.0.0.1","KUBERNETES_SERVICE_PORT=443","KUBERNETES_SERVICE_PORT_HTTPS=443","KUBERNETES_PORT=tcp://10.0.0.1:443"],"Cwd":"/","Capabilities":{"Bounding":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Effective":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Inheritable":[],"Permitted":["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"],"Ambient":[]},"Rlimits":[],"NoNewPrivileges":false,"ApparmorProfile":"cri-containerd.apparmor.d","OOMScoreAdj":1000,"SelinuxLabel":""},"Root":{"Path":"/run/kata-containers/shared/containers/432a988cdb7fd4798e112d1a27a272d86e665dbfbdc4fc6937af127120b3a03e/rootfs","Readonly":false},"Hostname":"","Mounts":[{"destination":"/proc","source":"proc","type_":"proc","options":["nosuid","noexec","nodev"]},{"destination":"/dev","source":"tmpfs","type_":"tmpfs","options":["nosuid","strictatime","mode=755","size=65536k"]},{"destination":"/dev/pts","source":"devpts","type_":"devpts","options":["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620","gid=5"]},{"destination":"/dev/mqueue","source":"mqueue","type_":"mqueue","options":["nosuid","noexec","nodev"]},{"destination":"/sys","source":"sysfs","type_":"sysfs","options":["nosuid","noexec","nodev","ro"]},{"destination":"/sys/fs/cgroup","source":"cgroup","type_":"cgroup","options":["nosuid","noexec","nodev","relatime","ro"]},{"destination":"/host/cache","source":"/run/kata-containers/shared/containers/c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5/rootfs/local/host-empty-vol","type_":"local","options":["rbind","rprivate","rw"]},{"destination":"/etc/hosts","source":"/run/kata-containers/shared/containers/432a988cdb7fd4798e112d1a27a272d86e665dbfbdc4fc6937af127120b3a03e-01336f00e960a39f-hosts","type_":"bind","options":["rbind","rprivate","rw"]},{"destination":"/dev/termination-log","source":"/run/kata-containers/shared/containers/432a988cdb7fd4798e112d1a27a272d86e665dbfbdc4fc6937af127120b3a03e-90afc9ee3516c39f-termination-log","type_":"bind","options":["rbind","rprivate","rw"]},{"destination":"/etc/hostname","source":"/run/kata-containers/shared/containers/432a988cdb7fd4798e112d1a27a272d86e665dbfbdc4fc6937af127120b3a03e-094a24e380ead97f-hostname","type_":"bind","options":["rbind","rprivate","rw"]},{"destination":"/etc/resolv.conf","source":"/run/kata-containers/shared/containers/432a988cdb7fd4798e112d1a27a272d86e665dbfbdc4fc6937af127120b3a03e-530431b992f6d296-resolv.conf","type_":"bind","options":["rbind","rprivate","rw"]},{"destination":"/dev/shm","source":"/run/kata-containers/sandbox/shm","type_":"bind","options":["rbind"]},{"destination":"/var/run/secrets/kubernetes.io/serviceaccount","source":"/run/kata-containers/shared/containers/432a988cdb7fd4798e112d1a27a272d86e665dbfbdc4fc6937af127120b3a03e-cb7632a83c5a9dc3-serviceaccount","type_":"bind","options":["rbind","rprivate","ro"]}],"Hooks":null,"Annotations":{"io.kubernetes.cri.sandbox-namespace":"kata-containers-k8s-tests","io.kubernetes.cri.container-name":"nginxtest","io.kubernetes.cri.sandbox-name":"policy-rc-test-674df","io.katacontainers.pkg.oci.container_type":"pod_container","io.katacontainers.pkg.oci.bundle_path":"/run/containerd/io.containerd.runtime.v2.task/k8s.io/432a988cdb7fd4798e112d1a27a272d86e665dbfbdc4fc6937af127120b3a03e","io.kubernetes.cri.container-type":"container","io.kubernetes.cri.sandbox-id":"c469f18bb1c58dab6be747450b77c3321e43ba5dd26d62d19a599b82bdb0ddc5","io.kubernetes.cri.sandbox-uid":"eeb60852-0dcd-4387-b0f9-eebd63ddb7a8","io.kubernetes.cri.image-name":"quay.io/sjenning/nginx:1.15-alpine"},"Linux":{"UIDMappings":[],"GIDMappings":[],"Sysctl":{},"Resources":{"Devices":[],"Memory":{"Limit":0,"Reservation":0,"Swap":0,"Kernel":0,"KernelTCP":0,"Swappiness":0,"DisableOOMKiller":false},"CPU":{"Shares":2,"Quota":0,"Period":100000,"RealtimeRuntime":0,"RealtimePeriod":0,"Cpus":"","Mems":""},"Pids":null,"BlockIO":null,"HugepageLimits":[],"Network":null},"CgroupsPath":"kubepods-besteffort-podeeb60852_0dcd_4387_b0f9_eebd63ddb7a8.slice:cri-containerd:432a988cdb7fd4798e112d1a27a272d86e665dbfbdc4fc6937af127120b3a03e","Namespaces":[{"Type":"ipc","Path":""},{"Type":"uts","Path":""},{"Type":"mount","Path":""}],"Devices":[],"Seccomp":null,"RootfsPropagation":"","MaskedPaths":["/proc/asound","/proc/acpi","/proc/kcore","/proc/keys","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug","/proc/scsi","/sys/firmware"],"ReadonlyPaths":["/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"],"MountLabel":"","IntelRdt":null},"Solaris":null,"Windows":null},"sandbox_pidns":false,"shared_mounts":[],"stdin_port":0,"stdout_port":0,"stderr_port":0}],
+
+["ep":"OnlineCPUMemRequest",{"wait":false,"nb_cpus":0,"cpu_only":false}],
+
+["ep":"UpdateEphemeralMountsRequest",{"storages":[]}],
+
+["ep":"StartContainerRequest",{"container_id":"432a988cdb7fd4798e112d1a27a272d86e665dbfbdc4fc6937af127120b3a03e"}],
+
+["ep":"OnlineCPUMemRequest",{"wait":false,"nb_cpus":0,"cpu_only":false}],
+
+["ep":"UpdateEphemeralMountsRequest",{"storages":[]}],
diff --git a/tests/kata/data/k8s-policy-rc/output.json b/tests/kata/data/k8s-policy-rc/output.json
new file mode 100644
index 00000000..c0f5c42d
--- /dev/null
+++ b/tests/kata/data/k8s-policy-rc/output.json
@@ -0,0 +1,19 @@
+[
+  false,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true,
+  true
+]
\ No newline at end of file
diff --git a/tests/kata/data/k8s-policy-rc/policy.rego b/tests/kata/data/k8s-policy-rc/policy.rego
new file mode 100644
index 00000000..757c88da
--- /dev/null
+++ b/tests/kata/data/k8s-policy-rc/policy.rego
@@ -0,0 +1,1695 @@
+# Copyright (c) 2023 Microsoft Corporation
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+package agent_policy
+
+import future.keywords.in
+import future.keywords.every
+
+# Default values, returned by OPA when rules cannot be evaluated to true.
+default AddARPNeighborsRequest := false
+default AddSwapRequest := false
+default CloseStdinRequest := false
+default CopyFileRequest := false
+default CreateContainerRequest := false
+default CreateSandboxRequest := true
+default DestroySandboxRequest := true
+default ExecProcessRequest := false
+default GetOOMEventRequest := true
+default GuestDetailsRequest := true
+default ListInterfacesRequest := false
+default ListRoutesRequest := false
+default MemHotplugByProbeRequest := false
+default OnlineCPUMemRequest := true
+default PauseContainerRequest := false
+default ReadStreamRequest := false
+default RemoveContainerRequest := true
+default RemoveStaleVirtiofsShareMountsRequest := true
+default ReseedRandomDevRequest := false
+default ResumeContainerRequest := false
+default SetGuestDateTimeRequest := false
+default SetPolicyRequest := false
+default SignalProcessRequest := true
+default StartContainerRequest := true
+default StartTracingRequest := false
+default StatsContainerRequest := true
+default StopTracingRequest := false
+default TtyWinResizeRequest := true
+default UpdateContainerRequest := false
+default UpdateEphemeralMountsRequest := true
+default UpdateInterfaceRequest := true
+default UpdateRoutesRequest := true
+default WaitProcessRequest := true
+default WriteStreamRequest := false
+
+# AllowRequestsFailingPolicy := true configures the Agent to *allow any
+# requests causing a policy failure*. This is an unsecure configuration
+# but is useful for allowing unsecure pods to start, then connect to
+# them and inspect OPA logs for the root cause of a failure.
+default AllowRequestsFailingPolicy := false
+
+CreateContainerRequest {
+    i_oci := input.OCI
+    i_storages := input.storages
+
+    some p_container in policy_data.containers
+    print("======== CreateContainerRequest: trying next policy container")
+
+    p_pidns := p_container.sandbox_pidns
+    i_pidns := input.sandbox_pidns
+    print("CreateContainerRequest: p_pidns =", p_pidns, "i_pidns =", i_pidns)
+    p_pidns == i_pidns
+
+    p_oci := p_container.OCI
+
+    print("CreateContainerRequest: p Version =", p_oci.Version, "i Version =", i_oci.Version)
+    p_oci.Version == i_oci.Version
+
+    print("CreateContainerRequest: p Readonly =", p_oci.Root.Readonly, "i Readonly =", i_oci.Root.Readonly)
+    p_oci.Root.Readonly == i_oci.Root.Readonly
+
+    allow_anno(p_oci, i_oci)
+
+    p_storages := p_container.storages
+    allow_by_anno(p_oci, i_oci, p_storages, i_storages)
+
+    allow_linux(p_oci, i_oci)
+
+    print("CreateContainerRequest: true")
+}
+
+# Reject unexpected annotations.
+allow_anno(p_oci, i_oci) {
+    print("allow_anno 1: start")
+
+    not i_oci.Annotations
+
+    print("allow_anno 1: true")
+}
+allow_anno(p_oci, i_oci) {
+    print("allow_anno 2: p Annotations =", p_oci.Annotations)
+    print("allow_anno 2: i Annotations =", i_oci.Annotations)
+
+    i_keys := object.keys(i_oci.Annotations)
+    print("allow_anno 2: i keys =", i_keys)
+
+    every i_key in i_keys {
+        allow_anno_key(i_key, p_oci)
+    }
+
+    print("allow_anno 2: true")
+}
+
+allow_anno_key(i_key, p_oci) {
+    print("allow_anno_key 1: i key =", i_key)
+
+    startswith(i_key, "io.kubernetes.cri.")
+
+    print("allow_anno_key 1: true")
+}
+allow_anno_key(i_key, p_oci) {
+    print("allow_anno_key 2: i key =", i_key)
+
+    some p_key, _ in p_oci.Annotations
+    p_key == i_key
+
+    print("allow_anno_key 2: true")
+}
+
+# Get the value of the "io.kubernetes.cri.sandbox-name" annotation and
+# correlate it with other annotations and process fields.
+allow_by_anno(p_oci, i_oci, p_storages, i_storages) {
+    print("allow_by_anno 1: start")
+
+    s_name := "io.kubernetes.cri.sandbox-name"
+
+    not p_oci.Annotations[s_name]
+
+    i_s_name := i_oci.Annotations[s_name]
+    print("allow_by_anno 1: i_s_name =", i_s_name)
+
+    allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, i_s_name)
+
+    print("allow_by_anno 1: true")
+}
+allow_by_anno(p_oci, i_oci, p_storages, i_storages) {
+    print("allow_by_anno 2: start")
+
+    s_name := "io.kubernetes.cri.sandbox-name"
+
+    p_s_name := p_oci.Annotations[s_name]
+    i_s_name := i_oci.Annotations[s_name]
+    print("allow_by_anno 2: i_s_name =", i_s_name, "p_s_name =", p_s_name)
+
+    allow_sandbox_name(p_s_name, i_s_name)
+    allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, i_s_name)
+
+    print("allow_by_anno 2: true")
+}
+
+allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, s_name) {
+    print("allow_by_sandbox_name: start")
+
+    s_namespace := "io.kubernetes.cri.sandbox-namespace"
+
+    p_namespace := p_oci.Annotations[s_namespace]
+    i_namespace := i_oci.Annotations[s_namespace]
+    print("allow_by_sandbox_name: p_namespace =", p_namespace, "i_namespace =", i_namespace)
+    p_namespace == i_namespace
+
+    allow_by_container_types(p_oci, i_oci, s_name, p_namespace)
+    allow_by_bundle_or_sandbox_id(p_oci, i_oci, p_storages, i_storages)
+    allow_process(p_oci, i_oci, s_name)
+
+    print("allow_by_sandbox_name: true")
+}
+
+allow_sandbox_name(p_s_name, i_s_name) {
+    print("allow_sandbox_name 1: start")
+
+    p_s_name == i_s_name
+
+    print("allow_sandbox_name 1: true")
+}
+allow_sandbox_name(p_s_name, i_s_name) {
+    print("allow_sandbox_name 2: start")
+
+    # TODO: should generated names be handled differently?
+    contains(p_s_name, "$(generated-name)")
+
+    print("allow_sandbox_name 2: true")
+}
+
+# Check that the "io.kubernetes.cri.container-type" and
+# "io.katacontainers.pkg.oci.container_type" annotations designate the
+# expected type - either a "sandbox" or a "container". Then, validate
+# other annotations based on the actual "sandbox" or "container" value
+# from the input container.
+allow_by_container_types(p_oci, i_oci, s_name, s_namespace) {
+    print("allow_by_container_types: checking io.kubernetes.cri.container-type")
+
+    c_type := "io.kubernetes.cri.container-type"
+    
+    p_cri_type := p_oci.Annotations[c_type]
+    i_cri_type := i_oci.Annotations[c_type]
+    print("allow_by_container_types: p_cri_type =", p_cri_type, "i_cri_type =", i_cri_type)
+    p_cri_type == i_cri_type
+
+    allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace)
+
+    print("allow_by_container_types: true")
+}
+
+allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace) {
+    print("allow_by_container_type 1: i_cri_type =", i_cri_type)
+    i_cri_type == "sandbox"
+
+    i_kata_type := i_oci.Annotations["io.katacontainers.pkg.oci.container_type"]
+    print("allow_by_container_type 1: i_kata_type =", i_kata_type)
+    i_kata_type == "pod_sandbox"
+
+    allow_sandbox_container_name(p_oci, i_oci)
+    allow_sandbox_net_namespace(p_oci, i_oci)
+    allow_sandbox_log_directory(p_oci, i_oci, s_name, s_namespace)
+
+    print("allow_by_container_type 1: true")
+}
+
+allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace) {
+    print("allow_by_container_type 2: i_cri_type =", i_cri_type)
+    i_cri_type == "container"
+
+    i_kata_type := i_oci.Annotations["io.katacontainers.pkg.oci.container_type"]
+    print("allow_by_container_type 2: i_kata_type =", i_kata_type)
+    i_kata_type == "pod_container"
+
+    allow_container_name(p_oci, i_oci)
+    allow_net_namespace(p_oci, i_oci)
+    allow_log_directory(p_oci, i_oci)
+
+    print("allow_by_container_type 2: true")
+}
+
+# "io.kubernetes.cri.container-name" annotation
+allow_sandbox_container_name(p_oci, i_oci) {
+    print("allow_sandbox_container_name: start")
+
+    container_annotation_missing(p_oci, i_oci, "io.kubernetes.cri.container-name")
+
+    print("allow_sandbox_container_name: true")
+}
+
+allow_container_name(p_oci, i_oci) {
+    print("allow_container_name: start")
+
+    allow_container_annotation(p_oci, i_oci, "io.kubernetes.cri.container-name")
+
+    print("allow_container_name: true")
+}
+
+container_annotation_missing(p_oci, i_oci, key) {
+    print("container_annotation_missing:", key)
+
+    not p_oci.Annotations[key]
+    not i_oci.Annotations[key]
+
+    print("container_annotation_missing: true")
+}
+
+allow_container_annotation(p_oci, i_oci, key) {
+    print("allow_container_annotation: key =", key)
+
+    p_value := p_oci.Annotations[key]
+    i_value := i_oci.Annotations[key]
+    print("allow_container_annotation: p_value =", p_value, "i_value =", i_value)
+
+    p_value == i_value
+
+    print("allow_container_annotation: true")
+}
+
+# "nerdctl/network-namespace" annotation
+allow_sandbox_net_namespace(p_oci, i_oci) {
+    print("allow_sandbox_net_namespace: start")
+
+    key := "nerdctl/network-namespace"
+
+    p_namespace := p_oci.Annotations[key]
+    i_namespace := i_oci.Annotations[key]
+    print("allow_sandbox_net_namespace: p_namespace =", p_namespace, "i_namespace =", i_namespace)
+
+    regex.match(p_namespace, i_namespace)
+
+    print("allow_sandbox_net_namespace: true")
+}
+
+allow_net_namespace(p_oci, i_oci) {
+    print("allow_net_namespace: start")
+
+    key := "nerdctl/network-namespace"
+
+    not p_oci.Annotations[key]
+    not i_oci.Annotations[key]
+
+    print("allow_net_namespace: true")
+}
+
+# "io.kubernetes.cri.sandbox-log-directory" annotation
+allow_sandbox_log_directory(p_oci, i_oci, s_name, s_namespace) {
+    print("allow_sandbox_log_directory: start")
+
+    key := "io.kubernetes.cri.sandbox-log-directory"
+
+    p_dir := p_oci.Annotations[key]
+    regex1 := replace(p_dir, "$(sandbox-name)", s_name)
+    regex2 := replace(regex1, "$(sandbox-namespace)", s_namespace)
+    print("allow_sandbox_log_directory: regex2 =", regex2)
+
+    i_dir := i_oci.Annotations[key]
+    print("allow_sandbox_log_directory: i_dir =", i_dir)
+
+    regex.match(regex2, i_dir)
+
+    print("allow_sandbox_log_directory: true")
+}
+
+allow_log_directory(p_oci, i_oci) {
+    print("allow_log_directory: start")
+
+    key := "io.kubernetes.cri.sandbox-log-directory"
+
+    not p_oci.Annotations[key]
+    not i_oci.Annotations[key]
+
+    print("allow_log_directory: true")
+}
+
+allow_linux(p_oci, i_oci) {
+    p_namespaces := p_oci.Linux.Namespaces
+    print("allow_linux: p namespaces =", p_namespaces)
+
+    i_namespaces := i_oci.Linux.Namespaces
+    print("allow_linux: i namespaces =", i_namespaces)
+
+    p_namespaces == i_namespaces
+
+    allow_masked_paths(p_oci, i_oci)
+    allow_readonly_paths(p_oci, i_oci)
+
+    print("allow_linux: true")
+}
+
+allow_masked_paths(p_oci, i_oci) {
+    p_paths := p_oci.Linux.MaskedPaths
+    print("allow_masked_paths 1: p_paths =", p_paths)
+
+    i_paths := i_oci.Linux.MaskedPaths
+    print("allow_masked_paths 1: i_paths =", i_paths)
+
+    allow_masked_paths_array(p_paths, i_paths)
+
+    print("allow_masked_paths 1: true")
+}
+allow_masked_paths(p_oci, i_oci) {
+    print("allow_masked_paths 2: start")
+
+    not p_oci.Linux.MaskedPaths
+    not i_oci.Linux.MaskedPaths
+
+    print("allow_masked_paths 2: true")
+}
+
+# All the policy masked paths must be masked in the input data too.
+# Input is allowed to have more masked paths than the policy.
+allow_masked_paths_array(p_array, i_array) {
+    every p_elem in p_array {
+        allow_masked_path(p_elem, i_array)
+    }
+}
+
+allow_masked_path(p_elem, i_array) {
+    print("allow_masked_path: p_elem =", p_elem)
+
+    some i_elem in i_array
+    p_elem == i_elem
+
+    print("allow_masked_path: true")
+}
+
+allow_readonly_paths(p_oci, i_oci) {
+    p_paths := p_oci.Linux.ReadonlyPaths
+    print("allow_readonly_paths 1: p_paths =", p_paths)
+
+    i_paths := i_oci.Linux.ReadonlyPaths
+    print("allow_readonly_paths 1: i_paths =", i_paths)
+
+    allow_readonly_paths_array(p_paths, i_paths, i_oci.Linux.MaskedPaths)
+
+    print("allow_readonly_paths 1: true")
+}
+allow_readonly_paths(p_oci, i_oci) {
+    print("allow_readonly_paths 2: start")
+
+    not p_oci.Linux.ReadonlyPaths
+    not i_oci.Linux.ReadonlyPaths
+
+    print("allow_readonly_paths 2: true")
+}
+
+# All the policy readonly paths must be either:
+# - Present in the input readonly paths, or
+# - Present in the input masked paths.
+# Input is allowed to have more readonly paths than the policy.
+allow_readonly_paths_array(p_array, i_array, masked_paths) {
+    every p_elem in p_array {
+        allow_readonly_path(p_elem, i_array, masked_paths)
+    }
+}
+
+allow_readonly_path(p_elem, i_array, masked_paths) {
+    print("allow_readonly_path 1: p_elem =", p_elem)
+
+    some i_elem in i_array
+    p_elem == i_elem
+
+    print("allow_readonly_path 1: true")
+}
+allow_readonly_path(p_elem, i_array, masked_paths) {
+    print("allow_readonly_path 2: p_elem =", p_elem)
+
+    some i_masked in masked_paths
+    p_elem == i_masked
+
+    print("allow_readonly_path 2: true")
+}
+
+# Check the consistency of the input "io.katacontainers.pkg.oci.bundle_path"
+# and io.kubernetes.cri.sandbox-id" values with other fields.
+allow_by_bundle_or_sandbox_id(p_oci, i_oci, p_storages, i_storages) {
+    print("allow_by_bundle_or_sandbox_id: start")
+
+    bundle_path := i_oci.Annotations["io.katacontainers.pkg.oci.bundle_path"]
+    bundle_id := replace(bundle_path, "/run/containerd/io.containerd.runtime.v2.task/k8s.io/", "")
+
+    key := "io.kubernetes.cri.sandbox-id"
+
+    p_regex := p_oci.Annotations[key]
+    sandbox_id := i_oci.Annotations[key]
+
+    print("allow_by_bundle_or_sandbox_id: sandbox_id =", sandbox_id, "regex =", p_regex)
+    regex.match(p_regex, sandbox_id)
+
+    allow_root_path(p_oci, i_oci, bundle_id)
+
+    every i_mount in input.OCI.Mounts {
+        allow_mount(p_oci, i_mount, bundle_id, sandbox_id)
+    }
+
+    # TODO: enable allow_storages() after fixing https://github.com/kata-containers/kata-containers/issues/8833
+    # allow_storages(p_storages, i_storages, bundle_id, sandbox_id)
+
+    print("allow_by_bundle_or_sandbox_id: true")
+}
+
+allow_process(p_oci, i_oci, s_name) {
+    p_process := p_oci.Process
+    i_process := i_oci.Process
+
+    print("allow_process: i terminal =", i_process.Terminal, "p terminal =", p_process.Terminal)
+    p_process.Terminal == i_process.Terminal
+
+    print("allow_process: i cwd =", i_process.Cwd, "i cwd =", p_process.Cwd)
+    p_process.Cwd == i_process.Cwd
+
+    print("allow_process: i noNewPrivileges =", i_process.NoNewPrivileges, "p noNewPrivileges =", p_process.NoNewPrivileges)
+    p_process.NoNewPrivileges == i_process.NoNewPrivileges
+
+    allow_caps(p_process.Capabilities, i_process.Capabilities)
+    allow_user(p_process, i_process)
+    allow_args(p_process, i_process, s_name)
+    allow_env(p_process, i_process, s_name)
+
+    print("allow_process: true")
+}
+
+allow_user(p_process, i_process) {
+    p_user := p_process.User
+    i_user := i_process.User
+
+    # TODO: track down the reason for mcr.microsoft.com/oss/bitnami/redis:6.0.8 being
+    #       executed with uid = 0 despite having "User": "1001" in its container image
+    #       config.
+    #print("allow_user: input uid =", i_user.UID, "policy uid =", p_user.UID)
+    #p_user.UID == i_user.UID
+
+    # TODO: track down the reason for registry.k8s.io/pause:3.9 being
+    #       executed with gid = 0 despite having "65535:65535" in its container image
+    #       config.
+    #print("allow_user: input gid =", i_user.GID, "policy gid =", p_user.GID)
+    #p_user.GID == i_user.GID
+
+    # TODO: compare the additionalGids field too after computing its value
+    # based on /etc/passwd and /etc/group from the container image.
+}
+
+allow_args(p_process, i_process, s_name) {
+    print("allow_args 1: no args")
+
+    not p_process.Args
+    not i_process.Args
+
+    print("allow_args 1: true")
+}
+allow_args(p_process, i_process, s_name) {
+    print("allow_args 2: policy args =", p_process.Args)
+    print("allow_args 2: input args =", i_process.Args)
+
+    count(p_process.Args) == count(i_process.Args)
+
+    every i, i_arg in i_process.Args {
+        allow_arg(i, i_arg, p_process, s_name)
+    }
+
+    print("allow_args 2: true")
+}
+allow_arg(i, i_arg, p_process, s_name) {
+    p_arg := p_process.Args[i]
+    print("allow_arg 1: i =", i, "i_arg =", i_arg, "p_arg =", p_arg)
+
+    p_arg2 := replace(p_arg, "$$", "$")
+    p_arg2 == i_arg
+
+    print("allow_arg 1: true")
+}
+allow_arg(i, i_arg, p_process, s_name) {
+    p_arg := p_process.Args[i]
+    print("allow_arg 2: i =", i, "i_arg =", i_arg, "p_arg =", p_arg)
+
+    # TODO: can $(node-name) be handled better?
+    contains(p_arg, "$(node-name)")
+
+    print("allow_arg 2: true")
+}
+allow_arg(i, i_arg, p_process, s_name) {
+    p_arg := p_process.Args[i]
+    print("allow_arg 3: i =", i, "i_arg =", i_arg, "p_arg =", p_arg)
+
+    p_arg2 := replace(p_arg, "$$", "$")
+    p_arg3 := replace(p_arg2, "$(sandbox-name)", s_name)
+    print("allow_arg 3: p_arg3 =", p_arg3)
+    p_arg3 == i_arg
+
+    print("allow_arg 3: true")
+}
+
+# OCI process.Env field
+allow_env(p_process, i_process, s_name) {
+    print("allow_env: p env =", p_process.Env)
+    print("allow_env: i env =", i_process.Env)
+
+    every i_var in i_process.Env {
+        print("allow_env: i_var =", i_var)
+        allow_var(p_process, i_process, i_var, s_name)
+    }
+
+    print("allow_env: true")
+}
+
+# Allow input env variables that are present in the policy data too.
+allow_var(p_process, i_process, i_var, s_name) {
+    some p_var in p_process.Env
+    p_var == i_var
+    print("allow_var 1: true")
+}
+
+# Match input with one of the policy variables, after substituting $(sandbox-name).
+allow_var(p_process, i_process, i_var, s_name) {
+    some p_var in p_process.Env
+    p_var2 := replace(p_var, "$(sandbox-name)", s_name)
+
+    print("allow_var 2: p_var2 =", p_var2)
+    p_var2 == i_var
+
+    print("allow_var 2: true")
+}
+
+# Allow input env variables that match with a request_defaults regex.
+allow_var(p_process, i_process, i_var, s_name) {
+    some p_regex1 in policy_data.request_defaults.CreateContainerRequest.allow_env_regex
+    p_regex2 := replace(p_regex1, "$(ipv4_a)", policy_data.common.ipv4_a)
+    p_regex3 := replace(p_regex2, "$(ip_p)", policy_data.common.ip_p)
+    p_regex4 := replace(p_regex3, "$(svc_name)", policy_data.common.svc_name)
+    p_regex5 := replace(p_regex4, "$(dns_label)", policy_data.common.dns_label)
+
+    print("allow_var 3: p_regex5 =", p_regex5)
+    regex.match(p_regex5, i_var)
+
+    print("allow_var 3: true")
+}
+
+# Allow fieldRef "fieldPath: status.podIP" values.
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+    is_ip(name_value[1])
+
+    some p_var in p_process.Env
+    allow_pod_ip_var(name_value[0], p_var)
+
+    print("allow_var 4: true")
+}
+
+# Allow common fieldRef variables.
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+
+    some p_var in p_process.Env
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == name_value[0]
+
+    # TODO: should these be handled in a different way?
+    always_allowed := ["$(host-name)", "$(node-name)", "$(pod-uid)"]
+    some allowed in always_allowed
+    contains(p_name_value[1], allowed)
+
+    print("allow_var 5: true")
+}
+
+# Allow fieldRef "fieldPath: status.hostIP" values.
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+    is_ip(name_value[1])
+
+    some p_var in p_process.Env
+    allow_host_ip_var(name_value[0], p_var)
+
+    print("allow_var 6: true")
+}
+
+# Allow resourceFieldRef values (e.g., "limits.cpu").
+allow_var(p_process, i_process, i_var, s_name) {
+    name_value := split(i_var, "=")
+    count(name_value) == 2
+
+    some p_var in p_process.Env
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == name_value[0]
+
+    # TODO: should these be handled in a different way?
+    always_allowed = ["$(resource-field)", "$(todo-annotation)"]
+    some allowed in always_allowed
+    contains(p_name_value[1], allowed)
+
+    print("allow_var 7: true")
+}
+
+allow_pod_ip_var(var_name, p_var) {
+    print("allow_pod_ip_var: var_name =", var_name, "p_var =", p_var)
+
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == var_name
+    p_name_value[1] == "$(pod-ip)"
+
+    print("allow_pod_ip_var: true")
+}
+
+allow_host_ip_var(var_name, p_var) {
+    print("allow_host_ip_var: var_name =", var_name, "p_var =", p_var)
+
+    p_name_value := split(p_var, "=")
+    count(p_name_value) == 2
+
+    p_name_value[0] == var_name
+    p_name_value[1] == "$(host-ip)"
+
+    print("allow_host_ip_var: true")
+}
+
+is_ip(value) {
+    bytes = split(value, ".")
+    count(bytes) == 4
+
+    is_ip_first_byte(bytes[0])
+    is_ip_other_byte(bytes[1])
+    is_ip_other_byte(bytes[2])
+    is_ip_other_byte(bytes[3])
+}
+is_ip_first_byte(component) {
+    number = to_number(component)
+    number >= 1
+    number <= 255
+}
+is_ip_other_byte(component) {
+    number = to_number(component)
+    number >= 0
+    number <= 255
+}
+
+# OCI root.Path
+allow_root_path(p_oci, i_oci, bundle_id) {
+    i_path := i_oci.Root.Path
+    p_path1 := p_oci.Root.Path
+    print("allow_root_path: i_path =", i_path, "p_path1 =", p_path1)
+
+    p_path2 := replace(p_path1, "$(cpath)", policy_data.common.cpath)
+    print("allow_root_path: p_path2 =", p_path2)
+
+    p_path3 := replace(p_path2, "$(bundle-id)", bundle_id)
+    print("allow_root_path: p_path3 =", p_path3)
+
+    p_path3 == i_path
+
+    print("allow_root_path: true")
+}
+
+# device mounts
+allow_mount(p_oci, i_mount, bundle_id, sandbox_id) {
+    print("allow_mount: i_mount =", i_mount)
+
+    some p_mount in p_oci.Mounts
+    print("allow_mount: p_mount =", p_mount)
+    check_mount(p_mount, i_mount, bundle_id, sandbox_id)
+
+    # TODO: are there any other required policy checks for mounts - e.g.,
+    #       multiple mounts with same source or destination?
+
+    print("allow_mount: true")
+}
+
+check_mount(p_mount, i_mount, bundle_id, sandbox_id) {
+    p_mount == i_mount
+    print("check_mount 1: true")
+}
+check_mount(p_mount, i_mount, bundle_id, sandbox_id) {
+    p_mount.destination == i_mount.destination
+    p_mount.type_ == i_mount.type_
+    p_mount.options == i_mount.options
+
+    mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id)
+
+    print("check_mount 2: true")
+}
+
+mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id) {
+    regex1 := p_mount.source
+    regex2 := replace(regex1, "$(sfprefix)", policy_data.common.sfprefix)
+    regex3 := replace(regex2, "$(cpath)", policy_data.common.cpath)
+    regex4 := replace(regex3, "$(bundle-id)", bundle_id)
+
+    print("mount_source_allows 1: regex4 =", regex4)
+    regex.match(regex4, i_mount.source)
+
+    print("mount_source_allows 1: true")
+}
+mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id) {
+    regex1 := p_mount.source
+    regex2 := replace(regex1, "$(sfprefix)", policy_data.common.sfprefix)
+    regex3 := replace(regex2, "$(cpath)", policy_data.common.cpath)
+    regex4 := replace(regex3, "$(sandbox-id)", sandbox_id)
+
+    print("mount_source_allows 2: regex4 =", regex4)
+    regex.match(regex4, i_mount.source)
+
+    print("mount_source_allows 2: true")
+}
+
+######################################################################
+# Storages
+
+allow_storages(p_storages, i_storages, bundle_id, sandbox_id) {
+    p_count := count(p_storages)
+    i_count := count(i_storages)
+    print("allow_storages: p_count =", p_count, "i_count =", i_count)
+
+    p_count == i_count
+
+    # Get the container image layer IDs and verity root hashes, from the "overlayfs" storage.
+    some overlay_storage in p_storages
+    overlay_storage.driver == "overlayfs"
+    print("allow_storages: overlay_storage =", overlay_storage)
+    count(overlay_storage.options) == 2
+
+    layer_ids := split(overlay_storage.options[0], ":")
+    print("allow_storages: layer_ids =", layer_ids)
+
+    root_hashes := split(overlay_storage.options[1], ":")
+    print("allow_storages: root_hashes =", root_hashes)
+
+    every i_storage in i_storages {
+        allow_storage(p_storages, i_storage, bundle_id, sandbox_id, layer_ids, root_hashes)
+    }
+
+    print("allow_storages: true")
+}
+
+allow_storage(p_storages, i_storage, bundle_id, sandbox_id, layer_ids, root_hashes) {
+    some p_storage in p_storages
+
+    print("allow_storage: p_storage =", p_storage)
+    print("allow_storage: i_storage =", i_storage)
+
+    p_storage.driver           == i_storage.driver
+    p_storage.driver_options   == i_storage.driver_options
+    p_storage.fs_group         == i_storage.fs_group
+
+    allow_storage_options(p_storage, i_storage, layer_ids, root_hashes)
+    allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids)
+
+    # TODO: validate the source field too.
+
+    print("allow_storage: true")
+}
+
+allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) {
+    print("allow_storage_options 1: start")
+
+    p_storage.driver != "blk"
+    p_storage.driver != "overlayfs"
+    p_storage.options == i_storage.options
+
+    print("allow_storage_options 1: true")
+}
+allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) {
+    print("allow_storage_options 2: start")
+
+    p_storage.driver == "overlayfs"
+    count(p_storage.options) == 2
+
+    policy_ids := split(p_storage.options[0], ":")
+    print("allow_storage_options 2: policy_ids =", policy_ids)
+    policy_ids == layer_ids
+
+    policy_hashes := split(p_storage.options[1], ":")
+    print("allow_storage_options 2: policy_hashes =", policy_hashes)
+
+    p_count := count(policy_ids)
+    print("allow_storage_options 2: p_count =", p_count)
+    p_count >= 1
+    p_count == count(policy_hashes)
+
+    i_count := count(i_storage.options)
+    print("allow_storage_options 2: i_count =", i_count)
+    i_count == p_count + 3
+
+    print("allow_storage_options 2: i_storage.options[0] =", i_storage.options[0])
+    i_storage.options[0] == "io.katacontainers.fs-opt.layer-src-prefix=/var/lib/containerd/io.containerd.snapshotter.v1.tardev/layers"
+
+    print("allow_storage_options 2: i_storage.options[i_count - 2] =", i_storage.options[i_count - 2])
+    i_storage.options[i_count - 2] == "io.katacontainers.fs-opt.overlay-rw"
+
+    lowerdir := concat("=", ["lowerdir", p_storage.options[0]])
+    print("allow_storage_options 2: lowerdir =", lowerdir)
+
+    i_storage.options[i_count - 1] == lowerdir
+    print("allow_storage_options 2: i_storage.options[i_count - 1] =", i_storage.options[i_count - 1])
+
+    every i, policy_id in policy_ids {
+        allow_overlay_layer(policy_id, policy_hashes[i], i_storage.options[i + 1])
+    }
+
+    print("allow_storage_options 2: true")
+}
+allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) {
+    print("allow_storage_options 3: start")
+
+    p_storage.driver == "blk"
+    count(p_storage.options) == 1
+
+    startswith(p_storage.options[0], "$(hash")
+    hash_suffix := trim_left(p_storage.options[0], "$(hash")
+
+    endswith(hash_suffix, ")")
+    hash_index := trim_right(hash_suffix, ")")
+    i := to_number(hash_index)
+    print("allow_storage_options 3: i =", i)
+
+    hash_option := concat("=", ["io.katacontainers.fs-opt.root-hash", root_hashes[i]])
+    print("allow_storage_options 3: hash_option =", hash_option)
+
+    count(i_storage.options) == 4
+    i_storage.options[0] == "ro"
+    i_storage.options[1] == "io.katacontainers.fs-opt.block_device=file"
+    i_storage.options[2] == "io.katacontainers.fs-opt.is-layer"
+    i_storage.options[3] == hash_option
+
+    print("allow_storage_options 3: true")
+}
+
+allow_overlay_layer(policy_id, policy_hash, i_option) {
+    print("allow_overlay_layer: policy_id =", policy_id, "policy_hash =", policy_hash)
+    print("allow_overlay_layer: i_option =", i_option)
+
+    startswith(i_option, "io.katacontainers.fs-opt.layer=")
+    i_value := replace(i_option, "io.katacontainers.fs-opt.layer=", "")
+    i_value_decoded := base64.decode(i_value)
+    print("allow_overlay_layer: i_value_decoded =", i_value_decoded)
+
+    policy_suffix := concat("=", ["tar,ro,io.katacontainers.fs-opt.block_device=file,io.katacontainers.fs-opt.is-layer,io.katacontainers.fs-opt.root-hash", policy_hash])
+    p_value := concat(",", [policy_id, policy_suffix])
+    print("allow_overlay_layer: p_value =", p_value)
+
+    p_value == i_value_decoded
+
+    print("allow_overlay_layer: true")
+}
+
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "tar"
+
+    startswith(p_storage.mount_point, "$(layer")
+    mount_suffix := trim_left(p_storage.mount_point, "$(layer")
+
+    endswith(mount_suffix, ")")
+    layer_index := trim_right(mount_suffix, ")")
+    i := to_number(layer_index)
+    print("allow_mount_point 1: i =", i)
+
+    layer_id := layer_ids[i]
+    print("allow_mount_point 1: layer_id =", layer_id)
+
+    p_mount := concat("/", ["/run/kata-containers/sandbox/layers", layer_id])
+    print("allow_mount_point 1: p_mount =", p_mount)
+
+    p_mount == i_storage.mount_point
+
+    print("allow_mount_point 1: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "fuse3.kata-overlay"
+
+    mount1 := replace(p_storage.mount_point, "$(cpath)", policy_data.common.cpath)
+    mount2 := replace(mount1, "$(bundle-id)", bundle_id)
+    print("allow_mount_point 2: mount2 =", mount2)
+
+    mount2 == i_storage.mount_point
+
+    print("allow_mount_point 2: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "local"
+
+    mount1 := p_storage.mount_point
+    print("allow_mount_point 3: mount1 =", mount1)
+
+    mount2 := replace(mount1, "$(cpath)", policy_data.common.cpath)
+    print("allow_mount_point 3: mount2 =", mount2)
+
+    mount3 := replace(mount2, "$(sandbox-id)", sandbox_id)
+    print("allow_mount_point 3: mount3 =", mount3)
+
+    regex.match(mount3, i_storage.mount_point)
+
+    print("allow_mount_point 3: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "bind"
+
+    mount1 := p_storage.mount_point
+    print("allow_mount_point 4: mount1 =", mount1)
+
+    mount2 := replace(mount1, "$(cpath)", policy_data.common.cpath)
+    print("allow_mount_point 4: mount2 =", mount2)
+
+    mount3 := replace(mount2, "$(bundle-id)", bundle_id)
+    print("allow_mount_point 4: mount3 =", mount3)
+
+    regex.match(mount3, i_storage.mount_point)
+
+    print("allow_mount_point 4: true")
+}
+allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) {
+    p_storage.fstype == "tmpfs"
+
+    mount1 := p_storage.mount_point
+    print("allow_mount_point 5: mount1 =", mount1)
+
+    regex.match(mount1, i_storage.mount_point)
+
+    print("allow_mount_point 5: true")
+}
+
+# process.Capabilities
+allow_caps(p_caps, i_caps) {
+    print("allow_caps: policy Ambient =", p_caps.Ambient)
+    print("allow_caps: input Ambient =", i_caps.Ambient)
+    match_caps(p_caps.Ambient, i_caps.Ambient)
+
+    print("allow_caps: policy Bounding =", p_caps.Bounding)
+    print("allow_caps: input Bounding =", i_caps.Bounding)
+    match_caps(p_caps.Bounding, i_caps.Bounding)
+
+    print("allow_caps: policy Effective =", p_caps.Effective)
+    print("allow_caps: input Effective =", i_caps.Effective)
+    match_caps(p_caps.Effective, i_caps.Effective)
+
+    print("allow_caps: policy Inheritable =", p_caps.Inheritable)
+    print("allow_caps: input Inheritable =", i_caps.Inheritable)
+    match_caps(p_caps.Inheritable, i_caps.Inheritable)
+
+    print("allow_caps: policy Permitted =", p_caps.Permitted)
+    print("allow_caps: input Permitted =", i_caps.Permitted)
+    match_caps(p_caps.Permitted, i_caps.Permitted)
+}
+
+match_caps(p_caps, i_caps) {
+    print("match_caps 1: start")
+
+    p_caps == i_caps
+
+    print("match_caps 1: true")
+}
+match_caps(p_caps, i_caps) {
+    print("match_caps 2: start")
+
+    count(p_caps) == 1
+    p_caps[0] == "$(default_caps)"
+
+    print("match_caps 2: default_caps =", policy_data.common.default_caps)
+    policy_data.common.default_caps == i_caps
+
+    print("match_caps 2: true")
+}
+match_caps(p_caps, i_caps) {
+    print("match_caps 3: start")
+
+    count(p_caps) == 1
+    p_caps[0] == "$(privileged_caps)"
+
+    print("match_caps 3: privileged_caps =", policy_data.common.privileged_caps)
+    policy_data.common.privileged_caps == i_caps
+
+    print("match_caps 3: true")
+}
+
+######################################################################
+CopyFileRequest {
+    print("CopyFileRequest: input.path =", input.path)
+
+    some regex1 in policy_data.request_defaults.CopyFileRequest
+    regex2 := replace(regex1, "$(cpath)", policy_data.common.cpath)
+    regex.match(regex2, input.path)
+
+    print("CopyFileRequest: true")
+}
+
+CreateSandboxRequest {
+    i_pidns := input.sandbox_pidns
+    print("CreateSandboxRequest: i_pidns =", i_pidns)
+    i_pidns == false
+}
+
+ExecProcessRequest {
+    print("ExecProcessRequest 1: input =", input)
+
+    i_command = concat(" ", input.process.Args)
+    print("ExecProcessRequest 1: i_command =", i_command)
+
+    some p_command in policy_data.request_defaults.ExecProcessRequest.commands
+    print("ExecProcessRequest 1: p_command =", p_command)
+    p_command == i_command
+
+    print("ExecProcessRequest 1: true")
+}
+ExecProcessRequest {
+    print("ExecProcessRequest 2: input =", input)
+
+    # TODO: match input container ID with its corresponding container.exec_commands.
+    i_command = concat(" ", input.process.Args)
+    print("ExecProcessRequest 3: i_command =", i_command)
+
+    some container in policy_data.containers
+    some p_command in container.exec_commands
+    print("ExecProcessRequest 2: p_command =", p_command)
+
+    # TODO: should other input data fields be validated as well?
+    p_command == i_command
+
+    print("ExecProcessRequest 2: true")
+}
+ExecProcessRequest {
+    print("ExecProcessRequest 3: input =", input)
+
+    i_command = concat(" ", input.process.Args)
+    print("ExecProcessRequest 3: i_command =", i_command)
+
+    some p_regex in policy_data.request_defaults.ExecProcessRequest.regex
+    print("ExecProcessRequest 3: p_regex =", p_regex)
+
+    regex.match(p_regex, i_command)
+
+    print("ExecProcessRequest 3: true")
+}
+
+CloseStdinRequest {
+    policy_data.request_defaults.CloseStdinRequest == true
+}
+
+ReadStreamRequest {
+    policy_data.request_defaults.ReadStreamRequest == true
+}
+
+WriteStreamRequest {
+    policy_data.request_defaults.WriteStreamRequest == true
+}
+
+policy_data := {
+  "containers": [
+    {
+      "OCI": {
+        "Version": "1.1.0",
+        "Process": {
+          "Terminal": false,
+          "User": {
+            "UID": 65535,
+            "GID": 65535,
+            "AdditionalGids": [],
+            "Username": ""
+          },
+          "Args": [
+            "/pause"
+          ],
+          "Env": [
+            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+          ],
+          "Cwd": "/",
+          "Capabilities": {
+            "Ambient": [],
+            "Bounding": [
+              "$(default_caps)"
+            ],
+            "Effective": [
+              "$(default_caps)"
+            ],
+            "Inheritable": [],
+            "Permitted": [
+              "$(default_caps)"
+            ]
+          },
+          "NoNewPrivileges": true
+        },
+        "Root": {
+          "Path": "$(cpath)/$(bundle-id)/rootfs",
+          "Readonly": true
+        },
+        "Mounts": [
+          {
+            "destination": "/proc",
+            "source": "proc",
+            "type_": "proc",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/dev",
+            "source": "tmpfs",
+            "type_": "tmpfs",
+            "options": [
+              "nosuid",
+              "strictatime",
+              "mode=755",
+              "size=65536k"
+            ]
+          },
+          {
+            "destination": "/dev/pts",
+            "source": "devpts",
+            "type_": "devpts",
+            "options": [
+              "nosuid",
+              "noexec",
+              "newinstance",
+              "ptmxmode=0666",
+              "mode=0620",
+              "gid=5"
+            ]
+          },
+          {
+            "destination": "/dev/shm",
+            "source": "/run/kata-containers/sandbox/shm",
+            "type_": "bind",
+            "options": [
+              "rbind"
+            ]
+          },
+          {
+            "destination": "/dev/mqueue",
+            "source": "mqueue",
+            "type_": "mqueue",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/sys",
+            "source": "sysfs",
+            "type_": "sysfs",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/etc/resolv.conf",
+            "source": "$(sfprefix)resolv.conf$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "ro",
+              "nosuid",
+              "nodev",
+              "noexec"
+            ]
+          }
+        ],
+        "Annotations": {
+          "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/$(bundle-id)",
+          "io.katacontainers.pkg.oci.container_type": "pod_sandbox",
+          "io.kubernetes.cri.container-type": "sandbox",
+          "io.kubernetes.cri.sandbox-id": "^[a-z0-9]{64}$",
+          "io.kubernetes.cri.sandbox-log-directory": "^/var/log/pods/$(sandbox-namespace)_$(sandbox-name)_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
+          "io.kubernetes.cri.sandbox-namespace": "kata-containers-k8s-tests",
+          "nerdctl/network-namespace": "^/var/run/netns/cni-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
+        },
+        "Linux": {
+          "Namespaces": [
+            {
+              "Type": "ipc",
+              "Path": ""
+            },
+            {
+              "Type": "uts",
+              "Path": ""
+            },
+            {
+              "Type": "mount",
+              "Path": ""
+            }
+          ],
+          "MaskedPaths": [
+            "/proc/acpi",
+            "/proc/asound",
+            "/proc/kcore",
+            "/proc/keys",
+            "/proc/latency_stats",
+            "/proc/timer_list",
+            "/proc/timer_stats",
+            "/proc/sched_debug",
+            "/sys/firmware",
+            "/proc/scsi"
+          ],
+          "ReadonlyPaths": [
+            "/proc/bus",
+            "/proc/fs",
+            "/proc/irq",
+            "/proc/sys",
+            "/proc/sysrq-trigger"
+          ]
+        }
+      },
+      "storages": [
+        {
+          "driver": "blk",
+          "driver_options": [],
+          "source": "",
+          "fstype": "tar",
+          "options": [
+            "$(hash0)"
+          ],
+          "mount_point": "$(layer0)",
+          "fs_group": null
+        },
+        {
+          "driver": "overlayfs",
+          "driver_options": [],
+          "source": "",
+          "fstype": "fuse3.kata-overlay",
+          "options": [
+            "5a5aad80055ff20012a50dc25f8df7a29924474324d65f7d5306ee8ee27ff71d",
+            "817250f1a3e336da76f5bd3fa784e1b26d959b9c131876815ba2604048b70c18"
+          ],
+          "mount_point": "$(cpath)/$(bundle-id)/rootfs",
+          "fs_group": null
+        }
+      ],
+      "sandbox_pidns": false,
+      "exec_commands": []
+    },
+    {
+      "OCI": {
+        "Version": "1.1.0",
+        "Process": {
+          "Terminal": false,
+          "User": {
+            "UID": 0,
+            "GID": 0,
+            "AdditionalGids": [],
+            "Username": ""
+          },
+          "Args": [
+            "nginx",
+            "-g",
+            "daemon off;"
+          ],
+          "Env": [
+            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+            "NGINX_VERSION=1.15.12",
+            "HOSTNAME=$(host-name)"
+          ],
+          "Cwd": "/",
+          "Capabilities": {
+            "Ambient": [],
+            "Bounding": [
+              "$(default_caps)"
+            ],
+            "Effective": [
+              "$(default_caps)"
+            ],
+            "Inheritable": [],
+            "Permitted": [
+              "$(default_caps)"
+            ]
+          },
+          "NoNewPrivileges": false
+        },
+        "Root": {
+          "Path": "$(cpath)/$(bundle-id)/rootfs",
+          "Readonly": false
+        },
+        "Mounts": [
+          {
+            "destination": "/proc",
+            "source": "proc",
+            "type_": "proc",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/dev",
+            "source": "tmpfs",
+            "type_": "tmpfs",
+            "options": [
+              "nosuid",
+              "strictatime",
+              "mode=755",
+              "size=65536k"
+            ]
+          },
+          {
+            "destination": "/dev/pts",
+            "source": "devpts",
+            "type_": "devpts",
+            "options": [
+              "nosuid",
+              "noexec",
+              "newinstance",
+              "ptmxmode=0666",
+              "mode=0620",
+              "gid=5"
+            ]
+          },
+          {
+            "destination": "/dev/shm",
+            "source": "/run/kata-containers/sandbox/shm",
+            "type_": "bind",
+            "options": [
+              "rbind"
+            ]
+          },
+          {
+            "destination": "/dev/mqueue",
+            "source": "mqueue",
+            "type_": "mqueue",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev"
+            ]
+          },
+          {
+            "destination": "/sys",
+            "source": "sysfs",
+            "type_": "sysfs",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/sys/fs/cgroup",
+            "source": "cgroup",
+            "type_": "cgroup",
+            "options": [
+              "nosuid",
+              "noexec",
+              "nodev",
+              "relatime",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/etc/hosts",
+            "source": "$(sfprefix)hosts$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "rw"
+            ]
+          },
+          {
+            "destination": "/dev/termination-log",
+            "source": "$(sfprefix)termination-log$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "rw"
+            ]
+          },
+          {
+            "destination": "/etc/hostname",
+            "source": "$(sfprefix)hostname$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "rw"
+            ]
+          },
+          {
+            "destination": "/etc/resolv.conf",
+            "source": "$(sfprefix)resolv.conf$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "rw"
+            ]
+          },
+          {
+            "destination": "/var/run/secrets/kubernetes.io/serviceaccount",
+            "source": "$(sfprefix)serviceaccount$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/var/run/secrets/azure/tokens",
+            "source": "$(sfprefix)tokens$",
+            "type_": "bind",
+            "options": [
+              "rbind",
+              "rprivate",
+              "ro"
+            ]
+          },
+          {
+            "destination": "/host/cache",
+            "source": "^$(cpath)/$(sandbox-id)/rootfs/local/host-empty-vol$",
+            "type_": "local",
+            "options": [
+              "rbind",
+              "rprivate",
+              "rw"
+            ]
+          }
+        ],
+        "Annotations": {
+          "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/$(bundle-id)",
+          "io.katacontainers.pkg.oci.container_type": "pod_container",
+          "io.kubernetes.cri.container-name": "nginxtest",
+          "io.kubernetes.cri.container-type": "container",
+          "io.kubernetes.cri.image-name": "quay.io/sjenning/nginx:1.15-alpine",
+          "io.kubernetes.cri.sandbox-id": "^[a-z0-9]{64}$",
+          "io.kubernetes.cri.sandbox-namespace": "kata-containers-k8s-tests"
+        },
+        "Linux": {
+          "Namespaces": [
+            {
+              "Type": "ipc",
+              "Path": ""
+            },
+            {
+              "Type": "uts",
+              "Path": ""
+            },
+            {
+              "Type": "mount",
+              "Path": ""
+            }
+          ],
+          "MaskedPaths": [
+            "/proc/acpi",
+            "/proc/kcore",
+            "/proc/keys",
+            "/proc/latency_stats",
+            "/proc/timer_list",
+            "/proc/timer_stats",
+            "/proc/sched_debug",
+            "/proc/scsi",
+            "/sys/firmware"
+          ],
+          "ReadonlyPaths": [
+            "/proc/asound",
+            "/proc/bus",
+            "/proc/fs",
+            "/proc/irq",
+            "/proc/sys",
+            "/proc/sysrq-trigger"
+          ]
+        }
+      },
+      "storages": [
+        {
+          "driver": "blk",
+          "driver_options": [],
+          "source": "",
+          "fstype": "tar",
+          "options": [
+            "$(hash0)"
+          ],
+          "mount_point": "$(layer0)",
+          "fs_group": null
+        },
+        {
+          "driver": "blk",
+          "driver_options": [],
+          "source": "",
+          "fstype": "tar",
+          "options": [
+            "$(hash1)"
+          ],
+          "mount_point": "$(layer1)",
+          "fs_group": null
+        },
+        {
+          "driver": "blk",
+          "driver_options": [],
+          "source": "",
+          "fstype": "tar",
+          "options": [
+            "$(hash2)"
+          ],
+          "mount_point": "$(layer2)",
+          "fs_group": null
+        },
+        {
+          "driver": "blk",
+          "driver_options": [],
+          "source": "",
+          "fstype": "tar",
+          "options": [
+            "$(hash3)"
+          ],
+          "mount_point": "$(layer3)",
+          "fs_group": null
+        },
+        {
+          "driver": "overlayfs",
+          "driver_options": [],
+          "source": "",
+          "fstype": "fuse3.kata-overlay",
+          "options": [
+            "524c870e3d999ee05dd1e2f1a8c2f36c517ad69234e37b223a020d8e8ab6b1cd:89f962330499abced6774003a72400193594cfc037ed75cc110fe0447bf15799:f0115a4f30fbf5902f6c38345d499cf6d388a71df3946be0b5a37302be29ca5e:5a28797aba5723bef07477ac9db6376701e13335241e2bb5c67df464dcce112d",
+            "7de469d18180ee313d8d7960b9e2f4edefcad5895bba6edf3fd6b16e57f45308:e926dd69c6c5a8604bdf1f8c92faab9672e308577d3f38cfd7895601f7c7d3e0:86fc0ee2db5cb5e7bf5e69d914e62600a01b376f7e20999de6e879328095778b:12055326c275405a192062137469888e1a498cd829a5c51bc0ddcc1880a10bc3"
+          ],
+          "mount_point": "$(cpath)/$(bundle-id)/rootfs",
+          "fs_group": null
+        },
+        {
+          "driver": "local",
+          "driver_options": [],
+          "source": "local",
+          "fstype": "local",
+          "options": [
+            "mode=0777"
+          ],
+          "mount_point": "^$(cpath)/$(sandbox-id)/local/host-empty-vol$",
+          "fs_group": null
+        }
+      ],
+      "sandbox_pidns": false,
+      "exec_commands": []
+    }
+  ],
+  "common": {
+    "cpath": "/run/kata-containers/shared/containers",
+    "sfprefix": "^$(cpath)/$(bundle-id)-[a-z0-9]{16}-",
+    "ipv4_a": "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])",
+    "ip_p": "[0-9]{1,5}",
+    "svc_name": "[A-Z_\\.\\-]+",
+    "dns_label": "[a-zA-Z0-9_\\.\\-]+",
+    "default_caps": [
+      "CAP_CHOWN",
+      "CAP_DAC_OVERRIDE",
+      "CAP_FSETID",
+      "CAP_FOWNER",
+      "CAP_MKNOD",
+      "CAP_NET_RAW",
+      "CAP_SETGID",
+      "CAP_SETUID",
+      "CAP_SETFCAP",
+      "CAP_SETPCAP",
+      "CAP_NET_BIND_SERVICE",
+      "CAP_SYS_CHROOT",
+      "CAP_KILL",
+      "CAP_AUDIT_WRITE"
+    ],
+    "privileged_caps": [
+      "CAP_CHOWN",
+      "CAP_DAC_OVERRIDE",
+      "CAP_DAC_READ_SEARCH",
+      "CAP_FOWNER",
+      "CAP_FSETID",
+      "CAP_KILL",
+      "CAP_SETGID",
+      "CAP_SETUID",
+      "CAP_SETPCAP",
+      "CAP_LINUX_IMMUTABLE",
+      "CAP_NET_BIND_SERVICE",
+      "CAP_NET_BROADCAST",
+      "CAP_NET_ADMIN",
+      "CAP_NET_RAW",
+      "CAP_IPC_LOCK",
+      "CAP_IPC_OWNER",
+      "CAP_SYS_MODULE",
+      "CAP_SYS_RAWIO",
+      "CAP_SYS_CHROOT",
+      "CAP_SYS_PTRACE",
+      "CAP_SYS_PACCT",
+      "CAP_SYS_ADMIN",
+      "CAP_SYS_BOOT",
+      "CAP_SYS_NICE",
+      "CAP_SYS_RESOURCE",
+      "CAP_SYS_TIME",
+      "CAP_SYS_TTY_CONFIG",
+      "CAP_MKNOD",
+      "CAP_LEASE",
+      "CAP_AUDIT_WRITE",
+      "CAP_AUDIT_CONTROL",
+      "CAP_SETFCAP",
+      "CAP_MAC_OVERRIDE",
+      "CAP_MAC_ADMIN",
+      "CAP_SYSLOG",
+      "CAP_WAKE_ALARM",
+      "CAP_BLOCK_SUSPEND",
+      "CAP_AUDIT_READ",
+      "CAP_PERFMON",
+      "CAP_BPF",
+      "CAP_CHECKPOINT_RESTORE"
+    ]
+  },
+  "request_defaults": {
+    "CreateContainerRequest": {
+      "allow_env_regex": [
+        "^HOSTNAME=$(dns_label)$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP=tcp://$(ipv4_a):$(ip_p)$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP_PROTO=tcp$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP_PORT=$(ip_p)$",
+        "^$(svc_name)_PORT_$(ip_p)_TCP_ADDR=$(ipv4_a)$",
+        "^$(svc_name)_SERVICE_HOST=$(ipv4_a)$",
+        "^$(svc_name)_SERVICE_PORT=$(ip_p)$",
+        "^$(svc_name)_SERVICE_PORT_$(dns_label)=$(ip_p)$",
+        "^$(svc_name)_PORT=tcp://$(ipv4_a):$(ip_p)$",
+        "^AZURE_CLIENT_ID=[A-Fa-f0-9-]+$",
+        "^AZURE_TENANT_ID=[A-Fa-f0-9-]+$",
+        "^AZURE_FEDERATED_TOKEN_FILE=/var/run/secrets/azure/tokens/azure-identity-token$",
+        "^AZURE_AUTHORITY_HOST=https://login\\.microsoftonline\\.com/$"
+      ]
+    },
+    "CopyFileRequest": [
+      "^$(cpath)/"
+    ],
+    "ExecProcessRequest": {
+      "commands": [],
+      "regex": []
+    },
+    "CloseStdinRequest": false,
+    "ReadStreamRequest": true,
+    "WriteStreamRequest": false
+  }
+}
diff --git a/tests/coco/data/large.rego b/tests/kata/data/large.rego
similarity index 100%
rename from tests/coco/data/large.rego
rename to tests/kata/data/large.rego
diff --git a/tests/kata/main.rs b/tests/kata/main.rs
new file mode 100644
index 00000000..c329725d
--- /dev/null
+++ b/tests/kata/main.rs
@@ -0,0 +1,115 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+use regorus::*;
+
+use std::path::Path;
+
+use anyhow::Result;
+use clap::Parser;
+use walkdir::WalkDir;
+
+fn run_kata_tests(tests_dir: &Path, generate: bool) -> Result<()> {
+    for entry in WalkDir::new(tests_dir)
+        .max_depth(1) // Do not recurse
+        .sort_by_file_name()
+        .into_iter()
+        .filter_map(|e| e.ok())
+    {
+        let path = entry.path();
+        if path == tests_dir || !path.is_dir() {
+            continue;
+        }
+
+        let policy_file = path.join("policy.rego");
+        let inputs_file = path.join("inputs.txt");
+        let outputs_file = path.join("output.json");
+
+        let mut engine = Engine::new();
+        engine.add_policy_from_file(&policy_file)?;
+        engine.set_gather_prints(true);
+        engine.set_strict_builtin_errors(false);
+
+        #[cfg(feature = "coverage")]
+        engine.set_enable_coverage(true);
+
+        // Keep a copy of the engine.
+        let engine_base = engine.clone();
+        let mut results = if generate {
+            vec![]
+        } else {
+            Value::from_json_str(&std::fs::read_to_string(&outputs_file)?)?
+                .as_array()?
+                .iter()
+                .cloned()
+                .rev()
+                .collect()
+        };
+
+        let inputs = std::fs::read_to_string(&inputs_file)?;
+        for (lineno, line) in inputs.split('\n').enumerate() {
+            let line = line.trim();
+            if line.is_empty() {
+                continue;
+            }
+            // Remove "ep":
+            let line = line.replace("\"ep\":", "");
+            // Remove trailing ,
+            let line = &line[0..line.len() - 1];
+
+            let request = Value::from_json_str(line)?;
+
+            let rule = format!("data.agent_policy.{}", request[0].as_string()?.as_ref());
+            let input = request[1].clone();
+
+            // Evaluate using engine.
+            engine.set_input(input.clone());
+            let r = engine.eval_rule(rule.clone())?;
+
+            // Evaluate using fresh engine.
+            let mut new_engine = engine_base.clone();
+            new_engine.set_input(input);
+            let r_new = new_engine.eval_rule(rule)?;
+
+            // Ensure that both evaluations produced the same result.
+            assert_eq!(r, r_new);
+
+            if generate {
+                results.push(r);
+            } else {
+                let expected = results.pop().unwrap();
+                assert_eq!(r, expected, "{lineno} failed in {}", inputs_file.display());
+            }
+        }
+
+        if generate {
+            std::fs::write(outputs_file, Value::from(results).to_json_str()?)?;
+        }
+        #[cfg(feature = "coverage")]
+        {
+            let report = engine.get_coverage_report()?;
+            println!("{}", report.to_colored_string()?);
+        }
+    }
+
+    println!("kata tests passed");
+    Ok(())
+}
+
+#[derive(clap::Parser)]
+#[command(author, version, about, long_about = None)]
+struct Cli {
+    /// Path to Kata test suite.
+    #[arg(long, short)]
+    #[clap(default_value = "tests/kata/data")]
+    test_dir: String,
+
+    /// Generate outputs instead of testing.
+    #[arg(long, short)]
+    #[clap(default_value = "false")]
+    generate: bool,
+}
+
+fn main() -> Result<()> {
+    let cli = Cli::parse();
+    run_kata_tests(&Path::new(&cli.test_dir), cli.generate)
+}
diff --git a/tests/lexer/mod.rs b/tests/lexer/mod.rs
index 7887f0a5..909709f9 100644
--- a/tests/lexer/mod.rs
+++ b/tests/lexer/mod.rs
@@ -226,7 +226,7 @@ fn invalid_line() -> Result<()> {
 
 #[test]
 fn file_more_than_64_kb_size() -> Result<()> {
-    let source = Source::from_file("tests/coco/data/large.rego")?;
+    let source = Source::from_file("tests/kata/data/large.rego")?;
     let mut lexer = Lexer::new(&source);
 
     let mut count = 0;