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

269 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) 29 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) 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) 52 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) 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.5.6.tar.gz (172.5 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.5.6-py3-none-any.whl (183.7 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for proofctl-0.5.6.tar.gz
Algorithm Hash digest
SHA256 7a8f469d2ad4850dfe941bc10d02b71d2dce5f09b9ef579c2d2f06cfcae7df00
MD5 fb8a31d3dc35dc5e0dec3f0c69cc8460
BLAKE2b-256 bf209c47ddb492ece6f3abbda35fd3939879fcd6ef887542ba61a3a2cc6940ab

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofctl-0.5.6.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.5.6-py3-none-any.whl.

File metadata

  • Download URL: proofctl-0.5.6-py3-none-any.whl
  • Upload date:
  • Size: 183.7 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.5.6-py3-none-any.whl
Algorithm Hash digest
SHA256 bc1b80c6ba4de4a040ec1bac129e1dd952c9ffe22ae0ca0962afc1409ab5cc9a
MD5 db9a3ee550e006a736ef7b6d551d0bbb
BLAKE2b-256 88c37c84221b4bfa6d6ebe8d15eadae57d1013d74ce588f6b29b1c4f9f60d8df

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofctl-0.5.6-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