kekkai-cli 2.0.0__tar.gz → 2.2.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.
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/PKG-INFO +22 -32
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/README.md +20 -31
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/pyproject.toml +3 -2
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/cli.py +46 -1
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/output.py +1 -1
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/triage/__init__.py +6 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/triage/app.py +12 -0
- kekkai_cli-2.2.0/src/kekkai/triage/code_context.py +345 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/triage/fix_screen.py +34 -4
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/triage/screens.py +225 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_cli.egg-info/PKG-INFO +22 -32
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_cli.egg-info/SOURCES.txt +4 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_cli.egg-info/requires.txt +1 -0
- kekkai_cli-2.2.0/tests/test_triage_code_context.py +273 -0
- kekkai_cli-2.2.0/tests/test_triage_editor.py +328 -0
- kekkai_cli-2.2.0/tests/test_triage_security.py +192 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/setup.cfg +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/compliance/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/compliance/hipaa.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/compliance/mappings.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/compliance/owasp.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/compliance/owasp_agentic.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/compliance/pci_dss.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/compliance/soc2.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/config.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/dojo.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/dojo_import.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/fix/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/fix/audit.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/fix/differ.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/fix/engine.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/fix/prompts.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/github/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/github/commenter.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/github/models.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/github/sanitizer.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/installer/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/installer/errors.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/installer/extract.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/installer/manager.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/installer/manifest.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/installer/verify.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/manifest.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/paths.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/policy.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/report/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/report/compliance_matrix.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/report/generator.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/report/html.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/report/pdf.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/report/unified.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/runner.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/backends/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/backends/base.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/backends/docker.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/backends/native.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/base.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/container.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/falco.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/gitleaks.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/semgrep.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/trivy.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/url_policy.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/scanners/zap.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/threatflow/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/threatflow/artifacts.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/threatflow/chunking.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/threatflow/core.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/threatflow/mermaid.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/threatflow/model_adapter.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/threatflow/prompts.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/threatflow/redaction.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/threatflow/sanitizer.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/triage/audit.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/triage/ignore.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/triage/loader.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/triage/models.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai/triage/widgets.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_cli.egg-info/dependency_links.txt +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_cli.egg-info/entry_points.txt +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_cli.egg-info/top_level.txt +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/ci/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/ci/benchmarks.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/ci/metadata.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/ci/validators.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/docker/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/docker/metadata.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/docker/sbom.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/docker/security.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/docker/signing.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/redaction.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/slsa/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/slsa/verify.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/windows/__init__.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/windows/chocolatey.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/windows/installer.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/windows/scoop.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/src/kekkai_core/windows/validators.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_cli_output.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_compliance.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_dojo_import.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_fix_engine.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_github_commenter_filter.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_github_commenter_format.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_github_commenter_limit.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_github_commenter_sanitize.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_installer_checksum.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_installer_extract.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_installer_manager.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_installer_manifest.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_installer_platform.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_kekkai_cli.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_kekkai_config.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_kekkai_dojo.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_kekkai_dojo_cli.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_kekkai_manifest.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_kekkai_paths.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_kekkai_runner.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_mermaid.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_policy.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_redaction.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_report.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_scanner_backends.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_scanner_base.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_scanner_container.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_scanner_digest_defaults.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_scanner_falco.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_scanner_gitleaks.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_scanner_native.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_scanner_semgrep.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_scanner_trivy.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_scanner_zap.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_slsa_provenance.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_threatflow_chunking.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_threatflow_model_adapter.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_threatflow_prompts.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_threatflow_redaction.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_threatflow_sanitizer.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_triage_audit.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_triage_ignore.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_triage_loader.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_triage_models.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_unified_report.py +0 -0
- {kekkai_cli-2.0.0 → kekkai_cli-2.2.0}/tests/test_url_policy.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kekkai-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Terminal UI for Trivy/Semgrep/Gitleaks. Local-first security triage.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -8,30 +8,32 @@ Requires-Dist: rich>=13.0.0
|
|
|
8
8
|
Requires-Dist: jsonschema>=4.20.0
|
|
9
9
|
Requires-Dist: textual>=0.50.0
|
|
10
10
|
Requires-Dist: httpx>=0.24.0
|
|
11
|
+
Requires-Dist: jinja2>=3.1.6
|
|
11
12
|
|
|
12
13
|
<p align="center">
|
|
13
14
|
<img src="https://raw.githubusercontent.com/kademoslabs/assets/main/logos/kekkai-slim.png" alt="Kekkai CLI Logo" width="250"/>
|
|
14
15
|
</p>
|
|
15
16
|
|
|
16
|
-
<p align="center"><strong>
|
|
17
|
+
<p align="center"><strong>Interactive security triage in the terminal.</strong></p>
|
|
17
18
|
|
|
18
19
|
<p align="center">
|
|
19
20
|
<img src="https://img.shields.io/github/actions/workflow/status/kademoslabs/kekkai/docker-publish.yml?logo=github"/>
|
|
20
21
|
<img src="https://img.shields.io/circleci/build/github/kademoslabs/kekkai?logo=circleci"/>
|
|
21
|
-
<img src="https://img.shields.io/pypi/v/kekkai-cli
|
|
22
|
+
<img alt="PyPI - Version" src="https://img.shields.io/pypi/v/kekkai-cli">
|
|
23
|
+
|
|
22
24
|
</p>
|
|
23
25
|
|
|
24
26
|
---
|
|
25
27
|
|
|
26
28
|
# Kekkai
|
|
27
29
|
|
|
28
|
-
**
|
|
30
|
+
**Stop parsing JSON.**
|
|
29
31
|
|
|
30
32
|
Kekkai is a small open-source CLI that wraps existing security scanners (Trivy, Semgrep, Gitleaks) and focuses on the part that tends to be slow and frustrating: reviewing and triaging results.
|
|
31
33
|
|
|
32
34
|
Running scanners is easy. Interpreting noisy output, dealing with false positives, and making CI usable is not. Kekkai exists to make that part tolerable..
|
|
33
35
|
|
|
34
|
-

