proofctl 0.4.0__tar.gz → 0.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- proofctl-0.4.1/PKG-INFO +352 -0
- proofctl-0.4.1/README.md +324 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/security.py +50 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/terraform.py +69 -2
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/cli.py +21 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/config.py +31 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/engine.py +88 -0
- proofctl-0.4.1/proofctl.egg-info/PKG-INFO +352 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/pyproject.toml +1 -6
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_engine.py +35 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_security_external.py +15 -0
- proofctl-0.4.0/PKG-INFO +0 -527
- proofctl-0.4.0/README.md +0 -496
- proofctl-0.4.0/proofctl.egg-info/PKG-INFO +0 -527
- {proofctl-0.4.0 → proofctl-0.4.1}/LICENSE +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/__init__.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/_globmatch.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/baseline.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/__init__.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/base.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/dockerfile.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/hcl_utils.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/imports.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/leakage.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/llm_integration.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/placeholders.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/quality.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/secrets.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/shell_checker.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/terragrunt.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/variants.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/checkers/yaml_checker.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/fixer.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/models.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/registry.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/reporters/__init__.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/reporters/html_reporter.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/reporters/json_reporter.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl/reporters/terminal.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl.egg-info/SOURCES.txt +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl.egg-info/dependency_links.txt +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl.egg-info/entry_points.txt +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl.egg-info/requires.txt +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/proofctl.egg-info/top_level.txt +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/setup.cfg +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_baseline.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_config_proofctl.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_dockerfile.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_fixer.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_imports.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_leakage.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_llm.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_local_packages.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_phase1_audit_fixes.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_phase2_audit_fixes.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_phase3_audit_fixes.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_phase4_audit_fixes.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_phase5_llm_imports_fixes.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_phase5_p_l_v_fixes.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_phase5_quality_fixes.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_phase6_audit_fixes.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_phase8_audit_fixes.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_placeholders.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_quality.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_quality_ext.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_secrets.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_security.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_security_ext.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_suppression.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_terraform.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_terraform_ext.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_terragrunt.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_variants.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_yaml.py +0 -0
- {proofctl-0.4.0 → proofctl-0.4.1}/tests/test_yaml_k8s_gha.py +0 -0
proofctl-0.4.1/PKG-INFO
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: proofctl
|
|
3
|
+
Version: 0.4.1
|
|
4
|
+
Summary: Zero-dependency linter for Python, Terraform, Dockerfiles, Kubernetes, and GitHub Actions — catches AI slop and security misconfigurations pre-commit
|
|
5
|
+
Author-email: Kolawolu Odunola <kolawolu.o@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: linter,security,terraform,kubernetes,ai,static-analysis,pre-commit
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
17
|
+
Classifier: Topic :: Security
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: typer[all]>=0.12
|
|
22
|
+
Requires-Dist: rich>=13
|
|
23
|
+
Requires-Dist: pyyaml>=6
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-mock>=3; extra == "dev"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# proofctl
|
|
30
|
+
|
|
31
|
+
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.
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
proofctl check .
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
PROOFCTL-S-001 module/auth.py:42 SQL injection via string format in .execute() ERROR
|
|
39
|
+
PROOFCTL-Q-001 module/models.py:17 Mutable default argument — list assigned at def time ERROR
|
|
40
|
+
PROOFCTL-S-AI-006 module/loaders.py:88 Empty `except Exception:` swallows all errors silently WARNING
|
|
41
|
+
PROOFCTL-TF-A024 infra/sg.tf:14 SG rule 0.0.0.0/0 ingress on port 22 ERROR
|
|
42
|
+
PROOFCTL-YAML-022 k8s/deployment.yaml:8 Workload uses default namespace WARNING
|
|
43
|
+
PROOFCTL-YAML-007 .github/workflows/release.yml:23 Action not pinned to SHA ERROR
|
|
44
|
+
PROOFCTL-SECRET-001 infra/lambda.tf:8 AWS access key (AKIA...) ERROR
|
|
45
|
+
──────────────────────────────────────────────────────────────────
|
|
46
|
+
7 findings (4 ERROR, 3 WARNING) — exit 2
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Why proofctl
|
|
52
|
+
|
|
53
|
+
LLM coding assistants ship code that compiles and looks correct but fails in predictable ways:
|
|
54
|
+
|
|
55
|
+
- **Insecure** — SQL/command injection, hardcoded credentials, weak crypto, JWT signature bypass, public S3 buckets, K8s default namespaces.
|
|
56
|
+
- **Broken** — hallucinated imports, phantom methods, type-hint vs implementation mismatch, `pass`-bodied functions shipped as real implementations.
|
|
57
|
+
- **Fragile** — broad `except Exception:`, no timeouts, mutable defaults, defensive guards for impossible states, unused configuration kwargs.
|
|
58
|
+
- **Wasteful** — single-method classes that should be functions, near-duplicate files, sprawling kwargs surface.
|
|
59
|
+
|
|
60
|
+
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**.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## What proofctl scans
|
|
65
|
+
|
|
66
|
+
| Language | What it catches |
|
|
67
|
+
|---|---|
|
|
68
|
+
| 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 |
|
|
69
|
+
| 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 |
|
|
70
|
+
| 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) |
|
|
71
|
+
| Helm / Mustache | Same K8s checks via Helm-templated YAML |
|
|
72
|
+
| Dockerfile | USER, base image pinning, secret leakage, root-running, OCI labels, HEALTHCHECK, AI-generated antipatterns |
|
|
73
|
+
| GitHub Actions | SHA-pinning, expression injection, missing permissions, missing timeouts, secret env-var leakage |
|
|
74
|
+
| Shell | Common mistake detection (set -euo, dangerous globbing) |
|
|
75
|
+
| Secrets (any text file) | AWS / GCP / GitHub / Slack / Stripe / private keys / JWTs / hardcoded passwords / K8s Secret YAML / HCL heredocs |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Installation
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install proofctl
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Requires:** Python 3.11+
|
|
86
|
+
|
|
87
|
+
No project dependencies are added — proofctl only scans, never imports your code.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Quick start
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
proofctl check . # scan current directory
|
|
95
|
+
proofctl check src/api/auth.py # scan a single file
|
|
96
|
+
proofctl check . --min-severity ERROR # only show ERROR
|
|
97
|
+
proofctl check . --fail-on ERROR # CI gate: exit non-zero only on ERROR
|
|
98
|
+
proofctl check . --changed-only # only files changed in this branch
|
|
99
|
+
proofctl check . --families S,Q,SECRET # restrict rule families
|
|
100
|
+
proofctl check . --format html --output report.html
|
|
101
|
+
proofctl rules # list all rules
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Pre-commit integration
|
|
107
|
+
|
|
108
|
+
Install proofctl from PyPI, then add a local hook in `.pre-commit-config.yaml`:
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
repos:
|
|
112
|
+
- repo: local
|
|
113
|
+
hooks:
|
|
114
|
+
- id: proofctl
|
|
115
|
+
name: proofctl
|
|
116
|
+
language: system
|
|
117
|
+
entry: proofctl check
|
|
118
|
+
args: [--no-pypi, --fail-on, ERROR]
|
|
119
|
+
pass_filenames: false
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## CI integration (GitHub Actions)
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
name: proofctl
|
|
128
|
+
on: [push, pull_request]
|
|
129
|
+
|
|
130
|
+
jobs:
|
|
131
|
+
proofctl:
|
|
132
|
+
runs-on: ubuntu-latest
|
|
133
|
+
steps:
|
|
134
|
+
- uses: actions/checkout@v4
|
|
135
|
+
- uses: actions/setup-python@v5
|
|
136
|
+
with:
|
|
137
|
+
python-version: "3.12"
|
|
138
|
+
- run: pip install proofctl
|
|
139
|
+
|
|
140
|
+
# --new-only compares against the committed baseline snapshot —
|
|
141
|
+
# CI fails only on regressions introduced in this PR.
|
|
142
|
+
- run: proofctl check . --no-pypi --fail-on ERROR --new-only
|
|
143
|
+
|
|
144
|
+
- name: Generate HTML report
|
|
145
|
+
if: failure()
|
|
146
|
+
run: proofctl check . --no-pypi --format html --output proofctl-report.html || true
|
|
147
|
+
|
|
148
|
+
- uses: actions/upload-artifact@v4
|
|
149
|
+
if: failure()
|
|
150
|
+
with:
|
|
151
|
+
name: proofctl-report
|
|
152
|
+
path: proofctl-report.html
|
|
153
|
+
retention-days: 14
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Create a baseline snapshot for an existing project so CI only flags regressions:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
proofctl baseline .
|
|
160
|
+
git add .proofctl-baseline.json
|
|
161
|
+
git commit -m "chore: add proofctl baseline"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Rules
|
|
167
|
+
|
|
168
|
+
264 rules across 14 families. Run `proofctl rules` after install to enumerate.
|
|
169
|
+
|
|
170
|
+
| Family | Count | What it covers |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| Python — Quality (`Q`) | 17 | Mutable defaults, broad `except`, complexity, long functions, AI-slop patterns (echo docstrings, single-method classes, stale kwargs, defensive guards) |
|
|
173
|
+
| 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 |
|
|
174
|
+
| Secrets (`SECRET`) | 13 | AWS / GCP / GitHub / Slack / Stripe / private keys / JWTs / hardcoded passwords / K8s Secret YAML / connection strings with embedded creds |
|
|
175
|
+
| Python — Other | 12 | Placeholders, leakage (cross-language idioms, PII), import hallucination, method complexity, variant detection, LLM guardrails (OWASP LLM Top-10) |
|
|
176
|
+
| 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 |
|
|
177
|
+
| 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 |
|
|
178
|
+
| 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 |
|
|
179
|
+
| Terraform — Azure (`TF-AZ`) | 14 | Storage public blob, SQL public network, AKS RBAC, Key Vault purge protection, NSG SSH/RDP, managed disk CMK |
|
|
180
|
+
| Terraform — Mechanics + Lifecycle (`TF-M`, `TF-V`) | 12 | SG inline mixing, `force_destroy`, mutable module refs, lifecycle antipatterns |
|
|
181
|
+
| Terragrunt (`TG`) | 6 | HCL inputs, mock_outputs, remote state encryption |
|
|
182
|
+
| Dockerfile (`DF`) | 15 | Latest tag, root user, secret leakage, OCI labels, HEALTHCHECK, legacy ENV form |
|
|
183
|
+
| Kubernetes YAML (`YAML`) | 26 | Pod Security Standards, hostPath, default namespace, image digest pinning, RBAC wildcards |
|
|
184
|
+
| GitHub Actions (`YAML-GHA`) | — | SHA pinning, expression injection, missing permissions, secret env-var leakage, missing timeouts |
|
|
185
|
+
| Shell (`SH`) | 8 | Dangerous globbing, missing `set -euo pipefail` |
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
proofctl rules # full list
|
|
189
|
+
proofctl rules --family S # filter by family
|
|
190
|
+
proofctl rules --severity ERROR
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Suppressing findings
|
|
196
|
+
|
|
197
|
+
Inline (single line):
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
result = subprocess.run(cmd, shell=True) # proofctl: ignore[PROOFCTL-S-002]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
```hcl
|
|
204
|
+
resource "aws_s3_bucket" "logs" {
|
|
205
|
+
acl = "public-read" # proofctl: ignore[PROOFCTL-TF-AI-003] // or # comment style
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Project-wide in `.proofctl.yaml`:
|
|
210
|
+
|
|
211
|
+
```yaml
|
|
212
|
+
disable:
|
|
213
|
+
- PROOFCTL-Q-004
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Configuration
|
|
219
|
+
|
|
220
|
+
Place `.proofctl.yaml` in the project root:
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
exclude_paths:
|
|
224
|
+
- "**/.venv/**"
|
|
225
|
+
- "**/migrations/**"
|
|
226
|
+
- "**/.terraform/**"
|
|
227
|
+
|
|
228
|
+
disable:
|
|
229
|
+
- PROOFCTL-Q-004
|
|
230
|
+
- PROOFCTL-DF-AI-002
|
|
231
|
+
|
|
232
|
+
severity_overrides:
|
|
233
|
+
PROOFCTL-P-002: ERROR # promote TODOs to ERROR
|
|
234
|
+
|
|
235
|
+
rules:
|
|
236
|
+
PROOFCTL-P-002:
|
|
237
|
+
allowed_formats:
|
|
238
|
+
- 'TODO\(#\d+\)' # allow TODO(#123) format
|
|
239
|
+
exclude_paths:
|
|
240
|
+
- tests/
|
|
241
|
+
|
|
242
|
+
PROOFCTL-Q-003:
|
|
243
|
+
any_threshold: 5
|
|
244
|
+
|
|
245
|
+
PROOFCTL-V-002:
|
|
246
|
+
similarity_threshold: 0.85
|
|
247
|
+
min_file_lines: 30
|
|
248
|
+
|
|
249
|
+
PROOFCTL-I-001:
|
|
250
|
+
local_namespaces:
|
|
251
|
+
- mycompany_
|
|
252
|
+
- internal_
|
|
253
|
+
|
|
254
|
+
PROOFCTL-TF-T013:
|
|
255
|
+
required_labels:
|
|
256
|
+
- environment
|
|
257
|
+
- team
|
|
258
|
+
- cost_center
|
|
259
|
+
|
|
260
|
+
# Substrings that mark a line as legitimate private infra. Findings from
|
|
261
|
+
# "name-shaped" rules (SECRET-*, TF-T005, TF-AI-001, DF-AI-*, Q-AI-*) on a
|
|
262
|
+
# line containing any of these tokens are silently dropped. Real
|
|
263
|
+
# code-shape security rules (S-001 SQLi, S-005 eval, etc.) keep firing.
|
|
264
|
+
internal_identifiers:
|
|
265
|
+
- mycompany-prod # internal hostname prefix
|
|
266
|
+
- mycompany.internal # internal DNS suffix
|
|
267
|
+
- INTERNAL_PROJECT_NAME # codename that sometimes looks like a placeholder
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Commands
|
|
273
|
+
|
|
274
|
+
### `proofctl check`
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
proofctl check [PATH] [OPTIONS]
|
|
278
|
+
|
|
279
|
+
Options:
|
|
280
|
+
--format / -f terminal | json | html (default: terminal)
|
|
281
|
+
--output / -o Write report to file
|
|
282
|
+
--changed-only Only scan git-changed files
|
|
283
|
+
--families P,Q,S,L,I,M,V,LLM,TF,TG,DF,YAML,SH,SECRET
|
|
284
|
+
--min-severity INFO | WARNING | ERROR
|
|
285
|
+
--fail-on Exit non-zero at this severity (default: WARNING)
|
|
286
|
+
--no-pypi Skip PyPI lookups (faster, works offline)
|
|
287
|
+
--new-only Suppress findings present in .proofctl-baseline.json
|
|
288
|
+
--fix Auto-fix fixable findings (Q-001 mutable defaults)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### `proofctl baseline`
|
|
292
|
+
|
|
293
|
+
Snapshot the current findings so future `--new-only` runs only surface regressions.
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
proofctl baseline .
|
|
297
|
+
git add .proofctl-baseline.json && git commit -m "chore: proofctl baseline"
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### `proofctl rules`
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
proofctl rules # list all
|
|
304
|
+
proofctl rules --family S # filter by family
|
|
305
|
+
proofctl rules --severity ERROR
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Exit codes
|
|
311
|
+
|
|
312
|
+
| Code | Meaning |
|
|
313
|
+
|------|---------|
|
|
314
|
+
| `0` | No findings |
|
|
315
|
+
| `1` | Findings present, none at or above `--fail-on` threshold |
|
|
316
|
+
| `2` | One or more findings at or above `--fail-on` threshold |
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Output formats
|
|
321
|
+
|
|
322
|
+
**Terminal** (default) — rich-coloured table with file:line, rule, severity, hint.
|
|
323
|
+
|
|
324
|
+
**JSON** — machine-readable for CI:
|
|
325
|
+
|
|
326
|
+
```json
|
|
327
|
+
{
|
|
328
|
+
"schema_version": 1,
|
|
329
|
+
"summary": { "total": 4, "ERROR": 3, "WARNING": 1 },
|
|
330
|
+
"findings": [
|
|
331
|
+
{
|
|
332
|
+
"file": "module/auth.py",
|
|
333
|
+
"line": 42,
|
|
334
|
+
"col": 8,
|
|
335
|
+
"rule_id": "PROOFCTL-S-001",
|
|
336
|
+
"rule_name": "SQL injection",
|
|
337
|
+
"severity": "ERROR",
|
|
338
|
+
"message": "SQL injection via string format in .execute()",
|
|
339
|
+
"hint": "Use parameterised queries: cursor.execute(sql, params)",
|
|
340
|
+
"authority": "OWASP A03:2021, CWE-89"
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**HTML** — self-contained single-file dashboard with severity summary cards and a filterable table.
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## License
|
|
351
|
+
|
|
352
|
+
MIT
|
proofctl-0.4.1/README.md
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# proofctl
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
proofctl check .
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
PROOFCTL-S-001 module/auth.py:42 SQL injection via string format in .execute() ERROR
|
|
11
|
+
PROOFCTL-Q-001 module/models.py:17 Mutable default argument — list assigned at def time ERROR
|
|
12
|
+
PROOFCTL-S-AI-006 module/loaders.py:88 Empty `except Exception:` swallows all errors silently WARNING
|
|
13
|
+
PROOFCTL-TF-A024 infra/sg.tf:14 SG rule 0.0.0.0/0 ingress on port 22 ERROR
|
|
14
|
+
PROOFCTL-YAML-022 k8s/deployment.yaml:8 Workload uses default namespace WARNING
|
|
15
|
+
PROOFCTL-YAML-007 .github/workflows/release.yml:23 Action not pinned to SHA ERROR
|
|
16
|
+
PROOFCTL-SECRET-001 infra/lambda.tf:8 AWS access key (AKIA...) ERROR
|
|
17
|
+
──────────────────────────────────────────────────────────────────
|
|
18
|
+
7 findings (4 ERROR, 3 WARNING) — exit 2
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Why proofctl
|
|
24
|
+
|
|
25
|
+
LLM coding assistants ship code that compiles and looks correct but fails in predictable ways:
|
|
26
|
+
|
|
27
|
+
- **Insecure** — SQL/command injection, hardcoded credentials, weak crypto, JWT signature bypass, public S3 buckets, K8s default namespaces.
|
|
28
|
+
- **Broken** — hallucinated imports, phantom methods, type-hint vs implementation mismatch, `pass`-bodied functions shipped as real implementations.
|
|
29
|
+
- **Fragile** — broad `except Exception:`, no timeouts, mutable defaults, defensive guards for impossible states, unused configuration kwargs.
|
|
30
|
+
- **Wasteful** — single-method classes that should be functions, near-duplicate files, sprawling kwargs surface.
|
|
31
|
+
|
|
32
|
+
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**.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## What proofctl scans
|
|
37
|
+
|
|
38
|
+
| Language | What it catches |
|
|
39
|
+
|---|---|
|
|
40
|
+
| 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 |
|
|
41
|
+
| 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 |
|
|
42
|
+
| 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) |
|
|
43
|
+
| Helm / Mustache | Same K8s checks via Helm-templated YAML |
|
|
44
|
+
| Dockerfile | USER, base image pinning, secret leakage, root-running, OCI labels, HEALTHCHECK, AI-generated antipatterns |
|
|
45
|
+
| GitHub Actions | SHA-pinning, expression injection, missing permissions, missing timeouts, secret env-var leakage |
|
|
46
|
+
| Shell | Common mistake detection (set -euo, dangerous globbing) |
|
|
47
|
+
| Secrets (any text file) | AWS / GCP / GitHub / Slack / Stripe / private keys / JWTs / hardcoded passwords / K8s Secret YAML / HCL heredocs |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install proofctl
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Requires:** Python 3.11+
|
|
58
|
+
|
|
59
|
+
No project dependencies are added — proofctl only scans, never imports your code.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Quick start
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
proofctl check . # scan current directory
|
|
67
|
+
proofctl check src/api/auth.py # scan a single file
|
|
68
|
+
proofctl check . --min-severity ERROR # only show ERROR
|
|
69
|
+
proofctl check . --fail-on ERROR # CI gate: exit non-zero only on ERROR
|
|
70
|
+
proofctl check . --changed-only # only files changed in this branch
|
|
71
|
+
proofctl check . --families S,Q,SECRET # restrict rule families
|
|
72
|
+
proofctl check . --format html --output report.html
|
|
73
|
+
proofctl rules # list all rules
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Pre-commit integration
|
|
79
|
+
|
|
80
|
+
Install proofctl from PyPI, then add a local hook in `.pre-commit-config.yaml`:
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
83
|
+
repos:
|
|
84
|
+
- repo: local
|
|
85
|
+
hooks:
|
|
86
|
+
- id: proofctl
|
|
87
|
+
name: proofctl
|
|
88
|
+
language: system
|
|
89
|
+
entry: proofctl check
|
|
90
|
+
args: [--no-pypi, --fail-on, ERROR]
|
|
91
|
+
pass_filenames: false
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## CI integration (GitHub Actions)
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
name: proofctl
|
|
100
|
+
on: [push, pull_request]
|
|
101
|
+
|
|
102
|
+
jobs:
|
|
103
|
+
proofctl:
|
|
104
|
+
runs-on: ubuntu-latest
|
|
105
|
+
steps:
|
|
106
|
+
- uses: actions/checkout@v4
|
|
107
|
+
- uses: actions/setup-python@v5
|
|
108
|
+
with:
|
|
109
|
+
python-version: "3.12"
|
|
110
|
+
- run: pip install proofctl
|
|
111
|
+
|
|
112
|
+
# --new-only compares against the committed baseline snapshot —
|
|
113
|
+
# CI fails only on regressions introduced in this PR.
|
|
114
|
+
- run: proofctl check . --no-pypi --fail-on ERROR --new-only
|
|
115
|
+
|
|
116
|
+
- name: Generate HTML report
|
|
117
|
+
if: failure()
|
|
118
|
+
run: proofctl check . --no-pypi --format html --output proofctl-report.html || true
|
|
119
|
+
|
|
120
|
+
- uses: actions/upload-artifact@v4
|
|
121
|
+
if: failure()
|
|
122
|
+
with:
|
|
123
|
+
name: proofctl-report
|
|
124
|
+
path: proofctl-report.html
|
|
125
|
+
retention-days: 14
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Create a baseline snapshot for an existing project so CI only flags regressions:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
proofctl baseline .
|
|
132
|
+
git add .proofctl-baseline.json
|
|
133
|
+
git commit -m "chore: add proofctl baseline"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Rules
|
|
139
|
+
|
|
140
|
+
264 rules across 14 families. Run `proofctl rules` after install to enumerate.
|
|
141
|
+
|
|
142
|
+
| Family | Count | What it covers |
|
|
143
|
+
|---|---|---|
|
|
144
|
+
| Python — Quality (`Q`) | 17 | Mutable defaults, broad `except`, complexity, long functions, AI-slop patterns (echo docstrings, single-method classes, stale kwargs, defensive guards) |
|
|
145
|
+
| 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 |
|
|
146
|
+
| Secrets (`SECRET`) | 13 | AWS / GCP / GitHub / Slack / Stripe / private keys / JWTs / hardcoded passwords / K8s Secret YAML / connection strings with embedded creds |
|
|
147
|
+
| Python — Other | 12 | Placeholders, leakage (cross-language idioms, PII), import hallucination, method complexity, variant detection, LLM guardrails (OWASP LLM Top-10) |
|
|
148
|
+
| 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 |
|
|
149
|
+
| 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 |
|
|
150
|
+
| 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 |
|
|
151
|
+
| Terraform — Azure (`TF-AZ`) | 14 | Storage public blob, SQL public network, AKS RBAC, Key Vault purge protection, NSG SSH/RDP, managed disk CMK |
|
|
152
|
+
| Terraform — Mechanics + Lifecycle (`TF-M`, `TF-V`) | 12 | SG inline mixing, `force_destroy`, mutable module refs, lifecycle antipatterns |
|
|
153
|
+
| Terragrunt (`TG`) | 6 | HCL inputs, mock_outputs, remote state encryption |
|
|
154
|
+
| Dockerfile (`DF`) | 15 | Latest tag, root user, secret leakage, OCI labels, HEALTHCHECK, legacy ENV form |
|
|
155
|
+
| Kubernetes YAML (`YAML`) | 26 | Pod Security Standards, hostPath, default namespace, image digest pinning, RBAC wildcards |
|
|
156
|
+
| GitHub Actions (`YAML-GHA`) | — | SHA pinning, expression injection, missing permissions, secret env-var leakage, missing timeouts |
|
|
157
|
+
| Shell (`SH`) | 8 | Dangerous globbing, missing `set -euo pipefail` |
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
proofctl rules # full list
|
|
161
|
+
proofctl rules --family S # filter by family
|
|
162
|
+
proofctl rules --severity ERROR
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Suppressing findings
|
|
168
|
+
|
|
169
|
+
Inline (single line):
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
result = subprocess.run(cmd, shell=True) # proofctl: ignore[PROOFCTL-S-002]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```hcl
|
|
176
|
+
resource "aws_s3_bucket" "logs" {
|
|
177
|
+
acl = "public-read" # proofctl: ignore[PROOFCTL-TF-AI-003] // or # comment style
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Project-wide in `.proofctl.yaml`:
|
|
182
|
+
|
|
183
|
+
```yaml
|
|
184
|
+
disable:
|
|
185
|
+
- PROOFCTL-Q-004
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Configuration
|
|
191
|
+
|
|
192
|
+
Place `.proofctl.yaml` in the project root:
|
|
193
|
+
|
|
194
|
+
```yaml
|
|
195
|
+
exclude_paths:
|
|
196
|
+
- "**/.venv/**"
|
|
197
|
+
- "**/migrations/**"
|
|
198
|
+
- "**/.terraform/**"
|
|
199
|
+
|
|
200
|
+
disable:
|
|
201
|
+
- PROOFCTL-Q-004
|
|
202
|
+
- PROOFCTL-DF-AI-002
|
|
203
|
+
|
|
204
|
+
severity_overrides:
|
|
205
|
+
PROOFCTL-P-002: ERROR # promote TODOs to ERROR
|
|
206
|
+
|
|
207
|
+
rules:
|
|
208
|
+
PROOFCTL-P-002:
|
|
209
|
+
allowed_formats:
|
|
210
|
+
- 'TODO\(#\d+\)' # allow TODO(#123) format
|
|
211
|
+
exclude_paths:
|
|
212
|
+
- tests/
|
|
213
|
+
|
|
214
|
+
PROOFCTL-Q-003:
|
|
215
|
+
any_threshold: 5
|
|
216
|
+
|
|
217
|
+
PROOFCTL-V-002:
|
|
218
|
+
similarity_threshold: 0.85
|
|
219
|
+
min_file_lines: 30
|
|
220
|
+
|
|
221
|
+
PROOFCTL-I-001:
|
|
222
|
+
local_namespaces:
|
|
223
|
+
- mycompany_
|
|
224
|
+
- internal_
|
|
225
|
+
|
|
226
|
+
PROOFCTL-TF-T013:
|
|
227
|
+
required_labels:
|
|
228
|
+
- environment
|
|
229
|
+
- team
|
|
230
|
+
- cost_center
|
|
231
|
+
|
|
232
|
+
# Substrings that mark a line as legitimate private infra. Findings from
|
|
233
|
+
# "name-shaped" rules (SECRET-*, TF-T005, TF-AI-001, DF-AI-*, Q-AI-*) on a
|
|
234
|
+
# line containing any of these tokens are silently dropped. Real
|
|
235
|
+
# code-shape security rules (S-001 SQLi, S-005 eval, etc.) keep firing.
|
|
236
|
+
internal_identifiers:
|
|
237
|
+
- mycompany-prod # internal hostname prefix
|
|
238
|
+
- mycompany.internal # internal DNS suffix
|
|
239
|
+
- INTERNAL_PROJECT_NAME # codename that sometimes looks like a placeholder
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Commands
|
|
245
|
+
|
|
246
|
+
### `proofctl check`
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
proofctl check [PATH] [OPTIONS]
|
|
250
|
+
|
|
251
|
+
Options:
|
|
252
|
+
--format / -f terminal | json | html (default: terminal)
|
|
253
|
+
--output / -o Write report to file
|
|
254
|
+
--changed-only Only scan git-changed files
|
|
255
|
+
--families P,Q,S,L,I,M,V,LLM,TF,TG,DF,YAML,SH,SECRET
|
|
256
|
+
--min-severity INFO | WARNING | ERROR
|
|
257
|
+
--fail-on Exit non-zero at this severity (default: WARNING)
|
|
258
|
+
--no-pypi Skip PyPI lookups (faster, works offline)
|
|
259
|
+
--new-only Suppress findings present in .proofctl-baseline.json
|
|
260
|
+
--fix Auto-fix fixable findings (Q-001 mutable defaults)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### `proofctl baseline`
|
|
264
|
+
|
|
265
|
+
Snapshot the current findings so future `--new-only` runs only surface regressions.
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
proofctl baseline .
|
|
269
|
+
git add .proofctl-baseline.json && git commit -m "chore: proofctl baseline"
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### `proofctl rules`
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
proofctl rules # list all
|
|
276
|
+
proofctl rules --family S # filter by family
|
|
277
|
+
proofctl rules --severity ERROR
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Exit codes
|
|
283
|
+
|
|
284
|
+
| Code | Meaning |
|
|
285
|
+
|------|---------|
|
|
286
|
+
| `0` | No findings |
|
|
287
|
+
| `1` | Findings present, none at or above `--fail-on` threshold |
|
|
288
|
+
| `2` | One or more findings at or above `--fail-on` threshold |
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Output formats
|
|
293
|
+
|
|
294
|
+
**Terminal** (default) — rich-coloured table with file:line, rule, severity, hint.
|
|
295
|
+
|
|
296
|
+
**JSON** — machine-readable for CI:
|
|
297
|
+
|
|
298
|
+
```json
|
|
299
|
+
{
|
|
300
|
+
"schema_version": 1,
|
|
301
|
+
"summary": { "total": 4, "ERROR": 3, "WARNING": 1 },
|
|
302
|
+
"findings": [
|
|
303
|
+
{
|
|
304
|
+
"file": "module/auth.py",
|
|
305
|
+
"line": 42,
|
|
306
|
+
"col": 8,
|
|
307
|
+
"rule_id": "PROOFCTL-S-001",
|
|
308
|
+
"rule_name": "SQL injection",
|
|
309
|
+
"severity": "ERROR",
|
|
310
|
+
"message": "SQL injection via string format in .execute()",
|
|
311
|
+
"hint": "Use parameterised queries: cursor.execute(sql, params)",
|
|
312
|
+
"authority": "OWASP A03:2021, CWE-89"
|
|
313
|
+
}
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**HTML** — self-contained single-file dashboard with severity summary cards and a filterable table.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## License
|
|
323
|
+
|
|
324
|
+
MIT
|