Detecting Terraform Misconfigurations with Trivy: A Comparison Demo with Checkov
In a previous post, we created a Terraform vulnerability detection demo using Checkov (Checkov). This time, we scan the same Terraform code with Trivy and compare the results between the two tools.
Trivy is an open-source security scanner developed by Aqua Security. While widely known for container image scanning, it also supports IaC misconfiguration detection for Terraform, CloudFormation, Kubernetes manifests, and more.
Demo repository: codenote-net/trivy-terraform-demo
Key differences from Checkov
Even with identical Terraform code, Checkov and Trivy produce different results. Here is a summary of the key differences.
| Item | Checkov | Trivy |
|---|---|---|
| Developer | Prisma Cloud (Palo Alto Networks) | Aqua Security |
| Check ID format | CKV_AWS_XXX, CKV2_AWS_XXX | AVD-AWS-XXXX |
| Severity levels | PASSED / FAILED only | CRITICAL / HIGH / MEDIUM / LOW |
| Install | pip install checkov | brew install trivy |
| Scan command | checkov -d <dir> | trivy config <dir> |
| Scope | IaC focused | Container images, filesystems, IaC, SBOM, and more |
| Custom policies | Python | Rego (OPA) |
| GitHub Actions | bridgecrewio/checkov-action | aquasecurity/trivy-action |
The biggest difference is severity handling. Checkov classifies results as PASSED or FAILED. Trivy assigns CRITICAL / HIGH / MEDIUM / LOW severity to each finding, making it easier to prioritize remediation.
Repository structure
The structure mirrors the Checkov version: insecure/ contains vulnerable code and secure/ contains hardened code.
trivy-terraform-demo/
├── insecure/ # Intentionally vulnerable Terraform code
│ ├── provider.tf
│ ├── s3.tf
│ ├── sg.tf
│ ├── rds.tf
│ ├── iam.tf
│ └── cloudtrail.tf
├── secure/ # Hardened Terraform code
│ ├── provider.tf
│ ├── s3.tf
│ ├── sg.tf
│ ├── rds.tf
│ ├── iam.tf
│ └── cloudtrail.tf
└── .github/workflows/
└── trivy.ymlThe Terraform code is identical to checkov-terraform-demo. See PR #1 for implementation details.
Scanning with Trivy
Install Trivy via Homebrew:
brew install trivyRun the scan against the insecure/ directory:
trivy config insecure/To scan Terraform files only:
trivy config --misconfig-scanners terraform insecure/To filter by severity:
trivy config --severity HIGH,CRITICAL insecure/Unlike Checkov, which shows PASSED checks by default, Trivy only shows failures. To see all results including passed checks:
trivy config --include-non-failures insecure/Scan results: 27 FAILURES
Trivy v0.69.3 detected 27 FAILURES. Compared to Checkov’s 37, that is 10 fewer detections.
Here is the breakdown by resource. Since Trivy assigns severity to each finding, the tables include a severity column.
S3: 11 findings (CRITICAL 1 / HIGH 6 / MEDIUM 1 / LOW 3)
| AVD ID | Severity | Description |
|---|---|---|
| AVD-AWS-0086 | HIGH | Not blocking public ACLs |
| AVD-AWS-0087 | HIGH | Not blocking public policies |
| AVD-AWS-0089 | LOW | Access logging disabled |
| AVD-AWS-0090 | MEDIUM | Versioning disabled |
| AVD-AWS-0091 | HIGH | Not ignoring public ACLs |
| AVD-AWS-0092 | HIGH | Public ACL (public-read) is set |
| AVD-AWS-0093 | HIGH | Not restricting public buckets |
| AVD-AWS-0094 | LOW | No corresponding public access block |
| AVD-AWS-0132 | HIGH | Not encrypted with a customer managed key |
| AVD-AWS-0161 | CRITICAL | CloudTrail S3 bucket is publicly exposed |
| AVD-AWS-0163 | LOW | CloudTrail S3 bucket logging disabled |
Checkov flagged 8 S3 issues. Trivy checks each public access block setting individually (block_public_acls, block_public_policy, ignore_public_acls, restrict_public_buckets), resulting in more granular findings. It also flags the CloudTrail S3 bucket exposure as CRITICAL (AVD-AWS-0161).
Security Group: 6 findings (CRITICAL 1 / HIGH 2 / LOW 3)
| AVD ID | Severity | Description |
|---|---|---|
| AVD-AWS-0104 | CRITICAL | Unrestricted egress to any IP address |
| AVD-AWS-0107 | HIGH | Unrestricted ingress, all ports open to 0.0.0.0/0 (x2) |
| AVD-AWS-0124 | LOW | Rule missing description (x3) |
Checkov also found 6 Security Group issues, but with different granularity. Checkov checks specific ports individually: SSH (CKV_AWS_24), RDP (CKV_AWS_25), HTTP (CKV_AWS_260). Trivy uses AVD-AWS-0107 as a catch-all for unrestricted ingress.
RDS: 6 findings (HIGH 2 / MEDIUM 3 / LOW 1)
| AVD ID | Severity | Description |
|---|---|---|
| AVD-AWS-0077 | MEDIUM | Low backup retention period |
| AVD-AWS-0080 | HIGH | Storage encryption disabled |
| AVD-AWS-0133 | LOW | Performance Insights not enabled |
| AVD-AWS-0176 | MEDIUM | IAM Database Authentication not enabled |
| AVD-AWS-0177 | MEDIUM | Deletion protection not enabled |
| AVD-AWS-0180 | HIGH | Public access enabled |
Checkov found 9 RDS issues. Trivy does not check for Enhanced Monitoring (CKV_AWS_118), CloudWatch Logs export (CKV_AWS_129), or Multi-AZ (CKV_AWS_157). On the other hand, Trivy adds checks for backup retention period (AVD-AWS-0077) and Performance Insights (AVD-AWS-0133).
CloudTrail: 4 findings (HIGH 2 / MEDIUM 1 / LOW 1)
| AVD ID | Severity | Description |
|---|---|---|
| AVD-AWS-0014 | MEDIUM | Not enabled across all regions |
| AVD-AWS-0015 | HIGH | Not encrypted with a customer managed key |
| AVD-AWS-0016 | HIGH | Log file validation disabled |
| AVD-AWS-0162 | LOW | CloudWatch logging not configured |
Checkov found 5 CloudTrail issues. The missing SNS topic (CKV_AWS_252) is not checked by Trivy.
IAM: 0 findings
This is the most significant difference. Checkov detected 9 FAILED checks for the IAM wildcard policy (Action: "*", Resource: "*"), including privilege escalation (CKV_AWS_286), credentials exposure (CKV_AWS_287), and data exfiltration (CKV_AWS_288). Trivy v0.69.3 detected none.
Checkov performs deep analysis of what a wildcard policy enables. Trivy does not cover this area. If IAM policy auditing is important, Trivy alone is insufficient. Consider pairing it with Checkov or IAM Access Analyzer.
Detection count comparison
| Resource | Checkov | Trivy | Delta |
|---|---|---|---|
| S3 | 8 | 11 | Trivy +3 |
| Security Group | 6 | 6 | Same |
| RDS | 9 | 6 | Checkov +3 |
| IAM | 9 | 0 | Checkov +9 |
| CloudTrail | 5 | 4 | Checkov +1 |
| Total | 37 | 27 | Checkov +10 |
Scan results for the secure code
trivy config secure/Result: 0 FAILURES. The same hardened code that passes all Checkov checks also passes all Trivy checks.
Integrating with GitHub Actions
The repository uses aquasecurity/trivy-action to run Trivy automatically on pushes and pull requests.
name: Trivy
on:
push:
branches: [main]
paths:
- "insecure/**"
- "secure/**"
- ".github/workflows/trivy.yml"
pull_request:
branches: [main]
paths:
- "insecure/**"
- "secure/**"
- ".github/workflows/trivy.yml"
permissions:
contents: read
pull-requests: write
jobs:
scan-insecure:
name: "Scan insecure/"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy on insecure/
uses: aquasecurity/trivy-action@0.35.0
with:
scan-type: config
scan-ref: insecure/
format: table
exit-code: "0"
severity: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL
hide-progress: true
scan-secure:
name: "Scan secure/"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy on secure/
uses: aquasecurity/trivy-action@0.35.0
with:
scan-type: config
scan-ref: secure/
format: table
exit-code: "1"
severity: UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL
hide-progress: trueThe CI control mechanism differs from Checkov. Checkov uses soft_fail: true/false, while Trivy uses exit-code. Setting exit-code: "0" allows CI to pass even with failures; "1" fails the build.
Takeaways
Checkov found 37 issues while Trivy found 27 against the same Terraform code. More detections does not necessarily mean one tool is better than the other.
Here is how to choose:
- If IAM policy auditing matters, use Checkov: Trivy does not detect IAM wildcards
- If you want prioritized remediation, use Trivy: Severity levels (CRITICAL/HIGH/MEDIUM/LOW) help triage
- If you also scan container images, use Trivy: One tool covers both IaC and containers
- For best coverage, use both: The check sets are complementary, not overlapping
Both tools require no terraform init and integrate easily into CI. Start with one, then add the other if you find coverage gaps.
- Trivy demo repository: codenote-net/trivy-terraform-demo
- Checkov demo repository: codenote-net/checkov-terraform-demo
- Trivy: aquasecurity/trivy
- Trivy documentation: trivy.dev
- Aqua Vulnerability Database: avd.aquasec.com/misconfig
That’s all from the Gemba, where we dual-wield Checkov and Trivy.