Tools · Secrets and dependency scanning · 05 / 06
Trivy: gate container images and Terraform in one pipeline step
Use Trivy to scan a built image for fixable high-severity vulnerabilities, scan Terraform for misconfigurations, and fail CI only on findings the team has agreed to block.
One tool, several targets
Trivy scans container images, filesystems, git repositories, and infrastructure-as-code. For images it reads OS packages and language lockfiles. For Terraform, Kubernetes manifests, and Dockerfiles it applies misconfiguration checks. Running both in the same CI step keeps the configuration in one place.
Base image choice usually matters more than any single upgrade. Scanning two candidates side by side, such as a full Debian image and its slim or distroless counterpart, often shows the smaller one carrying a fraction of the packages and findings. trivy config on the Dockerfile adds checks such as running as root or a missing HEALTHCHECK.
trivy image --severity HIGH,CRITICAL -q python:3.12 | grep Total trivy image --severity HIGH,CRITICAL -q python:3.12-slim | grep Total trivy config --severity HIGH,CRITICAL Dockerfile
An image gate that teams can live with
--severity limits results; --ignore-unfixed hides vulnerabilities with no released fix, because the team cannot act on them by upgrading; --exit-code 1 turns remaining results into a failed job.
trivy image --severity HIGH,CRITICAL --ignore-unfixed \ --exit-code 1 ghcr.io/hackblue-lab/api:2026.10.2 trivy image --format cyclonedx -o sbom.cdx.json ghcr.io/hackblue-lab/api:2026.10.2
The vulnerability table
Installed and fixed versions tell the developer exactly which upgrade closes each row.
ghcr.io/hackblue-lab/api:2026.10.2 (debian 12.7) Total: 2 (HIGH: 1, CRITICAL: 1) ┌──────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┐ │ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ ├──────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┤ │ libexpat1│ CVE-2024-45491 │ CRITICAL │ fixed │ 2.5.0-1 │ 2.5.0-1+deb12u1│ │ urllib3 │ CVE-2024-37891 │ HIGH │ fixed │ 2.0.7 │ 2.2.2 │ └──────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┘
Worked case: a Terraform bucket and an accepted risk
trivy config on the infra folder flags an S3 bucket without public access block settings. The module gains aws_s3_bucket_public_access_block with all four settings true. A second finding, a missing access log on a bucket that only holds build caches, is accepted: it goes into .trivyignore with the check id, a reason, and a review date.
trivy config --severity HIGH,CRITICAL --exit-code 1 infra/ # .trivyignore # build cache bucket, no customer data; review 2027-04-01 AVD-AWS-0089
Diagram
A Trivy step in CI
- Build
Produce the image the pipeline will ship.
docker build -t ghcr.io/hackblue-lab/api:2026.10.2 .
- Scan image
Block fixable high and critical issues.
trivy image --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 <image>
- Scan config
Check Terraform before apply.
trivy config --exit-code 1 infra/
- Record
Keep the SBOM with the release.
trivy image --format cyclonedx -o sbom.cdx.json <image>
Check yourself
1. Why do many teams pass --ignore-unfixed in a blocking gate?
2. Which Trivy command checks Terraform files for misconfigurations?
3. What should a .trivyignore entry carry besides the check id?