Repository: FairwindsOps/polaris
Pull request #806 (2022-07-18 14:09:18)

Bump k8s.io/client-go from 0.24.1 to 0.24.3
Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.24.1 to 0.24.3.
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/kubernetes/client-go/commit/ee1a5aaf793a9ace9c433f5fb26a19058ed5f37c"><code>ee1a5aa</code></a> Update dependencies to v0.24.3 tag</li>
<li>See full diff in <a href="https://github.com/kubernetes/client-go/compare/v0.24.1...v0.24.3">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=k8s.io/client-go&package-manager=go_modules&previous-version=0.24.1&new-version=0.24.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

fairwinds-insights[bot]: ## Fairwinds Insights CI Report
> [View the Full Report](https://insights.fairwinds.com/gh/FairwindsOps/polaris/dependabot%2Fgo_modules%2Fk8s.io%2Fclient-go-0.24.3)

:white_check_mark: No new Action Items detected!

Patch:
diff --git a/checks/automountServiceAccountToken.yaml b/checks/automountServiceAccountToken.yaml
new file mode 100644
index 00000000..69cc01c5
--- /dev/null
+++ b/checks/automountServiceAccountToken.yaml
@@ -0,0 +1,36 @@
+successMessage: The ServiceAccount will not be automounted
+failureMessage: The ServiceAccount will be automounted
+category: Security
+target: PodSpec
+schema:
+  '$schema': http://json-schema.org/draft-07/schema
+  type: object
+  required: ["serviceAccountName"]
+  properties:
+    serviceAccountName:
+      type: string
+    automountServiceAccountToken:
+      type: boolean
+      not:
+        const: true
+additionalSchemaStrings:
+  ServiceAccount: |
+    type: object
+    required:
+      - metadata
+      {{ if not (eq .Polaris.PodSpec.automountServiceAccountToken false) }}
+      - automountServiceAccountToken
+      {{ end }}
+    properties:
+      metadata:
+        type: object
+        required: ["name"]
+        properties:
+          name:
+            type: string
+            const: "{{ .Polaris.PodSpec.serviceAccountName }}"
+      {{ if not (eq .Polaris.PodSpec.automountServiceAccountToken false) }}
+      automountServiceAccountToken:
+        type: boolean
+        const: false
+      {{ end }}
diff --git a/checks/linuxHardening.yaml b/checks/linuxHardening.yaml
new file mode 100644
index 00000000..cbb64270
--- /dev/null
+++ b/checks/linuxHardening.yaml
@@ -0,0 +1,89 @@
+successMessage: One of AppArmor, Seccomp, SELinux, or dropping Linux Capabilities are used to restrict containers using unwanted privileges
+FailureMessage: Use one of AppArmor, Seccomp, SELinux, or dropping Linux Capabilities to restrict containers using unwanted privileges
+category: Security
+target: Container
+schemaString: |
+  '$schema': http://json-schema.org/draft-07/schema
+  definitions:
+    podOrContainerSeccompProfile:
+      type: object
+      {{ $podSeccompProfileType := .Polaris.PodSpec.securityContext.seccompProfile.type }}
+      required:
+      {{ if or (not $podSeccompProfileType) (eq $podSeccompProfileType "Unconfined") }}
+        - securityContext
+      {{ end }}
+      properties:
+        securityContext:
+          type: object
+          required:
+          {{ if or (not $podSeccompProfileType) (eq $podSeccompProfileType "Unconfined") }}
+            - seccompProfile
+          {{ end }}
+          properties:
+            seccompProfile:
+              type: object
+              required:
+              {{ if or (not $podSeccompProfileType) (eq $podSeccompProfileType "Unconfined") }}
+                - type
+              {{ end }}
+              properties:
+                type:
+                  type: string
+                  allOf:
+                    - not:
+                        const: "Unconfined"
+                    {{ if or (not $podSeccompProfileType) (eq $podSeccompProfileType "Unconfined") }}
+                    - minLength: 1
+                    {{ end }}
+    podOrContainerSELinuxOptions:
+      type: object
+      {{ $podSELinuxOptions := .Polaris.PodSpec.securityContext.seLinuxOptions }}
+      {{ if not $podSELinuxOptions }}
+      required: ["securityContext"]
+      properties:
+        securityContext:
+          type: object
+          required: ["seLinuxOptions"]
+          properties:
+            seLinuxOptions:
+              type: object
+              minProperties: 1
+      {{ end }}
+    containerDropCapabilities:
+      type: object
+      required: ["securityContext"]
+      properties:
+        securityContext:
+          type: object
+          required: ["capabilities"]
+          properties:
+            capabilities:
+              type: object
+              required: ["drop"]
+              properties:
+                drop:
+                  type: array
+                  minItems: 1
+                add:
+                  type: array
+                  items:
+                    type: string
+                    not:
+                      pattern: '^(?i)ALL$'
+  # End of definitions
+  {{/* Check for AppArmor which uses pod annotations. IF pod fields are missing,
+       require one of the other hardening measures. */}}
+  {{ $annotationName := (print "container.apparmor.security.beta.kubernetes.io/" .Polaris.Container.name) }}
+  {{/* Checking annotations before using index() avoids a nil panic when there are no annotations */}}
+  {{ $annotationExists := false }}
+  {{ if .Polaris.PodTemplate.metadata.annotations }}
+  {{ $annotationExists = index .Polaris "PodTemplate" "metadata" "annotations" $annotationName }}
+  {{ end }}
+  {{ if $annotationExists }}
+  type: object
+  {{ else }}
+  anyOf:
+  - $ref: "#/definitions/podOrContainerSeccompProfile"
+  - $ref: "#/definitions/podOrContainerSELinuxOptions"
+  - $ref: "#/definitions/containerDropCapabilities"
+  {{ end}}
diff --git a/checks/missingNetworkPolicy.yaml b/checks/missingNetworkPolicy.yaml
new file mode 100644
index 00000000..b349f87e
--- /dev/null
+++ b/checks/missingNetworkPolicy.yaml
@@ -0,0 +1,49 @@
+successMessage: A NetworkPolicy matches pod labels and contains egress and ingress rules
+failureMessage: A NetworkPolicy should match pod labels and contain applied egress and ingress rules
+category: Security
+target: PodTemplate
+schema:
+  '$schema': http://json-schema.org/draft-07/schema
+  type: object
+  properties:
+    metadata:
+      type: object
+      properties:
+        labels:
+          type: object
+          minProperties: 1
+additionalSchemaStrings:
+  networking.k8s.io/NetworkPolicy: |
+    type: object
+    properties:
+      spec:
+        type: object
+        required: ["podSelector", "egress", "ingress"]
+        properties:
+          podSelector:
+            type: object
+            required: ["matchLabels"]
+            properties:
+              matchLabels:
+                type: object
+                oneOf:
+                {{ range $key, $value := .Polaris.PodTemplate.metadata.labels }}
+                - properties:
+                    "{{ $key }}":
+                      type: string
+                      const: {{ $value }}
+                  required: ["{{ $key }}"]
+                {{ end }}
+          egress:
+            type: array
+            minItems: 1
+          ingress:
+            minItems: 1
+            type: array
+          policyTypes:
+            type: array
+            allOf:
+              - contains:
+                  pattern: '^(?i)Egress$'
+              - contains:
+                  pattern: '^(?i)Ingress$'
diff --git a/checks/sensitiveConfigmapContent.yaml b/checks/sensitiveConfigmapContent.yaml
new file mode 100644
index 00000000..48089159
--- /dev/null
+++ b/checks/sensitiveConfigmapContent.yaml
@@ -0,0 +1,43 @@
+successMessage: The ConfigMap does not contain potentially sensitive content in its keys and values
+failureMessage: Potentially sensitive content is detected in the ConfigMap keys or values
+category: Security
+target: /ConfigMap
+schemaString: |
+  '$schema': http://json-schema.org/draft-07/schema
+  type: object
+  required: ["metadata"]
+  properties:
+    metadata:
+      required: ["name"]
+      properties:
+        name:
+          type: string
+    data:
+      type: object
+      propertyNames:
+        '$comment': These ConfigMap keys will be disallowed.
+        allOf:
+          - not:
+              pattern: '(?i)^AWS_SECRET_ACCESS_KEY$'
+          - not:
+              pattern: '(?i)^GOOGLE_APPLICATION_CREDENTIALS$'
+          - not:
+              pattern: '(?i)^AZURE_.+KEY$'
+          - not:
+              pattern: '(?i)^OCI_CLI_KEY_CONTENT$'
+          - not:
+              pattern: '(?i)password'
+          - not:
+              pattern: '(?i)token'
+          - not:
+              pattern: '(?i)bearer'
+          - not:
+              pattern: '(?i)secret'
+            '$comment': This allows ConfigMap keys not excluded above.
+          - pattern: '(?i).*'
+      additionalProperties:
+        '$comment': These ConfigMap values will be disallowed.
+        allOf:
+        - not:
+            '$comment': THis matches variations like begin private key, begin rsa private key ...
+            pattern: '(?i)\s*-BEGIN\s+.*PRIVATE KEY-\s*'
diff --git a/checks/sensitiveContainerEnvVar.yaml b/checks/sensitiveContainerEnvVar.yaml
new file mode 100644
index 00000000..2a7ea903
--- /dev/null
+++ b/checks/sensitiveContainerEnvVar.yaml
@@ -0,0 +1,43 @@
+successMessage: The container does not set potentially sensitive environment variables
+failureMessage: The container sets potentially sensitive environment variables
+category: Security
+target: Container
+schemaString: |
+  '$schema': http://json-schema.org/draft-07/schema
+  type: object
+  properties:
+    env:
+      type: array
+      items:
+        type: object
+        required: ["name"]
+        properties:
+          name:
+            type: string
+            '$comment': These environment variable names will be disallowed.
+            allOf:
+              - not:
+                  pattern: '(?i)^AWS_SECRET_ACCESS_KEY$'
+              - not:
+                  pattern: '(?i)^GOOGLE_APPLICATION_CREDENTIALS$'
+              - not:
+                  pattern: '(?i)^AZURE_.+KEY$'
+              - not:
+                  pattern: '(?i)^OCI_CLI_KEY_CONTENT$'
+              - not:
+                  pattern: '(?i)password'
+              - not:
+                  pattern: '(?i)token'
+              - not:
+                  pattern: '(?i)bearer'
+              - not:
+                  pattern: '(?i)secret'
+                '$comment': This allows variable names not excluded above.
+              - pattern: '(?i).*'
+          value:
+            type: string
+            '$comment': These environment variable values will be disallowed.
+            allOf:
+            - not:
+                '$comment': THis matches variations like begin private key, begin rsa private key ...
+                pattern: '(?i)\s*-BEGIN\s+.*PRIVATE KEY-\s*'
diff --git a/docs/customization/custom-checks.md b/docs/customization/custom-checks.md
index 42aa940d..9d905d65 100644
--- a/docs/customization/custom-checks.md
+++ b/docs/customization/custom-checks.md
@@ -138,6 +138,7 @@ schema:
 * The object available via the go template is the full object, and not limited by `target`.
 * A check of `target: PodSpec` can directly access the pod specification via the go template variable `.Polaris.PodSpec`.
 * A check of `target: PodTemplate` can directly access the pod template via the go template variable `.Polaris.PodTemplate`.
+* A check of `target: Container` can directly access the container being checked via the go template variable `.Polaris.container`. The pod template and pod specification can also be accessed via the respective variables `.Polaris.PodTemplate` and `.Polaris.PodSpec`. Access to pod-level fields allows a container check to consult related fields from the pod, such as `securityContext`.
 
 You can also use the full [Go template syntax](https://golang.org/pkg/text/template/), though
 you may need to specify your schema as a string in order to use concepts like `range`. E.g.
diff --git a/examples/config-full.yaml b/examples/config-full.yaml
index 11613cdd..23c6c93e 100644
--- a/examples/config-full.yaml
+++ b/examples/config-full.yaml
@@ -12,8 +12,11 @@ checks:
   memoryRequestsMissing: warning
   memoryLimitsMissing: warning
   # security
+  automountServiceAccountToken: warning
   hostIPCSet: danger
   hostPIDSet: danger
+  linuxHardening: danger
+  missingNetworkPolicy: warning
   notReadOnlyRootFilesystem: warning
   privilegeEscalationAllowed: danger
   runAsRootAllowed: danger
@@ -22,6 +25,8 @@ checks:
   insecureCapabilities: warning
   hostNetworkSet: danger
   hostPortSet: warning
+  sensitiveContainerEnvVar: danger
+  sensitiveConfigmapContent: danger
   # custom
   resourceLimits: warning
   imageRegistry: danger
diff --git a/go.mod b/go.mod
index 23bee253..28ae0823 100644
--- a/go.mod
+++ b/go.mod
@@ -9,16 +9,16 @@ require (
 	github.com/gorilla/mux v1.8.0
 	github.com/qri-io/jsonschema v0.1.1
 	github.com/sirupsen/logrus v1.8.1
-	github.com/spf13/cobra v1.4.0
+	github.com/spf13/cobra v1.5.0
 	github.com/spf13/pflag v1.0.5
-	github.com/stretchr/testify v1.7.1
+	github.com/stretchr/testify v1.8.0
 	github.com/thoas/go-funk v0.9.2
 	golang.org/x/text v0.3.7 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1
 	k8s.io/api v0.24.3
 	k8s.io/apimachinery v0.24.3
-	k8s.io/client-go v0.24.1
+	k8s.io/client-go v0.24.3
 	sigs.k8s.io/controller-runtime v0.12.1
 	sigs.k8s.io/yaml v1.3.0
 )
diff --git a/go.sum b/go.sum
index 190309f7..68f76690 100644
--- a/go.sum
+++ b/go.sum
@@ -134,6 +134,7 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -539,8 +540,9 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
 github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
 github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
-github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
 github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
+github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
+github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -551,14 +553,16 @@ github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/thoas/go-funk v0.9.2 h1:oKlNYv0AY5nyf9g+/GhMgS/UO2ces0QRdPKwkhY3VCk=
 github.com/thoas/go-funk v0.9.2/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
@@ -1152,8 +1156,9 @@ k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg=
 k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
 k8s.io/apiserver v0.24.0/go.mod h1:WFx2yiOMawnogNToVvUYT9nn1jaIkMKj41ZYCVycsBA=
 k8s.io/client-go v0.24.0/go.mod h1:VFPQET+cAFpYxh6Bq6f4xyMY80G6jKKktU6G0m00VDw=
-k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E=
 k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8=
+k8s.io/client-go v0.24.3 h1:Nl1840+6p4JqkFWEW2LnMKU667BUxw03REfLAVhuKQY=
+k8s.io/client-go v0.24.3/go.mod h1:AAovolf5Z9bY1wIg2FZ8LPQlEdKHjLI7ZD4rw920BJw=
 k8s.io/code-generator v0.24.0/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w=
 k8s.io/component-base v0.24.0/go.mod h1:Dgazgon0i7KYUsS8krG8muGiMVtUZxG037l1MKyXgrA=
 k8s.io/component-base v0.24.1 h1:APv6W/YmfOWZfo+XJ1mZwep/f7g7Tpwvdbo9CQLDuts=
diff --git a/pkg/config/checks.go b/pkg/config/checks.go
index 48ce4f62..85387081 100644
--- a/pkg/config/checks.go
+++ b/pkg/config/checks.go
@@ -32,6 +32,7 @@ var (
 		"hostIPCSet",
 		"hostPIDSet",
 		"hostNetworkSet",
+		"automountServiceAccountToken",
 		// Container checks
 		"memoryLimitsMissing",
 		"memoryRequestsMissing",
@@ -49,11 +50,15 @@ var (
 		"dangerousCapabilities",
 		"insecureCapabilities",
 		"priorityClassNotSet",
+		"linuxHardening",
+		"sensitiveContainerEnvVar",
 		// Other checks
 		"tlsSettingsMissing",
 		"pdbDisruptionsIsZero",
 		"metadataAndNameMismatched",
 		"missingPodDisruptionBudget",
+		"missingNetworkPolicy",
+		"sensitiveConfigmapContent",
 	}
 )
 
diff --git a/pkg/kube/resource.go b/pkg/kube/resource.go
index cacde71f..65388ca1 100644
--- a/pkg/kube/resource.go
+++ b/pkg/kube/resource.go
@@ -54,7 +54,10 @@ func NewGenericResourceFromUnstructured(unst unstructured.Unstructured, podSpecM
 		return workload, err
 	}
 	workload.ObjectMeta = objMeta
-	workload.PodTemplate = GetPodTemplate(unst.UnstructuredContent())
+	workload.PodTemplate, err = GetPodTemplate(unst.UnstructuredContent())
+	if err != nil {
+		return workload, err
+	}
 
 	b, err := json.Marshal(&unst)
 	if err != nil {
@@ -248,11 +251,23 @@ func GetPodSpec(yaml map[string]interface{}) interface{} {
 // GetPodTemplate looks inside arbitrary YAML for a Pod template, containing
 // fields `spec.containers`.
 // For example, it returns the `spec.template` level of a Kubernetes Deployment yaml.
-func GetPodTemplate(yaml map[string]interface{}) interface{} {
+func GetPodTemplate(yaml map[string]interface{}) (podTemplate interface{}, err error) {
 	if yamlSpec, ok := yaml["spec"]; ok {
 		if yamlSpecMap, ok := yamlSpec.(map[string]interface{}); ok {
 			if _, ok := yamlSpecMap["containers"]; ok {
-				return yaml
+				// This is a hack around unstructured.SetNestedField using DeepCopy which does
+				// not support the type int, and panics.
+				// Related: https://github.com/kubernetes/kubernetes/issues/62769
+				podTemplateJSON, err := json.Marshal(yaml)
+				if err != nil {
+					return nil, err
+				}
+				podTemplateMap := make(map[string]interface{})
+				err = json.Unmarshal(podTemplateJSON, &podTemplateMap)
+				if err != nil {
+					return nil, err
+				}
+				return podTemplateMap, nil
 			}
 		}
 	}
@@ -261,5 +276,5 @@ func GetPodTemplate(yaml map[string]interface{}) interface{} {
 			return GetPodTemplate(childYaml.(map[string]interface{}))
 		}
 	}
-	return nil
+	return nil, nil
 }
diff --git a/pkg/kube/resources.go b/pkg/kube/resources.go
index d2276a64..2b1101ad 100644
--- a/pkg/kube/resources.go
+++ b/pkg/kube/resources.go
@@ -509,3 +509,17 @@ func SerializePod(pod *corev1.Pod) (map[string]interface{}, error) {
 	}
 	return podMap, nil
 }
+
+// SerializeContainer converts a typed Container into a map[string]interface{}
+func SerializeContainer(container *corev1.Container) (map[string]interface{}, error) {
+	containerJSON, err := json.Marshal(container)
+	if err != nil {
+		return nil, err
+	}
+	containerMap := make(map[string]interface{})
+	err = json.Unmarshal(containerJSON, &containerMap)
+	if err != nil {
+		return nil, err
+	}
+	return containerMap, nil
+}
diff --git a/pkg/validator/output.go b/pkg/validator/output.go
index 8b8a110c..bff4a474 100644
--- a/pkg/validator/output.go
+++ b/pkg/validator/output.go
@@ -16,6 +16,7 @@ package validator
 
 import (
 	"fmt"
+	"strings"
 	"time"
 
 	"github.com/fatih/color"
@@ -244,7 +245,7 @@ func (res ResultSet) GetPrettyOutput() string {
 			}
 		}
 		if color.NoColor {
-			status = status[2:] // remove emoji
+			status = strings.Fields(status)[1] // remove emoji
 		}
 		str += fmt.Sprintf("%s%s %s\n", indent, checkColor.Sprint(fillString(msg.ID, minIDLength-len(indent))), status)
 		str += fmt.Sprintf("%s    %s - %s\n", indent, msg.Category, msg.Message)
diff --git a/pkg/validator/schema.go b/pkg/validator/schema.go
index 3ed9c2fb..7f2b5815 100644
--- a/pkg/validator/schema.go
+++ b/pkg/validator/schema.go
@@ -84,7 +84,7 @@ func getTemplateInput(test schemaTestCase) (map[string]interface{}, error) {
 	if templateInput == nil {
 		return nil, nil
 	}
-	if test.Target == config.TargetPodSpec {
+	if test.Target == config.TargetPodSpec || test.Target == config.TargetContainer {
 		podSpecMap, err := kube.SerializePodSpec(test.Resource.PodSpec)
 		if err != nil {
 			return nil, err
@@ -93,12 +93,23 @@ func getTemplateInput(test schemaTestCase) (map[string]interface{}, error) {
 		if err != nil {
 			return nil, err
 		}
-		if podTemplateMap, ok := test.Resource.PodTemplate.(map[string]interface{}); ok {
+		podTemplateMap, ok := test.Resource.PodTemplate.(map[string]interface{})
+		if ok {
 			err := unstructured.SetNestedMap(templateInput, podTemplateMap, "Polaris", "PodTemplate")
 			if err != nil {
 				return nil, err
 			}
 		}
+		if test.Target == config.TargetContainer {
+			containerMap, err := kube.SerializeContainer(test.Container)
+			if err != nil {
+				return nil, err
+			}
+			err = unstructured.SetNestedMap(templateInput, containerMap, "Polaris", "Container")
+			if err != nil {
+				return nil, err
+			}
+		}
 	}
 	return templateInput, nil
 }


Test patch:
diff --git a/test/checks/automountServiceAccountToken/failure.all_true.yaml b/test/checks/automountServiceAccountToken/failure.all_true.yaml
new file mode 100644
index 00000000..5e4383c2
--- /dev/null
+++ b/test/checks/automountServiceAccountToken/failure.all_true.yaml
@@ -0,0 +1,19 @@
+# This fails because automounting is true for both the pod and ServiceAccount.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  automountServiceAccountToken: true
+  serviceAccountName: test
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: test
+automountServiceAccountToken: true
diff --git a/test/checks/automountServiceAccountToken/failure.pod_true_serviceaccount_false.yaml b/test/checks/automountServiceAccountToken/failure.pod_true_serviceaccount_false.yaml
new file mode 100644
index 00000000..aeb2d17f
--- /dev/null
+++ b/test/checks/automountServiceAccountToken/failure.pod_true_serviceaccount_false.yaml
@@ -0,0 +1,19 @@
+# This fails because automounting is true for the pod, overriding the ServiceAccount.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  automountServiceAccountToken: true
+  serviceAccountName: test
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: test
+automountServiceAccountToken: false
diff --git a/test/checks/automountServiceAccountToken/failure.uses_defaults.yaml b/test/checks/automountServiceAccountToken/failure.uses_defaults.yaml
new file mode 100644
index 00000000..57a63e4d
--- /dev/null
+++ b/test/checks/automountServiceAccountToken/failure.uses_defaults.yaml
@@ -0,0 +1,17 @@
+# This fails because automounting is not disabled anywhere.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  serviceAccountName: test
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: test
diff --git a/test/checks/automountServiceAccountToken/success.pod.yaml b/test/checks/automountServiceAccountToken/success.pod.yaml
new file mode 100644
index 00000000..1da4dc6a
--- /dev/null
+++ b/test/checks/automountServiceAccountToken/success.pod.yaml
@@ -0,0 +1,19 @@
+# This succeeds because automounting is disabled at the pod.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  automountServiceAccountToken: false
+  serviceAccountName: test
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: test
+
diff --git a/test/checks/automountServiceAccountToken/success.pod_and_serviceaccount.yaml b/test/checks/automountServiceAccountToken/success.pod_and_serviceaccount.yaml
new file mode 100644
index 00000000..6b13be49
--- /dev/null
+++ b/test/checks/automountServiceAccountToken/success.pod_and_serviceaccount.yaml
@@ -0,0 +1,19 @@
+# This succeeds because automounting is disabled at the pod and ServiceAccount.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  automountServiceAccountToken: false
+  serviceAccountName: test
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: test
+automountServiceAccountToken: false
diff --git a/test/checks/automountServiceAccountToken/success.pod_false_serviceaccount_true.yaml b/test/checks/automountServiceAccountToken/success.pod_false_serviceaccount_true.yaml
new file mode 100644
index 00000000..db568283
--- /dev/null
+++ b/test/checks/automountServiceAccountToken/success.pod_false_serviceaccount_true.yaml
@@ -0,0 +1,19 @@
+# This succeeds because automounting is disabled at the pod, overriding the ServiceAccount.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  automountServiceAccountToken: false
+  serviceAccountName: test
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: test
+automountServiceAccountToken: true
diff --git a/test/checks/automountServiceAccountToken/success.serviceaccount.yaml b/test/checks/automountServiceAccountToken/success.serviceaccount.yaml
new file mode 100644
index 00000000..509c9c7d
--- /dev/null
+++ b/test/checks/automountServiceAccountToken/success.serviceaccount.yaml
@@ -0,0 +1,18 @@
+# This succeeds because automounting is disabled at the ServiceAccount.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  serviceAccountName: test
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: test
+automountServiceAccountToken: false
diff --git a/test/checks/linuxHardening/failure.apparmor_incorrect_annotation_names.yaml b/test/checks/linuxHardening/failure.apparmor_incorrect_annotation_names.yaml
new file mode 100644
index 00000000..526f994a
--- /dev/null
+++ b/test/checks/linuxHardening/failure.apparmor_incorrect_annotation_names.yaml
@@ -0,0 +1,18 @@
+# This fails because the annotation names do not match the containers.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+  annotations:
+    container.apparmor.security.beta.kubernetes.io/container1: runtime/default
+    container.apparmor.security.beta.kubernetes.io/container2: runtime/default
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+  - name: nginx2
+    image: nginx
+    ports:
+    - containerPort: 81
diff --git a/test/checks/linuxHardening/failure.drop_capabilities_but_add_all.yaml b/test/checks/linuxHardening/failure.drop_capabilities_but_add_all.yaml
new file mode 100644
index 00000000..566de74c
--- /dev/null
+++ b/test/checks/linuxHardening/failure.drop_capabilities_but_add_all.yaml
@@ -0,0 +1,17 @@
+# This fails because ALL capabilities are also added.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    securityContext:
+      capabilities:
+        drop:
+          - ALL
+        add:
+          - all
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/failure.nothing_is_satisfied.yaml b/test/checks/linuxHardening/failure.nothing_is_satisfied.yaml
new file mode 100644
index 00000000..8e56f603
--- /dev/null
+++ b/test/checks/linuxHardening/failure.nothing_is_satisfied.yaml
@@ -0,0 +1,11 @@
+# This fails because none of the seccompProfile, seLinuxOptions, AppArmor, or dropping capabilities are present.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/failure.seccomp_profile_container_overrides_pod.yaml b/test/checks/linuxHardening/failure.seccomp_profile_container_overrides_pod.yaml
new file mode 100644
index 00000000..f47520b6
--- /dev/null
+++ b/test/checks/linuxHardening/failure.seccomp_profile_container_overrides_pod.yaml
@@ -0,0 +1,17 @@
+# This fails because the container overrides the pod profile type.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  securityContext:
+    seccompProfile:
+      type: RuntimeDefault
+  containers:
+  - name: nginx
+    securityContext:
+      seccompProfile:
+        type: Unconfined
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/failure.seccomp_profile_pod_missing_type.yaml b/test/checks/linuxHardening/failure.seccomp_profile_pod_missing_type.yaml
new file mode 100644
index 00000000..fd2c8e8e
--- /dev/null
+++ b/test/checks/linuxHardening/failure.seccomp_profile_pod_missing_type.yaml
@@ -0,0 +1,13 @@
+# This fails because the pod seccompPRofile is missing the type field.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  securityContext:
+    seccompProfile:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/failure.seccomp_profile_pod_unconfined.yaml b/test/checks/linuxHardening/failure.seccomp_profile_pod_unconfined.yaml
new file mode 100644
index 00000000..7415ae41
--- /dev/null
+++ b/test/checks/linuxHardening/failure.seccomp_profile_pod_unconfined.yaml
@@ -0,0 +1,14 @@
+# This fails because the container overrides the pod profile type.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  securityContext:
+    seccompProfile:
+      type: Unconfined
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/failure.selinux_container_missing_subfield.yaml b/test/checks/linuxHardening/failure.selinux_container_missing_subfield.yaml
new file mode 100644
index 00000000..ffc6869b
--- /dev/null
+++ b/test/checks/linuxHardening/failure.selinux_container_missing_subfield.yaml
@@ -0,0 +1,13 @@
+# This fails because seLinuxOptions is defined but without any sub-field.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    securityContext:
+      seLinuxOptions:
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/failure.selinux_pod_missing_subfield.yaml b/test/checks/linuxHardening/failure.selinux_pod_missing_subfield.yaml
new file mode 100644
index 00000000..884dbbb1
--- /dev/null
+++ b/test/checks/linuxHardening/failure.selinux_pod_missing_subfield.yaml
@@ -0,0 +1,13 @@
+# This fails because seLinuxOptions is defined but without any sub-field.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  securityContext:
+    seLinuxOptions:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/success.apparmor_2_containers.yaml b/test/checks/linuxHardening/success.apparmor_2_containers.yaml
new file mode 100644
index 00000000..88fd15c3
--- /dev/null
+++ b/test/checks/linuxHardening/success.apparmor_2_containers.yaml
@@ -0,0 +1,18 @@
+# This succeeds because AppArmor annotations exist matching both container names.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+  annotations:
+    container.apparmor.security.beta.kubernetes.io/nginx: runtime/default
+    container.apparmor.security.beta.kubernetes.io/nginx2: runtime/default
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+  - name: nginx2
+    image: nginx
+    ports:
+    - containerPort: 81
diff --git a/test/checks/linuxHardening/success.drop_capabilities.yaml b/test/checks/linuxHardening/success.drop_capabilities.yaml
new file mode 100644
index 00000000..5b6a0446
--- /dev/null
+++ b/test/checks/linuxHardening/success.drop_capabilities.yaml
@@ -0,0 +1,17 @@
+# This succeeds because SOME capability is dropped, and ALL capabilities are not added.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    securityContext:
+      capabilities:
+        drop:
+        - ALL
+        add:
+        - someCapabilities
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/success.seccomp_profile_container.yaml b/test/checks/linuxHardening/success.seccomp_profile_container.yaml
new file mode 100644
index 00000000..388722f8
--- /dev/null
+++ b/test/checks/linuxHardening/success.seccomp_profile_container.yaml
@@ -0,0 +1,14 @@
+# This succeeds because a seccomp profile is defined for the container.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    securityContext:
+      seccompProfile:
+        type: RuntimeDefault
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/success.seccomp_profile_pod.yaml b/test/checks/linuxHardening/success.seccomp_profile_pod.yaml
new file mode 100644
index 00000000..e9b62ff9
--- /dev/null
+++ b/test/checks/linuxHardening/success.seccomp_profile_pod.yaml
@@ -0,0 +1,14 @@
+# This succeeds because a seccomp profile is defined for the pod and not undefined for the container.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  securityContext:
+    seccompProfile:
+      type: RuntimeDefault
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/success.seccomp_profile_unconfined_for_pod_but_set_for_container.yaml b/test/checks/linuxHardening/success.seccomp_profile_unconfined_for_pod_but_set_for_container.yaml
new file mode 100644
index 00000000..b1f3e136
--- /dev/null
+++ b/test/checks/linuxHardening/success.seccomp_profile_unconfined_for_pod_but_set_for_container.yaml
@@ -0,0 +1,17 @@
+# This succeeds because the container seccomp profile  overrides the pod Unconfined setting.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  securityContext:
+    seccompProfile:
+      type: Unconfined
+  containers:
+  - name: nginx
+    securityContext:
+      seccompProfile:
+        type: RuntimeDefault
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/success.selinux_container.yaml b/test/checks/linuxHardening/success.selinux_container.yaml
new file mode 100644
index 00000000..0eb7af55
--- /dev/null
+++ b/test/checks/linuxHardening/success.selinux_container.yaml
@@ -0,0 +1,14 @@
+# This succeeds because seLinuxOptions are defined for the container.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    securityContext:
+      seLinuxOptions:
+        level:   "s0:c123,c456"
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/linuxHardening/success.selinux_pod.yaml b/test/checks/linuxHardening/success.selinux_pod.yaml
new file mode 100644
index 00000000..660f3d0e
--- /dev/null
+++ b/test/checks/linuxHardening/success.selinux_pod.yaml
@@ -0,0 +1,14 @@
+# This succeeds because seLinuxOptions is defined for the pod.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  securityContext:
+    seLinuxOptions:
+      level:   "s0:c123,c456"
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/missingNetworkPolicy/failure.netpol_missing_egress_in_policytypes.yaml b/test/checks/missingNetworkPolicy/failure.netpol_missing_egress_in_policytypes.yaml
new file mode 100644
index 00000000..21470991
--- /dev/null
+++ b/test/checks/missingNetworkPolicy/failure.netpol_missing_egress_in_policytypes.yaml
@@ -0,0 +1,41 @@
+# This failes because the NetworkPolicy `policyTypes` lacks `Egress`,
+# without which the egress rules will not be applied.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+  labels:
+    security: medium
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: test
+spec:
+  podSelector:
+    matchLabels:
+      security: medium
+  policyTypes:
+  - Ingress
+  ingress:
+  - from:
+    - ipBlock:
+        cidr: 0.0.0.0/0
+    ports:
+    - protocol: TCP
+      port: 8080
+  egress:
+  - to:
+    - podSelector:
+        matchLabels:
+          # Allow outbound with other medium-security pods.
+          security: medium
+    ports:
+    - protocol: TCP
+      port: 80
diff --git a/test/checks/missingNetworkPolicy/failure.netpol_missing_egress_rules.yaml b/test/checks/missingNetworkPolicy/failure.netpol_missing_egress_rules.yaml
new file mode 100644
index 00000000..01f78b82
--- /dev/null
+++ b/test/checks/missingNetworkPolicy/failure.netpol_missing_egress_rules.yaml
@@ -0,0 +1,32 @@
+# This fails because the NetworkPolicy lacks egress rules.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+  labels:
+    security: medium
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: test
+spec:
+  podSelector:
+    matchLabels:
+      security: medium
+  policyTypes:
+  - Egress
+  - Ingress
+  ingress:
+  - from:
+    - ipBlock:
+        cidr: 0.0.0.0/0
+    ports:
+    - protocol: TCP
+      port: 8080
diff --git a/test/checks/missingNetworkPolicy/failure.netpol_missing_ingress_in_policytypes.yaml b/test/checks/missingNetworkPolicy/failure.netpol_missing_ingress_in_policytypes.yaml
new file mode 100644
index 00000000..99fdea1f
--- /dev/null
+++ b/test/checks/missingNetworkPolicy/failure.netpol_missing_ingress_in_policytypes.yaml
@@ -0,0 +1,41 @@
+# This failes because the NetworkPolicy `policyTypes` lacks `Ingress`,
+# without which the egress rules will not be applied.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+  labels:
+    security: medium
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: test
+spec:
+  podSelector:
+    matchLabels:
+      security: medium
+  policyTypes:
+  - Egress
+  ingress:
+  - from:
+    - ipBlock:
+        cidr: 0.0.0.0/0
+    ports:
+    - protocol: TCP
+      port: 8080
+  egress:
+  - to:
+    - podSelector:
+        matchLabels:
+          # Allow outbound with other medium-security pods.
+          security: medium
+    ports:
+    - protocol: TCP
+      port: 80
diff --git a/test/checks/missingNetworkPolicy/failure.netpol_missing_ingress_rules.yaml b/test/checks/missingNetworkPolicy/failure.netpol_missing_ingress_rules.yaml
new file mode 100644
index 00000000..4d494ee7
--- /dev/null
+++ b/test/checks/missingNetworkPolicy/failure.netpol_missing_ingress_rules.yaml
@@ -0,0 +1,34 @@
+# This fails because the NetworkPolicy lacks ingress rules.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+  labels:
+    security: medium
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: test
+spec:
+  podSelector:
+    matchLabels:
+      security: medium
+  policyTypes:
+  - Egress
+  - Ingress
+  egress:
+  - to:
+    - podSelector:
+        matchLabels:
+          # Allow outbound with other medium-security pods.
+          security: medium
+    ports:
+    - protocol: TCP
+      port: 80
diff --git a/test/checks/missingNetworkPolicy/failure.netpol_targets_wrong_pod_label.yaml b/test/checks/missingNetworkPolicy/failure.netpol_targets_wrong_pod_label.yaml
new file mode 100644
index 00000000..389d3055
--- /dev/null
+++ b/test/checks/missingNetworkPolicy/failure.netpol_targets_wrong_pod_label.yaml
@@ -0,0 +1,41 @@
+# This fails because the NetworkPolicy matches a label the pod does not have
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+  labels:
+    security: medium
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: test
+spec:
+  podSelector:
+    matchLabels:
+      security: high
+  policyTypes:
+  - Egress
+  - Ingress
+  ingress:
+  - from:
+    - ipBlock:
+        cidr: 0.0.0.0/0
+    ports:
+    - protocol: TCP
+      port: 8080
+  egress:
+  - to:
+    - podSelector:
+        matchLabels:
+          # Allow outbound with other medium-security pods.
+          security: medium
+    ports:
+    - protocol: TCP
+      port: 80
diff --git a/test/checks/missingNetworkPolicy/success.yaml b/test/checks/missingNetworkPolicy/success.yaml
new file mode 100644
index 00000000..27d56ea2
--- /dev/null
+++ b/test/checks/missingNetworkPolicy/success.yaml
@@ -0,0 +1,41 @@
+# This succeeds because the NetworkPolicy contains and enables egress and ingress rules, and targets the pod's label.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+  labels:
+    security: medium
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: test
+spec:
+  podSelector:
+    matchLabels:
+      security: medium
+  policyTypes:
+  - Egress
+  - Ingress
+  ingress:
+  - from:
+    - ipBlock:
+        cidr: 0.0.0.0/0
+    ports:
+    - protocol: TCP
+      port: 8080
+  egress:
+  - to:
+    - podSelector:
+        matchLabels:
+          # Allow outbound with other medium-security pods.
+          security: medium
+    ports:
+    - protocol: TCP
+      port: 80
diff --git a/test/checks/sensitiveConfigmapContent/failure.key_aws_secret_access_key.yaml b/test/checks/sensitiveConfigmapContent/failure.key_aws_secret_access_key.yaml
new file mode 100644
index 00000000..66e6c3c5
--- /dev/null
+++ b/test/checks/sensitiveConfigmapContent/failure.key_aws_secret_access_key.yaml
@@ -0,0 +1,7 @@
+# This fails because the key AWS_SECRET_ACCESS_KEY is set.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: test-config
+data:
+  AWS_SECRET_ACCESS_KEY: xxxxx
diff --git a/test/checks/sensitiveConfigmapContent/failure.key_azure_batch_key.yaml b/test/checks/sensitiveConfigmapContent/failure.key_azure_batch_key.yaml
new file mode 100644
index 00000000..b8cd486d
--- /dev/null
+++ b/test/checks/sensitiveConfigmapContent/failure.key_azure_batch_key.yaml
@@ -0,0 +1,7 @@
+# This fails because the key AZURE_BATCH_KEY is set.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: test-config
+data:
+  AZURE_BATCH_KEY: xxxxx
diff --git a/test/checks/sensitiveConfigmapContent/failure.key_oci_cli_key_content.yaml b/test/checks/sensitiveConfigmapContent/failure.key_oci_cli_key_content.yaml
new file mode 100644
index 00000000..4341c4ef
--- /dev/null
+++ b/test/checks/sensitiveConfigmapContent/failure.key_oci_cli_key_content.yaml
@@ -0,0 +1,7 @@
+# This fails because the key OCI_CLI_KEY_CONTENT is set.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: test-config
+data:
+  OCI_CLI_KEY_CONTENT: xxxxx
diff --git a/test/checks/sensitiveConfigmapContent/failure.key_password.yaml b/test/checks/sensitiveConfigmapContent/failure.key_password.yaml
new file mode 100644
index 00000000..cafc1f68
--- /dev/null
+++ b/test/checks/sensitiveConfigmapContent/failure.key_password.yaml
@@ -0,0 +1,8 @@
+# This fails because a key contains "password".
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: test-config
+data:
+  db_username: postgres
+  db_password: abc123
diff --git a/test/checks/sensitiveConfigmapContent/failure.value_private_key.yaml b/test/checks/sensitiveConfigmapContent/failure.value_private_key.yaml
new file mode 100644
index 00000000..e65aa2b5
--- /dev/null
+++ b/test/checks/sensitiveConfigmapContent/failure.value_private_key.yaml
@@ -0,0 +1,11 @@
+# This fails because a value contains a private key.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: test-config
+data:
+  a_key: and value that are ok
+  ssh_access: |
+    -----BEGIN PRIVATE KEY-----
+    xxxxxxxx
+    -----END OPENSSH PRIVATE KEY-----
diff --git a/test/checks/sensitiveConfigmapContent/success.no_data.yaml b/test/checks/sensitiveConfigmapContent/success.no_data.yaml
new file mode 100644
index 00000000..ada55ce0
--- /dev/null
+++ b/test/checks/sensitiveConfigmapContent/success.no_data.yaml
@@ -0,0 +1,6 @@
+# This succeeds because no data nor keys nor values are defined.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: test-config
+  #data:
diff --git a/test/checks/sensitiveConfigmapContent/success.valid_keys_and_values.yaml b/test/checks/sensitiveConfigmapContent/success.valid_keys_and_values.yaml
new file mode 100644
index 00000000..b2fbddb5
--- /dev/null
+++ b/test/checks/sensitiveConfigmapContent/success.valid_keys_and_values.yaml
@@ -0,0 +1,8 @@
+# This succeeds because no keys or values are invalid.
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: test-config
+data:
+  message: This is a test
+  db_user: postgres
diff --git a/test/checks/sensitiveContainerEnvVar/failure.key_aws_secret_access_key.yaml b/test/checks/sensitiveContainerEnvVar/failure.key_aws_secret_access_key.yaml
new file mode 100644
index 00000000..c248dbba
--- /dev/null
+++ b/test/checks/sensitiveContainerEnvVar/failure.key_aws_secret_access_key.yaml
@@ -0,0 +1,16 @@
+# This fails because an environment variable name AWS_SECRET_ACCESS_KEY is set.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    env:
+    - name: password
+      value: abc123
+    - name: AWS_SECRET_ACCESS_KEY
+      value: xxx
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/sensitiveContainerEnvVar/failure.key_azure_batch_key.yaml b/test/checks/sensitiveContainerEnvVar/failure.key_azure_batch_key.yaml
new file mode 100644
index 00000000..6b7b1811
--- /dev/null
+++ b/test/checks/sensitiveContainerEnvVar/failure.key_azure_batch_key.yaml
@@ -0,0 +1,16 @@
+# This fails because an environment variable named AZURE_BATCH_KEY is set.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    env:
+    - name: password
+      value: abc123
+    - name: AZURE_BATCH_KEY
+      value: xxx
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/sensitiveContainerEnvVar/failure.key_oci_cli_key_content.yaml b/test/checks/sensitiveContainerEnvVar/failure.key_oci_cli_key_content.yaml
new file mode 100644
index 00000000..a8aa6e92
--- /dev/null
+++ b/test/checks/sensitiveContainerEnvVar/failure.key_oci_cli_key_content.yaml
@@ -0,0 +1,16 @@
+# This fails because an environment variable named OCI_CLI_KEY_CONTENT is set.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    env:
+    - name: password
+      value: abc123
+    - name: OCI_CLI_KEY_CONTENT
+      value: xxx
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/sensitiveContainerEnvVar/failure.key_password.yaml b/test/checks/sensitiveContainerEnvVar/failure.key_password.yaml
new file mode 100644
index 00000000..f535c3e7
--- /dev/null
+++ b/test/checks/sensitiveContainerEnvVar/failure.key_password.yaml
@@ -0,0 +1,16 @@
+# This fails because an environment variable name contains "password".
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    env:
+    - name: user_name
+      value: postgres
+    - name: my_password
+      value: abc123
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/sensitiveContainerEnvVar/failure.value_private_key.yaml b/test/checks/sensitiveContainerEnvVar/failure.value_private_key.yaml
new file mode 100644
index 00000000..06f8ec7e
--- /dev/null
+++ b/test/checks/sensitiveContainerEnvVar/failure.value_private_key.yaml
@@ -0,0 +1,19 @@
+# This fails because an environment variable value contains a private key.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    env:
+    - name: user_name
+      value: postgres
+    - name: the_good_stuff
+      value: |
+        ---BEGIN OPENSSH PRIVATE KEY---
+        xxxxx
+        ---END  OPENSSH PRIVATE KEY---
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/sensitiveContainerEnvVar/success.no_env_vars.yaml b/test/checks/sensitiveContainerEnvVar/success.no_env_vars.yaml
new file mode 100644
index 00000000..16e534c0
--- /dev/null
+++ b/test/checks/sensitiveContainerEnvVar/success.no_env_vars.yaml
@@ -0,0 +1,11 @@
+# This succeeds because there are no environment variables.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/checks/sensitiveContainerEnvVar/success.valid_env_vars.yaml b/test/checks/sensitiveContainerEnvVar/success.valid_env_vars.yaml
new file mode 100644
index 00000000..98e3d4e0
--- /dev/null
+++ b/test/checks/sensitiveContainerEnvVar/success.valid_env_vars.yaml
@@ -0,0 +1,14 @@
+# This succeeds because there are no sensitive environment variable names or values.
+apiVersion: v1
+kind: Pod
+metadata:
+  name: test-pod
+spec:
+  containers:
+  - name: nginx
+    env:
+    - name: greeting
+      value: hello
+    image: nginx
+    ports:
+    - containerPort: 80
diff --git a/test/fixtures.go b/test/fixtures.go
index b10be572..ba7d6c7b 100644
--- a/test/fixtures.go
+++ b/test/fixtures.go
@@ -199,6 +199,8 @@ func SetupTestAPI(objects ...runtime.Object) (kubernetes.Interface, dynamic.Inte
 			APIResources: []metav1.APIResource{
 				{Name: "pods", Namespaced: true, Kind: "Pod"},
 				{Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"},
+				{Name: "serviceaccounts", Namespaced: true, Kind: "ServiceAccount"},
+				{Name: "configmaps", Namespaced: true, Kind: "ConfigMap"},
 			},
 		},
 		{
@@ -239,6 +241,7 @@ func SetupTestAPI(objects ...runtime.Object) (kubernetes.Interface, dynamic.Inte
 			GroupVersion: "networking.k8s.io/v1",
 			APIResources: []metav1.APIResource{
 				{Name: "ingresses", Namespaced: true, Kind: "Ingress", Version: "v1"},
+				{Name: "networkpolicies", Namespaced: true, Kind: "NetworkPolicy", Version: "v1"},
 			},
 		},
 		{