proofctl 0.1.0__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.1.0/LICENSE +21 -0
- proofctl-0.1.0/PKG-INFO +563 -0
- proofctl-0.1.0/README.md +532 -0
- proofctl-0.1.0/proofctl/__init__.py +0 -0
- proofctl-0.1.0/proofctl/baseline.py +63 -0
- proofctl-0.1.0/proofctl/checkers/__init__.py +0 -0
- proofctl-0.1.0/proofctl/checkers/base.py +61 -0
- proofctl-0.1.0/proofctl/checkers/dockerfile.py +452 -0
- proofctl-0.1.0/proofctl/checkers/hcl_utils.py +180 -0
- proofctl-0.1.0/proofctl/checkers/imports.py +486 -0
- proofctl-0.1.0/proofctl/checkers/leakage.py +228 -0
- proofctl-0.1.0/proofctl/checkers/llm_integration.py +370 -0
- proofctl-0.1.0/proofctl/checkers/placeholders.py +261 -0
- proofctl-0.1.0/proofctl/checkers/quality.py +539 -0
- proofctl-0.1.0/proofctl/checkers/security.py +831 -0
- proofctl-0.1.0/proofctl/checkers/terraform.py +1979 -0
- proofctl-0.1.0/proofctl/checkers/terragrunt.py +206 -0
- proofctl-0.1.0/proofctl/checkers/variants.py +166 -0
- proofctl-0.1.0/proofctl/checkers/yaml_checker.py +1220 -0
- proofctl-0.1.0/proofctl/cli.py +246 -0
- proofctl-0.1.0/proofctl/config.py +92 -0
- proofctl-0.1.0/proofctl/engine.py +403 -0
- proofctl-0.1.0/proofctl/fixer.py +138 -0
- proofctl-0.1.0/proofctl/models.py +44 -0
- proofctl-0.1.0/proofctl/reporters/__init__.py +0 -0
- proofctl-0.1.0/proofctl/reporters/html_reporter.py +191 -0
- proofctl-0.1.0/proofctl/reporters/json_reporter.py +22 -0
- proofctl-0.1.0/proofctl/reporters/terminal.py +110 -0
- proofctl-0.1.0/proofctl.egg-info/PKG-INFO +563 -0
- proofctl-0.1.0/proofctl.egg-info/SOURCES.txt +54 -0
- proofctl-0.1.0/proofctl.egg-info/dependency_links.txt +1 -0
- proofctl-0.1.0/proofctl.egg-info/entry_points.txt +2 -0
- proofctl-0.1.0/proofctl.egg-info/requires.txt +7 -0
- proofctl-0.1.0/proofctl.egg-info/top_level.txt +1 -0
- proofctl-0.1.0/pyproject.toml +53 -0
- proofctl-0.1.0/setup.cfg +4 -0
- proofctl-0.1.0/tests/test_baseline.py +130 -0
- proofctl-0.1.0/tests/test_config_proofctl.py +101 -0
- proofctl-0.1.0/tests/test_dockerfile.py +470 -0
- proofctl-0.1.0/tests/test_engine.py +88 -0
- proofctl-0.1.0/tests/test_fixer.py +162 -0
- proofctl-0.1.0/tests/test_imports.py +312 -0
- proofctl-0.1.0/tests/test_leakage.py +179 -0
- proofctl-0.1.0/tests/test_llm.py +279 -0
- proofctl-0.1.0/tests/test_placeholders.py +239 -0
- proofctl-0.1.0/tests/test_quality.py +135 -0
- proofctl-0.1.0/tests/test_quality_ext.py +286 -0
- proofctl-0.1.0/tests/test_security.py +322 -0
- proofctl-0.1.0/tests/test_security_ext.py +470 -0
- proofctl-0.1.0/tests/test_suppression.py +137 -0
- proofctl-0.1.0/tests/test_terraform.py +861 -0
- proofctl-0.1.0/tests/test_terraform_ext.py +1121 -0
- proofctl-0.1.0/tests/test_terragrunt.py +259 -0
- proofctl-0.1.0/tests/test_variants.py +136 -0
- proofctl-0.1.0/tests/test_yaml.py +401 -0
- proofctl-0.1.0/tests/test_yaml_k8s_gha.py +1105 -0
proofctl-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Tameed Engineering
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
proofctl-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: proofctl
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Zero-dependency linter for Python, Terraform, Dockerfiles, Kubernetes, and GitHub Actions — catches AI slop and security misconfigurations pre-commit
|
|
5
|
+
Author-email: Tameed Engineering <eng@tameed.io>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/kolawoluu/proofctl
|
|
8
|
+
Project-URL: Repository, https://github.com/kolawoluu/proofctl
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/kolawoluu/proofctl/issues
|
|
10
|
+
Keywords: linter,security,terraform,kubernetes,ai,static-analysis,pre-commit
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Classifier: Topic :: Security
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: typer[all]>=0.12
|
|
25
|
+
Requires-Dist: rich>=13
|
|
26
|
+
Requires-Dist: pyyaml>=6
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-mock>=3; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# proofctl
|
|
33
|
+
|
|
34
|
+
Zero-dependency Python linter and infrastructure policy checker for pre-commit hooks. Detects AI-generated slop, security misconfigurations, and quality regressions across Python, Terraform (AWS/GCP/Azure), Dockerfiles, Kubernetes manifests, and GitHub Actions workflows.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
proofctl check .
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
PROOFCTL-S-001 module/auth.py:42 SQL injection via string format in .execute() ERROR
|
|
42
|
+
PROOFCTL-Q-001 module/models.py:17 Mutable default argument — list assigned at def time ERROR
|
|
43
|
+
PROOFCTL-P-001 module/tasks.py:89 Unimplemented function body (pass / ...) ERROR
|
|
44
|
+
PROOFCTL-TF-G004 infra/main.tf:14 GKE node pool has auto-repair disabled WARNING
|
|
45
|
+
──────────────────────────────────────────────────────────────────
|
|
46
|
+
4 findings (3 ERROR, 1 WARNING) exit 2
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Why proofctl
|
|
52
|
+
|
|
53
|
+
AI coding assistants generate plausible-looking code that is frequently:
|
|
54
|
+
- **Insecure** — SQL/command injection, hardcoded secrets, weak crypto, JWT bypass
|
|
55
|
+
- **Broken** — hallucinated imports, phantom methods, `pass` bodies shipped as real functions
|
|
56
|
+
- **Fragile** — mutable defaults, broad `except:`, no timeouts, untested paths
|
|
57
|
+
- **Wasteful** — near-duplicate files, single-implementation abstractions, TODO bombs
|
|
58
|
+
|
|
59
|
+
proofctl is a pre-commit linter purpose-built to catch all of this *before* it merges. It runs in under a second on a typical repo, requires no internet access, and adds zero runtime dependencies to your project.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install proofctl
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Or for development:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/kolawoluu/proofctl
|
|
73
|
+
cd proofctl
|
|
74
|
+
pip install -e ".[dev]"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Requires:** Python 3.11+
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Quick start
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Scan the current directory
|
|
85
|
+
proofctl check .
|
|
86
|
+
|
|
87
|
+
# Scan a single file
|
|
88
|
+
proofctl check src/api/auth.py
|
|
89
|
+
|
|
90
|
+
# Only report ERROR findings
|
|
91
|
+
proofctl check . --min-severity ERROR
|
|
92
|
+
|
|
93
|
+
# Exit non-zero only on ERROR (useful for CI gate)
|
|
94
|
+
proofctl check . --fail-on ERROR
|
|
95
|
+
|
|
96
|
+
# Scan only files changed in this branch
|
|
97
|
+
proofctl check . --changed-only
|
|
98
|
+
|
|
99
|
+
# Run only security and quality families
|
|
100
|
+
proofctl check . --families S,Q
|
|
101
|
+
|
|
102
|
+
# Generate an HTML report
|
|
103
|
+
proofctl check . --format html --output report.html
|
|
104
|
+
|
|
105
|
+
# List all rules
|
|
106
|
+
proofctl rules
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Pre-commit integration
|
|
112
|
+
|
|
113
|
+
Add to `.pre-commit-config.yaml`:
|
|
114
|
+
|
|
115
|
+
```yaml
|
|
116
|
+
repos:
|
|
117
|
+
- repo: local
|
|
118
|
+
hooks:
|
|
119
|
+
- id: proofctl
|
|
120
|
+
name: proofctl
|
|
121
|
+
language: system
|
|
122
|
+
entry: proofctl check
|
|
123
|
+
args: [--no-pypi, --fail-on, ERROR]
|
|
124
|
+
types: [python]
|
|
125
|
+
pass_filenames: false
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Or pin from the published GitHub repo:
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
repos:
|
|
132
|
+
- repo: https://github.com/kolawoluu/proofctl
|
|
133
|
+
rev: v0.1.0
|
|
134
|
+
hooks:
|
|
135
|
+
- id: proofctl
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## CI integration (GitHub Actions)
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
name: proofctl
|
|
144
|
+
|
|
145
|
+
on: [push, pull_request]
|
|
146
|
+
|
|
147
|
+
jobs:
|
|
148
|
+
proofctl:
|
|
149
|
+
runs-on: ubuntu-latest
|
|
150
|
+
steps:
|
|
151
|
+
- uses: actions/checkout@v4
|
|
152
|
+
- uses: actions/setup-python@v5
|
|
153
|
+
with:
|
|
154
|
+
python-version: "3.12"
|
|
155
|
+
- run: pip install proofctl
|
|
156
|
+
|
|
157
|
+
# --new-only compares against the committed baseline snapshot —
|
|
158
|
+
# CI fails only on regressions introduced in this PR.
|
|
159
|
+
- run: proofctl check . --no-pypi --fail-on ERROR --new-only
|
|
160
|
+
|
|
161
|
+
- name: Generate HTML report
|
|
162
|
+
if: failure()
|
|
163
|
+
run: proofctl check . --no-pypi --format html --output proofctl-report.html || true
|
|
164
|
+
|
|
165
|
+
- uses: actions/upload-artifact@v4
|
|
166
|
+
if: failure()
|
|
167
|
+
with:
|
|
168
|
+
name: proofctl-report
|
|
169
|
+
path: proofctl-report.html
|
|
170
|
+
retention-days: 14
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Create a baseline snapshot to suppress pre-existing findings:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
proofctl baseline .
|
|
177
|
+
git add .proofctl-baseline.json
|
|
178
|
+
git commit -m "chore: add proofctl baseline"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Rules
|
|
184
|
+
|
|
185
|
+
### Python — Placeholder (P)
|
|
186
|
+
|
|
187
|
+
| Rule | Severity | Description |
|
|
188
|
+
|------|----------|-------------|
|
|
189
|
+
| PROOFCTL-P-001 | ERROR | Unimplemented function body (`pass`, `...`, `raise NotImplementedError`) |
|
|
190
|
+
| PROOFCTL-P-002 | WARNING | TODO / FIXME / HACK / XXX comment present |
|
|
191
|
+
| PROOFCTL-P-003 | INFO | Commented-out code block |
|
|
192
|
+
|
|
193
|
+
### Python — Quality (Q)
|
|
194
|
+
|
|
195
|
+
| Rule | Severity | Description |
|
|
196
|
+
|------|----------|-------------|
|
|
197
|
+
| PROOFCTL-Q-001 | ERROR | Mutable default argument (`list`, `dict`, `set` at def time) |
|
|
198
|
+
| PROOFCTL-Q-002 | ERROR | Bare `except:` or broad `except Exception` with no re-raise |
|
|
199
|
+
| PROOFCTL-Q-003 | WARNING | Excessive `Any` usage or unexplained `# type: ignore` |
|
|
200
|
+
| PROOFCTL-Q-004 | INFO | Single-implementation abstraction (YAGNI violation) |
|
|
201
|
+
| PROOFCTL-Q-005 | WARNING/ERROR | High cyclomatic complexity (warn >10, error >20) |
|
|
202
|
+
| PROOFCTL-Q-006 | WARNING/ERROR | Function body too long (warn >50 lines, error >100 lines) |
|
|
203
|
+
| PROOFCTL-Q-007 | WARNING/ERROR | Too many parameters (warn >5, error >7) |
|
|
204
|
+
| PROOFCTL-Q-008 | WARNING | Boolean literal as positional argument (flag argument) |
|
|
205
|
+
| PROOFCTL-Q-009 | INFO | `print()` in non-test code instead of `logging` |
|
|
206
|
+
|
|
207
|
+
### Python — Security (S)
|
|
208
|
+
|
|
209
|
+
| Rule | Severity | Description | Authority |
|
|
210
|
+
|------|----------|-------------|-----------|
|
|
211
|
+
| PROOFCTL-S-001 | ERROR | SQL injection via string formatting in `.execute()` | OWASP A03, CWE-89 |
|
|
212
|
+
| PROOFCTL-S-002 | ERROR | Command injection via `shell=True` or `os.system()` | OWASP A03, CWE-78 |
|
|
213
|
+
| PROOFCTL-S-003 | ERROR | Unsafe deserialization (`pickle.loads`, `yaml.load` without SafeLoader) | OWASP A08, CWE-502 |
|
|
214
|
+
| PROOFCTL-S-004 | ERROR | Weak cryptographic primitive (`md5`/`sha1` for auth, `random` for secrets) | OWASP A02, CWE-327 |
|
|
215
|
+
| PROOFCTL-S-005 | ERROR | `eval()` / `exec()` on non-literal input | CWE-95 |
|
|
216
|
+
| PROOFCTL-S-006 | WARNING | Missing `timeout=` on HTTP calls (`requests`, `httpx`) | CWE-400 |
|
|
217
|
+
| PROOFCTL-S-007 | ERROR | JWT signature bypass (`verify_signature: False`, `algorithms: ["none"]`) | OWASP A07 |
|
|
218
|
+
| PROOFCTL-S-008 | ERROR | Path traversal — `open()` with user-supplied path | CWE-22 |
|
|
219
|
+
| PROOFCTL-S-009 | ERROR | Insecure UUID for security token (`uuid1`/`uuid3` for token/secret/key) | CWE-338 |
|
|
220
|
+
| PROOFCTL-S-010 | WARNING | Secret value leaked into logs (password/token in `logging.*` call) | OWASP A09 |
|
|
221
|
+
| PROOFCTL-S-011 | ERROR/WARNING | Insecure cipher mode (`ECB`, `ARC4`, `TripleDES`) | OWASP A02 |
|
|
222
|
+
| PROOFCTL-S-012 | WARNING | Regex injection — non-literal pattern passed to `re.compile`/`re.search` | CWE-625 |
|
|
223
|
+
| PROOFCTL-S-013 | WARNING | Open redirect — `redirect()` with user-supplied URL | CWE-601 |
|
|
224
|
+
|
|
225
|
+
### Python — Leakage (L)
|
|
226
|
+
|
|
227
|
+
| Rule | Severity | Description |
|
|
228
|
+
|------|----------|-------------|
|
|
229
|
+
| PROOFCTL-L-001 | ERROR | Hardcoded secret or credential in source |
|
|
230
|
+
| PROOFCTL-L-002 | WARNING | PII pattern in source (email, SSN, phone number) |
|
|
231
|
+
| PROOFCTL-L-003 | WARNING | Cross-language idiom (JS/Java/Go pattern in Python) |
|
|
232
|
+
|
|
233
|
+
### Python — Imports (I)
|
|
234
|
+
|
|
235
|
+
| Rule | Severity | Description |
|
|
236
|
+
|------|----------|-------------|
|
|
237
|
+
| PROOFCTL-I-001 | WARNING | Import from unknown or unresolvable package (hallucination) |
|
|
238
|
+
| PROOFCTL-I-002 | ERROR | High-risk or deprecated package (`telnetlib`, `pickle`, etc.) |
|
|
239
|
+
|
|
240
|
+
### Python — Methods (M)
|
|
241
|
+
|
|
242
|
+
| Rule | Severity | Description |
|
|
243
|
+
|------|----------|-------------|
|
|
244
|
+
| PROOFCTL-M-001 | WARNING | Method too complex — abstraction opportunity |
|
|
245
|
+
|
|
246
|
+
### Python — Variants (V)
|
|
247
|
+
|
|
248
|
+
| Rule | Severity | Description |
|
|
249
|
+
|------|----------|-------------|
|
|
250
|
+
| PROOFCTL-V-001 | WARNING | Near-duplicate function bodies (DRY violation) |
|
|
251
|
+
| PROOFCTL-V-002 | INFO | Near-duplicate file (Jaccard similarity above threshold) |
|
|
252
|
+
|
|
253
|
+
### Python — LLM Guardrails (LLM)
|
|
254
|
+
|
|
255
|
+
| Rule | Severity | Description | Authority |
|
|
256
|
+
|------|----------|-------------|-----------|
|
|
257
|
+
| PROOFCTL-LLM-001 | ERROR | Unsanitised user input in LLM prompt (f-string in `content=`) | OWASP LLM01 |
|
|
258
|
+
| PROOFCTL-LLM-002 | WARNING | Missing `max_tokens` on LLM API call | OWASP LLM10 |
|
|
259
|
+
| PROOFCTL-LLM-003 | WARNING | LLM API call inside a loop (unbounded cost) | OWASP LLM10 |
|
|
260
|
+
| PROOFCTL-LLM-004 | WARNING | PII variable name in LLM prompt | OWASP LLM02 |
|
|
261
|
+
| PROOFCTL-LLM-005 | ERROR | `while True:` agentic loop with LLM call and no iteration guard | OWASP LLM06 |
|
|
262
|
+
|
|
263
|
+
### Terraform — General (TF-T)
|
|
264
|
+
|
|
265
|
+
| Rule | Severity | Description |
|
|
266
|
+
|------|----------|-------------|
|
|
267
|
+
| PROOFCTL-TF-T001 | ERROR | Empty or null resource block |
|
|
268
|
+
| PROOFCTL-TF-T002 | WARNING | `count = 0` resource |
|
|
269
|
+
| PROOFCTL-TF-T003 | ERROR | Hardcoded secret in resource attribute |
|
|
270
|
+
| PROOFCTL-TF-T004 | WARNING | Wildcard IAM permission (`*`) |
|
|
271
|
+
| PROOFCTL-TF-T005 | WARNING | `lifecycle { ignore_changes = all }` |
|
|
272
|
+
| PROOFCTL-TF-T006 | WARNING | Local-only module source (no registry or git ref) |
|
|
273
|
+
| PROOFCTL-TF-T007 | ERROR | Mutable git ref in module source (branch / HEAD) |
|
|
274
|
+
| PROOFCTL-TF-T008 | WARNING | Missing `description` on variable |
|
|
275
|
+
| PROOFCTL-TF-T009 | WARNING | Missing `description` on output |
|
|
276
|
+
| PROOFCTL-TF-T010 | WARNING | Null-default variable without validation block |
|
|
277
|
+
| PROOFCTL-TF-T011 | ERROR | Remote state without encryption |
|
|
278
|
+
| PROOFCTL-TF-T012 | WARNING | Provisioner block (prefer cloud-init or user_data) |
|
|
279
|
+
| PROOFCTL-TF-T013 | ERROR | Missing required resource labels |
|
|
280
|
+
| PROOFCTL-TF-T014 | WARNING | No `terraform { required_version }` constraint |
|
|
281
|
+
| PROOFCTL-TF-T015 | WARNING | Sensitive-named output without `sensitive = true` |
|
|
282
|
+
|
|
283
|
+
### Terraform — GCP (TF-G)
|
|
284
|
+
|
|
285
|
+
25 rules covering GCP compute, Cloud SQL, GKE, Cloud Storage, IAM, KMS, VPC, Cloud Run, BigQuery, Pub/Sub, and Cloud Functions. Highlights:
|
|
286
|
+
|
|
287
|
+
- `PROOFCTL-TF-G001` — GKE node pool with auto-repair disabled
|
|
288
|
+
- `PROOFCTL-TF-G004` — Cloud SQL with public IP
|
|
289
|
+
- `PROOFCTL-TF-G008` — Storage bucket with `allUsers` ACL
|
|
290
|
+
- `PROOFCTL-TF-G015` — GCS bucket without versioning
|
|
291
|
+
- `PROOFCTL-TF-G026` — IAM binding with `allUsers` / `allAuthenticatedUsers`
|
|
292
|
+
- `PROOFCTL-TF-G030` — BigQuery dataset accessible to `allAuthenticatedUsers`
|
|
293
|
+
|
|
294
|
+
### Terraform — AWS (TF-A)
|
|
295
|
+
|
|
296
|
+
14 rules covering EC2 IMDSv2, RDS, EKS, EBS, Lambda, ElastiCache, S3, ECR, CloudTrail, and VPC flow logs. Highlights:
|
|
297
|
+
|
|
298
|
+
- `PROOFCTL-TF-A001` — EC2 without IMDSv2 enforced
|
|
299
|
+
- `PROOFCTL-TF-A003` — RDS with public access
|
|
300
|
+
- `PROOFCTL-TF-A011` — No CloudTrail resource in file
|
|
301
|
+
- `PROOFCTL-TF-A014` — Lambda function with hardcoded secret in env vars
|
|
302
|
+
|
|
303
|
+
### Terraform — Azure (TF-AZ)
|
|
304
|
+
|
|
305
|
+
12 rules covering Azure storage, SQL/Postgres/MySQL, AKS, Key Vault, Monitor, RBAC, App Service, and VMs. Highlights:
|
|
306
|
+
|
|
307
|
+
- `PROOFCTL-TF-AZ001` — Storage account with `allow_blob_public_access = true`
|
|
308
|
+
- `PROOFCTL-TF-AZ003` — SQL server with `public_network_access_enabled = true`
|
|
309
|
+
- `PROOFCTL-TF-AZ005` — AKS cluster with unrestricted API server access
|
|
310
|
+
- `PROOFCTL-TF-AZ007` — Key Vault without purge protection
|
|
311
|
+
- `PROOFCTL-TF-AZ011` — App Service without `https_only = true`
|
|
312
|
+
|
|
313
|
+
### Terraform — Mechanics (TF-M) and Lifecycle (TF-V)
|
|
314
|
+
|
|
315
|
+
7 + 4 rules covering security group mixing, `ignore_changes = all`, external provisioners, `force_destroy`, `prevent_destroy`, and mutable module refs.
|
|
316
|
+
|
|
317
|
+
### Terragrunt (TG)
|
|
318
|
+
|
|
319
|
+
6 rules covering `var.*` in HCL inputs, missing `mock_outputs`, invalid `if_exists` values, and unencrypted remote state.
|
|
320
|
+
|
|
321
|
+
### Dockerfile (DF)
|
|
322
|
+
|
|
323
|
+
| Rule | Severity | Description |
|
|
324
|
+
|------|----------|-------------|
|
|
325
|
+
| PROOFCTL-DF-001 | WARNING | `FROM` uses `latest` tag |
|
|
326
|
+
| PROOFCTL-DF-002 | ERROR | Running as root (`USER root` or no `USER` directive) |
|
|
327
|
+
| PROOFCTL-DF-003 | WARNING | `apt-get install` without `--no-install-recommends` |
|
|
328
|
+
| PROOFCTL-DF-004 | ERROR | Secret in `ARG` or `ENV` (password/token/key name) |
|
|
329
|
+
| PROOFCTL-DF-005 | WARNING | `ADD` used where `COPY` is sufficient |
|
|
330
|
+
| PROOFCTL-DF-006 | INFO | Multiple `RUN` commands that could be merged |
|
|
331
|
+
| PROOFCTL-DF-007 | ERROR | `curl`/`wget` output piped directly to shell |
|
|
332
|
+
| PROOFCTL-DF-008 | WARNING | `ADD` from URL without checksum verification |
|
|
333
|
+
| PROOFCTL-DF-009 | WARNING | `COPY` of dependency manifest without pinned install |
|
|
334
|
+
| PROOFCTL-DF-010 | INFO | Missing OCI image labels (`org.opencontainers.image.*`) |
|
|
335
|
+
|
|
336
|
+
### YAML / Kubernetes (YAML-K8S)
|
|
337
|
+
|
|
338
|
+
13 rules based on Kubernetes Pod Security Standards. Highlights:
|
|
339
|
+
|
|
340
|
+
| Rule | Severity | Description |
|
|
341
|
+
|------|----------|-------------|
|
|
342
|
+
| PROOFCTL-YAML-009 | ERROR | Container missing `runAsNonRoot: true` |
|
|
343
|
+
| PROOFCTL-YAML-010 | ERROR | Container missing `allowPrivilegeEscalation: false` |
|
|
344
|
+
| PROOFCTL-YAML-011 | WARNING | Container missing `readOnlyRootFilesystem: true` |
|
|
345
|
+
| PROOFCTL-YAML-012 | WARNING | Container missing `drop: [ALL]` capabilities |
|
|
346
|
+
| PROOFCTL-YAML-013 | WARNING | Pod missing `seccompProfile` annotation |
|
|
347
|
+
| PROOFCTL-YAML-014 | WARNING | `automountServiceAccountToken: true` on pod or service account |
|
|
348
|
+
| PROOFCTL-YAML-015 | ERROR | ClusterRoleBinding to `cluster-admin` |
|
|
349
|
+
| PROOFCTL-YAML-016 | WARNING | RBAC rule with wildcard verb or resource (`*`) |
|
|
350
|
+
| PROOFCTL-YAML-017 | ERROR | RBAC rule with `escalate`/`impersonate`/`bind` verb |
|
|
351
|
+
| PROOFCTL-YAML-018 | WARNING | Deployment/DaemonSet container missing liveness or readiness probe |
|
|
352
|
+
| PROOFCTL-YAML-019 | WARNING | Ingress without TLS configuration |
|
|
353
|
+
| PROOFCTL-YAML-020 | ERROR | Plaintext secret in environment variable (`value:` on secret-named var) |
|
|
354
|
+
| PROOFCTL-YAML-021 | WARNING | Namespace-scoped RoleBinding using `cluster-admin` |
|
|
355
|
+
|
|
356
|
+
### YAML / GitHub Actions (YAML-GHA)
|
|
357
|
+
|
|
358
|
+
| Rule | Severity | Description |
|
|
359
|
+
|------|----------|-------------|
|
|
360
|
+
| PROOFCTL-YAML-007 | ERROR | Expression injection — `${{ github.event.* }}` in `run:` step |
|
|
361
|
+
| PROOFCTL-YAML-GHA-001 | ERROR | `pull_request_target` with `actions/checkout` of PR head ref |
|
|
362
|
+
| PROOFCTL-YAML-GHA-002 | WARNING | Workflow missing top-level `permissions:` block |
|
|
363
|
+
| PROOFCTL-YAML-GHA-003 | WARNING | Secret passed directly to environment variable in workflow |
|
|
364
|
+
| PROOFCTL-YAML-GHA-004 | ERROR | `ACTIONS_ALLOW_UNSECURE_COMMANDS: true` in workflow |
|
|
365
|
+
| PROOFCTL-YAML-GHA-005 | WARNING | Job missing `timeout-minutes` |
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Suppressing findings
|
|
370
|
+
|
|
371
|
+
Inline suppression for a single line:
|
|
372
|
+
|
|
373
|
+
```python
|
|
374
|
+
result = subprocess.run(cmd, shell=True) # proofctl: ignore[PROOFCTL-S-002]
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Project-wide suppression in `.proofctl.yaml`:
|
|
378
|
+
|
|
379
|
+
```yaml
|
|
380
|
+
disable:
|
|
381
|
+
- PROOFCTL-Q-004 # YAGNI check too noisy for this repo
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Configuration
|
|
387
|
+
|
|
388
|
+
Place `.proofctl.yaml` in the project root:
|
|
389
|
+
|
|
390
|
+
```yaml
|
|
391
|
+
exclude_paths:
|
|
392
|
+
- "**/.venv/**"
|
|
393
|
+
- "**/migrations/**"
|
|
394
|
+
- "**/.terraform/**"
|
|
395
|
+
|
|
396
|
+
disable:
|
|
397
|
+
- PROOFCTL-Q-004
|
|
398
|
+
|
|
399
|
+
severity_overrides:
|
|
400
|
+
PROOFCTL-P-002: ERROR # promote TODOs to ERROR
|
|
401
|
+
|
|
402
|
+
rules:
|
|
403
|
+
PROOFCTL-P-002:
|
|
404
|
+
allowed_formats:
|
|
405
|
+
- 'TODO\(#\d+\)' # allow TODO(#123) format
|
|
406
|
+
exclude_paths:
|
|
407
|
+
- tests/
|
|
408
|
+
|
|
409
|
+
PROOFCTL-Q-003:
|
|
410
|
+
any_threshold: 5
|
|
411
|
+
|
|
412
|
+
PROOFCTL-V-002:
|
|
413
|
+
similarity_threshold: 0.85
|
|
414
|
+
min_file_lines: 30
|
|
415
|
+
|
|
416
|
+
PROOFCTL-I-001:
|
|
417
|
+
local_namespaces:
|
|
418
|
+
- mycompany_
|
|
419
|
+
- internal_
|
|
420
|
+
|
|
421
|
+
PROOFCTL-TF-T013:
|
|
422
|
+
required_labels:
|
|
423
|
+
- environment
|
|
424
|
+
- team
|
|
425
|
+
- cost_center
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## Commands
|
|
431
|
+
|
|
432
|
+
### `proofctl check`
|
|
433
|
+
|
|
434
|
+
```
|
|
435
|
+
proofctl check [PATH] [OPTIONS]
|
|
436
|
+
|
|
437
|
+
Options:
|
|
438
|
+
--format / -f terminal | json | html (default: terminal)
|
|
439
|
+
--output / -o Write report to file
|
|
440
|
+
--changed-only Only scan git-changed files
|
|
441
|
+
--families Comma-separated families: P,Q,S,L,I,M,V,LLM,TF,TG,DF,YAML
|
|
442
|
+
--min-severity INFO | WARNING | ERROR
|
|
443
|
+
--fail-on Exit non-zero at this severity (default: WARNING)
|
|
444
|
+
--no-pypi Skip PyPI lookups (faster, works offline)
|
|
445
|
+
--new-only Suppress findings present in .proofctl-baseline.json
|
|
446
|
+
--fix Auto-fix fixable findings (Q-001 mutable defaults)
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### `proofctl baseline`
|
|
450
|
+
|
|
451
|
+
Snapshot the current findings so future `--new-only` runs only surface regressions.
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
proofctl baseline .
|
|
455
|
+
git add .proofctl-baseline.json && git commit -m "chore: proofctl baseline"
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### `proofctl rules`
|
|
459
|
+
|
|
460
|
+
Print all rule IDs, names, and severities.
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## Exit codes
|
|
465
|
+
|
|
466
|
+
| Code | Meaning |
|
|
467
|
+
|------|---------|
|
|
468
|
+
| `0` | No findings |
|
|
469
|
+
| `1` | Findings present, none at or above `--fail-on` threshold |
|
|
470
|
+
| `2` | One or more findings at or above `--fail-on` threshold |
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## Output formats
|
|
475
|
+
|
|
476
|
+
**Terminal** (default) — rich-coloured table with file:line, rule, severity, and hint.
|
|
477
|
+
|
|
478
|
+
**JSON** — machine-readable array for CI integrations:
|
|
479
|
+
|
|
480
|
+
```json
|
|
481
|
+
{
|
|
482
|
+
"summary": { "total": 4, "ERROR": 3, "WARNING": 1 },
|
|
483
|
+
"findings": [
|
|
484
|
+
{
|
|
485
|
+
"file": "module/auth.py",
|
|
486
|
+
"line": 42,
|
|
487
|
+
"col": 8,
|
|
488
|
+
"rule_id": "PROOFCTL-S-001",
|
|
489
|
+
"severity": "ERROR",
|
|
490
|
+
"message": "SQL injection via string format in .execute()",
|
|
491
|
+
"hint": "Use parameterised queries: cursor.execute(sql, params)",
|
|
492
|
+
"authority": "OWASP A03:2021, CWE-89"
|
|
493
|
+
}
|
|
494
|
+
]
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
**HTML** — self-contained single-file dashboard with severity summary cards and a filterable table.
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
## Architecture
|
|
503
|
+
|
|
504
|
+
proofctl is a pure Python AST linter with zero subprocess dependencies. It walks the repository with Python's `pathlib`, runs AST visitors for Python files, and custom regex/text parsers for Terraform HCL, Dockerfiles, and YAML. This makes it:
|
|
505
|
+
|
|
506
|
+
- **Fast** — typically < 1s for a 10k-line Python repo
|
|
507
|
+
- **Offline** — no network calls needed with `--no-pypi`
|
|
508
|
+
- **Dependency-free** — only `typer`, `rich`, `pyyaml` are required at runtime
|
|
509
|
+
|
|
510
|
+
```
|
|
511
|
+
proofctl/
|
|
512
|
+
├── cli.py # typer commands: check, baseline, rules
|
|
513
|
+
├── engine.py # orchestrator: walks files, dispatches checkers
|
|
514
|
+
├── config.py # .proofctl.yaml loader
|
|
515
|
+
├── models.py # Finding dataclass + Severity enum
|
|
516
|
+
├── baseline.py # snapshot save/load/filter
|
|
517
|
+
├── fixer.py # auto-fix for Q-001
|
|
518
|
+
├── checkers/
|
|
519
|
+
│ ├── placeholders.py # P family
|
|
520
|
+
│ ├── quality.py # Q family
|
|
521
|
+
│ ├── security.py # S family (S-001 to S-013)
|
|
522
|
+
│ ├── leakage.py # L family
|
|
523
|
+
│ ├── imports.py # I family
|
|
524
|
+
│ ├── methods.py # M family
|
|
525
|
+
│ ├── variants.py # V family
|
|
526
|
+
│ ├── llm.py # LLM family
|
|
527
|
+
│ ├── terraform.py # TF/TG families (T, G, A, AZ, M, V, TG)
|
|
528
|
+
│ ├── dockerfile.py # DF family
|
|
529
|
+
│ └── yaml_checker.py # YAML/K8s/GHA families
|
|
530
|
+
└── reporters/
|
|
531
|
+
├── terminal.py
|
|
532
|
+
├── json_reporter.py
|
|
533
|
+
└── html_reporter.py
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## Development
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
pip install -e ".[dev]"
|
|
542
|
+
pytest tests/ -v
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
635 tests across all rule families. To run a specific family:
|
|
546
|
+
|
|
547
|
+
```bash
|
|
548
|
+
pytest tests/test_security*.py -v
|
|
549
|
+
pytest tests/test_terraform*.py -v
|
|
550
|
+
pytest tests/test_yaml*.py -v
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
## Companion tool
|
|
556
|
+
|
|
557
|
+
[**shieldctl**](https://github.com/kolawoluu/shieldctl) — CI-stage scanner that wraps best-of-breed external tools (checkov, tfsec, gitleaks, shellcheck, hadolint, actionlint, pip-audit) for deeper infrastructure security scanning. Run proofctl pre-commit; run shieldctl in CI.
|
|
558
|
+
|
|
559
|
+
---
|
|
560
|
+
|
|
561
|
+
## License
|
|
562
|
+
|
|
563
|
+
MIT
|