container-hardening — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited container-hardening (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill covers the container-image layer: what's inside, how it runs, and how you prove it checks out. It's the foundation k8s-security builds on (K8s takes these images and adds cluster-level controls).
Activates on:
Dockerfile, Containerfile, docker-compose.yml, .dockerignore, multi-stage build script.security-review phase 3 (container in scope) or from k8s-security (PodSecurityContext points to an image-level issue).supply-chain.k8s-security. The image is the ingredient; K8s is the cook.supply-chain. This skill calls sigstore; that one explains it.cve-triage for triage.secrets-scanner on image history.cicd-hardening.secure-coding or the framework skill.Six phases. Phases 1–3 are image content, phase 4 is signing, phase 5 is runtime, phase 6 is verification.
The base image is 80% of your attack surface. Choose deliberately.
Options, smallest to largest:
gcr.io/distroless/java21-debian12, gcr.io/distroless/python3-debian12, etc. With a :debug variant for troubleshooting.node:latest, python:latest). Don't use. latest is unreproducible, and these tags carry far more than needed.Rules:
FROM gcr.io/distroless/python3-debian12@sha256:<digest>. A tag can mutate under your feet.The instructions that shape image content. Each of these is a classic foot-gun.
USER app or USER 10001 on a dedicated UID. Without USER: every process in the container is root in the container, and on container escape it's root on the host.docker run --read-only or Kubernetes readOnlyRootFilesystem: true. Limit write needs to explicit tmpfs or volume mounts. Exposure: if an attacker gets execution, they can't drop malware.--cap-drop=ALL plus explicit additions for what you need (e.g. --cap-add=NET_BIND_SERVICE for ports < 1024). In Kubernetes: securityContext.capabilities.gcc, make, git from ending up in the production image. See Docker docs for syntax..env, .git, node_modules, test fixtures from landing in the image via COPY . .. Without .dockerignore your container has the whole source plus any local secrets.ENV PASSWORD=..., ARG SECRET=..., or COPY .env . commits secrets into image history. Even after a later layer-delete the secret is still there. Use BuildKit secrets (RUN --mount=type=secret,id=...) or runtime injection via K8s secret / Vault.apt-get install --no-install-recommends plus rm -rf /var/lib/apt/lists/* in the same RUN. Otherwise package lists stay in the layer.EXPOSE 8080 is documentation. Publishing happens at docker run -p or via a K8s Service.Anti-pattern:
FROM node:latest # latest tag, unreproducible
COPY . /app # including .env, .git, tests
RUN npm install --unsafe-perm # suggests root permission need
USER root # explicitly root
CMD ["npm", "start"]Better variant:
FROM node:20-bookworm-slim@sha256:<digest> AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY src ./src
FROM gcr.io/distroless/nodejs20-debian12@sha256:<digest>
WORKDIR /app
COPY --from=build /app /app
USER 10001
CMD ["src/index.js"]Before the image hits the registry:
trivy image --severity HIGH,CRITICAL --exit-code 1 <image>:<tag>Tie output to cve-triage. All high-severity findings face that skill's blocker criteria (KEV, reachable, exposed). Don't auto-block on every HIGH; you drown in low-impact base-image noise.
Scan configurations too: Trivy has --scanners misconfig for Dockerfile linting (overlapping with phase 2's rules).
See supply-chain for the full explanation; here are the container-specific calls.
cosign sign --yes <registry>/<image>@sha256:<digest> syft <image>:<tag> -o cyclonedx-json > sbom.json
cosign attest --yes --predicate sbom.json --type cyclonedx <image>@sha256:<digest>slsa-framework/slsa-github-generator for GitHub Actions builds.k8s-security phase 1.Registry choice:
The image is only half. Runtime policy decides what the container is allowed to do once it runs.
moby/moby repo or via tools like dockerd-rootless-setuptool.--security-opt apparmor=profile-name.--security-opt=no-new-privileges:true prevents setuid escalation inside the container.Ephemeral runtime: containers are stateless. State in volumes, not in writable layers. --rm for one-off runs, emptyDir or persistent-volume in K8s.
Layer 1: scope (every Dockerfile in the repo covered? base images pinned by digest? USER non-root in every stage?), assumptions ("distroless so safe" only with the concrete image hash verified), gaps (both build-time and runtime controls present?), consistency (trivy severity threshold consistent across images?).
Layer 2: image-tag and digest syntax correct, CVE references in scan triage verified against NVD, SLSA-level claim backed by the concrete builder config, no fabricated seccomp-profile names.
Container hardening review — <image(s)>
Dockerfiles in scope: <list>
Base images: <used, pinned by digest: yes/no>
Image content:
USER non-root: <yes/no per image>
Multi-stage build: <yes/no>
Read-only compatible: <yes/no, and which write paths>
.dockerignore present: <yes/no>
Secrets in layers: <none | found: ...>
Scan output (trivy/grype):
Blockers (HIGH/CRITICAL): <N, with cve-triage handoff>
Medium: <N>
Misconfigs: <list>
Signing + provenance:
Signed: <yes/no, cosign identity>
SBOM attested: <yes/no, format>
SLSA level effective: <L0–L3>
Runtime policy:
Seccomp: <default | custom | none>
AppArmor/SELinux: <profile | none>
Capabilities dropped: <ALL + adds | default>
Rootless runtime: <yes/no>
Findings (severity-sorted)
Verification-loop: ...~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.