Why Infrastructure as Code Needs Static Analysis Too
Infrastructure as Code (IaC) — Terraform, Pulumi, OpenTofu, CloudFormation — turned infrastructure into something version-controlled, reviewable, and reproducible. But that also means a single misconfigured line — an S3 bucket left public, a security group open to 0.0.0.0/0, an unencrypted database — can be committed, merged, and deployed automatically, at scale, before anyone notices.
Static Application Security Testing (SAST) for IaC works the same way it does for application code: it parses configuration files without deploying anything, and flags insecure patterns against a known rule set. OWASP's list of Source Code Analysis Tools includes several options for this. This article focuses on Checkov, an open-source policy-as-code scanner built by Bridgecrew (now part of Palo Alto Networks), chosen here specifically because it's free, multi-cloud, and multi-framework — without relying on tfsec.
Why Checkov
Checkov scans IaC files and builds a graph representation of the resources and their relationships, then evaluates that graph against hundreds of built-in security and compliance policies.
Key features:
- Supports Terraform, OpenTofu, CloudFormation, Kubernetes manifests, Dockerfiles, Helm charts, Serverless framework, ARM templates, and more — not limited to one IaC tool.
- Over 1,000 built-in policies covering AWS, Azure, GCP, and Kubernetes best practices (CIS Benchmarks aligned).
- Supports custom policies written in Python or YAML, so teams can encode their own organizational rules.
- Outputs in CLI text, JSON, JUnit XML, and SARIF — SARIF format integrates directly with GitHub's Security tab.
- Can suppress specific findings inline with comments, without disabling the whole rule globally.
Installing and Running Checkov
pip install checkov
# Scan a directory of Terraform files
checkov -d ./infrastructure
# Scan a single file
checkov -f main.tf
# Output as JSON for CI pipelines
checkov -d ./infrastructure -o json --output-file-path ./checkov_report.json
A Real Example
Consider this Terraform snippet — a common pattern that looks harmless but hides several real misconfigurations:
resource "aws_s3_bucket" "data" {
bucket = "company-data-bucket"
acl = "public-read"
}
resource "aws_security_group" "web" {
name = "web-sg"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_db_instance" "app_db" {
identifier = "app-database"
engine = "mysql"
instance_class = "db.t3.micro"
allocated_storage = 20
storage_encrypted = false
publicly_accessible = true
}
Running checkov -f main.tf produces output like:
Check: CKV_AWS_20: "S3 Bucket has an ACL defined which allows public READ access."
FAILED for resource: aws_s3_bucket.data
File: main.tf:1-4
Check: CKV_AWS_24: "Ensure no security groups allow ingress from 0.0.0.0:0 to port 22"
FAILED for resource: aws_security_group.web
File: main.tf:6-13
Check: CKV_AWS_16: "Ensure RDS instances have storage_encrypted enabled"
FAILED for resource: aws_db_instance.app_db
File: main.tf:15-21
Check: CKV_AWS_17: "Ensure RDS instances are not publicly accessible"
FAILED for resource: aws_db_instance.app_db
File: main.tf:15-21
Each check includes a rule ID (CKV_AWS_20, CKV_AWS_24...), a human-readable description, the exact resource name, and the file/line range — enough for a developer to jump straight to the fix.
Fixing the Findings
resource "aws_s3_bucket" "data" {
bucket = "company-data-bucket"
acl = "private"
}
resource "aws_security_group" "web" {
name = "web-sg"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"] # restricted to internal VPC range
}
}
resource "aws_db_instance" "app_db" {
identifier = "app-database"
engine = "mysql"
instance_class = "db.t3.micro"
allocated_storage = 20
storage_encrypted = true
publicly_accessible = false
}
Re-running Checkov against the fixed file shows all four checks passing.
Integrating Checkov into a CI Pipeline
A minimal GitHub Actions workflow that scans every Terraform change and fails the build on findings:
name: IaC Security Scan with Checkov
on: [push, pull_request]
jobs:
checkov-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: ./infrastructure
framework: terraform
output_format: sarif
output_file_path: checkov-results.sarif
- name: Upload SARIF to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: checkov-results.sarif
Uploading the SARIF report to GitHub's Security tab means findings show up alongside other code scanning alerts, with no separate dashboard needed.
Public Repository Example
You can see Checkov applied to real Terraform code here:
-
github.com/bridgecrewio/checkov — the tool's own repository, which includes hundreds of example Terraform/CloudFormation/Kubernetes files used as test fixtures for its own rule set, under
tests/terraform/checks. - github.com/bridgecrewio/terragoat — a deliberately vulnerable, multi-cloud Terraform application built specifically to be scanned by tools like Checkov, useful as a public sandbox to practice against realistic misconfigurations.
Cloning either repo and running checkov -d . locally demonstrates the tool against real, non-trivial infrastructure code.
Where Checkov Fits Among IaC SAST Tools
| Tool | IaC scope | License | Notes |
|---|---|---|---|
| Checkov | Terraform, CloudFormation, Kubernetes, Helm, Dockerfile, ARM, Serverless | Open source (Apache 2.0) | Broadest framework coverage, graph-based analysis |
| Terrascan | Terraform, Kubernetes, Helm | Open source | OPA/Rego-based policies |
| KICS (Checkmarx) | Terraform, CloudFormation, Kubernetes, Docker, Ansible | Open source | Large community rule library |
| Cloud Custodian | Cloud resources (runtime, not pure static) | Open source | More focused on ongoing compliance than pre-deploy scanning |
Compared to single-framework scanners, Checkov's advantage is breadth: one tool can cover Terraform, Kubernetes manifests, and Dockerfiles in the same pipeline run, which reduces the number of separate security tools a team needs to maintain.
Conclusion
Securing infrastructure code doesn't require a heavyweight commercial platform to get started. An open-source, policy-as-code scanner like Checkov can catch real, high-impact misconfigurations — public buckets, open security groups, unencrypted databases — before terraform apply ever runs, and it does so across multiple IaC frameworks at once. For teams adopting DevSecOps practices, plugging a tool like this into CI is a low-cost, high-value first step toward "shifting left" on cloud security.
Top comments (0)