Skip to content

[REVIEW] container-security: add RuntimeClass and admission policy evidence gates #168

@hermesbountyhunter

Description

@hermesbountyhunter

Skill Being Reviewed

Skill name: container-security
Skill path: skills/cloud/container-security/

False Positive Analysis

Benign code that triggers a false positive:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  template:
    spec:
      securityContext:
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      initContainers:
        - name: volume-permissions
          image: busybox:1.36@sha256:4be8f0d59d41a3a494f1c350f064a9f1c8b3b7595510e164e47f2c6d3fda25a2
          command: ["sh", "-c", "chown -R 65532:65532 /work"]
          securityContext:
            runAsUser: 0
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
          volumeMounts:
            - name: work
              mountPath: /work
      containers:
        - name: app
          image: ghcr.io/example/app@sha256:1111111111111111111111111111111111111111111111111111111111111111
          securityContext:
            runAsUser: 65532
            runAsGroup: 65532
            runAsNonRoot: true
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
      volumes:
        - name: work
          emptyDir: {}

Why this is a false positive:

The skill's severity table uses "running as root" as a high-severity example and the common pitfalls correctly note that init containers must be reviewed. However, not every root-running init container has the same risk as a root application container. A short-lived init container that only changes ownership on an emptyDir, drops all capabilities, denies privilege escalation, uses RuntimeDefault seccomp, has no hostPath, and is digest-pinned is still a policy exception, but it should be scored differently from a long-running root workload with host mounts or broad capabilities. The report should require reviewers to capture container type, duration, mounts, capabilities, and compensating controls before assigning severity.

Coverage Gaps

Missed variant 1: RuntimeClass / sandboxed runtime evidence is not collected

apiVersion: apps/v1
kind: Deployment
metadata:
  name: untrusted-plugin-runner
spec:
  template:
    spec:
      runtimeClassName: gvisor
      containers:
        - name: runner
          image: ghcr.io/example/plugin-runner@sha256:2222222222222222222222222222222222222222222222222222222222222222
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              drop: ["ALL"]

Why it should be caught:

For multi-tenant runners, plugin execution, CI workloads, or untrusted document/media processing, the isolation boundary may depend on RuntimeClass choices such as gVisor, Kata Containers, or another sandboxed runtime. The current skill checks ordinary Pod Security controls but does not ask reviewers to identify whether high-risk workloads use the default runtime versus a sandboxed runtime, nor whether the referenced RuntimeClass object exists and is approved for that namespace.

Missed variant 2: Admission controls may mutate or validate security context after manifests are reviewed

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-runtime-default-seccomp
spec:
  validationFailureAction: Enforce
  rules:
    - name: require-runtime-default
      match:
        any:
          - resources:
              kinds: ["Pod"]
      validate:
        pattern:
          spec:
            securityContext:
              seccompProfile:
                type: RuntimeDefault

Why it should be caught:

The skill mentions OPA/Gatekeeper in the use cases, but the main process and report template do not require evidence from Kyverno, Gatekeeper, or Kubernetes ValidatingAdmissionPolicy/MutatingAdmissionPolicy resources. That can create both false negatives (a manifest looks unsafe but is blocked/mutated before admission) and false positives (a manifest looks safe but the policy is only Audit, excludes the namespace, or has a broad exception). Reviewers should record policy engine, mode (Enforce vs Audit/dry-run), namespace selectors, exception resources, and whether policies cover init and ephemeral containers.

Edge Cases

  • A runtimeClassName string in a Pod is not enough evidence by itself; the review should check for a matching RuntimeClass object and, when possible, the handler/runtime implementation used by the cluster.
  • Admission policies often have namespace or service-account exceptions. A policy existing in the repo should not be counted as protection unless the workload under review is actually selected by it.
  • Kubernetes' newer CEL-based admission policies can enforce controls without Gatekeeper/Kyverno CRDs, so discovery should include ValidatingAdmissionPolicy, ValidatingAdmissionPolicyBinding, MutatingAdmissionPolicy, and MutatingAdmissionPolicyBinding manifests.
  • Root init containers should be treated as scoped exceptions requiring justification and compensating controls, not automatically bucketed with privileged long-running application containers.

Remediation Quality

  • Fix resolves the vulnerability
  • Fix doesn't introduce new security issues
  • Fix doesn't break functionality
  • Issues found: The existing remediation guidance is strong for non-root users, capabilities, network policies, and read-only filesystems. It would be stronger if it added an evidence matrix for runtime isolation and admission policy enforcement, and if it separated "root init container with tight constraints" from "root application container with broad privileges" in severity guidance.

Comparison to Other Tools

Tool Catches this? Notes
Semgrep Partial Semgrep can flag Kubernetes fields such as privileged containers or missing runAsNonRoot, but repository-local rules usually cannot prove whether a RuntimeClass exists in-cluster or whether admission policies are enforced for a namespace.
CodeQL No CodeQL is not typically used for Kubernetes admission-policy reachability or runtime-class validation.
Other: Kyverno / Gatekeeper / Kubernetes admission Partial These tools can enforce the desired controls at admission time, but a source review still needs to verify policy mode, selectors, exceptions, and workload coverage.

Overall Assessment

Strengths:

  • Clear mapping to CIS Docker, CIS Kubernetes, NIST SP 800-190, and Pod Security Standards.
  • Good coverage of common high-impact misconfigurations: privileged pods, host namespaces, missing network policies, wildcard RBAC, writable root filesystems, and plaintext secrets.
  • Useful practical pitfalls around init/sidecar coverage, Helm values, default namespaces, and BuildKit-compatible remediation.

Needs improvement:

  • Runtime isolation (runtimeClassName, gVisor/Kata/default runtime) is not part of the context checklist or output format.
  • Admission policy evidence is mentioned generally but not collected systematically, so reviewers can miss enforcement modes, selectors, and exceptions.
  • Severity guidance does not distinguish tightly scoped root init containers from long-running root application containers with broader privileges.

Priority recommendations:

  1. Add RuntimeClass and runtimeClassName discovery to Step 1, plus a report field showing default/sandboxed runtime per high-risk workload.
  2. Add an admission-control evidence section covering Kyverno, Gatekeeper, and Kubernetes CEL admission policy resources, including mode, selectors, and exceptions.
  3. Refine severity examples so root init containers with dropped capabilities/no hostPath/no privilege escalation are treated as justified exceptions or medium-risk hardening gaps unless additional escape paths are present.

Bounty Info

  • I have read and agree to the CONTRIBUTING.md bounty terms
  • Preferred payment method: PayPal; payment details can be provided privately after acceptance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions