kekkai-cli 1.0.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.
Files changed (90) hide show
  1. kekkai/__init__.py +7 -0
  2. kekkai/cli.py +1038 -0
  3. kekkai/config.py +403 -0
  4. kekkai/dojo.py +419 -0
  5. kekkai/dojo_import.py +213 -0
  6. kekkai/github/__init__.py +16 -0
  7. kekkai/github/commenter.py +198 -0
  8. kekkai/github/models.py +56 -0
  9. kekkai/github/sanitizer.py +112 -0
  10. kekkai/installer/__init__.py +39 -0
  11. kekkai/installer/errors.py +23 -0
  12. kekkai/installer/extract.py +161 -0
  13. kekkai/installer/manager.py +252 -0
  14. kekkai/installer/manifest.py +189 -0
  15. kekkai/installer/verify.py +86 -0
  16. kekkai/manifest.py +77 -0
  17. kekkai/output.py +218 -0
  18. kekkai/paths.py +46 -0
  19. kekkai/policy.py +326 -0
  20. kekkai/runner.py +70 -0
  21. kekkai/scanners/__init__.py +67 -0
  22. kekkai/scanners/backends/__init__.py +14 -0
  23. kekkai/scanners/backends/base.py +73 -0
  24. kekkai/scanners/backends/docker.py +178 -0
  25. kekkai/scanners/backends/native.py +240 -0
  26. kekkai/scanners/base.py +110 -0
  27. kekkai/scanners/container.py +144 -0
  28. kekkai/scanners/falco.py +237 -0
  29. kekkai/scanners/gitleaks.py +237 -0
  30. kekkai/scanners/semgrep.py +227 -0
  31. kekkai/scanners/trivy.py +246 -0
  32. kekkai/scanners/url_policy.py +163 -0
  33. kekkai/scanners/zap.py +340 -0
  34. kekkai/threatflow/__init__.py +94 -0
  35. kekkai/threatflow/artifacts.py +476 -0
  36. kekkai/threatflow/chunking.py +361 -0
  37. kekkai/threatflow/core.py +438 -0
  38. kekkai/threatflow/mermaid.py +374 -0
  39. kekkai/threatflow/model_adapter.py +491 -0
  40. kekkai/threatflow/prompts.py +277 -0
  41. kekkai/threatflow/redaction.py +228 -0
  42. kekkai/threatflow/sanitizer.py +643 -0
  43. kekkai/triage/__init__.py +33 -0
  44. kekkai/triage/app.py +168 -0
  45. kekkai/triage/audit.py +203 -0
  46. kekkai/triage/ignore.py +269 -0
  47. kekkai/triage/models.py +185 -0
  48. kekkai/triage/screens.py +341 -0
  49. kekkai/triage/widgets.py +169 -0
  50. kekkai_cli-1.0.0.dist-info/METADATA +135 -0
  51. kekkai_cli-1.0.0.dist-info/RECORD +90 -0
  52. kekkai_cli-1.0.0.dist-info/WHEEL +5 -0
  53. kekkai_cli-1.0.0.dist-info/entry_points.txt +3 -0
  54. kekkai_cli-1.0.0.dist-info/top_level.txt +3 -0
  55. kekkai_core/__init__.py +3 -0
  56. kekkai_core/ci/__init__.py +11 -0
  57. kekkai_core/ci/benchmarks.py +354 -0
  58. kekkai_core/ci/metadata.py +104 -0
  59. kekkai_core/ci/validators.py +92 -0
  60. kekkai_core/docker/__init__.py +17 -0
  61. kekkai_core/docker/metadata.py +153 -0
  62. kekkai_core/docker/sbom.py +173 -0
  63. kekkai_core/docker/security.py +158 -0
  64. kekkai_core/docker/signing.py +135 -0
  65. kekkai_core/redaction.py +84 -0
  66. kekkai_core/slsa/__init__.py +13 -0
  67. kekkai_core/slsa/verify.py +121 -0
  68. kekkai_core/windows/__init__.py +29 -0
  69. kekkai_core/windows/chocolatey.py +335 -0
  70. kekkai_core/windows/installer.py +256 -0
  71. kekkai_core/windows/scoop.py +165 -0
  72. kekkai_core/windows/validators.py +220 -0
  73. portal/__init__.py +19 -0
  74. portal/api.py +155 -0
  75. portal/auth.py +103 -0
  76. portal/enterprise/__init__.py +32 -0
  77. portal/enterprise/audit.py +435 -0
  78. portal/enterprise/licensing.py +342 -0
  79. portal/enterprise/rbac.py +276 -0
  80. portal/enterprise/saml.py +595 -0
  81. portal/ops/__init__.py +53 -0
  82. portal/ops/backup.py +553 -0
  83. portal/ops/log_shipper.py +469 -0
  84. portal/ops/monitoring.py +517 -0
  85. portal/ops/restore.py +469 -0
  86. portal/ops/secrets.py +408 -0
  87. portal/ops/upgrade.py +591 -0
  88. portal/tenants.py +340 -0
  89. portal/uploads.py +259 -0
  90. portal/web.py +384 -0
