Metadata-Version: 2.4
Name: proofctl
Version: 0.4.1
Summary: Zero-dependency linter for Python, Terraform, Dockerfiles, Kubernetes, and GitHub Actions — catches AI slop and security misconfigurations pre-commit
Author-email: Kolawolu Odunola <kolawolu.o@gmail.com>
License: MIT
Keywords: linter,security,terraform,kubernetes,ai,static-analysis,pre-commit
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Security
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer[all]>=0.12
Requires-Dist: rich>=13
Requires-Dist: pyyaml>=6
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-mock>=3; extra == "dev"
Dynamic: license-file

# 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

```bash
pip install proofctl
```

**Requires:** Python 3.11+

No project dependencies are added — proofctl only scans, never imports your code.

---

## Quick start

```bash
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`:

```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)

```yaml
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:

```bash
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` |

```bash
proofctl rules                 # full list
proofctl rules --family S      # filter by family
proofctl rules --severity ERROR
```

---

## Suppressing findings

Inline (single line):

```python
result = subprocess.run(cmd, shell=True)  # proofctl: ignore[PROOFCTL-S-002]
```

```hcl
resource "aws_s3_bucket" "logs" {
  acl = "public-read"  # proofctl: ignore[PROOFCTL-TF-AI-003]  // or # comment style
}
```

Project-wide in `.proofctl.yaml`:

```yaml
disable:
  - PROOFCTL-Q-004
```

---

## Configuration

Place `.proofctl.yaml` in the project root:

```yaml
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.

```bash
proofctl baseline .
git add .proofctl-baseline.json && git commit -m "chore: proofctl baseline"
```

### `proofctl rules`

```bash
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:

```json
{
  "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
