proofctl
A static-analysis linter and security scanner for the DevOps stack — Python, Terraform, Kubernetes, Helm, Dockerfiles, GitHub Actions, Shell — that catches the specific kinds of mistakes AI coding tools introduce. 271 rules, no external tools, runs in under a second.
proofctl check .
| Rule | Location | Issue | Severity |
|---|---|---|---|
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-Q-002 |
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) — exits non-zero (
exit 2) so CI fails the build.
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, 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 in pre-commit hooks, CI pipelines, or against an existing tree, catching 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
271 rules across 13 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 | 15 | Placeholders, leakage (cross-language idioms, PII), import hallucination, variant detection, LLM guardrails (OWASP LLM Top-10) |
Terraform — General + AI (TF-T, TF-AI) |
25 | 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, unused variable declarations |
Terraform — AWS (TF-A) |
36 | 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) |
48 | GKE hardening, GCS public-access prevention, IAM roles, KMS rotation, Cloud Function/Run public, BigQuery, Pub/Sub CMEK, Cloud SQL (TLS ssl_mode aware), firewall ingress, compute access scopes, Secret Manager CMEK consistency |
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) |
10 | 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) |
34 | Pod Security Standards, hostPath, default namespace, image digest pinning, RBAC wildcards |
GitHub Actions (YAML-GHA) |
6 | 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,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
Metadata
Release files for proofctl 0.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| proofctl-0.6.1.tar.gz | 174.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| proofctl-0.6.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 360.2 kB
Release files / proofctl-0.6.1.tar.gz
| Download URL | proofctl-0.6.1.tar.gz |
|---|---|
| Size | 174.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8f5b875d962b9ab39cc30d6a9a89feeff19aea48aee4b76869cc9e4f532e48e4
|
|
BLAKE2b-256 checksum How to use checksums |
c3afd79b2e3cc03c7378ee26a8ea6bb5f1f0aa6197f8bf36131cb8e0deb646c1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 24, 2026.
Transparency logRelease files / proofctl-0.6.1-py3-none-any.whl
| Download URL | proofctl-0.6.1-py3-none-any.whl |
|---|---|
| Size | 185.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7cf01ba47e985cf3f716c810591e406730bce5ad4b8612740d9b1ed03cf33d08
|
|
BLAKE2b-256 checksum How to use checksums |
b08838fa9c93a8d2670c9050ba9a0b0c168862fb8e0636b69cef651c7cf9ae3b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 24, 2026.
Transparency log