Run the same Terraform and OpenTofu checks across your pipelines.
ctfkit by compliance.tf is an Apache-2.0 analyzer runtime for Terraform and OpenTofu. It
turns configuration, plans and module manifests into consistent SARIF findings and an
evidence receipt, and hands its results to the policy workflow you already have. Its checks
(scan, report, verify-lock) run offline in your CI: no network connection and no cloud
or Terraform credentials. When a compliance.tf module would have prevented a finding, it names
the exact module source.
Try it on a directory of .tf files, before terraform init:
ctfkit scan ./infraHomebrew, release archives, Linux packages and checksums: Install.
ctfkit produces findings. OPA, tfpolicy or your own CI logic decide what to do with them, and it runs alongside Sentinel. Tools like Checkov, Trivy and tfsec tell you a resource is misconfigured. ctfkit tells you what your estate requires, and which module closes each gap.
scan,report,verify-lockandconfigrun offline: they never run terraform or tofu and need no network or credentials.report --readmeandconfig initwrite files.- The governed apply, evidence and run task commands reach further (per command):
gate planandapplyrun terraform;gate signandverdict verifyrun only terraformversion,initandshow -jsonon the sealed plan;gate signandevidence shipcall AWS KMS;evidence shipuploads to S3;runtask servedownloads plan JSON from HCP Terraform and posts results back;catalog pulldownloads the full control catalog from the compliance.tf registry with your registry token. gate signwritesverdict.jsonsigned with the key named by--signer(your AWS KMS key), andevidence shipsigns the run manifest with its--signerkey. Findings, SARIF, JSON and receipts are not signed; receipt signing is planned. compliance.tf does not sign, attest or verify anything for you.- Release builds have no Evidence API client yet, so
floor_mode: onlinerefuses at apply andevidence ship --apialways spools. - The env0 flow has run only against stub binaries, not yet on a live env0 account.
- There is no IDE integration.
macOS and Linux, with Homebrew:
brew install compliancetf/tap/ctfkitEvery release on GitHub Releases also has
archives for Linux, macOS and Windows, Linux packages and checksums.txt. This installs the
newest Linux x86_64 release:
gh release download --repo compliancetf/ctfkit \
--pattern 'ctfkit_*_Linux_x86_64.tar.gz' --pattern checksums.txt &&
sha256sum --ignore-missing -c checksums.txt &&
tar -xzf ctfkit_*_Linux_x86_64.tar.gz ctfkit &&
sudo install -m 0755 ctfkit /usr/local/bin/ctfkit &&
ctfkit versionHomebrew and gh release download with no tag take the newest release. To pin one, put its tag
after download, such as gh release download v1.2.3.
Windows (x86_64 and arm64), other platforms, deb, rpm and apk packages, container images
(ghcr.io/compliancetf/ctfkit), a download without gh: docs/install.md.
Release files come with SBOMs and build provenance attestations.
ctfkit scan ./infra # pre_plan: .tf files before init, no credentials
ctfkit ./infra # the same, short form
ctfkit scan plan.json # post_plan: terraform show -json tfplan > plan.json
ctfkit scan plan.json -f sarif --out findings.sarif
ctfkit scan plan.json -f json --fail-on never > findings.json # for OPA
ctfkit report # after terraform init
ctfkit config init # write a starter .ctfkit.yaml
ctfkit config validate .ctfkit.yaml # check itOutput formats: SARIF (CFF), JSON, text, pretty, JUnit and Spacelift metadata.
ctfkit alone prints help. All flags: docs/commands.md.
repos:
- repo: https://github.com/compliancetf/ctfkit
rev: v0.7.1
hooks:
- id: ctfkitpre-commit autoupdate moves rev to the newest release.
| Hook id | Runs | Pick it for |
|---|---|---|
ctfkit |
ctfkit scan --stage pre_plan <staged files> |
a repository of deployable roots |
ctfkit-scan-dir |
ctfkit scan --stage pre_plan . |
a module repository: the directory scan tells reusable modules from deployable roots |
Both run on .tf, .tofu, .tf.json and .tofu.json files and build from source; for an
installed binary, see docs/install.md.
-
GitHub Action (plan scans; governed apply in three jobs from this repository's workflow template): its default
command: reportreads the module cache, so it needsinitto have run. For the path with no init, plan or credentials, usecommand: scan:- uses: compliancetf/ctfkit-action@v0 with: command: scan stage: pre_plan chdir: infra
@v0and noversionrun the newest action and ctfkit releases. For a reproducible pipeline, pin the action to a release commit SHA and setversionto the ctfkit release you reviewed. -
Spacelift: stack hooks, plan policy and governed apply: adapters/spacelift.
-
OPA: a Rego policy over
scan -f jsonoutput: adapters/opa. -
env0: a custom flow for governed apply: adapters/env0.
-
HCP Terraform and Terraform Enterprise run task:
ctfkit runtask serve, advisory findings and a receipt per run: adapters/hcp. -
Any other CI:
ctfkit scan infra -f sarif --out findings.sarif.
| Code | Meaning |
|---|---|
0 |
nothing at or above --fail-on (default error) |
1 |
unsuppressed findings at or above --fail-on; a -f spacelift run alone returns 0, because the plan policy reads the file's verdict |
2 |
could not run, or refused: an unreadable or invalid plan, configuration, catalog, settings or exceptions file, or an invalid flag |
--fail-on is error, warning, note or never; the flag wins over CTFKIT_FAIL_ON, which
wins over fail_on in the settings file.
.ctfkit.yaml is optional. scan reads it from the configuration directory, report from the
working directory, verify-lock from its DIR, unless --config FILE names one. Any pull
request can edit it, so a CI run that must hold regardless passes --config, --exceptions,
--catalog and --fail-on from outside the repository.
allowed_providers:
- registry.terraform.io/hashicorp/aws
required_tags: [Owner, Environment]
fail_on: errorEvery key, allowed_endpoints included, and the exception registry:
docs/configuration.md.
| Page | Covers |
|---|---|
| docs/install.md | Homebrew, archives, Windows, packages, checksums, pre-commit with an installed binary |
| docs/commands.md | every command and flag, network and credentials per command, exit codes |
| docs/analyzers.md | every rule, rule packs, code that runs before plan, lock file rules |
| docs/configuration.md | .ctfkit.yaml keys, the exception registry, the catalog |
| docs/stages.md | what each stage can and cannot see |
| docs/governed-apply.md | gate plan, gate sign, verdict verify, apply, run custody, limits |
| docs/evidence.md | evidence ship, runner class, receipt status, known limit |
| docs/cff.md | CFF, the findings format |
| docs/rule-packs.md | rule pack format and adding a provider |
| docs/threat-model.md | threats and the planned plan-to-apply binding |
The documentation site is https://compliance.tf/docs/ctfkit/, with a
Spacelift integration guide: runner
image recipe, stack hooks, plan policy, per-stack ctfkit:enforce label. Related:
compliance.tf, the module registry whose requirements ctfkit reports
against, and antonbabenko/pre-commit-terraform,
the hook framework this plugs into.
Bug reports and feature requests are welcome as
issues; report a vulnerability as described in
SECURITY.md. Pull requests come from maintainers only. Building and testing from
source: CONTRIBUTING.md. To write your own analyzer, use the SDK in
pkg/ctfkit and its conformance harness.
Apache 2.0. See LICENSE.
Maintained by Anton Babenko - @antonbabenko.