proofctl 0.4.2__tar.gz → 0.4.3__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.2/proofctl.egg-info → proofctl-0.4.3}/PKG-INFO +5 -5
- {proofctl-0.4.2 → proofctl-0.4.3}/README.md +4 -4
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/terraform.py +179 -0
- {proofctl-0.4.2 → proofctl-0.4.3/proofctl.egg-info}/PKG-INFO +5 -5
- {proofctl-0.4.2 → proofctl-0.4.3}/pyproject.toml +1 -1
- {proofctl-0.4.2 → proofctl-0.4.3}/LICENSE +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/MANIFEST.in +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/__init__.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/_globmatch.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/baseline.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/__init__.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/base.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/dockerfile.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/hcl_utils.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/imports.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/leakage.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/llm_integration.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/placeholders.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/quality.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/secrets.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/security.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/shell_checker.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/terragrunt.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/variants.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/checkers/yaml_checker.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/cli.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/config.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/engine.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/fixer.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/models.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/registry.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/reporters/__init__.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/reporters/html_reporter.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/reporters/json_reporter.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl/reporters/terminal.py +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl.egg-info/SOURCES.txt +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl.egg-info/dependency_links.txt +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl.egg-info/entry_points.txt +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl.egg-info/requires.txt +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/proofctl.egg-info/top_level.txt +0 -0
- {proofctl-0.4.2 → proofctl-0.4.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proofctl
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Zero-dependency linter for Python, Terraform, Dockerfiles, Kubernetes, and GitHub Actions — catches AI slop and security misconfigurations pre-commit
|
|
5
5
|
Author-email: Kolawolu Odunola <kolawolu.o@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -28,7 +28,7 @@ Dynamic: license-file
|
|
|
28
28
|
|
|
29
29
|
# proofctl
|
|
30
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. **
|
|
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. **267 rules**, zero dependencies, runs in under a second.
|
|
32
32
|
|
|
33
33
|
```
|
|
34
34
|
proofctl check .
|
|
@@ -165,7 +165,7 @@ git commit -m "chore: add proofctl baseline"
|
|
|
165
165
|
|
|
166
166
|
## Rules
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
267 rules across 14 families. Run `proofctl rules` after install to enumerate.
|
|
169
169
|
|
|
170
170
|
| Family | Count | What it covers |
|
|
171
171
|
|---|---|---|
|
|
@@ -173,9 +173,9 @@ git commit -m "chore: add proofctl baseline"
|
|
|
173
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
174
|
| Secrets (`SECRET`) | 13 | AWS / GCP / GitHub / Slack / Stripe / private keys / JWTs / hardcoded passwords / K8s Secret YAML / connection strings with embedded creds |
|
|
175
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`) |
|
|
176
|
+
| Terraform — General + AI (`TF-T`, `TF-AI`) | 28 | 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 |
|
|
177
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`) |
|
|
178
|
+
| Terraform — GCP (`TF-G`) | 51 | GKE hardening, GCS public-access prevention, IAM roles, KMS rotation, Cloud Function/Run public, BigQuery, Pub/Sub CMEK, Cloud SQL, firewall ingress, compute access scopes |
|
|
179
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
180
|
| Terraform — Mechanics + Lifecycle (`TF-M`, `TF-V`) | 12 | SG inline mixing, `force_destroy`, mutable module refs, lifecycle antipatterns |
|
|
181
181
|
| Terragrunt (`TG`) | 6 | HCL inputs, mock_outputs, remote state encryption |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# proofctl
|
|
2
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. **
|
|
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. **267 rules**, zero dependencies, runs in under a second.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
proofctl check .
|
|
@@ -137,7 +137,7 @@ git commit -m "chore: add proofctl baseline"
|
|
|
137
137
|
|
|
138
138
|
## Rules
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
267 rules across 14 families. Run `proofctl rules` after install to enumerate.
|
|
141
141
|
|
|
142
142
|
| Family | Count | What it covers |
|
|
143
143
|
|---|---|---|
|
|
@@ -145,9 +145,9 @@ git commit -m "chore: add proofctl baseline"
|
|
|
145
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
146
|
| Secrets (`SECRET`) | 13 | AWS / GCP / GitHub / Slack / Stripe / private keys / JWTs / hardcoded passwords / K8s Secret YAML / connection strings with embedded creds |
|
|
147
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`) |
|
|
148
|
+
| Terraform — General + AI (`TF-T`, `TF-AI`) | 28 | 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 |
|
|
149
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`) |
|
|
150
|
+
| Terraform — GCP (`TF-G`) | 51 | GKE hardening, GCS public-access prevention, IAM roles, KMS rotation, Cloud Function/Run public, BigQuery, Pub/Sub CMEK, Cloud SQL, firewall ingress, compute access scopes |
|
|
151
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
152
|
| Terraform — Mechanics + Lifecycle (`TF-M`, `TF-V`) | 12 | SG inline mixing, `force_destroy`, mutable module refs, lifecycle antipatterns |
|
|
153
153
|
| Terragrunt (`TG`) | 6 | HCL inputs, mock_outputs, remote state encryption |
|
|
@@ -294,6 +294,7 @@ class TerraformChecker(HclFileChecker):
|
|
|
294
294
|
out += self._g044(path, blocks)
|
|
295
295
|
out += self._g045(path, blocks)
|
|
296
296
|
out += self._g046(path, blocks)
|
|
297
|
+
out += self._g047(path, blocks)
|
|
297
298
|
|
|
298
299
|
# Group AZ — Azure
|
|
299
300
|
out += self._az001(path, blocks)
|
|
@@ -327,6 +328,8 @@ class TerraformChecker(HclFileChecker):
|
|
|
327
328
|
out += self._ai009(path, lines)
|
|
328
329
|
out += self._ai010(path, lines)
|
|
329
330
|
out += self._ai011(path, lines)
|
|
331
|
+
out += self._ai012(path, blocks)
|
|
332
|
+
out += self._ai013(path, blocks)
|
|
330
333
|
|
|
331
334
|
return out
|
|
332
335
|
|
|
@@ -2935,6 +2938,36 @@ class TerraformChecker(HclFileChecker):
|
|
|
2935
2938
|
break
|
|
2936
2939
|
return out
|
|
2937
2940
|
|
|
2941
|
+
def _g047(self, path: Path, blocks: list[HclBlock]) -> list[Finding]:
|
|
2942
|
+
"""G-047 — Compute instance with `cloud-platform` access scope.
|
|
2943
|
+
|
|
2944
|
+
Access scopes are a coarse, IAM-bypassing layer on top of service-account
|
|
2945
|
+
permissions. `cloud-platform` grants the metadata server permission to
|
|
2946
|
+
mint tokens for every GCP API — so any role the SA ever acquires becomes
|
|
2947
|
+
immediately usable from the VM. Best practice: rely on IAM only, and
|
|
2948
|
+
narrow the scope to `cloud-platform.read-only` or use the default
|
|
2949
|
+
scopes set with explicit IAM roles.
|
|
2950
|
+
"""
|
|
2951
|
+
out = []
|
|
2952
|
+
for b in blocks:
|
|
2953
|
+
if b.kind != "resource" or b.label1 != "google_compute_instance":
|
|
2954
|
+
continue
|
|
2955
|
+
for sa in b.nested("service_account"):
|
|
2956
|
+
# Match "cloud-platform" exactly inside the scopes list — not
|
|
2957
|
+
# cloud-platform.read-only or other narrower scopes.
|
|
2958
|
+
if sa.contains(r'"cloud-platform"'):
|
|
2959
|
+
out.append(self._f(
|
|
2960
|
+
path, sa.attr_line("scopes") or sa.start_line,
|
|
2961
|
+
"PROOFCTL-TF-G047", "Compute instance with cloud-platform scope",
|
|
2962
|
+
Severity.WARNING,
|
|
2963
|
+
f'google_compute_instance "{b.label2}" has access scope "cloud-platform" — broadest possible API access',
|
|
2964
|
+
hint='Use narrower scopes (e.g. "cloud-platform.read-only") or omit and rely on IAM bindings on the service account.',
|
|
2965
|
+
authority="GCP Compute Engine — Access scopes vs IAM",
|
|
2966
|
+
docs_url="https://cloud.google.com/compute/docs/access/service-accounts#scopes_best_practice",
|
|
2967
|
+
))
|
|
2968
|
+
break
|
|
2969
|
+
return out
|
|
2970
|
+
|
|
2938
2971
|
# ════════════════════════════════════════════════════════════════════════
|
|
2939
2972
|
# Group AZ — Azure data-exfiltration rules (extended)
|
|
2940
2973
|
# ════════════════════════════════════════════════════════════════════════
|
|
@@ -3328,6 +3361,152 @@ class TerraformChecker(HclFileChecker):
|
|
|
3328
3361
|
))
|
|
3329
3362
|
return out
|
|
3330
3363
|
|
|
3364
|
+
# GCE boot disk that references an image *family* rather than a specific
|
|
3365
|
+
# image is the GCP equivalent of `:latest` — Google promotes new images
|
|
3366
|
+
# into a family on its own schedule. Any apply that triggers VM rebuild
|
|
3367
|
+
# picks up whatever's current that day. Sneaky drift / surprise OS upgrade
|
|
3368
|
+
# vector that AI tools default to because the canonical examples use it.
|
|
3369
|
+
_IMAGE_FAMILY_RE = re.compile(
|
|
3370
|
+
r'image\s*=\s*"[^"]*?/images/family/[^"]+"',
|
|
3371
|
+
)
|
|
3372
|
+
|
|
3373
|
+
def _ai012(self, path: Path, blocks: list[HclBlock]) -> list[Finding]:
|
|
3374
|
+
"""AI-012 — GCE boot disk references an image *family* (mutable)."""
|
|
3375
|
+
out = []
|
|
3376
|
+
for b in blocks:
|
|
3377
|
+
if b.kind != "resource" or b.label1 != "google_compute_instance":
|
|
3378
|
+
continue
|
|
3379
|
+
for bd in b.nested("boot_disk"):
|
|
3380
|
+
# `image` can live either directly on boot_disk or inside
|
|
3381
|
+
# the nested initialize_params block.
|
|
3382
|
+
targets = [bd] + bd.nested("initialize_params")
|
|
3383
|
+
for t in targets:
|
|
3384
|
+
line = t.any_line_matches(self._IMAGE_FAMILY_RE.pattern)
|
|
3385
|
+
if line is not None:
|
|
3386
|
+
out.append(self._f(
|
|
3387
|
+
path, line,
|
|
3388
|
+
"PROOFCTL-TF-AI-012",
|
|
3389
|
+
"GCE boot disk uses image family (mutable)",
|
|
3390
|
+
Severity.WARNING,
|
|
3391
|
+
f'google_compute_instance "{b.label2}" references an image family — Google promotes new images into the family at any time',
|
|
3392
|
+
hint="Pin to a specific image name (e.g. ubuntu-2404-amd64-20251101) or set lifecycle { ignore_changes = [boot_disk[0].initialize_params[0].image] } to opt out of rebuilds.",
|
|
3393
|
+
authority="GCP Compute Engine — Image families",
|
|
3394
|
+
docs_url="https://cloud.google.com/compute/docs/images/image-families-best-practices",
|
|
3395
|
+
))
|
|
3396
|
+
break # one finding per boot_disk is enough
|
|
3397
|
+
return out
|
|
3398
|
+
|
|
3399
|
+
# Variables likely to hold credential material — used by AI-013 to decide
|
|
3400
|
+
# which `var.X` references are dangerous to embed in compute metadata.
|
|
3401
|
+
# Aligns with the SECRET-007 name set so engineers don't have to learn
|
|
3402
|
+
# two parallel taxonomies.
|
|
3403
|
+
_SENSITIVE_VAR_NAME_RE = re.compile(
|
|
3404
|
+
r"\bvar\.((?:[a-zA-Z0-9_]*_)?"
|
|
3405
|
+
r"(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|"
|
|
3406
|
+
r"private[_-]?key|client[_-]?secret|encryption[_-]?key|auth[_-]?token|"
|
|
3407
|
+
r"bearer[_-]?token|jwt|credential|tunnel[_-]?token)"
|
|
3408
|
+
r"[a-zA-Z0-9_]*)\b",
|
|
3409
|
+
re.IGNORECASE,
|
|
3410
|
+
)
|
|
3411
|
+
|
|
3412
|
+
@staticmethod
|
|
3413
|
+
def _line_range_of_attr(
|
|
3414
|
+
block: HclBlock, attr_name: str
|
|
3415
|
+
) -> tuple[int, int] | None:
|
|
3416
|
+
"""Find (start_idx, end_idx) into block.raw_lines that span the attribute
|
|
3417
|
+
``attr_name = <value>`` including any continuation lines opened by
|
|
3418
|
+
parentheses or brackets.
|
|
3419
|
+
|
|
3420
|
+
Returns indices relative to ``block.raw_lines`` (0-based), inclusive of
|
|
3421
|
+
the closing bracket / paren line. Returns None if the attribute is not
|
|
3422
|
+
present.
|
|
3423
|
+
"""
|
|
3424
|
+
head = re.compile(rf"^\s*{re.escape(attr_name)}\s*=\s*(.*)$")
|
|
3425
|
+
for i, line in enumerate(block.raw_lines):
|
|
3426
|
+
m = head.match(line)
|
|
3427
|
+
if not m:
|
|
3428
|
+
continue
|
|
3429
|
+
rest = m.group(1)
|
|
3430
|
+
depth = rest.count("(") + rest.count("[") + rest.count("{")
|
|
3431
|
+
depth -= rest.count(")") + rest.count("]") + rest.count("}")
|
|
3432
|
+
if depth <= 0:
|
|
3433
|
+
return (i, i)
|
|
3434
|
+
j = i + 1
|
|
3435
|
+
while j < len(block.raw_lines) and depth > 0:
|
|
3436
|
+
ln = block.raw_lines[j]
|
|
3437
|
+
depth += ln.count("(") + ln.count("[") + ln.count("{")
|
|
3438
|
+
depth -= ln.count(")") + ln.count("]") + ln.count("}")
|
|
3439
|
+
j += 1
|
|
3440
|
+
return (i, j - 1)
|
|
3441
|
+
return None
|
|
3442
|
+
|
|
3443
|
+
def _ai013(self, path: Path, blocks: list[HclBlock]) -> list[Finding]:
|
|
3444
|
+
"""AI-013 — Sensitive variable interpolated into compute metadata.
|
|
3445
|
+
|
|
3446
|
+
Pattern: `var.<secret-shaped-name>` appears inside ``metadata_startup_script``
|
|
3447
|
+
or any nested ``metadata { ... }`` block on a ``google_compute_instance``
|
|
3448
|
+
resource.
|
|
3449
|
+
|
|
3450
|
+
Risk: anything that ends up in VM metadata is readable by any caller
|
|
3451
|
+
with ``compute.instances.get`` and is written to plan / state. The
|
|
3452
|
+
Cloudflare-tunnel-in-startup-script pattern is the canonical AI-slop
|
|
3453
|
+
instance.
|
|
3454
|
+
"""
|
|
3455
|
+
out = []
|
|
3456
|
+
seen: set[tuple[Path, int]] = set()
|
|
3457
|
+
|
|
3458
|
+
def _emit(b: HclBlock, line: int, var_name: str, surface: str) -> None:
|
|
3459
|
+
key = (path, line)
|
|
3460
|
+
if key in seen:
|
|
3461
|
+
return
|
|
3462
|
+
seen.add(key)
|
|
3463
|
+
out.append(self._f(
|
|
3464
|
+
path, line,
|
|
3465
|
+
"PROOFCTL-TF-AI-013",
|
|
3466
|
+
"Sensitive variable in compute metadata",
|
|
3467
|
+
Severity.ERROR,
|
|
3468
|
+
f'google_compute_instance "{b.label2}" passes var.{var_name} into {surface} — secret will be readable from VM metadata and visible in plan/state',
|
|
3469
|
+
hint=(
|
|
3470
|
+
"Mount the secret from Secret Manager inside the VM at runtime "
|
|
3471
|
+
"(google_secret_manager_secret_iam_member + a startup script that "
|
|
3472
|
+
"calls `gcloud secrets versions access`). Do not bake the value into metadata."
|
|
3473
|
+
),
|
|
3474
|
+
authority="CIS GCP Benchmark 4.6 / OWASP A02 Cryptographic Failures",
|
|
3475
|
+
docs_url="https://cloud.google.com/compute/docs/metadata/overview#metadata_security_considerations",
|
|
3476
|
+
))
|
|
3477
|
+
|
|
3478
|
+
for b in blocks:
|
|
3479
|
+
if b.kind != "resource" or b.label1 != "google_compute_instance":
|
|
3480
|
+
continue
|
|
3481
|
+
# 1. Scan metadata_startup_script line range.
|
|
3482
|
+
rng = self._line_range_of_attr(b, "metadata_startup_script")
|
|
3483
|
+
if rng is not None:
|
|
3484
|
+
lo, hi = rng
|
|
3485
|
+
for offset in range(lo, hi + 1):
|
|
3486
|
+
line_text = b.raw_lines[offset]
|
|
3487
|
+
m = self._SENSITIVE_VAR_NAME_RE.search(line_text)
|
|
3488
|
+
if m:
|
|
3489
|
+
_emit(b, b.start_line + offset, m.group(1),
|
|
3490
|
+
"metadata_startup_script")
|
|
3491
|
+
# 2. Scan nested metadata { ... } blocks (HCL block form).
|
|
3492
|
+
for md in b.nested("metadata"):
|
|
3493
|
+
for offset, line_text in enumerate(md.raw_lines):
|
|
3494
|
+
m = self._SENSITIVE_VAR_NAME_RE.search(line_text)
|
|
3495
|
+
if m:
|
|
3496
|
+
_emit(b, md.start_line + offset, m.group(1),
|
|
3497
|
+
"metadata block")
|
|
3498
|
+
# 3. Scan metadata = { ... } attribute (HCL object-attribute form).
|
|
3499
|
+
rng = self._line_range_of_attr(b, "metadata")
|
|
3500
|
+
if rng is not None:
|
|
3501
|
+
lo, hi = rng
|
|
3502
|
+
for offset in range(lo, hi + 1):
|
|
3503
|
+
line_text = b.raw_lines[offset]
|
|
3504
|
+
m = self._SENSITIVE_VAR_NAME_RE.search(line_text)
|
|
3505
|
+
if m:
|
|
3506
|
+
_emit(b, b.start_line + offset, m.group(1),
|
|
3507
|
+
"metadata block")
|
|
3508
|
+
return out
|
|
3509
|
+
|
|
3331
3510
|
|
|
3332
3511
|
# ── Directory-level checker (cross-file rules) ───────────────────────────────
|
|
3333
3512
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proofctl
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Zero-dependency linter for Python, Terraform, Dockerfiles, Kubernetes, and GitHub Actions — catches AI slop and security misconfigurations pre-commit
|
|
5
5
|
Author-email: Kolawolu Odunola <kolawolu.o@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -28,7 +28,7 @@ Dynamic: license-file
|
|
|
28
28
|
|
|
29
29
|
# proofctl
|
|
30
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. **
|
|
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. **267 rules**, zero dependencies, runs in under a second.
|
|
32
32
|
|
|
33
33
|
```
|
|
34
34
|
proofctl check .
|
|
@@ -165,7 +165,7 @@ git commit -m "chore: add proofctl baseline"
|
|
|
165
165
|
|
|
166
166
|
## Rules
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
267 rules across 14 families. Run `proofctl rules` after install to enumerate.
|
|
169
169
|
|
|
170
170
|
| Family | Count | What it covers |
|
|
171
171
|
|---|---|---|
|
|
@@ -173,9 +173,9 @@ git commit -m "chore: add proofctl baseline"
|
|
|
173
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
174
|
| Secrets (`SECRET`) | 13 | AWS / GCP / GitHub / Slack / Stripe / private keys / JWTs / hardcoded passwords / K8s Secret YAML / connection strings with embedded creds |
|
|
175
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`) |
|
|
176
|
+
| Terraform — General + AI (`TF-T`, `TF-AI`) | 28 | 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 |
|
|
177
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`) |
|
|
178
|
+
| Terraform — GCP (`TF-G`) | 51 | GKE hardening, GCS public-access prevention, IAM roles, KMS rotation, Cloud Function/Run public, BigQuery, Pub/Sub CMEK, Cloud SQL, firewall ingress, compute access scopes |
|
|
179
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
180
|
| Terraform — Mechanics + Lifecycle (`TF-M`, `TF-V`) | 12 | SG inline mixing, `force_destroy`, mutable module refs, lifecycle antipatterns |
|
|
181
181
|
| Terragrunt (`TG`) | 6 | HCL inputs, mock_outputs, remote state encryption |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "proofctl"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.3"
|
|
8
8
|
description = "Zero-dependency linter for Python, Terraform, Dockerfiles, Kubernetes, and GitHub Actions — catches AI slop and security misconfigurations pre-commit"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|