Skip to main content

IaC Scanning Tools: Checkov, TFLint, Grype, and pre-commit

·5 mins

Tool Overview #

ToolRoleWhat it catches
CheckovIaC security scannerMisconfigurations in Terraform, CloudFormation, K8s
TFLintTerraform linterInvalid resource types, deprecated syntax, bad practices
GrypeVulnerability scannerCVEs in container images, filesystems, SBOMs
pre-commitGit hook frameworkRuns all of the above automatically on every commit
git commit → pre-commit → TFLint + Checkov (local)
                ↓
           CI pipeline → Checkov + TFLint + Grype (full gate)

1. Checkov #

Static analysis tool for IaC security. 1,000+ built-in policies covering AWS, Azure, GCP, and Kubernetes. Maintained by Palo Alto Networks (acquired Bridgecrew) under Apache 2.0.

Install & Run #

pip install checkov

checkov -d .                              # scan current directory
checkov -d ./terraform --framework terraform
checkov -d . --output sarif              # SARIF for GitHub
checkov -d . --skip-check CKV_AWS_18    # skip a check
checkov -d . --soft-fail                 # always exit 0
checkov --config-file checkov.yaml       # use config file

checkov.yaml #

directory:
  - terraform/
framework:
  - terraform
skip-check:
  - CKV_AWS_144
output:
  - cli
  - sarif
compact: true

GitHub Actions #

- uses: bridgecrewio/checkov-action@master
  with:
    directory: ./terraform
    framework: terraform
    output_format: sarif

Security note: The official docs use @master, but best practice is to pin to a specific commit SHA (e.g., bridgecrewio/checkov-action@<sha>) to guard against supply chain attacks.


2. TFLint #

Terraform linter — not a security scanner. Catches provider-specific errors that terraform validate misses entirely (invalid instance types, nonexistent AMIs, deprecated attributes).

terraform validate checks HCL syntax. TFLint checks whether your values are valid in AWS/Azure/GCP.

Install & Run #

brew install tflint                         # macOS
curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash

tflint --init                               # download plugins
tflint                                      # lint current directory
tflint --chdir=./terraform/modules/vpc
tflint --recursive
tflint --format json

.tflint.hcl #

plugin "aws" {
  enabled = true
  version = "0.47.0"   # verify latest at github.com/terraform-linters/tflint-ruleset-aws/releases
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}

plugin "terraform" {
  enabled = true
  preset  = "recommended"
}

What It Catches #

instance_type = "t1.2xlarge"  # ERROR: invalid type — t1 family only has t1.micro
acl           = "private"     # WARNING: acl is deprecated since AWS provider v4;
                               # use aws_s3_bucket_acl resource instead
# missing required_version    # WARNING

AWS context (as of 2026-06-08): Amazon S3 disabled ACLs by default for new buckets in April 2023. The acl argument on aws_s3_bucket was deprecated in the AWS Terraform provider v4.

Inline Ignore #

instance_type = "t1.2xlarge"  # tflint-ignore: aws_instance_invalid_type

GitHub Actions #

- uses: terraform-linters/setup-tflint@v6   # v6 is latest as of 2026-06-08
- run: tflint --init
- run: tflint --recursive --format compact

3. Grype #

Vulnerability scanner for container images, filesystems, and SBOMs. Maintained by Anchore under Apache 2.0. Pairs with Syft for SBOM-first scanning.

Install & Run #

curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin

grype alpine:latest              # scan image from registry
grype dir:./my-project           # scan local directory
grype sbom:./sbom.json           # scan a Syft SBOM
grype alpine:latest -o sarif     # SARIF output
grype alpine:latest --fail-on high  # fail build on HIGH+ CVEs

SBOM-First Workflow (Syft + Grype) #

# Generate SBOM once
syft nginx:latest -o syft-json > sbom.json

# Scan now — and rescan later without re-pulling the image
grype sbom:./sbom.json

.grype.yaml #

fail-on-severity: high

ignore:
  - vulnerability: CVE-2023-1234
    reason: "not exploitable in this context"

output:
  - table
  - sarif

GitHub Actions #

- uses: anchore/scan-action@v7   # v7 is latest as of 2026-06-08
  with:
    image: my-app:${{ github.sha }}
    fail-build: true
    severity-cutoff: high
    output-format: sarif

4. pre-commit #

Git hook framework that runs checks automatically on every git commit. One config file, committed to git, runs consistently across the entire team.

Install & Setup #

pip install pre-commit     # or: brew install pre-commit
pre-commit install          # one-time per repo — hooks now run on every commit

.pre-commit-config.yaml #

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v6.0.0   # latest as of 2026-06-08
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-merge-conflict

  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.106.0
    hooks:
      - id: terraform_fmt
      - id: terraform_validate
      - id: terraform_tflint
      - id: terraform_checkov

  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.30.1   # latest as of 2026-06-08
    hooks:
      - id: gitleaks

Run pre-commit autoupdate periodically to refresh all rev: pins to the latest releases.

Common Commands #

pre-commit run --all-files                        # run all hooks against all files
pre-commit run terraform_checkov --all-files      # run one hook only
pre-commit autoupdate                             # update all hook versions
git commit --no-verify -m "skip hooks"           # bypass all (use sparingly)
SKIP=terraform_checkov git commit -m "msg"       # skip one hook only

Full GitHub Actions Pipeline #

name: IaC Security Pipeline
on: [push, pull_request]

jobs:
  checkov:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: bridgecrewio/checkov-action@master
        with:
          directory: ./terraform
          output_format: sarif

  tflint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: terraform-linters/setup-tflint@v6
      - run: tflint --init && tflint --recursive

  grype:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: docker build -t app:${{ github.sha }} .
      - uses: anchore/scan-action@v7
        with:
          image: app:${{ github.sha }}
          fail-build: true
          severity-cutoff: high

Key Takeaways #

  • TFLint ≠ Checkov — run both; they catch completely different things
  • Grype runs in CI, not pre-commit — image scanning is too slow for local hooks
  • Pin rev: versions in .pre-commit-config.yaml for consistent team behavior; run pre-commit autoupdate regularly
  • Use SARIF output to surface findings as GitHub pull request annotations
  • Syft + Grype — generate the SBOM once, rescan cheaply as new CVEs emerge
  • Pin GitHub Actions to a SHA instead of @master for supply chain safety

References #