|
|
35
37
|
|
|
36
38
|
---
|
|
37
39
|
|
|
@@ -72,6 +74,17 @@ kekkai triage
|
|
|
72
74
|
# Interactive TUI to review findings with keyboard navigation
|
|
73
75
|
```
|
|
74
76
|
|
|
77
|
+
### ⚡️ Auto-Install (Pre-commit)
|
|
78
|
+
|
|
79
|
+
Add this to your `.pre-commit-config.yaml` to scan on every commit:
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
- repo: [https://github.com/kademoslabs/kekkai](https://github.com/kademoslabs/kekkai)
|
|
83
|
+
rev: v2.0.1
|
|
84
|
+
hooks:
|
|
85
|
+
- id: kekkai-scan
|
|
86
|
+
```
|
|
87
|
+
|
|
75
88
|
No signup, no cloud service required.
|
|
76
89
|
|
|
77
90
|
---
|
|
@@ -107,40 +120,17 @@ kekkai triage
|
|
|
107
120
|
- `Ctrl+S`: Save decisions
|
|
108
121
|
- `q`: Quit
|
|
109
122
|
|
|
110
|
-
|
|
123
|
+

|
|
111
124
|
|
|
112
125
|
[Full Triage Documentation →](docs/triage/README.md)
|
|
113
126
|
|
|
114
127
|
---
|
|
115
128
|
|
|
116
|
-
### CI/CD
|
|
117
|
-
|
|
118
|
-
Break builds on severity thresholds.
|
|
119
|
-
|
|
120
|
-
Kekkai can be used as a CI gate based on severity thresholds.
|
|
129
|
+
### 🚦 CI/CD in 1 Second
|
|
121
130
|
|
|
131
|
+
Don't write YAML. Run this in your repo:
|
|
122
132
|
```bash
|
|
123
|
-
|
|
124
|
-
kekkai scan --ci --fail-on high
|
|
125
|
-
|
|
126
|
-
# Fail only on critical
|
|
127
|
-
kekkai scan --ci --fail-on critical
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
**Exit Codes:**
|
|
131
|
-
| Code | Meaning |
|
|
132
|
-
|------|---------|
|
|
133
|
-
| 0 | No findings above threshold |
|
|
134
|
-
| 1 | Findings exceed threshold |
|
|
135
|
-
| 2 | Scanner error |
|
|
136
|
-
|
|
137
|
-
**GitHub Actions Example:**
|
|
138
|
-
|
|
139
|
-
```yaml
|
|
140
|
-
- name: Security Scan
|
|
141
|
-
run: |
|
|
142
|
-
pipx install kekkai-cli
|
|
143
|
-
kekkai scan --ci --fail-on high
|
|
133
|
+
kekkai init --ci
|
|
144
134
|
```
|
|
145
135
|
|
|
146
136
|
[Full CI Documentation →](docs/ci/ci-mode.md)
|
|
@@ -2,25 +2,26 @@
|
|
|
2
2
|
<img src="https://raw.githubusercontent.com/kademoslabs/assets/main/logos/kekkai-slim.png" alt="Kekkai CLI Logo" width="250"/>
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<p align="center"><strong>
|
|
5
|
+
<p align="center"><strong>Interactive security triage in the terminal.</strong></p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<img src="https://img.shields.io/github/actions/workflow/status/kademoslabs/kekkai/docker-publish.yml?logo=github"/>
|
|
9
9
|
<img src="https://img.shields.io/circleci/build/github/kademoslabs/kekkai?logo=circleci"/>
|
|
10
|
-
<img src="https://img.shields.io/pypi/v/kekkai-cli
|
|
10
|
+
<img alt="PyPI - Version" src="https://img.shields.io/pypi/v/kekkai-cli">
|
|
11
|
+
|
|
11
12
|
</p>
|
|
12
13
|
|
|
13
14
|
---
|
|
14
15
|
|
|
15
16
|
# Kekkai
|
|
16
17
|
|
|
17
|
-
**
|
|
18
|
+
**Stop parsing JSON.**
|
|
18
19
|
|
|
19
20
|
Kekkai is a small open-source CLI that wraps existing security scanners (Trivy, Semgrep, Gitleaks) and focuses on the part that tends to be slow and frustrating: reviewing and triaging results.
|
|
20
21
|
|
|
21
22
|
Running scanners is easy. Interpreting noisy output, dealing with false positives, and making CI usable is not. Kekkai exists to make that part tolerable..
|
|
22
23
|
|
|
23
|
-

|
|
24
25
|
|
|
25
26
|
---
|
|
26
27
|
|
|
@@ -61,6 +62,17 @@ kekkai triage
|
|
|
61
62
|
# Interactive TUI to review findings with keyboard navigation
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
### ⚡️ Auto-Install (Pre-commit)
|
|
66
|
+
|
|
67
|
+
Add this to your `.pre-commit-config.yaml` to scan on every commit:
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
- repo: [https://github.com/kademoslabs/kekkai](https://github.com/kademoslabs/kekkai)
|
|
71
|
+
rev: v2.0.1
|
|
72
|
+
hooks:
|
|
73
|
+
- id: kekkai-scan
|
|
74
|
+
```
|
|
75
|
+
|
|
64
76
|
No signup, no cloud service required.
|
|
65
77
|
|
|
66
78
|
---
|
|
@@ -96,40 +108,17 @@ kekkai triage
|
|
|
96
108
|
- `Ctrl+S`: Save decisions
|
|
97
109
|
- `q`: Quit
|
|
98
110
|
|
|
99
|
-
|
|
111
|
+

