Verified11 commandsAI-assisted

IaC Config Scanning and SBOM

.md

Verified against Trivy 0.68.2, flags verified via `trivy config --help` / `trivy sbom --help` run · official docs

What it is and where it fits#

Trivy's config command covers Terraform, CloudFormation, Kubernetes manifests, Helm charts, Dockerfiles and Azure ARM in one scanner — this is what absorbed tfsec's entire check library after Aqua put tfsec into maintenance mode (see the tfsec cheat sheet). Trivy's sbom command closes the loop with Syft/Grype's generate-then-scan pattern, but from a single tool: Trivy can both generate an SBOM from an image and later re-scan that saved SBOM for newly published vulnerabilities without ever touching the original image again. See the companion page for image/filesystem vulnerability scanning.

Config / IaC misconfiguration scanning#

trivy config .                                       # auto-detects Terraform/K8s/CloudFormation/Dockerfile/Helm in the directory
trivy config --severity CRITICAL,HIGH .
trivy config --exit-code 1 --severity CRITICAL,HIGH .   # gate CI — exit code defaults to 0 here too
trivy config main.tf                                 # a single file

Sample output shape (illustrative — exact check IDs/messages evolve with the checks-bundle version Trivy downloads):

main.tf (terraform) ==================== Tests: 24 (SUCCESSES: 20, FAILURES: 4, EXCEPTIONS: 0) Failures: 4 (LOW: 0, MEDIUM: 1, HIGH: 2, CRITICAL: 1) CRITICAL: Bucket does not have logging enabled. ════════════════════════════════════════ S3 bucket does not have logging enabled, exposing it to audit-trail gaps. ──────────────────────────────────────── main.tf:3-8 ──────────────────────────────────────── 3 ┌ resource "aws_s3_bucket" "example" { 4 │ bucket = "my-bucket" 5 └ } ────────────────────────────────────────

Scoping to a specific IaC type#

trivy config --misconfig-scanners terraform .
trivy config --misconfig-scanners kubernetes .
trivy config --misconfig-scanners dockerfile,cloudformation .

Helm chart scanning#

trivy config ./mychart \
  --helm-set replicaCount=3 \
  --helm-values ./mychart/values-prod.yaml

Trivy renders the chart with the given values (the same way helm template would) before scanning the resulting manifests — a chart with security-sensitive defaults that get overridden safely in your actual values file won't produce false-positive findings, as long as you point Trivy at the real values file.

Compliance report mode#

trivy image --compliance docker-cis-1.6.0 myapp:latest   # map findings to a CIS Docker Benchmark report

SBOM: generating one from an image#

trivy image --format cyclonedx --output sbom.cdx.json myapp:latest
trivy image --format spdx-json --output sbom.spdx.json myapp:latest

SBOM: scanning an existing SBOM for vulnerabilities#

trivy sbom sbom.cdx.json
trivy sbom /path/to/report.cdx.intoto.jsonl          # a signed/attested CycloneDX document

Real-world scenario: same-day response to a newly published CVE, no re-scan needed#

This is the "instant search across every artifact we've ever built" use case: if you've been saving a CycloneDX SBOM alongside every release, a newly published CVE against a library you've shipped in the past can be checked instantly against every historical SBOM, without re-pulling or re-scanning the original images:

