Repository: bridgecrewio/checkov

Issue:
CKV_K8S_31 failure with DockerDefault configured
**Describe the bug**
CKV_K8S_31 failure when the seccompProfile type is configured as Docker/Default or Runtime/Default

**To Reproduce**
Define security context as below.

```
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: clust3rf8ck
  name: clust3rf8ck
  namespace: clust3rf8ck
spec:
  replicas: 2
  selector:
    matchLabels:
      app: clust3rf8ck
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: clust3rf8ck
      annotations:
        seccomp.security.alpha.kubernetes.io/pod: "docker/default"
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values:
                      - clust3rf8ck
              topologyKey: "kubernetes.io/hostname"
      containers:
        - image: eurogig/clust3rf8ck@sha256:a374eb5853e0e17d06bcf37afc2fcb40892aa3477caf362ea3581c71373cb90a
          name: clust3rf8ck
          imagePullPolicy: Always
          resources:
            limits:
              cpu: "1"
              memory: "200Mi"
            requests:
              cpu: "0.6"
              memory: "100Mi"
          livenessProbe:
            exec:
              command:
                - /bin/sh
                - -c
                - "[ -f /var/run/nginx.pid ] && ps -A | grep nginx"
            initialDelaySeconds: 10
            periodSeconds: 5
          readinessProbe:
            httpGet:
              scheme: HTTP
              path: /index.html
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            capabilities:
              drop:
                - ALL
                - NET_RAW
          volumeMounts:
            - mountPath: /var/cache/nginx
              name: cache-volume
            - mountPath: /var/run
              name: pid-volume
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
        runAsUser: 10014
        runAsGroup: 10014
      volumes:
        - name: cache-volume
          emptyDir: {}
        - name: pid-volume
          emptyDir: {}
status: {}
---
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    app: clust3rf8ck
  name: cflb
  namespace: clust3rf8ck
spec:
  ports:
    - name: 80-80
      port: 80
      protocol: TCP
      targetPort: 8080
  selector:
    app: clust3rf8ck
  type: LoadBalancer
```
2. Run checkov for kubernetes.
```
checkov --framework=kubernetes --quiet -d .
```
**Expected behavior**
CKV_K8S_31 to pass with the following configuration.

```
spec:
  replicas: 2
  selector:
    matchLabels:
      app: clust3rf8ck
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: clust3rf8ck
      annotations:
        seccomp.security.alpha.kubernetes.io/pod: "docker/default"
```

