Zero-dependency linter for Python, Terraform, Dockerfiles, Kubernetes, and GitHub Actions — catches AI slop and security misconfigurations pre-commit
Project description
proofctl
A linter built for the DevOps stack — Python, Terraform, Kubernetes, Helm, Dockerfiles, GitHub Actions, Shell — that catches the specific kinds of mistakes AI coding tools introduce. 267 rules, zero dependencies, runs in under a second.
proofctl check .
PROOFCTL-S-001 module/auth.py:42 SQL injection via string format in .execute() ERROR
PROOFCTL-Q-001 module/models.py:17 Mutable default argument — list assigned at def time ERROR
PROOFCTL-S-AI-006 module/loaders.py:88 Empty `except Exception:` swallows all errors silently WARNING
PROOFCTL-TF-A024 infra/sg.tf:14 SG rule 0.0.0.0/0 ingress on port 22 ERROR
PROOFCTL-YAML-022 k8s/deployment.yaml:8 Workload uses default namespace WARNING
PROOFCTL-YAML-007 .github/workflows/release.yml:23 Action not pinned to SHA ERROR
PROOFCTL-SECRET-001 infra/lambda.tf:8 AWS access key (AKIA...) ERROR
──────────────────────────────────────────────────────────────────
7 findings (4 ERROR, 3 WARNING) — exit 2
Why proofctl
LLM coding assistants ship code that compiles and looks correct but fails in predictable ways:
- Insecure — SQL/command injection, hardcoded credentials, weak crypto, JWT signature bypass, public S3 buckets, K8s default namespaces.
- Broken — hallucinated imports, phantom methods, type-hint vs implementation mismatch,
pass-bodied functions shipped as real implementations. - Fragile — broad
except Exception:, no timeouts, mutable defaults, defensive guards for impossible states, unused configuration kwargs. - Wasteful — single-method classes that should be functions, near-duplicate files, sprawling kwargs surface.
proofctl runs as a pre-commit linter and catches these patterns before they merge. The top-firing rules on real AI-authored code are Q-002 broad except, Q-005 complexity, Q-006 long functions, YAML-007 / YAML-GHA-006 GitHub Actions hygiene, and YAML-GHA-003 missing permissions.
What proofctl scans
| Language | What it catches |
|---|---|
| Python | Security (SQLi, injection, eval, JWT bypass, weak crypto), quality (mutable defaults, broad except, complexity, long functions), AI-slop patterns (echo docstrings, single-method classes, stale kwargs), import hallucinations |
| Terraform / HCL | AWS / GCP / Azure resource hardening (200+ rules across IAM, encryption, public exposure, audit logs), AI-slop patterns (deprecated resources, AWS region in GCP, placeholder values), required_version, sensitive outputs |
| Kubernetes YAML | Pod Security Standards (runAsNonRoot, allowPrivilegeEscalation, readOnlyRootFilesystem, drop ALL caps, seccomp), data-exfiltration vectors (default namespace, hostPath, low UID, RBAC wildcards, automountServiceAccountToken), image hygiene (latest tag, digest pinning) |
| Helm / Mustache | Same K8s checks via Helm-templated YAML |
| Dockerfile | USER, base image pinning, secret leakage, root-running, OCI labels, HEALTHCHECK, AI-generated antipatterns |
| GitHub Actions | SHA-pinning, expression injection, missing permissions, missing timeouts, secret env-var leakage |
| Shell | Common mistake detection (set -euo, dangerous globbing) |
| Secrets (any text file) | AWS / GCP / GitHub / Slack / Stripe / private keys / JWTs / hardcoded passwords / K8s Secret YAML / HCL heredocs |
Installation
pip install proofctl
Requires: Python 3.11+
No project dependencies are added — proofctl only scans, never imports your code.
Quick start
proofctl check . # scan current directory
proofctl check src/api/auth.py # scan a single file
proofctl check . --min-severity ERROR # only show ERROR
proofctl check . --fail-on ERROR # CI gate: exit non-zero only on ERROR
proofctl check . --changed-only # only files changed in this branch
proofctl check . --families S,Q,SECRET # restrict rule families
proofctl check . --format html --output report.html
proofctl rules # list all rules
Pre-commit integration
Install proofctl from PyPI, then add a local hook in .pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: proofctl
name: proofctl
language: system
entry: proofctl check
args: [--no-pypi, --fail-on, ERROR]
pass_filenames: false
CI integration (GitHub Actions)
name: proofctl
on: [push, pull_request]
jobs:
proofctl:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install proofctl
# --new-only compares against the committed baseline snapshot —
# CI fails only on regressions introduced in this PR.
- run: proofctl check . --no-pypi --fail-on ERROR --new-only
- name: Generate HTML report
if: failure()
run: proofctl check . --no-pypi --format html --output proofctl-report.html || true
- uses: actions/upload-artifact@v4
if: failure()
with:
name: proofctl-report
path: proofctl-report.html
retention-days: 14
Create a baseline snapshot for an existing project so CI only flags regressions:
proofctl baseline .
git add .proofctl-baseline.json
git commit -m "chore: add proofctl baseline"
Rules
267 rules across 14 families. Run proofctl rules after install to enumerate.
| Family | Count | What it covers |
|---|---|---|
Python — Quality (Q) |
17 | Mutable defaults, broad except, complexity, long functions, AI-slop patterns (echo docstrings, single-method classes, stale kwargs, defensive guards) |
Python — Security (S) |
24 | OWASP Top-10 + CWE coverage: SQLi, command injection, weak crypto, JWT bypass, path traversal, XSS, SSRF, file upload, CSRF, missing authz, XPath injection, open redirect, TLS bypass |
Secrets (SECRET) |
13 | AWS / GCP / GitHub / Slack / Stripe / private keys / JWTs / hardcoded passwords / K8s Secret YAML / connection strings with embedded creds |
| Python — Other | 12 | Placeholders, leakage (cross-language idioms, PII), import hallucination, method complexity, variant detection, LLM guardrails (OWASP LLM Top-10) |
Terraform — General + AI (TF-T, TF-AI) |
28 | required_version, sensitive outputs, IAM wildcards, mutable git refs, deprecated resources, AWS-region-in-GCP, placeholder values, image-family pinning, sensitive vars in VM metadata |
Terraform — AWS (TF-A) |
40 | EC2 IMDSv2, S3 public-access-block, EBS encryption, VPC flow logs, KMS rotation, RDS hardening, SG / NACL / EKS / DynamoDB / ElastiCache / CloudTrail / IAM |
Terraform — GCP (TF-G) |
51 | GKE hardening, GCS public-access prevention, IAM roles, KMS rotation, Cloud Function/Run public, BigQuery, Pub/Sub CMEK, Cloud SQL, firewall ingress, compute access scopes |
Terraform — Azure (TF-AZ) |
14 | Storage public blob, SQL public network, AKS RBAC, Key Vault purge protection, NSG SSH/RDP, managed disk CMK |
Terraform — Mechanics + Lifecycle (TF-M, TF-V) |
12 | SG inline mixing, force_destroy, mutable module refs, lifecycle antipatterns |
Terragrunt (TG) |
6 | HCL inputs, mock_outputs, remote state encryption |
Dockerfile (DF) |
15 | Latest tag, root user, secret leakage, OCI labels, HEALTHCHECK, legacy ENV form |
Kubernetes YAML (YAML) |
26 | Pod Security Standards, hostPath, default namespace, image digest pinning, RBAC wildcards |
GitHub Actions (YAML-GHA) |
— | SHA pinning, expression injection, missing permissions, secret env-var leakage, missing timeouts |
Shell (SH) |
8 | Dangerous globbing, missing set -euo pipefail |
proofctl rules # full list
proofctl rules --family S # filter by family
proofctl rules --severity ERROR
Suppressing findings
Inline (single line):
result = subprocess.run(cmd, shell=True) # proofctl: ignore[PROOFCTL-S-002]
resource "aws_s3_bucket" "logs" {
acl = "public-read" # proofctl: ignore[PROOFCTL-TF-AI-003] // or # comment style
}
Project-wide in .proofctl.yaml:
disable:
- PROOFCTL-Q-004
Configuration
Place .proofctl.yaml in the project root:
exclude_paths:
- "**/.venv/**"
- "**/migrations/**"
- "**/.terraform/**"
disable:
- PROOFCTL-Q-004
- PROOFCTL-DF-AI-002
severity_overrides:
PROOFCTL-P-002: ERROR # promote TODOs to ERROR
rules:
PROOFCTL-P-002:
allowed_formats:
- 'TODO\(#\d+\)' # allow TODO(#123) format
exclude_paths:
- tests/
PROOFCTL-Q-003:
any_threshold: 5
PROOFCTL-V-002:
similarity_threshold: 0.85
min_file_lines: 30
PROOFCTL-I-001:
local_namespaces:
- mycompany_
- internal_
PROOFCTL-TF-T013:
required_labels:
- environment
- team
- cost_center
# Substrings that mark a line as legitimate private infra. Findings from
# "name-shaped" rules (SECRET-*, TF-T005, TF-AI-001, DF-AI-*, Q-AI-*) on a
# line containing any of these tokens are silently dropped. Real
# code-shape security rules (S-001 SQLi, S-005 eval, etc.) keep firing.
internal_identifiers:
- mycompany-prod # internal hostname prefix
- mycompany.internal # internal DNS suffix
- INTERNAL_PROJECT_NAME # codename that sometimes looks like a placeholder
Commands
proofctl check
proofctl check [PATH] [OPTIONS]
Options:
--format / -f terminal | json | html (default: terminal)
--output / -o Write report to file
--changed-only Only scan git-changed files
--families P,Q,S,L,I,M,V,LLM,TF,TG,DF,YAML,SH,SECRET
--min-severity INFO | WARNING | ERROR
--fail-on Exit non-zero at this severity (default: WARNING)
--no-pypi Skip PyPI lookups (faster, works offline)
--new-only Suppress findings present in .proofctl-baseline.json
--fix Auto-fix fixable findings (Q-001 mutable defaults)
proofctl baseline
Snapshot the current findings so future --new-only runs only surface regressions.
proofctl baseline .
git add .proofctl-baseline.json && git commit -m "chore: proofctl baseline"
proofctl rules
proofctl rules # list all
proofctl rules --family S # filter by family
proofctl rules --severity ERROR
Exit codes
| Code | Meaning |
|---|---|
0 |
No findings |
1 |
Findings present, none at or above --fail-on threshold |
2 |
One or more findings at or above --fail-on threshold |
Output formats
Terminal (default) — rich-coloured table with file:line, rule, severity, hint.
JSON — machine-readable for CI:
{
"schema_version": 1,
"summary": { "total": 4, "ERROR": 3, "WARNING": 1 },
"findings": [
{
"file": "module/auth.py",
"line": 42,
"col": 8,
"rule_id": "PROOFCTL-S-001",
"rule_name": "SQL injection",
"severity": "ERROR",
"message": "SQL injection via string format in .execute()",
"hint": "Use parameterised queries: cursor.execute(sql, params)",
"authority": "OWASP A03:2021, CWE-89"
}
]
}
HTML — self-contained single-file dashboard with severity summary cards and a filterable table.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file proofctl-0.4.3.tar.gz.
File metadata
- Download URL: proofctl-0.4.3.tar.gz
- Upload date:
- Size: 166.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6f411f348314c7c93655aaca04db6a1bcbf4ea48422b420910f8f8abe7a12c2a
|
|
| MD5 |
4c8d7ce10c9a0ba5ef7276608d250914
|
|
| BLAKE2b-256 |
8482a83c8c9f95e27841e1be9ca5784bf24e5d8342f6539a2c76226b41ec286c
|
Provenance
The following attestation bundles were made for proofctl-0.4.3.tar.gz:
Publisher:
publish.yml on kolawoluu/proofctl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
proofctl-0.4.3.tar.gz -
Subject digest:
6f411f348314c7c93655aaca04db6a1bcbf4ea48422b420910f8f8abe7a12c2a - Sigstore transparency entry: 1518705000
- Sigstore integration time:
-
Permalink:
kolawoluu/proofctl@e3f846ebce1afa52ce5d51bea4ddf99f87dc4d3b -
Branch / Tag:
refs/tags/v0.4.3 - Owner: https://github.com/kolawoluu
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e3f846ebce1afa52ce5d51bea4ddf99f87dc4d3b -
Trigger Event:
push
-
Statement type:
File details
Details for the file proofctl-0.4.3-py3-none-any.whl.
File metadata
- Download URL: proofctl-0.4.3-py3-none-any.whl
- Upload date:
- Size: 177.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
279d24c4755c17a6a0bc17264a0a4967c2adad4975b8f92a9911d91f7dece8aa
|
|
| MD5 |
76451bda0335784d89b1fc16da3b947c
|
|
| BLAKE2b-256 |
b9902d6e78b315e84ce024c00c9b3b4d5eaa7f8e0b400f91dec33c6c27585db2
|
Provenance
The following attestation bundles were made for proofctl-0.4.3-py3-none-any.whl:
Publisher:
publish.yml on kolawoluu/proofctl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
proofctl-0.4.3-py3-none-any.whl -
Subject digest:
279d24c4755c17a6a0bc17264a0a4967c2adad4975b8f92a9911d91f7dece8aa - Sigstore transparency entry: 1518705033
- Sigstore integration time:
-
Permalink:
kolawoluu/proofctl@e3f846ebce1afa52ce5d51bea4ddf99f87dc4d3b -
Branch / Tag:
refs/tags/v0.4.3 - Owner: https://github.com/kolawoluu
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e3f846ebce1afa52ce5d51bea4ddf99f87dc4d3b -
Trigger Event:
push
-
Statement type: