kekkai-cli 1.0.5__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.
@@ -2,15 +2,15 @@
2
2
 
3
3
  Security controls:
4
4
  - Server-side license enforcement
5
- - Signed license tokens to prevent tampering
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
- class LicenseValidator:
159
- """Validates enterprise licenses."""
162
+ def generate_keypair() -> tuple[bytes, bytes]:
163
+ """Generate a new ECDSA P-256 keypair for license signing.
160
164
 
161
- def __init__(self, signing_key: str) -> None:
162
- self._signing_key = signing_key
163
- self._cache: dict[str, tuple[float, LicenseCheckResult]] = {}
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 = hmac.new(
175
- self._signing_key.encode(),
207
+ signature = self._private_key.sign(
176
208
  payload_b64.encode(),
177
- hashlib.sha256,
178
- ).hexdigest()
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
- return f"{payload_b64}.{signature}"
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, signature = parts
260
+ payload_b64, signature_b64 = parts
206
261
 
207
- expected_sig = hmac.new(
208
- self._signing_key.encode(),
209
- payload_b64.encode(),
210
- hashlib.sha256,
211
- ).hexdigest()
212
-
213
- if not hmac.compare_digest(signature, expected_sig):
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.5
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
- ![Hero GIF](https://raw.githubusercontent.com/kademoslabs/assets/main/screenshots/kekkai-recording.gif)
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