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.1.tar.gz (241.2 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.1-py3-none-any.whl (175.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: proofctl-0.4.1.tar.gz
  • Upload date:
  • Size: 241.2 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.1.tar.gz
Algorithm Hash digest
SHA256 63fbd608a40e42833c6ecccd304492f150ae10edae88af4e04577feea4a482de
MD5 020388185101a9d522ba221fbc459d6e
BLAKE2b-256 c19f564ae81cdfcbb306268b33730e27c4ffb725013a59b3a61aef7988134f63

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: proofctl-0.4.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 74e62911bd4699a6efb66c4df7cb08e0a2ac559635ff00ae8e3c0fc65a2dd7c8
MD5 2aef837e7ad75af6aaea9e8a317d8a07
BLAKE2b-256 dfddf04c2995cca66abe67f8d3b7217d558ae845084914d8ee59eabd7367e64f

See more details on using hashes here.

Provenance

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