cheat-sheets

Container Scanning

Vulnerability scanners compare the packages in an image, filesystem or repo against CVE databases, and can also check IaC for misconfiguration and find embedded secrets. This sheet covers Trivy (all-rounder), Grype and Syft (scanner plus SBOM generator). Used with Docker, Dockerfile and Kubernetes; for secrets in Git see Secret Scanning.

Documentation: Trivy · Grype · Syft

Install #

bash
# Trivy (Debian/Ubuntu apt repo is documented at trivy.dev), or:
brew install trivy grype syft
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy image <IMAGE>

Trivy #

COMMANDDESCRIPTION
trivy image <IMAGE>Scan an image (pulled from a registry, or the local Docker daemon)
trivy image --severity HIGH,CRITICAL <IMAGE>Filter by severity
trivy image --ignore-unfixed <IMAGE>Hide CVEs with no fix available
trivy image --exit-code 1 --severity CRITICAL <IMAGE>Fail (for CI) on criticals
trivy image --scanners vuln,secret,misconfig <IMAGE>Choose scanners (default is vuln,secret)
trivy image -f json -o report.json <IMAGE>JSON report (table, json, sarif, cyclonedx, spdx-json)
trivy image --input image.tarScan a docker save tarball
trivy fs .Scan a project directory (lockfiles, secrets)
trivy repo https://github.com/<ORG>/<REPO>Scan a remote repo
trivy config .Check Dockerfiles, Compose, Kubernetes and Terraform for misconfigurations
trivy k8s --report summary clusterScan a running Kubernetes cluster
trivy image --format cyclonedx -o sbom.json <IMAGE>Generate an SBOM
trivy sbom sbom.jsonScan an existing SBOM
trivy image --skip-db-update <IMAGE>Use the cached DB (offline/air-gapped)
trivy image --download-db-onlyJust refresh the vulnerability DB
trivy clean --allClear caches

Ignore Findings #

.trivyignore (one CVE per line, optional expiry and comment):

text
# not reachable, upstream fix pending
CVE-2024-12345 exp:2026-12-31

Rich ignores (paths, purls) go in trivy.yaml / .trivyignore.yaml; see the Trivy filtering docs.

CI (GitHub Actions) #

yaml
- uses: aquasecurity/trivy-action@<TAG>
  with:
    image-ref: ghcr.io/<OWNER>/<IMAGE>:${{ github.sha }}
    severity: CRITICAL,HIGH
    ignore-unfixed: true
    exit-code: "1"

Pin the action to a tag or commit SHA. Scan the freshly built image before pushing the latest tag.

Grype and Syft #

COMMANDDESCRIPTION
grype <IMAGE>Scan an image
grype dir:.Scan a directory
grype sbom:./sbom.jsonScan an SBOM
grype <IMAGE> --fail-on highExit non-zero at or above a severity
grype <IMAGE> --only-fixedOnly CVEs that have a fix
grype <IMAGE> -o jsonOutput as table, json, sarif, cyclonedx
grype db updateUpdate the vulnerability DB
grype db statusShow the DB build date
syft <IMAGE> -o spdx-json=sbom.spdx.jsonGenerate an SBOM
syft <IMAGE> -o cyclonedx-json=sbom.cdx.jsonCycloneDX SBOM
syft dir:.Catalogue a directory

.grype.yaml ignore rules:

yaml
ignore:
  - vulnerability: CVE-2024-12345
  - package:
      name: busybox
      type: apk

Reducing Findings #

  • Pick small bases (alpine, debian-slim, distroless) and multi-stage builds (Dockerfile).
  • Pin by digest (image: nginx@sha256:...) and let Renovate raise update PRs.
  • Run containers as non-root, read-only filesystem, drop capabilities:
yaml
services:
  app:
    image: ghcr.io/example/app:1.2.3
    user: "1000:1000"
    read_only: true
    cap_drop: [ALL]
    security_opt: ["no-new-privileges:true"]
    tmpfs: [/tmp]

See Docker-Compose.

  • Rescan on a schedule, because new CVEs appear against old images.

Troubleshooting #

SYMPTOMFIX
failed to download vulnerability DB / rate limitedRetry later, or mirror the DB with --db-repository; use --skip-db-update once cached
Image not foundPull it first, or use --image-src docker,podman,remote to pick where to look
Scan is slow in CICache ~/.cache/trivy between runs
Same CVE shows in Trivy but not Grype (or vice versa)Databases and matching differ; pick one gate and use the other as a cross-check
Noise from unfixable OS CVEs--ignore-unfixed / --only-fixed, or rebase to a newer base image
Search all sheets

Tip: / or Ctrl/Cmd+K to open, Esc to close.