for sbom in releases/*/sbom.cdx.json; do
  echo "=== $sbom ==="
  trivy sbom "$sbom" --severity CRITICAL
done

At real scale this is exactly the job a tool like Dependency-Track automates continuously (see the DevSecOps tutorial series' supply-chain chapter) — the loop above is the same idea done by hand for a small number of saved SBOMs.

Ignoring specific findings#

# .trivyignore in the scan root
CVE-2023-12345
AVD-AWS-0001   # a specific misconfig check ID, with an optional expiry: exp:2026-12-31
trivy config --ignorefile .trivyignore.yaml .        # override the default .trivyignore filename

The plain .trivyignore format is a flat ID-per-line list; a .trivyignore.yaml variant supports structured per-finding metadata (expiry dates, statements) if you need an auditable reason attached to each suppression — worth the switch the moment a security review asks "why is this ignored."

Real-world scenario: GitHub Actions IaC + SBOM recipe#

# .github/workflows/trivy-iac-sbom.yml
name: Trivy IaC + SBOM
on: [pull_request]
jobs:
  iac:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: aquasecurity/trivy-action@0.28.0
        with:
          scan-type: config
          scan-ref: .
          exit-code: '1'
          severity: 'CRITICAL,HIGH'
  sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .
      - uses: aquasecurity/trivy-action@0.28.0
        with:
          image-ref: myapp:${{ github.sha }}
          format: cyclonedx
          output: sbom.cdx.json
      - uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.cdx.json

Full config-scan flag reference#

FlagWhat it does
--misconfig-scannersComma-separated list of which IaC types to scan (default: azure-arm, cloudformation, dockerfile, helm, kubernetes, terraform, terraformplan-json, terraformplan-snapshot)
--tf-varsPath(s) to .tfvars files to load when evaluating Terraform — needed for variables that affect security-relevant attributes
--tf-exclude-downloaded-modulesSkip findings inside anything Terraform downloaded into .terraform/modules
--k8s-versionKubernetes version to validate deprecated/removed API usage against
--helm-set / --helm-set-string / --helm-set-fileSame semantics as helm template's equivalent flags — override chart values before scanning the rendered output
--helm-valuesPath(s) to override values.yaml files
--complianceMap findings to a named compliance report (e.g. docker-cis-1.6.0)
--reportall or summary — how much detail the compliance report includes
--config-checkPath to custom Rego check files/directories to load alongside the built-ins
--config-dataPath(s) providing data available to custom Rego checks
--include-deprecated-checksInclude checks Aqua has since deprecated — useful when comparing historical scan results
--file-patternsOverride which file patterns are treated as config files worth scanning

Troubleshooting#

"Terraform scan shows false positives for a resource whose real config comes from a variable" — pass --tf-vars path/to/terraform.tfvars explicitly; Trivy doesn't automatically discover every possible .tfvars file the way terraform plan does with its precedence rules.

"Helm chart scan doesn't reflect my production values" — same root cause as the Terraform case: Trivy renders the chart with whatever values you give it via --helm-values/--helm-set; without them, it scans the chart's default values, which are frequently more permissive than what actually deploys.

"SBOM scan says 0 vulnerabilities but I know the image has some" — confirm the SBOM was generated before the vulnerability in question was published to the DB, or that trivy sbom isn't using a stale cached DB (trivy image --download-db-only refreshes it).

Real-world scenario: compliance report for an audit#

A team preparing for a CIS Docker Benchmark review needs a structured report mapping technical findings to specific benchmark controls, not just a raw vulnerability list:

trivy image --compliance docker-cis-1.6.0 --report summary myapp:latest

This reframes the same underlying scan data around the benchmark's own control numbering — the shape an auditor actually expects to see, rather than a generic CVE table they'd need to manually cross-reference against the benchmark themselves.

Common pitfalls#

  • Scanning raw Terraform without expected variables — misconfig checks can produce false positives/negatives if a resource's security-relevant attribute is only set via a .tfvars file Trivy didn't load; pass --tf-vars explicitly when a project relies on external variable files.
  • Confusing .trivyignore scope — it applies per invocation directory; a monorepo with several IaC roots needs either one ignore file per root or an explicit --ignorefile path per scan.
  • Forgetting --exit-code 1 here too — the config scanner shares Trivy's global "reports, doesn't gate by default" behavior.

When to reach for something else#

For Terraform specifically, tfsec's check library is now literally sourced from Trivy, so there's no functional gap to fill by adding tfsec alongside Trivy — pick one. Checkov remains meaningfully different: it covers a few IaC ecosystems Trivy doesn't (notably Ansible and Bicep as of this writing) and has its own distinct check catalog worth cross-checking on a security-critical project rather than assuming full overlap.