kekkai-cli 1.0.4__py3-none-any.whl → 1.1.0__py3-none-any.whl
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.py +693 -14
- kekkai/compliance/__init__.py +68 -0
- kekkai/compliance/hipaa.py +235 -0
- kekkai/compliance/mappings.py +136 -0
- kekkai/compliance/owasp.py +517 -0
- kekkai/compliance/owasp_agentic.py +267 -0
- kekkai/compliance/pci_dss.py +205 -0
- kekkai/compliance/soc2.py +209 -0
- kekkai/dojo.py +91 -14
- kekkai/fix/__init__.py +47 -0
- kekkai/fix/audit.py +278 -0
- kekkai/fix/differ.py +427 -0
- kekkai/fix/engine.py +500 -0
- kekkai/fix/prompts.py +251 -0
- kekkai/output.py +10 -12
- kekkai/report/__init__.py +41 -0
- kekkai/report/compliance_matrix.py +98 -0
- kekkai/report/generator.py +365 -0
- kekkai/report/html.py +69 -0
- kekkai/report/pdf.py +63 -0
- kekkai/scanners/container.py +33 -3
- kekkai/scanners/gitleaks.py +3 -1
- kekkai/scanners/semgrep.py +1 -1
- kekkai/scanners/trivy.py +1 -1
- kekkai/threatflow/model_adapter.py +143 -1
- kekkai_cli-1.1.0.dist-info/METADATA +359 -0
- {kekkai_cli-1.0.4.dist-info → kekkai_cli-1.1.0.dist-info}/RECORD +33 -16
- portal/enterprise/__init__.py +15 -2
- portal/enterprise/licensing.py +88 -22
- portal/web.py +9 -0
- kekkai_cli-1.0.4.dist-info/METADATA +0 -135
- {kekkai_cli-1.0.4.dist-info → kekkai_cli-1.1.0.dist-info}/WHEEL +0 -0
- {kekkai_cli-1.0.4.dist-info → kekkai_cli-1.1.0.dist-info}/entry_points.txt +0 -0
- {kekkai_cli-1.0.4.dist-info → kekkai_cli-1.1.0.dist-info}/top_level.txt +0 -0
portal/enterprise/licensing.py
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
Security controls:
|
|
4
4
|
- Server-side license enforcement
|
|
5
|
-
-
|
|
5
|
+
- Asymmetric (ECDSA P-256) signed license tokens to prevent tampering
|
|
6
6
|
- Grace period handling for expiration
|
|
7
|
+
- Private key for signing (admin only), public key for validation (distributed)
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
10
|
from __future__ import annotations
|
|
10
11
|
|
|
11
12
|
import base64
|
|
12
13
|
import hashlib
|
|
13
|
-
import hmac
|
|
14
14
|
import json
|
|
15
15
|
import logging
|
|
16
16
|
import time
|
|
@@ -19,6 +19,10 @@ from datetime import UTC, datetime, timedelta
|
|
|
19
19
|
from enum import Enum
|
|
20
20
|
from typing import TYPE_CHECKING, Any
|
|
21
21
|
|
|
22
|
+
from cryptography.exceptions import InvalidSignature
|
|
23
|
+
from cryptography.hazmat.primitives import hashes, serialization
|
|
24
|
+
from cryptography.hazmat.primitives.asymmetric import ec
|
|
25
|
+
|
|
22
26
|
if TYPE_CHECKING:
|
|
23
27
|
pass
|
|
24
28
|
|
|
@@ -155,29 +159,80 @@ class LicenseCheckResult:
|
|
|
155
159
|
grace_days_remaining: int | None = None
|
|
156
160
|
|
|
157
161
|
|
|
158
|
-
|
|
159
|
-
"""
|
|
162
|
+
def generate_keypair() -> tuple[bytes, bytes]:
|
|
163
|
+
"""Generate a new ECDSA P-256 keypair for license signing.
|
|
160
164
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
Returns:
|
|
166
|
+
Tuple of (private_key_pem, public_key_pem) as bytes.
|
|
167
|
+
Keep private_key_pem SECRET - only used for signing licenses.
|
|
168
|
+
Distribute public_key_pem with the application for verification.
|
|
169
|
+
"""
|
|
170
|
+
private_key = ec.generate_private_key(ec.SECP256R1())
|
|
171
|
+
private_pem = private_key.private_bytes(
|
|
172
|
+
encoding=serialization.Encoding.PEM,
|
|
173
|
+
format=serialization.PrivateFormat.PKCS8,
|
|
174
|
+
encryption_algorithm=serialization.NoEncryption(),
|
|
175
|
+
)
|
|
176
|
+
public_pem = private_key.public_key().public_bytes(
|
|
177
|
+
encoding=serialization.Encoding.PEM,
|
|
178
|
+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
179
|
+
)
|
|
180
|
+
return private_pem, public_pem
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class LicenseSigner:
|
|
184
|
+
"""Signs enterprise licenses using ECDSA private key.
|
|
185
|
+
|
|
186
|
+
This class should only be used server-side by administrators
|
|
187
|
+
to generate license tokens. Never distribute the private key.
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
_private_key: ec.EllipticCurvePrivateKey
|
|
191
|
+
|
|
192
|
+
def __init__(self, private_key_pem: bytes) -> None:
|
|
193
|
+
loaded_key = serialization.load_pem_private_key(private_key_pem, password=None)
|
|
194
|
+
if not isinstance(loaded_key, ec.EllipticCurvePrivateKey):
|
|
195
|
+
raise ValueError("Expected ECDSA private key")
|
|
196
|
+
self._private_key = loaded_key
|
|
164
197
|
|
|
165
198
|
def create_license_token(self, license: EnterpriseLicense) -> str:
|
|
166
|
-
"""Create a signed license token.
|
|
199
|
+
"""Create a signed license token using ECDSA.
|
|
167
200
|
|
|
168
|
-
The token format is: base64(payload).signature
|
|
201
|
+
The token format is: base64(payload).base64(signature)
|
|
169
202
|
"""
|
|
170
203
|
payload = license.to_dict()
|
|
171
204
|
payload_json = json.dumps(payload, separators=(",", ":"), sort_keys=True)
|
|
172
205
|
payload_b64 = base64.urlsafe_b64encode(payload_json.encode()).decode().rstrip("=")
|
|
173
206
|
|
|
174
|
-
signature =
|
|
175
|
-
self._signing_key.encode(),
|
|
207
|
+
signature = self._private_key.sign(
|
|
176
208
|
payload_b64.encode(),
|
|
177
|
-
|
|
178
|
-
)
|
|
209
|
+
ec.ECDSA(hashes.SHA256()),
|
|
210
|
+
)
|
|
211
|
+
signature_b64 = base64.urlsafe_b64encode(signature).decode().rstrip("=")
|
|
212
|
+
|
|
213
|
+
return f"{payload_b64}.{signature_b64}"
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class LicenseValidator:
|
|
217
|
+
"""Validates enterprise licenses using ECDSA public key.
|
|
179
218
|
|
|
180
|
-
|
|
219
|
+
This class can be safely distributed with the application.
|
|
220
|
+
It only requires the public key for verification.
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
_public_key: ec.EllipticCurvePublicKey
|
|
224
|
+
|
|
225
|
+
def __init__(self, public_key_pem: bytes) -> None:
|
|
226
|
+
loaded_key = serialization.load_pem_public_key(public_key_pem)
|
|
227
|
+
if not isinstance(loaded_key, ec.EllipticCurvePublicKey):
|
|
228
|
+
raise ValueError("Expected ECDSA public key")
|
|
229
|
+
self._public_key = loaded_key
|
|
230
|
+
self._cache: dict[str, tuple[float, LicenseCheckResult]] = {}
|
|
231
|
+
|
|
232
|
+
@classmethod
|
|
233
|
+
def from_public_key_string(cls, public_key_str: str) -> LicenseValidator:
|
|
234
|
+
"""Create validator from PEM string (convenience method)."""
|
|
235
|
+
return cls(public_key_str.encode())
|
|
181
236
|
|
|
182
237
|
def validate_token(self, token: str) -> LicenseCheckResult:
|
|
183
238
|
"""Validate a license token and return the license if valid.
|
|
@@ -202,20 +257,31 @@ class LicenseValidator:
|
|
|
202
257
|
message="Invalid license token format",
|
|
203
258
|
)
|
|
204
259
|
|
|
205
|
-
payload_b64,
|
|
260
|
+
payload_b64, signature_b64 = parts
|
|
206
261
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
262
|
+
try:
|
|
263
|
+
sig_padding = 4 - len(signature_b64) % 4
|
|
264
|
+
if sig_padding != 4:
|
|
265
|
+
signature_b64 += "=" * sig_padding
|
|
266
|
+
signature = base64.urlsafe_b64decode(signature_b64)
|
|
267
|
+
|
|
268
|
+
self._public_key.verify(
|
|
269
|
+
signature,
|
|
270
|
+
payload_b64.encode(),
|
|
271
|
+
ec.ECDSA(hashes.SHA256()),
|
|
272
|
+
)
|
|
273
|
+
except InvalidSignature:
|
|
214
274
|
logger.warning("license.invalid_signature")
|
|
215
275
|
return LicenseCheckResult(
|
|
216
276
|
status=LicenseStatus.INVALID,
|
|
217
277
|
message="Invalid license signature",
|
|
218
278
|
)
|
|
279
|
+
except Exception as e:
|
|
280
|
+
logger.warning("license.signature_error: %s", e)
|
|
281
|
+
return LicenseCheckResult(
|
|
282
|
+
status=LicenseStatus.INVALID,
|
|
283
|
+
message="Failed to verify license signature",
|
|
284
|
+
)
|
|
219
285
|
|
|
220
286
|
try:
|
|
221
287
|
padding = 4 - len(payload_b64) % 4
|
portal/web.py
CHANGED
|
@@ -23,6 +23,15 @@ from .auth import authenticate_request
|
|
|
23
23
|
from .tenants import Tenant, TenantStore
|
|
24
24
|
from .uploads import process_upload, validate_upload
|
|
25
25
|
|
|
26
|
+
try:
|
|
27
|
+
from .enterprise import ENTERPRISE_AVAILABLE
|
|
28
|
+
from .enterprise import rbac as enterprise_rbac
|
|
29
|
+
from .enterprise import saml as enterprise_saml
|
|
30
|
+
except ImportError:
|
|
31
|
+
ENTERPRISE_AVAILABLE = False
|
|
32
|
+
enterprise_saml = None # type: ignore[assignment]
|
|
33
|
+
enterprise_rbac = None # type: ignore[assignment]
|
|
34
|
+
|
|
26
35
|
logger = logging.getLogger(__name__)
|
|
27
36
|
|
|
28
37
|
Environ = dict[str, Any]
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: kekkai-cli
|
|
3
|
-
Version: 1.0.4
|
|
4
|
-
Summary: Kekkai monorepo (local-first AppSec orchestration + compliance checker)
|
|
5
|
-
Requires-Python: >=3.12
|
|
6
|
-
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: rich>=13.0.0
|
|
8
|
-
Requires-Dist: jsonschema>=4.20.0
|
|
9
|
-
Requires-Dist: textual>=0.50.0
|
|
10
|
-
Requires-Dist: httpx>=0.24.0
|
|
11
|
-
|
|
12
|
-
<p align="center">
|
|
13
|
-
<img src="https://raw.githubusercontent.com/kademoslabs/assets/main/logos/kekkai-slim.png" alt="Kekkai CLI Logo" width="250"/>
|
|
14
|
-
</p>
|
|
15
|
-
<p align="center"><i>One command. Clean AppSec reports.</i></p>
|
|
16
|
-
<p align="center">
|
|
17
|
-
<img src="https://img.shields.io/badge/license-MIT-blue.svg"/>
|
|
18
|
-
<img src="https://img.shields.io/badge/status-active-brightgreen"/>
|
|
19
|
-
</p>
|
|
20
|
-
|
|
21
|
-
# Kekkai 🛡️
|
|
22
|
-
|
|
23
|
-
**Security that moves at developer speed.**
|
|
24
|
-
*Local-first orchestration for Trivy, Semgrep, and DefectDojo.*
|
|
25
|
-
|
|
26
|
-

|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## ⚡ Quick Start
|
|
31
|
-
|
|
32
|
-
Stop fighting with Docker Compose. Start scanning in 30 seconds.
|
|
33
|
-
|
|
34
|
-
### Installation
|
|
35
|
-
|
|
36
|
-
**Option 1: pipx (Recommended - Isolated Environment)**
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
pipx install kekkai-cli
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
**Option 2: Homebrew (macOS/Linux)**
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
brew tap kademoslabs/tap
|
|
46
|
-
brew install kekkai
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
**Option 3: Docker (No Python Required)**
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
# Build image
|
|
53
|
-
docker build -t kademoslabs/kekkai:latest -f apps/kekkai/Dockerfile .
|
|
54
|
-
|
|
55
|
-
# Run via wrapper script
|
|
56
|
-
./scripts/kekkai-docker --help
|
|
57
|
-
|
|
58
|
-
# Or set up alias
|
|
59
|
-
alias kekkai="$(pwd)/scripts/kekkai-docker"
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
**Option 4: Scoop (Windows)**
|
|
63
|
-
|
|
64
|
-
```powershell
|
|
65
|
-
scoop bucket add kademoslabs https://github.com/kademoslabs/scoop-bucket
|
|
66
|
-
scoop install kekkai
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**Option 5: pip (Traditional)**
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
pip install kekkai-cli
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
### 1. Scan your project (Local)
|
|
78
|
-
|
|
79
|
-
Run industry-standard scanners (Trivy, Semgrep, Gitleaks) in unified Docker containers without installing them individually.
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
cd your-repo
|
|
83
|
-
kekkai scan
|
|
84
|
-
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### 2. Spin up DefectDojo
|
|
88
|
-
|
|
89
|
-
Launch a full local vulnerability management platform (Nginx, Postgres, Redis, Celery) with one command.
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
kekkai dojo up --wait --open
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### 3. Generate a Threat Model (AI)
|
|
97
|
-
|
|
98
|
-
Generate a STRIDE threat model and Data Flow Diagram using your local LLM.
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
kekkai threatflow --repo . --model-mode local
|
|
102
|
-
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
---
|
|
106
|
-
|
|
107
|
-
## 🛑 The Problem vs. Kekkai
|
|
108
|
-
|
|
109
|
-
| Feature | The Old Way | The Kekkai Way |
|
|
110
|
-
| --- | --- | --- |
|
|
111
|
-
| **Tooling** | Manually install/update 5+ tools (Trivy, Semgrep, etc.) | **One Binary.** `kekkai scan` auto-pulls and runs the latest scanner containers. |
|
|
112
|
-
| **Reporting** | Parse 5 different JSON formats manually. | **Unified Output.** One deduplicated `kekkai-report.json` for all findings. |
|
|
113
|
-
| **DefectDojo** | Write a 200-line `docker-compose.yml` and debug networking. | **One Command.** `kekkai dojo up` automates the entire stack setup. |
|
|
114
|
-
| **Threat Modeling** | Expensive consultants or manual whiteboarding. | **AI Agent.** `kekkai threatflow` generates `THREATS.md` locally. |
|
|
115
|
-
| **CI/CD** | Write complex bash scripts to break builds. | **Policy Engine.** `kekkai scan --ci --fail-on high`. |
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
## 🔒 Enterprise Features (Portal)
|
|
120
|
-
|
|
121
|
-
For teams that need centralized management, **Kekkai Portal** offers:
|
|
122
|
-
|
|
123
|
-
* **SAML 2.0 SSO** with Replay Protection
|
|
124
|
-
* **Role-Based Access Control (RBAC)**
|
|
125
|
-
* **Cryptographically Signed Audit Logs**
|
|
126
|
-
|
|
127
|
-
*Built by Kademos Labs.*
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
## 📚 Documentation
|
|
132
|
-
|
|
133
|
-
- **[Automated Distribution Updates](docs/ci/automated-distributions.md)** - CI/CD distribution triggers
|
|
134
|
-
- **[CI Architecture](/.docs/development/ci-architecture.md)** - Developer guide for distribution automation
|
|
135
|
-
- **[Homebrew Maintenance](docs/ci/homebrew-maintenance.md)** - Homebrew tap management
|
|
File without changes
|
|
File without changes
|
|
File without changes
|