how to check container base images for CVEs
A skill for scanning container base images for known CVEs: listing your base images, scanning with trivy or scout, distinguishing base-layer from app-layer findings, and rebuilding on a patched base. Use when hardening images, responding to a base-image CVE, or adding scanning to CI. Triggers: 'scan base image CVEs', 'trivy base image', 'docker scout CVEs'. Not for: scanning application dependencies, runtime container security.
how to check container base images for CVEs
TL;DR
List the base images in your Dockerfiles, scan them with trivy or docker scout, and separate base-layer findings from your app-layer findings. When the CVE is in the base, the fix is a newer base tag plus a rebuild, not a patch to your code.
how to check container base images for CVEsUse this when
- A CVE is announced in a common base image (Debian, Alpine, Ubuntu, distroless)
- You are hardening images before production or a compliance review
- Your scanner flags OS-package CVEs and you need to know which layer they come from
- You want base-image scanning added to the build pipeline
Not for this skill when
- The findings are all in your application dependencies (scan the lockfile instead)
- You need runtime protection for running containers (different tooling)
- You are choosing a base image from scratch (that is an architecture decision, this is a scanning workflow)
Steps
1. List every base image you actually use
Grep the FROM lines across all Dockerfiles. Repos accumulate Dockerfiles, and the one you forgot is the one with the ancient base.
grep -rn "^FROM" --include="Dockerfile*" .Expected: a complete list of base images and tags. Pin down any latest tags while you are here; unpinned bases make every future scan a surprise.
2. Scan the base image directly
Scan the base tag itself to see what it ships with, separate from your app layers.
trivy image [your-base-image]:[tag]Expected: a CVE list for the base image alone. Alternatives: docker scout cves [image] or grype [image]. Compare scanners occasionally; their databases differ and one may know a CVE the other misses.
3. Separate base-layer from app-layer findings
Scan your built image and diff against the base scan. Findings present in both live in the base; findings only in the built image are yours.
Expected: two buckets with different owners and different fixes. Base-layer CVEs get fixed by the base maintainer and picked up via rebuild; app-layer CVEs are your dependency upgrades.
4. Bump the base tag and rebuild
Move to the patched base tag, rebuild from scratch with no cache for the base stages, and re-scan.
docker build --no-cache -t [your-image]:[new-tag] .Expected: the re-scan shows the base-layer CVEs gone. If they persist, the base maintainer has not shipped a fix yet; check their security tracker and consider a slimmer base in the meantime.
5. Put the scan in the pipeline
Fail the build on new fixable critical CVEs in the base, and alert (not fail) on the rest. Rebuild images on a schedule even when your code has not changed, because bases rot.
Expected: CI catches the next base-image CVE at build time instead of in production. Scheduled rebuilds, weekly or so, keep the base fresh without manual effort.
Variant: distroless and minimal bases
Minimal bases have tiny CVE lists, which is the point, but the scanner still needs the right database for them. If trivy reports almost nothing on a distroless image, verify the scanner supports that base rather than assuming you are clean.
Variant: air-gapped or proxied environments
Scanners need their vulnerability databases refreshed. Mirror the database internally or allow the scanner's update endpoint through the proxy; a stale database is a false sense of security.
Why this happens
Base images are full Linux distributions with hundreds of OS packages, and CVEs land in those packages constantly. Your application code can be perfect and the image still ships known vulnerabilities, which is why the base gets its own scan and its own rebuild cadence.
Edge cases and pitfalls
- Multi-stage builds: scan the final stage's base, not just the build stage.
latesttags: the scan result is a moving target; pin to digest for reproducibility.- Unfixable CVEs in the base: some have no patched package yet; track them and move on, do not block every build forever.
- Scanner database lag: a CVE announced today may not be in the database until tomorrow.
- Language-specific packages in the base (like a Python in a slim image): those are base-layer too, fix via the base tag.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_pt8QCsGchwMpUTmsh4k6ZA
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.