trivy-config
Runs Trivy's misconfiguration scanner (`trivy config`) against IaC directories to detect security issues across Terraform, CloudFormation, Kubernetes manifests, Helm charts, Dockerfiles, and Azure ARM templates - installs Trivy, scans with severity gating via `--exit-code`, suppresses findings via `.trivyignore` / `.trivyignore.yaml` or inline annotations, extends built-in checks with custom Rego policies, and emits SARIF for GitHub Code Scanning. Trivy is the forward path from tfsec (per Aqua Security's own migration guidance). Use when adopting a consolidated IaC scanner for new projects, migrating away from tfsec, or scanning mixed IaC stacks with a single tool.
Install with skills.sh (any agent)
npx skills add testland/qa --skill trivy-configtrivy-config
Overview
Trivy is Aqua Security's consolidated misconfiguration scanner (trivy config) and the forward path from tfsec. Per the tfsec skill, trivy.dev misconfiguration docs (opens in new window), and Aqua's own documentation, new projects should evaluate Trivy first; tfsec's checks ship inside Trivy under trivy config.
Pinned versions
Bump these together when updating; they are the only version-sensitive tokens in this skill. GitHub release assets are version-stamped, so the RPM URL in Step 1 pins a tag rather than using latest/download.
| Component | Pin | Used in |
|---|---|---|
| Trivy | v0.72.0 (latest as of 2026-06-30) | Step 1 install |
aquasecurity/trivy-action | 0.31.0 | CI workflow (Step 7) |
actions/checkout | v5 | CI workflow (Step 7) |
github/codeql-action/upload-sarif | v3 | CI workflow (Step 7) |
Step 1 - Install
Per trivy.dev installation docs (opens in new window) (the RPM URL pins the Trivy tag from the Pinned versions section above):
# macOS
brew install trivy
# Debian / Ubuntu
sudo apt-get install wget apt-transport-https gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key \
| gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] \
https://aquasecurity.github.io/trivy-repo/deb generic main" \
| sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt-get update && sudo apt-get install trivy
# RPM (RHEL / Fedora)
sudo rpm -ivh https://github.com/aquasecurity/trivy/releases/download/v0.72.0/trivy_0.72.0_Linux-64bit.rpm
# Docker (no local install)
docker run --rm -v $(pwd):/workspace aquasec/trivy config /workspaceVerify: trivy --version.
Step 2 - First scan
Per cli reference (opens in new window), trivy config accepts a path (file or directory). Trivy auto-detects IaC types - Terraform, CloudFormation, Kubernetes manifests, Helm charts, Dockerfiles, and Azure ARM templates can all coexist in the same directory per mc docs (opens in new window).
# Scan the current directory (all IaC types)
trivy config .
# Scan a specific subdirectory
trivy config ./infra/
# Show only HIGH and CRITICAL findings
trivy config --severity HIGH,CRITICAL .
# Fail CI when any finding is found (exit code 1)
trivy config --exit-code 1 --severity HIGH,CRITICAL .
# Include passed checks alongside failures
trivy config --include-non-failures .Built-in checks are distributed as an OPA bundle at ghcr.io/aquasecurity/trivy-checks (per checks repo (opens in new window)). Trivy caches the bundle locally and refreshes every 24 hours. An embedded fallback is included in the binary for air-gapped environments.
Step 3 - Severity gating
Per cli reference (opens in new window), --exit-code and --severity are the two levers for CI gating:
| Flag | Purpose | Example |
|---|---|---|
--exit-code int | Exit code when findings match | --exit-code 1 |
--severity strings | Comma-separated severity filter | HIGH,CRITICAL |
Pattern: gate hard on CRITICAL first; expand to HIGH after the team has reviewed the initial finding set.
# Hard fail on CRITICAL only (bootstrap phase)
trivy config --exit-code 1 --severity CRITICAL .
# Ratchet: add HIGH once existing findings are triaged
trivy config --exit-code 1 --severity HIGH,CRITICAL .Step 4 - Output formats
Per trivy.dev reporting docs (opens in new window), --format accepts:
| Value | Use case |
|---|---|
table (default) | Human-readable terminal output |
json | Machine-parseable; pipe to jq |
sarif | GitHub Code Scanning / SARIF 2.1.0 |
template | Custom templates (JUnit, ASFF, HTML via contrib/) |
# JSON for parsing / badge generation
trivy config --format json --output trivy.json .
# SARIF for GitHub Code Scanning
trivy config --format sarif --output trivy.sarif .
# JUnit XML for CI test reporting
trivy config --format template \
--template "@contrib/junit.tpl" \
--output trivy-junit.xml .Step 5 - Suppressing findings
Per trivy.dev filtering docs (opens in new window):
.trivyignore (check-ID list)
# .trivyignore
# Suppress a specific misconfig check
AVD-DS-0002
# Suppress a CVE alongside a misconfig in the same file
CVE-2018-14618.trivyignore.yaml (structured suppression)
Per filter docs (opens in new window), the YAML variant supports scoped expiring suppressions:
# .trivyignore.yaml
misconfigurations:
- id: AVD-DS-0001
- id: AVD-DS-0002
paths:
- "infra/legacy/Dockerfile"
statement: "Legacy image; migration tracked in JIRA-4321"
expired_at: "2026-12-31"Run with: trivy config --ignorefile ./.trivyignore.yaml .
Per cli reference (opens in new window), --ignorefile defaults to .trivyignore and accepts an alternate path.
Rego-based ignore policy
Per filter docs (opens in new window), pass --ignore-policy with a Rego file that contains a trivy package and an ignore rule:
# ignore_legacy.rego
package trivy
default ignore = false
ignore {
input.Type == "terraform"
input.Namespace == "user.legacy"
}trivy config --ignore-policy ignore_legacy.rego .Step 6 - Custom Rego policies
Per trivy.dev custom checks docs (opens in new window), pass custom policies with --config-check and scope them with --namespaces:
trivy config \
--config-check ./policies/ \
--namespaces user \
./infra/Per custom docs (opens in new window), the --namespaces value (here: user) must match the first segment of the package path. For a full policy file (METADATA block + deny rule) and the list of supported input.selector types, see references/trivy-config.md.
Step 7 - CI integration (GitHub Actions)
Per trivy.dev reporting docs (opens in new window), SARIF output integrates directly with GitHub Code Scanning. For the full GitHub Actions workflow (with security-events: write, the pinned trivy-action, and an if: always() SARIF upload so findings surface even when the scan exits non-zero), see references/trivy-config.md.
To scope the scan to a single IaC type, pass --misconfig-scanners per cli reference (opens in new window):
# Terraform only
trivy config \
--misconfig-scanners terraform \
./terraform/
# Kubernetes manifests only
trivy config \
--misconfig-scanners kubernetes \
./k8s/Per cli reference (opens in new window), --misconfig-scanners accepts a comma-separated list from: azure-arm, cloudformation, dockerfile, helm, kubernetes, terraform, terraformplan-json, terraformplan-snapshot.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
--exit-code 0 in CI | Misconfigs are logged but never block | Use --exit-code 1 with --severity HIGH,CRITICAL (Step 3) |
.trivyignore entries without a statement | Invisible to reviewers; silent security debt | Use .trivyignore.yaml with statement + expired_at (Step 5) |
Running trivy config and tfsec in parallel without a unifier | Duplicate findings flood CI output | Route both through a single unifying reporter |
| Custom policies missing METADATA block | No severity, no title in report output | Always include METADATA with id, severity, schemas (Step 6) |
Skipping bundle updates (--skip-check-update) permanently | Stale checks miss new misconfig rules | Use for caching in CI; re-enable updates on a scheduled run |
Limitations
References
trivy-config - deep reference
View source (opens in new window)trivy-config - deep reference
Long inline blocks moved out of the SKILL.md spine to keep the core scan flow lean. Version pins referenced below live in the Pinned versions section of SKILL.md.
Custom Rego policy - full example
Per trivy.dev custom checks docs (opens in new window), each policy file requires a unique package declaration and a METADATA annotation block:
# policies/require_cost_center_tag.rego
# METADATA
# title: "EC2 instances must have cost_center tag"
# description: "Untagged resources cannot be allocated to cost centers"
# schemas:
# - input: schema["cloud"]
# custom:
# id: USER-TF-001
# severity: HIGH
# input:
# selector:
# - type: cloud
package user.terraform.USER-TF-001
import rego.v1
deny contains res if {
instance := input.aws.ec2.instances[_]
not instance.tags["cost_center"]
res := result.new(
sprintf("EC2 instance '%s' missing cost_center tag", [instance.id.value]),
instance,
)
}The --namespaces value (here: user) must match the first segment of the package path. Supported input.selector types include cloud (Terraform / CloudFormation), kubernetes, dockerfile, yaml, json, toml, and terraform-raw.
GitHub Actions workflow - full YAML
SARIF output integrates directly with GitHub Code Scanning. The if: always() on the upload step ensures findings appear in the Security tab even when the scan step exits non-zero:
# .github/workflows/trivy-iac.yml
jobs:
trivy-config:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v5
- name: Run Trivy config scan
uses: aquasecurity/trivy-action@0.31.0
with:
scan-type: config
scan-ref: .
severity: HIGH,CRITICAL
exit-code: 1
format: sarif
output: trivy.sarif
ignore-unfixed: true
- name: Upload SARIF to GitHub Code Scanning
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy.sarifRelated skills
checkov-policy
Configures Checkov for IaC security scanning across Terraform, CloudFormation, Kubernetes, Helm, ARM, Serverless, AWS CDK - `pip install checkov`, custom Python checks, SARIF / JUnit output, and `--baseline` gating so CI fails only on new findings in legacy code. Use for the broadest built-in rule set with Python custom checks; for Terraform-only scanning use tfsec-policy, for wider platform breadth (OpenAPI / Pulumi / Crossplane) use kics-policy, and for a consolidated new-project scanner use trivy-config.
helm-chart-tester
Configures helm-unittest for Helm chart unit testing - installs the `helm-unittest` plugin, authors `tests/*.yaml` per template, asserts on rendered manifests (`isKind`, `equal`, `matchRegex`, snapshots), plus `helm lint` and `helm template` render testing. Use to verify a chart's template logic and rendered shape; this is unit-level correctness testing, not security scanning - to enforce policy on rendered manifests use policy-as-code-runner, and to scan charts for misconfigurations use checkov-policy or kics-policy.
kics-policy
Configures KICS (Keeping Infrastructure as Code Secure), Checkmarx's scanner covering Terraform, Kubernetes, Helm, Dockerfile, OpenAPI, Ansible, ARM, CloudFormation, Pulumi, Crossplane - CLI / Docker / GitHub Action / pre-commit, JSON / SARIF / HTML / JUnit output, custom Rego queries. Use for the widest platform breadth, especially OpenAPI / Pulumi / Crossplane; for the broadest built-in checks with Python custom rules use checkov-policy, for Terraform-only scanning use tfsec-policy, and for a consolidated scanner use trivy-config.
policy-as-code-runner
Configures policy-as-code testing using OPA / Conftest / Cedar - authors policies in Rego (OPA's language), runs Conftest against Kubernetes manifests / Terraform plans / Dockerfiles / arbitrary structured data, integrates with CI for PR-time policy gates. Per OPA's docs: "an open source, general-purpose policy engine that unifies policy enforcement across the stack." Use to express + enforce custom policies (cost limits, tagging requirements, security baselines) that Checkov / tfsec / KICS don't cover.
tfsec-policy
Configures tfsec for Terraform-specific security scanning - covers AWS / Azure / GCP / Kubernetes / OpenStack / Oracle / DigitalOcean / CloudStack, custom YAML rules, and SARIF / JUnit / Markdown output. Note: tfsec is transitioning to Trivy per Aqua Security, so new projects evaluate that first. Use for an existing Terraform-only tfsec stack; for the consolidated forward-path scanner use trivy-config, for the broadest multi-framework checks use checkov-policy, and for wider platform breadth use kics-policy.