**Actual Behaviour**
```
[terraformpipeline] checkov --framework=kubernetes --quiet -d .                                                                               20:52:07  ☁  master ☂ ⚡
kubernetes scan results:

Passed checks: 89, Failed checks: 1, Skipped checks: 0

Check: CKV_K8S_31: "Ensure that the seccomp profile is set to docker/default or runtime/default"
	FAILED for resource: Deployment.clust3rf8ck.clust3rf8ck
	File: /k8s-sample.yaml:35-114
	Guide: https://docs.bridgecrew.io/docs/bc_k8s_29

		35  | apiVersion: apps/v1
		36  | kind: Deployment
		37  | metadata:
		38  |   labels:
		39  |     app: clust3rf8ck
		40  |   name: clust3rf8ck
		41  |   namespace: clust3rf8ck
		42  | spec:
		43  |   replicas: 2
		44  |   selector:
		45  |     matchLabels:
		46  |       app: clust3rf8ck
		47  |   strategy: {}
		48  |   template:
		49  |     metadata:
		50  |       creationTimestamp: null
		51  |       labels:
		52  |         app: clust3rf8ck
		53  |       annotations:
		54  |         seccomp.security.alpha.kubernetes.io/pod: "docker/default"
		55  |     spec:
		56  |       affinity:
		57  |         podAntiAffinity:
		58  |           requiredDuringSchedulingIgnoredDuringExecution:
		59  |             - labelSelector:
		60  |                 matchExpressions:
		61  |                   - key: app
		62  |                     operator: In
		63  |                     values:
		64  |                       - clust3rf8ck
		65  |               topologyKey: "kubernetes.io/hostname"
		66  |       containers:
		67  |         - image: eurogig/clust3rf8ck@sha256:a374eb5853e0e17d06bcf37afc2fcb40892aa3477caf362ea3581c71373cb90a
		68  |           name: clust3rf8ck
		69  |           imagePullPolicy: Always
		70  |           resources:
		71  |             limits:
		72  |               cpu: "1"
		73  |               memory: "200Mi"
		74  |             requests:
		75  |               cpu: "0.6"
		76  |               memory: "100Mi"
		77  |           livenessProbe:
		78  |             exec:
		79  |               command:
		80  |                 - /bin/sh
		81  |                 - -c
		82  |                 - "[ -f /var/run/nginx.pid ] && ps -A | grep nginx"
		83  |             initialDelaySeconds: 10
		84  |             periodSeconds: 5
		85  |           readinessProbe:
		86  |             httpGet:
		87  |               scheme: HTTP
		88  |               path: /index.html
		89  |               port: 8080
		90  |             initialDelaySeconds: 10
		91  |             periodSeconds: 5
		92  |           securityContext:
		93  |             readOnlyRootFilesystem: true
		94  |             allowPrivilegeEscalation: false
		95  |             capabilities:
		96  |               drop:
		97  |                 - ALL
		98  |                 - NET_RAW
		99  |           volumeMounts:
		100 |             - mountPath: /var/cache/nginx
		101 |               name: cache-volume
		102 |             - mountPath: /var/run
		103 |               name: pid-volume
		104 |       automountServiceAccountToken: false
		105 |       securityContext:
		106 |         runAsNonRoot: true
		107 |         runAsUser: 10014
		108 |         runAsGroup: 10014
		109 |       volumes:
		110 |         - name: cache-volume
		111 |           emptyDir: {}
		112 |         - name: pid-volume
		113 |           emptyDir: {}
		114 | status: {}
```


**Desktop (please complete the following information):**
 - OS: Big Sur 11.5.2 
 - Checkov Version 2.0.479

**Additional context**
Took the K8s example from this blog https://bridgecrew.io/blog/creating-a-secure-kubernetes-nginx-deployment-using-checkov/



Discussion:

Resolving patch:
diff --git a/checkov/kubernetes/base_spec_check.py b/checkov/kubernetes/base_spec_check.py
--- a/checkov/kubernetes/base_spec_check.py
+++ b/checkov/kubernetes/base_spec_check.py
@@ -1,4 +1,5 @@
 from abc import abstractmethod
+from typing import Dict, Any
 
 from checkov.common.checks.base_check import BaseCheck
 from checkov.common.multi_signature import multi_signature
@@ -34,7 +35,7 @@ def wrapper(self, conf, entity_type=None):
         return wrapper
 
     @staticmethod
-    def get_inner_entry(conf, entry_name):
+    def get_inner_entry(conf: Dict[str, Any], entry_name: str) -> Dict[str, Any]:
         spec = {}
         if conf.get("spec") and conf.get("spec").get("template"):
             spec = conf.get("spec").get("template").get(entry_name, {})
diff --git a/checkov/kubernetes/checks/Seccomp.py b/checkov/kubernetes/checks/Seccomp.py
--- a/checkov/kubernetes/checks/Seccomp.py
+++ b/checkov/kubernetes/checks/Seccomp.py
@@ -37,7 +37,9 @@ def scan_spec_conf(self, conf):
             security_profile = find_in_dict(conf, 'spec/template/spec/securityContext/seccompProfile/type')
             if security_profile:
                 return CheckResult.PASSED if security_profile == 'RuntimeDefault' else CheckResult.FAILED
-            if "metadata" in conf:
+
+            metadata = self.get_inner_entry(conf, "metadata")
+            if not metadata and "metadata" in conf:
                 metadata = conf["metadata"]
         elif conf['kind'] == 'CronJob':
             if "spec" in conf:


Test patch:
diff --git a/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED.yaml b/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED.yaml
--- a/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED.yaml
+++ b/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED.yaml
@@ -2,7 +2,7 @@
 apiVersion: v1
 kind: Pod
 metadata:
-  name: seccomp-passed
+  name: seccomp-passed-metadata-annotations-docker
   annotations:
     seccomp.security.alpha.kubernetes.io/pod: docker/default
 spec:
