# All Dockerfile FROM statements use official base images with SHA-256 digest pinning

- **Pattern:** `ab-001630` (`infrastructure-hardening.container-image-security.from-official-pinned`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001630
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001630)

## Why it matters

Floating tags like `latest` are mutable — the same tag can silently deliver a different, compromised image on the next build. An attacker who poisons an upstream image layer (CWE-1357, supply-chain attack) gains code execution in your container without triggering any diff in your repository. NIST 800-53 CM-14 and SLSA L3 both require provenance guarantees that floating tags cannot provide. CIS Docker Benchmark 5.31 flags this as a supply-chain control. SHA-256 digest pinning is the only mechanism that makes each build deterministic and independently verifiable.

## Severity rationale

Critical because a poisoned base image executes attacker-controlled code in every container instance derived from it, with no application-level defense possible.

## Remediation

Pin every Dockerfile `FROM` statement to a SHA-256 digest. Retrieve the digest from Docker Hub's tag detail page or with `docker inspect --format='{{index .RepoDigests 0}}' <image>`. Update `Dockerfile`:

```dockerfile
# Before
FROM node:20-alpine

# After
FROM node:20-alpine@sha256:d4b15b3d48f42059a15bd659be60afe21762aed87f80a581e029b26d60e0e0e0
```

For minimal attack surface, prefer distroless bases: `FROM gcr.io/distroless/nodejs20-debian12@sha256:...`. Automate digest refresh with Dependabot or Renovate so pins stay current without manual tracking.

## Detection

- **ID:** `from-official-pinned`
- **Severity:** `critical`
- **What to look for:** Enumerate every FROM statement across all Dockerfiles in the project. For each FROM, classify it as: (a) official image with digest pin, (b) official image without digest pin, or (c) unofficial/untrusted image. Count the total FROM statements and how many are properly pinned.
- **Pass criteria:** Every FROM statement across all Dockerfiles references an official or trusted base image pinned with a SHA-256 digest (e.g., `FROM node@sha256:abc123...` or `FROM gcr.io/distroless/base@sha256:...`). No floating tags such as `latest`, `main`, or bare version numbers are used — 100% of FROM statements must be pinned. Report the count: "X of Y FROM statements are digest-pinned."
- **Fail criteria:** Any FROM statement uses a floating tag (`latest`, `main`, version without digest) or references an unofficial/untrusted image source without digest pinning.
- **Do NOT pass when:** Only some FROM statements are pinned while others use floating tags — all must be pinned for pass.
- **Skip (N/A) when:** The project does not have a Dockerfile or container images.
- **Cross-reference:** Image scanning in `image-scanning` check verifies the scanned image matches the pinned digest.
- **Detail on fail:** Name the Dockerfile stages and their FROM statements. Example: `"Dockerfile stage 'builder' uses 'node:20' without digest. Stage 'final' uses 'distroless/nodejs' without digest pinning."`
- **Remediation:** Floating tags allow image content to change unexpectedly, introducing supply chain risk. Pin all base images with their SHA-256 digest in `Dockerfile`:

  ```dockerfile
  # Instead of: FROM node:20-alpine
  FROM node:20-alpine@sha256:abc123def456...

  # Or use the distroless secure base:
  FROM gcr.io/distroless/nodejs20-debian12@sha256:...
  ```

  Find digests on Docker Hub or your registry's tag details page. This ensures every build gets the exact image version you tested.

## External references

- cwe CWE-1357
- nist CM-14
- slsa L3
- external CIS-Docker-5.31 — https://www.cisecurity.org/benchmark/docker

Taxons: supply-chain, operational-readiness

HTML version: https://auditbuffet.com/patterns/ab-001630
