# Trivy Cheat Sheet — IaC Config Scanning and SBOM

> **Tool:** Trivy
> **Category:** Security & Compliance
> **Verified against:** Trivy 0.68.2, flags verified via `trivy config --help` / `trivy sbom --help` run
> locally, 2026-08-29
> **Official docs:** https://trivy.dev/latest/

## 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

```bash
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

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

## Helm chart scanning

```bash
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

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

## SBOM: generating one from an image

```bash
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

```bash
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:

```bash
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

```bash
# .trivyignore in the scan root
CVE-2023-12345
AVD-AWS-0001   # a specific misconfig check ID, with an optional expiry: exp:2026-12-31
```

```bash
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

```yaml
# .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

| Flag | What it does |
|---|---|
| `--misconfig-scanners` | Comma-separated list of which IaC types to scan (default: azure-arm, cloudformation, dockerfile, helm, kubernetes, terraform, terraformplan-json, terraformplan-snapshot) |
| `--tf-vars` | Path(s) to `.tfvars` files to load when evaluating Terraform — needed for variables that affect security-relevant attributes |
| `--tf-exclude-downloaded-modules` | Skip findings inside anything Terraform downloaded into `.terraform/modules` |
| `--k8s-version` | Kubernetes version to validate deprecated/removed API usage against |
| `--helm-set` / `--helm-set-string` / `--helm-set-file` | Same semantics as `helm template`'s equivalent flags — override chart values before scanning the rendered output |
| `--helm-values` | Path(s) to override `values.yaml` files |
| `--compliance` | Map findings to a named compliance report (e.g. `docker-cis-1.6.0`) |
| `--report` | `all` or `summary` — how much detail the compliance report includes |
| `--config-check` | Path to custom Rego check files/directories to load alongside the built-ins |
| `--config-data` | Path(s) providing data available to custom Rego checks |
| `--include-deprecated-checks` | Include checks Aqua has since deprecated — useful when comparing historical scan results |
| `--file-patterns` | Override 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:

```bash
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.
