Image Scanning & Vulnerabilities
Image Scanning & Vulnerabilities
Every container image you ship is a software supply-chain artifact. It contains an OS layer, system libraries, language runtimes, and your application dependencies — any of which may carry known security vulnerabilities (CVEs). At big-tech scale, security teams routinely block deployments when critical CVEs are present, and on-call engineers get paged when a zero-day appears in a base image that is already running in production.
This lesson covers the two scanners most widely used in professional pipelines — Trivy and Grype — how to read and triage their output, how to decide which CVEs actually matter, and the base-image update strategy that keeps your fleet clean without breaking production.
Why Image Scanning Belongs in CI, Not Just Pre-Deployment
The instinct is to scan once before you push. The reality is that a CVE published on any given Tuesday can affect images you built and shipped months ago. Big-tech security posture requires:
- Shift-left scanning — fail the build in CI when new Critical/High CVEs appear in your image.
- Continuous registry scanning — nightly re-scans of every tag in production, because CVE databases are updated daily.
- Automated rebases — triggered when the upstream base image publishes a patched digest.
Scanning only at build time without continuous re-scanning is security theatre. Your image is clean on Monday and vulnerable on Thursday when the NVD publishes a new advisory for OpenSSL.
Trivy — Industry Standard Scanner
Trivy (by Aqua Security) is the de facto standard in most pipelines. It scans OS packages (Alpine apk, Debian apt, RHEL rpm), language ecosystem files (package-lock.json, go.sum, requirements.txt, Gemfile.lock, pom.xml), and can also lint Dockerfiles and Kubernetes manifests for misconfigurations.
Grype — The Anchore Alternative
Grype (by Anchore) is Trivy's main rival. Both consume the same CVE databases (NVD, GitHub Advisory, OS-vendor advisories), but Grype pairs with Syft to generate a reusable SBOM (Software Bill of Materials) that can be scanned by any compatible tool — a key requirement in regulated environments and US federal procurement (NIST SSDF, EO 14028).
syft attest or store it in your registry), then re-scan that SBOM nightly without rebuilding the image. This is dramatically cheaper than re-pulling and re-scanning multi-GB images every night.Reading Scanner Output — CVE Triage
A raw Trivy scan of a real production image will surface dozens to hundreds of findings. Not all are equal. The professional triage workflow uses four filters:
- Severity — CRITICAL and HIGH are mandatory; MEDIUM are tracked; LOW and UNKNOWN are almost never actioned unless the library is directly invoked by your code.
- Fixed version available — if no fix exists, you cannot patch it by upgrading; you can only mitigate by removing the component or accept the risk. CVEs with no fix available are triage as "monitor" rather than "block".
- Reachability — is the vulnerable code path actually invoked by your application? Trivy and Grype do static analysis, not dynamic analysis. A vulnerable XML parser in a package you use only for JSON processing is low actual risk.
- CVSS vs EPSS — CVSS scores are assigned severity labels (Critical = 9.0–10.0). EPSS (Exploit Prediction Scoring System) gives the probability of exploitation in the wild in the next 30 days. A CVSS 9.8 with EPSS 0.001 is far lower priority than a CVSS 7.5 with EPSS 0.45. Grype now ships EPSS data; Trivy supports it via
--vex.
Integrating Trivy into a GitHub Actions Pipeline
The most common production pattern is a two-step job: build the image, scan it, and fail if Critical/High vulnerabilities are found. Attaching the SARIF report to GitHub Advanced Security gives your security team a searchable, unified CVE dashboard across every repo.
@master in production: aquasecurity/trivy-action@a20de5420d57c4102486cdd9349b532d1d638e94. Supply-chain attacks on GitHub Actions are real — a compromised action with write permissions could exfiltrate registry credentials from your runner environment.Base Image Update Strategy
Most CVEs in a container image come from the OS layer — not your application code. The fix is to rebuild on top of a patched base image. At big-tech companies, this process is automated:
- Dependabot / Renovate for Docker — open a PR automatically when the upstream base image digest changes. Renovate is more configurable and widely preferred.
- Nightly rebuild jobs — a scheduled pipeline that rebuilds every image from its pinned base tag. If
alpine:3.20received a patch, a nightly rebuild gets you the fix the next morning. - Digest pinning + automated updates — pin to
FROM alpine:3.20@sha256:abc123so builds are reproducible, but let Renovate update that SHA when a new digest is published.
requiredStatusChecks gate in Renovate (or your CI branch protection rule) ensures the scan must pass before the PR merges. Skipping this has caused multiple production incidents where a "security patch" introduced a regression.Vulnerability Exceptions (VEX / .trivyignore)
In a mature pipeline, some CVEs are legitimately acceptable — the library is present but the vulnerable code path is unreachable, or a fix does not yet exist and the risk is formally accepted. Recording these decisions as code prevents them from being silently forgotten and re-opened on every scan run.
Summary
Image scanning is not a checkbox — it is a continuous process. Use Trivy or Grype in CI as a hard gate on Critical/High findings with available fixes. Triage aggressively using EPSS alongside CVSS severity. Automate base image updates with Renovate, gated behind your scan job. Record accepted risk in version-controlled suppression files with expiry dates. This posture — scan, triage, automate, document — is what separates professional container security from security theatre.