proofctl 0.1.2__tar.gz → 0.1.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.1.2 → proofctl-0.1.3}/PKG-INFO +1 -43
- {proofctl-0.1.2 → proofctl-0.1.3}/README.md +0 -42
- proofctl-0.1.3/proofctl/reporters/terminal.py +88 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl.egg-info/PKG-INFO +1 -43
- {proofctl-0.1.2 → proofctl-0.1.3}/pyproject.toml +1 -1
- proofctl-0.1.2/proofctl/reporters/terminal.py +0 -110
- {proofctl-0.1.2 → proofctl-0.1.3}/LICENSE +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/__init__.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/baseline.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/__init__.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/base.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/dockerfile.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/hcl_utils.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/imports.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/leakage.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/llm_integration.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/placeholders.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/quality.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/security.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/terraform.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/terragrunt.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/variants.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/checkers/yaml_checker.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/cli.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/config.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/engine.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/fixer.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/models.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/reporters/__init__.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/reporters/html_reporter.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl/reporters/json_reporter.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl.egg-info/SOURCES.txt +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl.egg-info/dependency_links.txt +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl.egg-info/entry_points.txt +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl.egg-info/requires.txt +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/proofctl.egg-info/top_level.txt +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/setup.cfg +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_baseline.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_config_proofctl.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_dockerfile.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_engine.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_fixer.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_imports.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_leakage.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_llm.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_placeholders.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_quality.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_quality_ext.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_security.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_security_ext.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_suppression.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_terraform.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_terraform_ext.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_terragrunt.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_variants.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_yaml.py +0 -0
- {proofctl-0.1.2 → proofctl-0.1.3}/tests/test_yaml_k8s_gha.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proofctl
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.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
|
|
@@ -491,48 +491,6 @@ Print all rule IDs, names, and severities.
|
|
|
491
491
|
|
|
492
492
|
---
|
|
493
493
|
|
|
494
|
-
## Architecture
|
|
495
|
-
|
|
496
|
-
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:
|
|
497
|
-
|
|
498
|
-
- **Fast** — typically < 1s for a 10k-line Python repo
|
|
499
|
-
- **Offline** — no network calls needed with `--no-pypi`
|
|
500
|
-
- **Dependency-free** — only `typer`, `rich`, `pyyaml` are required at runtime
|
|
501
|
-
|
|
502
|
-
```
|
|
503
|
-
proofctl/
|
|
504
|
-
├── cli.py # typer commands: check, baseline, rules
|
|
505
|
-
├── engine.py # orchestrator: walks files, dispatches checkers
|
|
506
|
-
├── config.py # .proofctl.yaml loader
|
|
507
|
-
├── models.py # Finding dataclass + Severity enum
|
|
508
|
-
├── baseline.py # snapshot save/load/filter
|
|
509
|
-
├── fixer.py # auto-fix for Q-001
|
|
510
|
-
├── checkers/
|
|
511
|
-
│ ├── placeholders.py # P family
|
|
512
|
-
│ ├── quality.py # Q family
|
|
513
|
-
│ ├── security.py # S family (S-001 to S-013)
|
|
514
|
-
│ ├── leakage.py # L family
|
|
515
|
-
│ ├── imports.py # I family
|
|
516
|
-
│ ├── methods.py # M family
|
|
517
|
-
│ ├── variants.py # V family
|
|
518
|
-
│ ├── llm.py # LLM family
|
|
519
|
-
│ ├── terraform.py # TF/TG families (T, G, A, AZ, M, V, TG)
|
|
520
|
-
│ ├── dockerfile.py # DF family
|
|
521
|
-
│ └── yaml_checker.py # YAML/K8s/GHA families
|
|
522
|
-
└── reporters/
|
|
523
|
-
├── terminal.py
|
|
524
|
-
├── json_reporter.py
|
|
525
|
-
└── html_reporter.py
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
---
|
|
529
|
-
|
|
530
|
-
## Companion tool
|
|
531
|
-
|
|
532
|
-
[**shieldctl**](https://pypi.org/project/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.
|
|
533
|
-
|
|
534
|
-
---
|
|
535
|
-
|
|
536
494
|
## License
|
|
537
495
|
|
|
538
496
|
MIT
|
|
@@ -460,48 +460,6 @@ Print all rule IDs, names, and severities.
|
|
|
460
460
|
|
|
461
461
|
---
|
|
462
462
|
|
|
463
|
-
## Architecture
|
|
464
|
-
|
|
465
|
-
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:
|
|
466
|
-
|
|
467
|
-
- **Fast** — typically < 1s for a 10k-line Python repo
|
|
468
|
-
- **Offline** — no network calls needed with `--no-pypi`
|
|
469
|
-
- **Dependency-free** — only `typer`, `rich`, `pyyaml` are required at runtime
|
|
470
|
-
|
|
471
|
-
```
|
|
472
|
-
proofctl/
|
|
473
|
-
├── cli.py # typer commands: check, baseline, rules
|
|
474
|
-
├── engine.py # orchestrator: walks files, dispatches checkers
|
|
475
|
-
├── config.py # .proofctl.yaml loader
|
|
476
|
-
├── models.py # Finding dataclass + Severity enum
|
|
477
|
-
├── baseline.py # snapshot save/load/filter
|
|
478
|
-
├── fixer.py # auto-fix for Q-001
|
|
479
|
-
├── checkers/
|
|
480
|
-
│ ├── placeholders.py # P family
|
|
481
|
-
│ ├── quality.py # Q family
|
|
482
|
-
│ ├── security.py # S family (S-001 to S-013)
|
|
483
|
-
│ ├── leakage.py # L family
|
|
484
|
-
│ ├── imports.py # I family
|
|
485
|
-
│ ├── methods.py # M family
|
|
486
|
-
│ ├── variants.py # V family
|
|
487
|
-
│ ├── llm.py # LLM family
|
|
488
|
-
│ ├── terraform.py # TF/TG families (T, G, A, AZ, M, V, TG)
|
|
489
|
-
│ ├── dockerfile.py # DF family
|
|
490
|
-
│ └── yaml_checker.py # YAML/K8s/GHA families
|
|
491
|
-
└── reporters/
|
|
492
|
-
├── terminal.py
|
|
493
|
-
├── json_reporter.py
|
|
494
|
-
└── html_reporter.py
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
---
|
|
498
|
-
|
|
499
|
-
## Companion tool
|
|
500
|
-
|
|
501
|
-
[**shieldctl**](https://pypi.org/project/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.
|
|
502
|
-
|
|
503
|
-
---
|
|
504
|
-
|
|
505
463
|
## License
|
|
506
464
|
|
|
507
465
|
MIT
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import Counter
|
|
4
|
+
|
|
5
|
+
from rich import box
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.padding import Padding
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
|
|
11
|
+
from ..models import Finding, Severity
|
|
12
|
+
|
|
13
|
+
_BADGE: dict[Severity, tuple[str, str]] = {
|
|
14
|
+
Severity.ERROR: ("bold white on red", "ERROR"),
|
|
15
|
+
Severity.WARNING: ("bold black on yellow", "WARNING"),
|
|
16
|
+
Severity.INFO: ("bold white on grey50", "INFO"),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
_SEV_STYLE: dict[Severity, str] = {
|
|
20
|
+
Severity.ERROR: "bold red",
|
|
21
|
+
Severity.WARNING: "bold yellow",
|
|
22
|
+
Severity.INFO: "dim",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console = Console(highlight=False, width=160)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _location(f: Finding) -> str:
|
|
29
|
+
if f.line:
|
|
30
|
+
return f"{f.file}:{f.line}"
|
|
31
|
+
return f.file
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TerminalReporter:
|
|
35
|
+
def render(self, findings: list[Finding]) -> str:
|
|
36
|
+
if not findings:
|
|
37
|
+
console.print(Padding("[bold green]✓ No findings.[/bold green]", (1, 2)))
|
|
38
|
+
return ""
|
|
39
|
+
|
|
40
|
+
console.print()
|
|
41
|
+
|
|
42
|
+
sorted_findings = sorted(
|
|
43
|
+
findings, key=lambda x: (-x.severity, x.file, x.line or 0)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
table = Table(
|
|
47
|
+
box=box.HORIZONTALS,
|
|
48
|
+
show_header=True,
|
|
49
|
+
show_edge=True,
|
|
50
|
+
show_lines=True,
|
|
51
|
+
header_style="bold",
|
|
52
|
+
padding=(0, 1),
|
|
53
|
+
)
|
|
54
|
+
table.add_column("Severity", no_wrap=True, min_width=10, max_width=10)
|
|
55
|
+
table.add_column("Rule", no_wrap=True, min_width=20, max_width=28)
|
|
56
|
+
table.add_column("Family", no_wrap=True, min_width=10, max_width=30)
|
|
57
|
+
table.add_column("Location", no_wrap=True, min_width=24, max_width=44)
|
|
58
|
+
table.add_column("Message", no_wrap=True, min_width=20, max_width=60)
|
|
59
|
+
|
|
60
|
+
for f in sorted_findings:
|
|
61
|
+
badge_style, badge_label = _BADGE[f.severity]
|
|
62
|
+
sev_cell = Text(f" {badge_label} ", style=badge_style)
|
|
63
|
+
msg = f.message if len(f.message) <= 60 else f.message[:57] + "..."
|
|
64
|
+
table.add_row(
|
|
65
|
+
sev_cell,
|
|
66
|
+
Text(f.rule_id, style="bold"),
|
|
67
|
+
Text(f.rule_name, style="dim"),
|
|
68
|
+
Text(_location(f), style=_SEV_STYLE[f.severity]),
|
|
69
|
+
Text(msg),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
console.print(Padding(table, (0, 2)))
|
|
73
|
+
console.print()
|
|
74
|
+
|
|
75
|
+
# Summary bar
|
|
76
|
+
counts = Counter(f.severity for f in findings)
|
|
77
|
+
total = len(findings)
|
|
78
|
+
bar = Text(f" {total} finding{'s' if total != 1 else ''} ")
|
|
79
|
+
for sev in reversed(Severity):
|
|
80
|
+
if not counts[sev]:
|
|
81
|
+
continue
|
|
82
|
+
badge_style, badge_label = _BADGE[sev]
|
|
83
|
+
bar.append(f" {badge_label} ", style=badge_style)
|
|
84
|
+
bar.append(f" {counts[sev]} ", style="bold")
|
|
85
|
+
console.print(bar)
|
|
86
|
+
console.print()
|
|
87
|
+
|
|
88
|
+
return ""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proofctl
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.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
|
|
@@ -491,48 +491,6 @@ Print all rule IDs, names, and severities.
|
|
|
491
491
|
|
|
492
492
|
---
|
|
493
493
|
|
|
494
|
-
## Architecture
|
|
495
|
-
|
|
496
|
-
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:
|
|
497
|
-
|
|
498
|
-
- **Fast** — typically < 1s for a 10k-line Python repo
|
|
499
|
-
- **Offline** — no network calls needed with `--no-pypi`
|
|
500
|
-
- **Dependency-free** — only `typer`, `rich`, `pyyaml` are required at runtime
|
|
501
|
-
|
|
502
|
-
```
|
|
503
|
-
proofctl/
|
|
504
|
-
├── cli.py # typer commands: check, baseline, rules
|
|
505
|
-
├── engine.py # orchestrator: walks files, dispatches checkers
|
|
506
|
-
├── config.py # .proofctl.yaml loader
|
|
507
|
-
├── models.py # Finding dataclass + Severity enum
|
|
508
|
-
├── baseline.py # snapshot save/load/filter
|
|
509
|
-
├── fixer.py # auto-fix for Q-001
|
|
510
|
-
├── checkers/
|
|
511
|
-
│ ├── placeholders.py # P family
|
|
512
|
-
│ ├── quality.py # Q family
|
|
513
|
-
│ ├── security.py # S family (S-001 to S-013)
|
|
514
|
-
│ ├── leakage.py # L family
|
|
515
|
-
│ ├── imports.py # I family
|
|
516
|
-
│ ├── methods.py # M family
|
|
517
|
-
│ ├── variants.py # V family
|
|
518
|
-
│ ├── llm.py # LLM family
|
|
519
|
-
│ ├── terraform.py # TF/TG families (T, G, A, AZ, M, V, TG)
|
|
520
|
-
│ ├── dockerfile.py # DF family
|
|
521
|
-
│ └── yaml_checker.py # YAML/K8s/GHA families
|
|
522
|
-
└── reporters/
|
|
523
|
-
├── terminal.py
|
|
524
|
-
├── json_reporter.py
|
|
525
|
-
└── html_reporter.py
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
---
|
|
529
|
-
|
|
530
|
-
## Companion tool
|
|
531
|
-
|
|
532
|
-
[**shieldctl**](https://pypi.org/project/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.
|
|
533
|
-
|
|
534
|
-
---
|
|
535
|
-
|
|
536
494
|
## License
|
|
537
495
|
|
|
538
496
|
MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "proofctl"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.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" }
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from collections import Counter
|
|
4
|
-
|
|
5
|
-
from rich import box
|
|
6
|
-
from rich.console import Console
|
|
7
|
-
from rich.padding import Padding
|
|
8
|
-
from rich.table import Table
|
|
9
|
-
from rich.text import Text
|
|
10
|
-
|
|
11
|
-
from ..models import Finding, Severity
|
|
12
|
-
|
|
13
|
-
_BADGE: dict[Severity, tuple[str, str]] = {
|
|
14
|
-
Severity.ERROR: ("bold white on red", "ERROR"),
|
|
15
|
-
Severity.WARNING: ("bold black on yellow", "WARNING"),
|
|
16
|
-
Severity.INFO: ("bold white on grey50", "INFO"),
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
_LOC_COLOUR: dict[Severity, str] = {
|
|
20
|
-
Severity.ERROR: "red",
|
|
21
|
-
Severity.WARNING: "yellow",
|
|
22
|
-
Severity.INFO: "dim",
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
_FAMILY_LABEL: dict[str, str] = {
|
|
26
|
-
"P": "Placeholder",
|
|
27
|
-
"L": "Leakage",
|
|
28
|
-
"Q": "Quality",
|
|
29
|
-
"I": "Import",
|
|
30
|
-
"M": "Method",
|
|
31
|
-
"V": "Variant",
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
console = Console(highlight=False)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def _location(f: Finding) -> str:
|
|
38
|
-
if f.line:
|
|
39
|
-
return f"{f.file}:{f.line}"
|
|
40
|
-
return f.file
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class TerminalReporter:
|
|
44
|
-
def render(self, findings: list[Finding]) -> str:
|
|
45
|
-
if not findings:
|
|
46
|
-
console.print(Padding("[bold green]✓ No findings.[/bold green]", (1, 2)))
|
|
47
|
-
return ""
|
|
48
|
-
|
|
49
|
-
console.print()
|
|
50
|
-
|
|
51
|
-
by_severity: dict[Severity, list[Finding]] = {}
|
|
52
|
-
for f in findings:
|
|
53
|
-
by_severity.setdefault(f.severity, []).append(f)
|
|
54
|
-
|
|
55
|
-
for sev in reversed(Severity):
|
|
56
|
-
group = by_severity.get(sev)
|
|
57
|
-
if not group:
|
|
58
|
-
continue
|
|
59
|
-
|
|
60
|
-
badge_style, badge_label = _BADGE[sev]
|
|
61
|
-
loc_colour = _LOC_COLOUR[sev]
|
|
62
|
-
count = len(group)
|
|
63
|
-
|
|
64
|
-
header = Text(" ")
|
|
65
|
-
header.append(f" {badge_label} ", style=badge_style)
|
|
66
|
-
header.append(f" {count} finding{'s' if count != 1 else ''}",
|
|
67
|
-
style="bold")
|
|
68
|
-
console.print(header)
|
|
69
|
-
console.print()
|
|
70
|
-
|
|
71
|
-
table = Table(
|
|
72
|
-
box=box.SIMPLE_HEAD,
|
|
73
|
-
show_header=True,
|
|
74
|
-
show_edge=False,
|
|
75
|
-
header_style="dim",
|
|
76
|
-
padding=(0, 1),
|
|
77
|
-
)
|
|
78
|
-
table.add_column("Rule", no_wrap=True, min_width=20, max_width=24)
|
|
79
|
-
table.add_column("Found at", overflow="fold", ratio=1)
|
|
80
|
-
|
|
81
|
-
for f in group:
|
|
82
|
-
rule_cell = Text()
|
|
83
|
-
rule_cell.append(f.rule_id, style="bold")
|
|
84
|
-
rule_cell.append(f"\n{f.rule_name}", style="dim")
|
|
85
|
-
|
|
86
|
-
found_cell = Text()
|
|
87
|
-
found_cell.append(_location(f), style=loc_colour)
|
|
88
|
-
found_cell.append(f"\n {f.message}", style="default")
|
|
89
|
-
if f.hint:
|
|
90
|
-
found_cell.append(f"\n → {f.hint}", style="dim italic")
|
|
91
|
-
|
|
92
|
-
table.add_row(rule_cell, found_cell)
|
|
93
|
-
|
|
94
|
-
console.print(Padding(table, (0, 2)))
|
|
95
|
-
console.print()
|
|
96
|
-
|
|
97
|
-
# Summary bar
|
|
98
|
-
counts = Counter(f.severity for f in findings)
|
|
99
|
-
total = len(findings)
|
|
100
|
-
bar = Text(f" {total} finding{'s' if total != 1 else ''} ")
|
|
101
|
-
for sev in reversed(Severity):
|
|
102
|
-
if not counts[sev]:
|
|
103
|
-
continue
|
|
104
|
-
badge_style, badge_label = _BADGE[sev]
|
|
105
|
-
bar.append(f" {badge_label} ", style=badge_style)
|
|
106
|
-
bar.append(f" {counts[sev]} ", style="bold")
|
|
107
|
-
console.print(bar)
|
|
108
|
-
console.print()
|
|
109
|
-
|
|
110
|
-
return ""
|
|
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
|
|
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
|