@@ -0,0 +1,135 @@
1
+ """Docker image signing and verification with Cosign."""
2
+
3
+ import os
4
+ import subprocess
5
+ from pathlib import Path
6
+
7
+
8
+ class CosignError(Exception):
9
+ """Raised when Cosign operations fail."""
10
+
11
+
12
+ def sign_image(
13
+ image: str,
14
+ key_path: Path | None = None,
15
+ password: str | None = None,
16
+ ) -> bool:
17
+ """
18
+ Sign Docker image with Cosign.
19
+
20
+ Args:
21
+ image: Docker image to sign (e.g., 'kademoslabs/kekkai:latest')
22
+ key_path: Path to Cosign private key (optional, uses keyless if None)
23
+ password: Password for private key (optional)
24
+
25
+ Returns:
26
+ True if signing succeeded
27
+
28
+ Raises:
29
+ CosignError: If signing fails
30
+ """
31
+ cmd = ["cosign", "sign", "--yes"]
32
+
33
+ if key_path:
34
+ cmd.extend(["--key", str(key_path)])
35
+
36
+ cmd.append(image)
37
+
38
+ try:
39
+ env = {}
40
+ if password:
41
+ env["COSIGN_PASSWORD"] = password
42
+
43
+ result = subprocess.run(
44
+ cmd,
45
+ capture_output=True,
46
+ text=True,
47
+ check=True,
48
+ timeout=120,
49
+ env={**os.environ, **env} if env else None,
50
+ )
51
+ return result.returncode == 0
52
+
53
+ except subprocess.CalledProcessError as e:
54
+ raise CosignError(f"Image signing failed: {e.stderr}") from e
55
+ except subprocess.TimeoutExpired as e:
56
+ raise CosignError("Image signing timed out after 120s") from e
57
+
58
+
59
+ def verify_signature(
60
+ image: str,
61
+ key_path: Path | None = None,
62
+ ) -> bool:
63
+ """
64
+ Verify Docker image signature with Cosign.
65
+
66
+ Args:
67
+ image: Docker image to verify (e.g., 'kademoslabs/kekkai:latest')
68
+ key_path: Path to Cosign public key (optional, uses keyless if None)
69
+
70
+ Returns:
71
+ True if signature is valid
72
+
73
+ Raises:
74
+ CosignError: If verification fails
75
+ """
76
+ cmd = ["cosign", "verify"]
77
+
78
+ if key_path:
79
+ cmd.extend(["--key", str(key_path)])
80
+
81
+ cmd.append(image)
82
+
83
+ try:
84
+ result = subprocess.run(
85
+ cmd,
86
+ capture_output=True,
87
+ text=True,
88
+ check=False, # Don't raise on non-zero exit
89
+ timeout=120,
90
+ )
91
+ return result.returncode == 0
92
+
93
+ except subprocess.TimeoutExpired as e:
94
+ raise CosignError("Signature verification timed out after 120s") from e
95
+
96
+
97
+ def generate_keypair(output_dir: Path) -> tuple[Path, Path]:
98
+ """
99
+ Generate Cosign keypair.
100
+
101
+ Args:
102
+ output_dir: Directory to store keys
103
+
104
+ Returns:
105
+ Tuple of (private_key_path, public_key_path)
106
+
107
+ Raises:
108
+ CosignError: If key generation fails
109
+ """
110
+ output_dir.mkdir(parents=True, exist_ok=True)
111
+
112
+ private_key = output_dir / "cosign.key"
113
+ public_key = output_dir / "cosign.pub"
114
+
115
+ cmd = ["cosign", "generate-key-pair"]
116
+
117
+ try:
118
+ subprocess.run(
119
+ cmd,
120
+ capture_output=True,
121
+ text=True,
122
+ check=True,
123
+ cwd=str(output_dir),
124
+ timeout=60,
125
+ )
126
+
127
+ if not private_key.exists() or not public_key.exists():
128
+ raise CosignError("Key generation succeeded but keys not found")
129
+
130
+ return (private_key, public_key)
131
+
132
+ except subprocess.CalledProcessError as e:
133
+ raise CosignError(f"Key generation failed: {e.stderr}") from e
134
+ except subprocess.TimeoutExpired as e:
135
+ raise CosignError("Key generation timed out after 60s") from e
@@ -0,0 +1,84 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+
5
+ # Conservative redaction: hide common token/secret patterns while preserving debugging structure.
6
+ _SECRET_PATTERNS: list[re.Pattern[str]] = [
7
+ re.compile(r"(?i)(api[_-]?key|token|secret|password)\s*[:=]\s*([^\s,;]+)"),
8
+ re.compile(r"(?i)\b(bearer)\s+([a-z0-9\-\._~\+\/]+=*)"),
9
+ ]
10
+
11
+ # Extended patterns for comprehensive secret detection
12
+ _EXTENDED_PATTERNS: list[re.Pattern[str]] = [
13
+ # AWS keys
14
+ re.compile(r"\b(AKIA[0-9A-Z]{16})\b"),
15
+ re.compile(r"(?i)(aws[_-]?secret[_-]?access[_-]?key)\s*[:=]\s*([A-Za-z0-9/+=]{40})"),
16
+ # GitHub tokens
17
+ re.compile(r"\b(ghp_[A-Za-z0-9]{36})\b"),
18
+ re.compile(r"\b(gho_[A-Za-z0-9]{36})\b"),
19
+ re.compile(r"\b(github_pat_[A-Za-z0-9_]{22,})\b"),
20
+ # GitLab tokens
21
+ re.compile(r"\b(glpat-[A-Za-z0-9\-_]{20,})\b"),
22
+ # Slack tokens
23
+ re.compile(r"\b(xox[baprs]-[A-Za-z0-9\-]+)\b"),
24
+ # Stripe keys
25
+ re.compile(r"\b(sk_(?:live|test)_[A-Za-z0-9]{24,})\b"),
26
+ re.compile(r"\b(pk_(?:live|test)_[A-Za-z0-9]{24,})\b"),
27
+ # Private key headers
28
+ re.compile(r"-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----"),
29
+ # JWT tokens (simplified)
30
+ re.compile(r"\beyJ[A-Za-z0-9_-]*\.eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\b"),
31
+ ]
32
+
33
+
34
+ def redact(text: str) -> str:
35
+ """Redact likely secrets from a string (best-effort, non-destructive)."""
36
+ redacted = text
37
+ for pat in _SECRET_PATTERNS:
38
+ redacted = pat.sub(lambda m: f"{m.group(1)} [REDACTED]", redacted)
39
+ return redacted
40
+
41
+
42
+ def redact_extended(text: str) -> str:
43
+ """Redact secrets using both core and extended patterns.
44
+
45
+ Includes AWS, GitHub, GitLab, Slack, Stripe tokens, private keys, and JWTs.
46
+ """
47
+ result = redact(text)
48
+ for pat in _EXTENDED_PATTERNS:
49
+ result = pat.sub("[REDACTED]", result)
50
+ return result
51
+
52
+
53
+ def detect_secrets(text: str) -> list[str]:
54
+ """Detect types of secrets present in text without returning values.
55
+
56
+ Returns list of pattern names/descriptions found.
57
+ """
58
+ found: list[str] = []
59
+
60
+ # Check core patterns
61
+ for pat in _SECRET_PATTERNS:
62
+ if pat.search(text):
63
+ found.append("credential_pattern")
64
+
65
+ # Check extended patterns with descriptions
66
+ pattern_names = [
67
+ ("aws_key", _EXTENDED_PATTERNS[0]),
68
+ ("aws_secret", _EXTENDED_PATTERNS[1]),
69
+ ("github_token", _EXTENDED_PATTERNS[2]),
70
+ ("github_oauth", _EXTENDED_PATTERNS[3]),
71
+ ("github_pat", _EXTENDED_PATTERNS[4]),
72
+ ("gitlab_token", _EXTENDED_PATTERNS[5]),
73
+ ("slack_token", _EXTENDED_PATTERNS[6]),
74
+ ("stripe_secret", _EXTENDED_PATTERNS[7]),
75
+ ("stripe_publishable", _EXTENDED_PATTERNS[8]),
76
+ ("private_key", _EXTENDED_PATTERNS[9]),
77
+ ("jwt_token", _EXTENDED_PATTERNS[10]),
78
+ ]
79
+
80
+ for name, pat in pattern_names:
81
+ if pat.search(text):
82
+ found.append(name)
83
+
84
+ return list(set(found)) # Dedupe
@@ -0,0 +1,13 @@
1
+ """SLSA Level 3 provenance verification for kekkai releases."""
2
+
3
+ from kekkai_core.slsa.verify import (
4
+ AttestationError,
5
+ ProvenanceResult,
6
+ verify_provenance,
7
+ )
8
+
9
+ __all__ = [
10
+ "AttestationError",
11
+ "ProvenanceResult",
12
+ "verify_provenance",
13
+ ]
@@ -0,0 +1,121 @@
1
+ """SLSA provenance verification using slsa-verifier."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import subprocess
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+
10
+
11
+ class AttestationError(Exception):
12
+ """Raised when attestation verification fails."""
13
+
14
+
15
+ @dataclass
16
+ class ProvenanceResult:
17
+ """Result of SLSA provenance verification."""
18
+
19
+ verified: bool
20
+ builder_id: str | None = None
21
+ source_repo: str | None = None
22
+ commit_sha: str | None = None
23
+ error: str | None = None
24
+
25
+
26
+ def verify_provenance(
27
+ artifact_path: Path,
28
+ provenance_path: Path,
29
+ expected_repo: str = "kademoslabs/kekkai",
30
+ ) -> ProvenanceResult:
31
+ """
32
+ Verify SLSA provenance for an artifact.
33
+
34
+ Args:
35
+ artifact_path: Path to the artifact to verify
36
+ provenance_path: Path to the provenance attestation file
37
+ expected_repo: Expected GitHub repository (owner/repo)
38
+
39
+ Returns:
40
+ ProvenanceResult with verification status and metadata
41
+
42
+ Raises:
43
+ AttestationError: If verification encounters an unrecoverable error
44
+ """
45
+ if not artifact_path.exists():
46
+ return ProvenanceResult(
47
+ verified=False,
48
+ error=f"Artifact not found: {artifact_path}",
49
+ )
50
+
51
+ if not provenance_path.exists():
52
+ return ProvenanceResult(
53
+ verified=False,
54
+ error=f"Provenance file not found: {provenance_path}",
55
+ )
56
+
57
+ try:
58
+ result = subprocess.run(
59
+ [
60
+ "slsa-verifier", # nosec B607 - trusted binary
61
+ "verify-artifact",
62
+ str(artifact_path),
63
+ "--provenance-path",
64
+ str(provenance_path),
65
+ "--source-uri",
66
+ f"github.com/{expected_repo}",
67
+ ],
68
+ capture_output=True,
69
+ text=True,
70
+ timeout=60,
71
+ )
72
+
73
+ if result.returncode != 0:
74
+ return ProvenanceResult(
75
+ verified=False,
76
+ error=result.stderr.strip() or "Verification failed",
77
+ )
78
+
79
+ # Parse provenance for metadata
80
+ provenance_data = _parse_provenance(provenance_path)
81
+
82
+ return ProvenanceResult(
83
+ verified=True,
84
+ builder_id=provenance_data.get("builder_id"),
85
+ source_repo=provenance_data.get("source_repo"),
86
+ commit_sha=provenance_data.get("commit_sha"),
87
+ )
88
+
89
+ except subprocess.TimeoutExpired:
90
+ return ProvenanceResult(
91
+ verified=False,
92
+ error="Verification timed out after 60s",
93
+ )
94
+ except FileNotFoundError:
95
+ return ProvenanceResult(
96
+ verified=False,
97
+ error="slsa-verifier not found. Install: https://github.com/slsa-framework/slsa-verifier",
98
+ )
99
+ except Exception as e:
100
+ raise AttestationError(f"Unexpected error during verification: {e}") from e
101
+
102
+
103
+ def _parse_provenance(provenance_path: Path) -> dict[str, str | None]:
104
+ """Extract metadata from provenance file."""
105
+ try:
106
+ data = json.loads(provenance_path.read_text())
107
+
108
+ # Handle SLSA provenance format
109
+ predicate = data.get("predicate", {})
110
+ invocation = predicate.get("invocation", {})
111
+ config_source = invocation.get("configSource", {})
112
+
113
+ builder = predicate.get("builder", {})
114
+
115
+ return {
116
+ "builder_id": builder.get("id"),
117
+ "source_repo": config_source.get("uri"),
118
+ "commit_sha": config_source.get("digest", {}).get("sha1"),
119
+ }
120
+ except (json.JSONDecodeError, KeyError):
121
+ return {}
@@ -0,0 +1,29 @@
1
+ """Windows distribution integration for Kekkai.
2
+
3
+ This module provides utilities for Windows package managers:
4
+ - Scoop manifest generation and validation
5
+ - PowerShell installer script generation
6
+ - Windows-specific validation (Python version, PATH)
7
+ """
8
+
9
+ from kekkai_core.windows.installer import (
10
+ generate_installer_script,
11
+ generate_uninstaller_script,
12
+ )
13
+ from kekkai_core.windows.scoop import (
14
+ generate_scoop_manifest,
15
+ validate_scoop_manifest,
16
+ )
17
+ from kekkai_core.windows.validators import (
18
+ validate_python_version,
19
+ validate_windows_path,
20
+ )
21
+
22
+ __all__ = [
23
+ "generate_scoop_manifest",
24
+ "validate_scoop_manifest",
25
+ "generate_installer_script",
26
+ "generate_uninstaller_script",
27
+ "validate_python_version",
28
+ "validate_windows_path",
29
+ ]