kekkai-cli 1.0.5__py3-none-any.whl → 1.1.1__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.
Files changed (53) hide show
  1. kekkai/cli.py +789 -19
  2. kekkai/compliance/__init__.py +68 -0
  3. kekkai/compliance/hipaa.py +235 -0
  4. kekkai/compliance/mappings.py +136 -0
  5. kekkai/compliance/owasp.py +517 -0
  6. kekkai/compliance/owasp_agentic.py +267 -0
  7. kekkai/compliance/pci_dss.py +205 -0
  8. kekkai/compliance/soc2.py +209 -0
  9. kekkai/dojo.py +91 -14
  10. kekkai/dojo_import.py +9 -1
  11. kekkai/fix/__init__.py +47 -0
  12. kekkai/fix/audit.py +278 -0
  13. kekkai/fix/differ.py +427 -0
  14. kekkai/fix/engine.py +500 -0
  15. kekkai/fix/prompts.py +251 -0
  16. kekkai/output.py +10 -12
  17. kekkai/report/__init__.py +41 -0
  18. kekkai/report/compliance_matrix.py +98 -0
  19. kekkai/report/generator.py +365 -0
  20. kekkai/report/html.py +69 -0
  21. kekkai/report/pdf.py +63 -0
  22. kekkai/report/unified.py +226 -0
  23. kekkai/scanners/container.py +33 -3
  24. kekkai/scanners/gitleaks.py +3 -1
  25. kekkai/scanners/semgrep.py +1 -1
  26. kekkai/scanners/trivy.py +1 -1
  27. kekkai/threatflow/model_adapter.py +143 -1
  28. kekkai/triage/__init__.py +54 -1
  29. kekkai/triage/loader.py +196 -0
  30. kekkai_cli-1.1.1.dist-info/METADATA +379 -0
  31. {kekkai_cli-1.0.5.dist-info → kekkai_cli-1.1.1.dist-info}/RECORD +34 -33
  32. {kekkai_cli-1.0.5.dist-info → kekkai_cli-1.1.1.dist-info}/entry_points.txt +0 -1
  33. {kekkai_cli-1.0.5.dist-info → kekkai_cli-1.1.1.dist-info}/top_level.txt +0 -1
  34. kekkai_cli-1.0.5.dist-info/METADATA +0 -135
  35. portal/__init__.py +0 -19
  36. portal/api.py +0 -155
  37. portal/auth.py +0 -103
  38. portal/enterprise/__init__.py +0 -32
  39. portal/enterprise/audit.py +0 -435
  40. portal/enterprise/licensing.py +0 -342
  41. portal/enterprise/rbac.py +0 -276
  42. portal/enterprise/saml.py +0 -595
  43. portal/ops/__init__.py +0 -53
  44. portal/ops/backup.py +0 -553
  45. portal/ops/log_shipper.py +0 -469
  46. portal/ops/monitoring.py +0 -517
  47. portal/ops/restore.py +0 -469
  48. portal/ops/secrets.py +0 -408
  49. portal/ops/upgrade.py +0 -591
  50. portal/tenants.py +0 -340
  51. portal/uploads.py +0 -259
  52. portal/web.py +0 -384
  53. {kekkai_cli-1.0.5.dist-info → kekkai_cli-1.1.1.dist-info}/WHEEL +0 -0