|
|
100
112
|
|
|
101
113
|
[Full Triage Documentation →](docs/triage/README.md)
|
|
102
114
|
|
|
103
115
|
---
|
|
104
116
|
|
|
105
|
-
### CI/CD
|
|
106
|
-
|
|
107
|
-
Break builds on severity thresholds.
|
|
108
|
-
|
|
109
|
-
Kekkai can be used as a CI gate based on severity thresholds.
|
|
117
|
+
### 🚦 CI/CD in 1 Second
|
|
110
118
|
|
|
119
|
+
Don't write YAML. Run this in your repo:
|
|
111
120
|
```bash
|
|
112
|
-
|
|
113
|
-
kekkai scan --ci --fail-on high
|
|
114
|
-
|
|
115
|
-
# Fail only on critical
|
|
116
|
-
kekkai scan --ci --fail-on critical
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
**Exit Codes:**
|
|
120
|
-
| Code | Meaning |
|
|
121
|
-
|------|---------|
|
|
122
|
-
| 0 | No findings above threshold |
|
|
123
|
-
| 1 | Findings exceed threshold |
|
|
124
|
-
| 2 | Scanner error |
|
|
125
|
-
|
|
126
|
-
**GitHub Actions Example:**
|
|
127
|
-
|
|
128
|
-
```yaml
|
|
129
|
-
- name: Security Scan
|
|
130
|
-
run: |
|
|
131
|
-
pipx install kekkai-cli
|
|
132
|
-
kekkai scan --ci --fail-on high
|
|
121
|
+
kekkai init --ci
|
|
133
122
|
```
|
|
134
123
|
|
|
135
124
|
[Full CI Documentation →](docs/ci/ci-mode.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "kekkai-cli"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.2.0"
|
|
4
4
|
description = "Terminal UI for Trivy/Semgrep/Gitleaks. Local-first security triage."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -9,6 +9,7 @@ dependencies = [
|
|
|
9
9
|
"jsonschema>=4.20.0",
|
|
10
10
|
"textual>=0.50.0",
|
|
11
11
|
"httpx>=0.24.0",
|
|
12
|
+
"jinja2>=3.1.6",
|
|
12
13
|
]
|
|
13
14
|
|
|
14
15
|
[project.scripts]
|
|
@@ -20,7 +21,7 @@ line-length = 100
|
|
|
20
21
|
extend-exclude = ["dist", "build", ".venv"]
|
|
21
22
|
lint.select = ["E", "F", "I", "B", "UP", "S", "SIM"]
|
|
22
23
|
lint.ignore = ["S101"] # allow asserts in tests
|
|
23
|
-
lint.per-file-ignores = { "tests/**" = ["S"], "src/kekkai_core/docker/**" = ["S603"], "src/kekkai_core/slsa/**" = ["S603", "S607"], "src/kekkai/github/**" = ["S105"] }
|
|
24
|
+
lint.per-file-ignores = { "tests/**" = ["S", "SIM117"], "src/kekkai_core/docker/**" = ["S603"], "src/kekkai_core/slsa/**" = ["S603", "S607"], "src/kekkai/github/**" = ["S105"] }
|
|
24
25
|
|
|
25
26
|
[tool.mypy]
|
|
26
27
|
python_version = "3.12"
|
|
@@ -221,6 +221,17 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
221
221
|
type=str,
|
|
222
222
|
help="Path for .kekkaiignore output (default: .kekkaiignore)",
|
|
223
223
|
)
|
|
224
|
+
triage_parser.add_argument(
|
|
225
|
+
"--repo",
|
|
226
|
+
type=str,
|
|
227
|
+
help="Repository root path (default: auto-detect from run.json)",
|
|
228
|
+
)
|
|
229
|
+
triage_parser.add_argument(
|
|
230
|
+
"--context-lines",
|
|
231
|
+
type=int,
|
|
232
|
+
default=10,
|
|
233
|
+
help="Context lines before/after line (default: 10, range: 5-100)",
|
|
234
|
+
)
|
|
224
235
|
|
|
225
236
|
# Fix subcommand - AI-powered remediation
|
|
226
237
|
fix_parser = subparsers.add_parser("fix", help="generate AI-powered code fixes for findings")
|
|
@@ -1186,6 +1197,11 @@ def _command_triage(parsed: argparse.Namespace) -> int:
|
|
|
1186
1197
|
|
|
1187
1198
|
input_path_str = cast(str | None, getattr(parsed, "input", None))
|
|
1188
1199
|
output_path_str = cast(str | None, getattr(parsed, "output", None))
|
|
1200
|
+
repo_path_str = cast(str | None, getattr(parsed, "repo", None))
|
|
1201
|
+
context_lines = cast(int, getattr(parsed, "context_lines", 10))
|
|
1202
|
+
|
|
1203
|
+
# Validate context_lines range
|
|
1204
|
+
context_lines = max(5, min(100, context_lines))
|
|
1189
1205
|
|
|
1190
1206
|
# Default to latest run if no input specified
|
|
1191
1207
|
if not input_path_str:
|
|
@@ -1232,7 +1248,36 @@ def _command_triage(parsed: argparse.Namespace) -> int:
|
|
|
1232
1248
|
|
|
1233
1249
|
console.print(f"[info]Loaded {len(findings)} finding(s)[/info]\n")
|
|
1234
1250
|
|
|
1235
|
-
|
|
1251
|
+
# Auto-detect repo_path from run.json if not explicitly provided
|
|
1252
|
+
repo_path: Path | None = None
|
|
1253
|
+
if repo_path_str:
|
|
1254
|
+
repo_path = Path(repo_path_str).expanduser().resolve()
|
|
1255
|
+
elif input_path.is_dir():
|
|
1256
|
+
# Try to read repo_path from run.json
|
|
1257
|
+
manifest_path = input_path / "run.json"
|
|
1258
|
+
if manifest_path.exists():
|
|
1259
|
+
try:
|
|
1260
|
+
import json
|
|
1261
|
+
|
|
1262
|
+
with manifest_path.open() as f:
|
|
1263
|
+
manifest_data = json.load(f)
|
|
1264
|
+
stored_repo = manifest_data.get("repo_path")
|
|
1265
|
+
if stored_repo:
|
|
1266
|
+
repo_path = Path(stored_repo).expanduser().resolve()
|
|
1267
|
+
console.print(f"[dim]Using repo path from run metadata: {repo_path}[/dim]\n")
|
|
1268
|
+
except (OSError, json.JSONDecodeError, KeyError):
|
|
1269
|
+
pass
|
|
1270
|
+
|
|
1271
|
+
# Fall back to current directory if still not set
|
|
1272
|
+
if repo_path is None:
|
|
1273
|
+
repo_path = Path.cwd()
|
|
1274
|
+
|
|
1275
|
+
return run_triage(
|
|
1276
|
+
findings=findings,
|
|
1277
|
+
output_path=output_path,
|
|
1278
|
+
repo_path=repo_path,
|
|
1279
|
+
context_lines=context_lines,
|
|
1280
|
+
)
|
|
1236
1281
|
|
|
1237
1282
|
|
|
1238
1283
|
def _command_fix(parsed: argparse.Namespace) -> int:
|
|
@@ -29,6 +29,8 @@ def run_triage(
|
|
|
29
29
|
input_path: Path | None = None,
|
|
30
30
|
output_path: Path | None = None,
|
|
31
31
|
findings: Sequence[FindingEntry] | None = None,
|
|
32
|
+
repo_path: Path | None = None,
|
|
33
|
+
context_lines: int = 10,
|
|
32
34
|
) -> int:
|
|
33
35
|
"""Run the triage TUI (lazy import).
|
|
34
36
|
|
|
@@ -36,6 +38,8 @@ def run_triage(
|
|
|
36
38
|
input_path: Path to findings JSON file.
|
|
37
39
|
output_path: Path for .kekkaiignore output.
|
|
38
40
|
findings: Pre-loaded findings (alternative to input_path).
|
|
41
|
+
repo_path: Repository root path for code context display.
|
|
42
|
+
context_lines: Number of lines to show before/after vulnerable line.
|
|
39
43
|
|
|
40
44
|
Returns:
|
|
41
45
|
Exit code (0 for success).
|
|
@@ -50,6 +54,8 @@ def run_triage(
|
|
|
50
54
|
input_path=input_path,
|
|
51
55
|
output_path=output_path,
|
|
52
56
|
findings=findings,
|
|
57
|
+
repo_path=repo_path,
|
|
58
|
+
context_lines=context_lines,
|
|
53
59
|
)
|
|
54
60
|
except ImportError as e:
|
|
55
61
|
raise RuntimeError(
|
|
@@ -51,6 +51,8 @@ class TriageApp(App[None]):
|
|
|
51
51
|
input_path: Path | None = None,
|
|
52
52
|
output_path: Path | None = None,
|
|
53
53
|
audit_path: Path | None = None,
|
|
54
|
+
repo_path: Path | None = None,
|
|
55
|
+
context_lines: int = 10,
|
|
54
56
|
) -> None:
|
|
55
57
|
"""Initialize triage application.
|
|
56
58
|
|
|
@@ -59,6 +61,8 @@ class TriageApp(App[None]):
|
|
|
59
61
|
input_path: Path to findings JSON file.
|
|
60
62
|
output_path: Path for .kekkaiignore output.
|
|
61
63
|
audit_path: Path for audit log.
|
|
64
|
+
repo_path: Repository root path for code context display.
|
|
65
|
+
context_lines: Number of lines to show before/after vulnerable line.
|
|
62
66
|
"""
|
|
63
67
|
super().__init__()
|
|
64
68
|
self._input_path = input_path
|
|
@@ -66,6 +70,8 @@ class TriageApp(App[None]):
|
|
|
66
70
|
self.ignore_file = IgnoreFile(output_path)
|
|
67
71
|
self.audit_log = TriageAuditLog(audit_path)
|
|
68
72
|
self._decisions: dict[str, TriageDecision] = {}
|
|
73
|
+
self.repo_path = repo_path or Path.cwd()
|
|
74
|
+
self.context_lines = context_lines
|
|
69
75
|
|
|
70
76
|
@property
|
|
71
77
|
def findings(self) -> list[FindingEntry]:
|
|
@@ -148,6 +154,8 @@ def run_triage(
|
|
|
148
154
|
input_path: Path | None = None,
|
|
149
155
|
output_path: Path | None = None,
|
|
150
156
|
findings: Sequence[FindingEntry] | None = None,
|
|
157
|
+
repo_path: Path | None = None,
|
|
158
|
+
context_lines: int = 10,
|
|
151
159
|
) -> int:
|
|
152
160
|
"""Run the triage TUI.
|
|
153
161
|
|
|
@@ -155,6 +163,8 @@ def run_triage(
|
|
|
155
163
|
input_path: Path to findings JSON file.
|
|
156
164
|
output_path: Path for .kekkaiignore output.
|
|
157
165
|
findings: Pre-loaded findings (alternative to input_path).
|
|
166
|
+
repo_path: Repository root path for code context display.
|
|
167
|
+
context_lines: Number of lines to show before/after vulnerable line.
|
|
158
168
|
|
|
159
169
|
Returns:
|
|
160
170
|
Exit code (0 for success).
|
|
@@ -163,6 +173,8 @@ def run_triage(
|
|
|
163
173
|
findings=findings,
|
|
164
174
|
input_path=input_path,
|
|
165
175
|
output_path=output_path,
|
|
176
|
+
repo_path=repo_path,
|
|
177
|
+
context_lines=context_lines,
|
|
166
178
|
)
|
|
167
179
|
app.run()
|
|
168
180
|
return 0
|