diff --git a/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED2.yaml b/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED2.yaml
--- a/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED2.yaml
+++ b/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED2.yaml
@@ -2,7 +2,7 @@
 apiVersion: v1
 kind: Pod
 metadata:
-  name: seccomp-passed
+  name: seccomp-passed-metadata-annotations-runtime
   annotations:
     seccomp.security.alpha.kubernetes.io/pod: runtime/default
 spec:
diff --git a/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED3.yaml b/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED3.yaml
--- a/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED3.yaml
+++ b/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED3.yaml
@@ -2,7 +2,7 @@
 apiVersion: v1
 kind: Pod
 metadata:
-  name: seccomp-passed
+  name: seccomp-passed-security-context
 spec:
   securityContext:
     runAsUser: 1000
diff --git a/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED6.yaml b/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED6.yaml
new file mode 100644
--- /dev/null
+++ b/tests/kubernetes/checks/example_Seccomp/pod-seccomp-PASSED6.yaml
@@ -0,0 +1,24 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  labels:
+    app: test
+  name: seccomp-passed-metadata-annotations
+spec:
+  replicas: 2
+  selector:
+    matchLabels:
+      app: test
+  strategy: {}
+  template:
+    metadata:
+      labels:
+        app: test
+      annotations:
+        seccomp.security.alpha.kubernetes.io/pod: "docker/default"
+    spec:
+      containers:
+      - name: test
+        image: quay.io/test:0.1
+        ports:
+        - containerPort: 1234
diff --git a/tests/kubernetes/checks/test_Seccomp.py b/tests/kubernetes/checks/test_Seccomp.py
--- a/tests/kubernetes/checks/test_Seccomp.py
+++ b/tests/kubernetes/checks/test_Seccomp.py
@@ -1,26 +1,47 @@
 import os
 import unittest
+from pathlib import Path
 
+from checkov.cloudformation.checks.resource.base_resource_check import BaseResourceCheck
 from checkov.kubernetes.checks.Seccomp import check
 from checkov.kubernetes.runner import Runner
 from checkov.runner_filter import RunnerFilter
 
 
 class TestSeccomp(unittest.TestCase):
-
     def test_summary(self):
-        runner = Runner()
-        current_dir = os.path.dirname(os.path.realpath(__file__))
-
-        test_files_dir = current_dir + "/example_Seccomp"
-        report = runner.run(root_folder=test_files_dir,runner_filter=RunnerFilter(checks=[check.id]))
-        summary = report.get_summary()
+        # given
+        test_files_dir = Path(__file__).parent / "example_Seccomp"
 
-        self.assertEqual(summary['passed'], 6)
-        self.assertEqual(summary['failed'], 2)
-        self.assertEqual(summary['skipped'], 0)
-        self.assertEqual(summary['parsing_errors'], 0)
+        # when
+        report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id]))
 
-
-if __name__ == '__main__':
+        # then
+        summary = report.get_summary()
+        passed_resources = [check.resource for check in report.passed_checks]
+        failed_resources = [check.resource for check in report.failed_checks]
+
+        self.assertEqual(summary["passed"], 7)
+        self.assertEqual(summary["failed"], 2)
+        self.assertEqual(summary["skipped"], 0)
+        self.assertEqual(summary["parsing_errors"], 0)
+
+        expected_passed_resources = [
+            "CronJob.cronjob-passed.default",
+            "Deployment.seccomp-passed-deployment.default",
+            "Deployment.seccomp-passed-metadata-annotations.default",
+            "Pod.seccomp-passed-metadata-annotations-docker.default",
+            "Pod.seccomp-passed-metadata-annotations-runtime.default",
+            "Pod.seccomp-passed-security-context.default",
+            "StatefulSet.RELEASE-NAME.default",
+        ]
+        expected_failed_resources = [
+            "Deployment.app-cert-manager.infra",
+            "Pod.seccomp-failed.default",
+        ]
+        self.assertCountEqual(expected_passed_resources, passed_resources)
+        self.assertCountEqual(expected_failed_resources, failed_resources)
+
+
+if __name__ == "__main__":
     unittest.main()