@@ -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
portal/__init__.py DELETED
@@ -1,19 +0,0 @@
1
- """Kekkai Hosted Portal - DefectDojo-backed multi-tenant security dashboard."""
2
-
3
- from __future__ import annotations
4
-
5
- __all__ = [
6
- "AuthMethod",
7
- "AuthResult",
8
- "SAMLTenantConfig",
9
- "Tenant",
10
- "TenantStore",
11
- "UploadResult",
12
- "authenticate_request",
13
- "process_upload",
14
- "validate_upload",
15
- ]
16
-
17
- from .auth import AuthResult, authenticate_request
18
- from .tenants import AuthMethod, SAMLTenantConfig, Tenant, TenantStore
19
- from .uploads import UploadResult, process_upload, validate_upload
portal/api.py DELETED
@@ -1,155 +0,0 @@
1
- """Portal API endpoints for programmatic access.
2
-
3
- Provides REST API endpoints that expose the same data visible in the UI.
4
- All endpoints require authentication and enforce tenant isolation.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- import json
10
- import logging
11
- import os
12
- from dataclasses import dataclass
13
- from pathlib import Path
14
- from typing import Any
15
-
16
- from .tenants import Tenant
17
-
18
- logger = logging.getLogger(__name__)
19
-
20
-
21
- @dataclass(frozen=True)
22
- class UploadInfo:
23
- """Information about an upload."""
24
-
25
- upload_id: str
26
- filename: str
27
- timestamp: str
28
- file_hash: str
29
- size_bytes: int
30
-
31
-
32
- @dataclass(frozen=True)
33
- class TenantStats:
34
- """Statistics for a tenant."""
35
-
36
- total_uploads: int
37
- total_size_bytes: int
38
- last_upload_time: str | None
39
-
40
-
41
- def get_tenant_info(tenant: Tenant) -> dict[str, Any]:
42
- """Get tenant information for API response.
43
-
44
- Args:
45
- tenant: The authenticated tenant
46
-
47
- Returns:
48
- Dictionary containing tenant metadata
49
- """
50
- return {
51
- "id": tenant.id,
52
- "name": tenant.name,
53
- "dojo_product_id": tenant.dojo_product_id,
54
- "dojo_engagement_id": tenant.dojo_engagement_id,
55
- "enabled": tenant.enabled,
56
- "max_upload_size_mb": tenant.max_upload_size_mb,
57
- "auth_method": tenant.auth_method.value,
58
- "default_role": tenant.default_role,
59
- }
60
-
61
-
62
- def list_uploads(tenant: Tenant, limit: int = 50) -> list[dict[str, Any]]:
63
- """List recent uploads for a tenant.
64
-
65
- Args:
66
- tenant: The authenticated tenant
67
- limit: Maximum number of uploads to return
68
-
69
- Returns:
70
- List of upload metadata dictionaries
71
- """
72
- upload_dir = Path(os.environ.get("PORTAL_UPLOAD_DIR", "/var/lib/kekkai-portal/uploads"))
73
- tenant_dir = upload_dir / tenant.id
74
-
75
- if not tenant_dir.exists():
76
- return []
77
-
78
- uploads: list[dict[str, Any]] = []
79
-
80
- # Get all upload files for this tenant
81
- try:
82
- upload_files = sorted(
83
- tenant_dir.glob("*.json"),
84
- key=lambda p: p.stat().st_mtime,
85
- reverse=True,
86
- )[:limit]
87
-
88
- for upload_file in upload_files:
89
- stat = upload_file.stat()
90
- uploads.append(
91
- {
92
- "upload_id": upload_file.stem,
93
- "filename": upload_file.name,
94
- "timestamp": str(int(stat.st_mtime)),
95
- "size_bytes": stat.st_size,
96
- }
97
- )
98
- except (OSError, PermissionError) as e:
99
- logger.warning("Failed to list uploads for tenant %s: %s", tenant.id, e)
100
-
101
- return uploads
102
-
103
-
104
- def get_tenant_stats(tenant: Tenant) -> dict[str, Any]:
105
- """Get statistics for a tenant.
106
-
107
- Args:
108
- tenant: The authenticated tenant
109
-
110
- Returns:
111
- Dictionary containing tenant statistics
112
- """
113
- upload_dir = Path(os.environ.get("PORTAL_UPLOAD_DIR", "/var/lib/kekkai-portal/uploads"))
114
- tenant_dir = upload_dir / tenant.id
115
-
116
- if not tenant_dir.exists():
117
- return {
118
- "total_uploads": 0,
119
- "total_size_bytes": 0,
120
- "last_upload_time": None,
121
- }
122
-
123
- total_uploads = 0
124
- total_size_bytes = 0
125
- last_upload_time: int | None = None
126
-
127
- try:
128
- for upload_file in tenant_dir.glob("*.json"):
129
- stat = upload_file.stat()
130
- total_uploads += 1
131
- total_size_bytes += stat.st_size
132
-
133
- if last_upload_time is None or stat.st_mtime > last_upload_time:
134
- last_upload_time = int(stat.st_mtime)
135
-
136
- except (OSError, PermissionError) as e:
137
- logger.warning("Failed to get stats for tenant %s: %s", tenant.id, e)
138
-
139
- return {
140
- "total_uploads": total_uploads,
141
- "total_size_bytes": total_size_bytes,
142
- "last_upload_time": str(last_upload_time) if last_upload_time else None,
143
- }
144
-
145
-
146
- def serialize_api_response(data: dict[str, Any]) -> bytes:
147
- """Serialize API response to JSON bytes.
148
-
149
- Args:
150
- data: Response data dictionary
151
-
152
- Returns:
153
- JSON-encoded bytes
154
- """
155
- return json.dumps(data, indent=2).encode("utf-8")
portal/auth.py DELETED
@@ -1,103 +0,0 @@
1
- """Authentication middleware for portal API.
2
-
3
- Security controls:
4
- - ASVS V16.3.2: Log failed authorization attempts
5
- - Constant-time API key comparison to prevent timing attacks
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- import logging
11
- import re
12
- from dataclasses import dataclass
13
- from typing import TYPE_CHECKING
14
-
15
- from kekkai_core import redact
16
-
17
- from .tenants import Tenant, TenantStore
18
-
19
- if TYPE_CHECKING:
20
- from collections.abc import Mapping
21
-
22
- logger = logging.getLogger(__name__)
23
-
24
- BEARER_PATTERN = re.compile(r"^Bearer\s+(\S+)$", re.IGNORECASE)
25
-
26
-
27
- @dataclass(frozen=True)
28
- class AuthResult:
29
- """Result of authentication attempt."""
30
-
31
- authenticated: bool
32
- tenant: Tenant | None = None
33
- error: str | None = None
34
-
35
-
36
- def authenticate_request(
37
- headers: Mapping[str, str],
38
- tenant_store: TenantStore,
39
- client_ip: str = "unknown",
40
- ) -> AuthResult:
41
- """Authenticate a request using Bearer token.
42
-
43
- Args:
44
- headers: Request headers (case-insensitive lookup)
45
- tenant_store: Tenant storage for API key verification
46
- client_ip: Client IP for logging failed attempts
47
-
48
- Returns:
49
- AuthResult with tenant if authenticated, error otherwise
50
- """
51
- auth_header = _get_header(headers, "Authorization")
52
- if not auth_header:
53
- _log_auth_failure(client_ip, "missing_header")
54
- return AuthResult(authenticated=False, error="Missing Authorization header")
55
-
56
- match = BEARER_PATTERN.match(auth_header)
57
- if not match:
58
- _log_auth_failure(client_ip, "invalid_format")
59
- return AuthResult(authenticated=False, error="Invalid Authorization format")
60
-
61
- api_key = match.group(1)
62
- if not api_key:
63
- _log_auth_failure(client_ip, "empty_token")
64
- return AuthResult(authenticated=False, error="Empty API token")
65
-
66
- tenant = tenant_store.get_by_api_key(api_key)
67
- if not tenant:
68
- _log_auth_failure(client_ip, "invalid_token", api_key_prefix=api_key[:8])
69
- return AuthResult(authenticated=False, error="Invalid API key")
70
-
71
- if not tenant.enabled:
72
- _log_auth_failure(client_ip, "tenant_disabled", tenant_id=tenant.id)
73
- return AuthResult(authenticated=False, error="Tenant is disabled")
74
-
75
- logger.info(
76
- "auth.success client_ip=%s tenant_id=%s",
77
- redact(client_ip),
78
- tenant.id,
79
- )
80
- return AuthResult(authenticated=True, tenant=tenant)
81
-
82
-
83
- def _get_header(headers: Mapping[str, str], name: str) -> str | None:
84
- """Get header value with case-insensitive lookup."""
85
- for key, value in headers.items():
86
- if key.lower() == name.lower():
87
- return value
88
- return None
89
-
90
-
91
- def _log_auth_failure(
92
- client_ip: str,
93
- reason: str,
94
- tenant_id: str | None = None,
95
- api_key_prefix: str | None = None,
96
- ) -> None:
97
- """Log authentication failure for security monitoring (ASVS V16.3.2)."""
98
- parts = [f"auth.failure reason={reason}", f"client_ip={redact(client_ip)}"]
99
- if tenant_id:
100
- parts.append(f"tenant_id={tenant_id}")
101
- if api_key_prefix:
102
- parts.append(f"api_key_prefix={api_key_prefix}...")
103
- logger.warning(" ".join(parts))
@@ -1,32 +0,0 @@
1
- """Enterprise features for Kekkai Portal.
2
-
3
- Provides:
4
- - RBAC (Role-Based Access Control)
5
- - SAML 2.0 SSO integration
6
- - Audit logging
7
- - Enterprise license gating
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- from .audit import AuditEvent, AuditEventType, AuditLog
13
- from .licensing import EnterpriseLicense, LicenseStatus, LicenseValidator
14
- from .rbac import AuthorizationResult, Permission, RBACManager, Role
15
- from .saml import SAMLAssertion, SAMLConfig, SAMLError, SAMLProcessor
16
-
17
- __all__ = [
18
- "AuditEvent",
19
- "AuditEventType",
20
- "AuditLog",
21
- "AuthorizationResult",
22
- "EnterpriseLicense",
23
- "LicenseStatus",
24
- "LicenseValidator",
25
- "Permission",
26
- "RBACManager",
27
- "Role",
28
- "SAMLAssertion",
29
- "SAMLConfig",
30
- "SAMLError",
31
- "SAMLProcessor",
32
- ]