Skip to main content

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. 264 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

264 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) 26 required_version, sensitive outputs, IAM wildcards, mutable git refs, deprecated resources, AWS-region-in-GCP, placeholder values
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) 50 GKE hardening, GCS public-access prevention, IAM roles, KMS rotation, Cloud Function/Run public, BigQuery, Pub/Sub CMEK, Cloud SQL, firewall ingress
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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

proofctl-0.4.2.tar.gz (164.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

proofctl-0.4.2-py3-none-any.whl (175.2 kB view details)

Uploaded Python 3

File details

Details for the file proofctl-0.4.2.tar.gz.

File metadata

  • Download URL: proofctl-0.4.2.tar.gz
  • Upload date:
  • Size: 164.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for proofctl-0.4.2.tar.gz
Algorithm Hash digest
SHA256 6b8860e690167c8a4c619ae38272236b396876e86331458a1d9e8b91329614bc
MD5 7abaf8f095280a116aedc04485266d91
BLAKE2b-256 fcc64248351e26b57a75b0a23ca104260844cab9e88b21280602aee0425841db

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofctl-0.4.2.tar.gz:

Publisher: publish.yml on kolawoluu/proofctl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file proofctl-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: proofctl-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 175.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for proofctl-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 19d1b57bd3c32b2c2119e82c7c8d1379ccbe35ee96a58ebfc5f636543e0b4185
MD5 37bb97d8b0e2d76fe726c04b54aba399
BLAKE2b-256 6b378ed72faefb51b70cf61e6938fcb4c7ca1f22d6b9e0851c328e983d860119

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofctl-0.4.2-py3-none-any.whl:

Publisher: publish.yml on kolawoluu/proofctl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page