vaspera 2.8.0 → 2.9.2
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 +109 -7
- 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 +25 -4
- package/dist/agents/adversary/config.d.ts.map +1 -1
- package/dist/agents/adversary/config.js +38 -8
- package/dist/agents/adversary/config.js.map +1 -1
- package/dist/agents/adversary/index.d.ts +7 -0
- package/dist/agents/adversary/index.d.ts.map +1 -1
- package/dist/agents/adversary/index.js +83 -1
- package/dist/agents/adversary/index.js.map +1 -1
- 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 +66 -10
- package/dist/agents/adversary/types.d.ts.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/types.d.ts +1 -1
- package/dist/certification/types.d.ts.map +1 -1
- 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 +515 -19
- 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/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/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.js +4 -4
- 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 +252 -0
- package/dist/telemetry/usage.js.map +1 -0
- package/package.json +2 -6
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Tactics Module
|
|
3
|
+
*
|
|
4
|
+
* Detects authentication and session management vulnerabilities including
|
|
5
|
+
* JWT weaknesses, session fixation, OAuth bypass, and hardcoded credentials.
|
|
6
|
+
* Priority 2 - core security control.
|
|
7
|
+
*
|
|
8
|
+
* @module agents/adversary/tactics/auth
|
|
9
|
+
*/
|
|
10
|
+
import { registerTactic, generateFindingId, } from "./index.js";
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Patterns
|
|
13
|
+
// ============================================================================
|
|
14
|
+
const JWT_PATTERNS = [
|
|
15
|
+
{
|
|
16
|
+
id: "jwt-none-algorithm",
|
|
17
|
+
name: "JWT None Algorithm Accepted",
|
|
18
|
+
description: "JWT verification may accept 'none' algorithm",
|
|
19
|
+
cwe: "CWE-327",
|
|
20
|
+
severity: "critical",
|
|
21
|
+
regex: /jwt\.verify\s*\([^)]*,\s*(?:null|undefined|['"][\s]*['"])\s*\)|algorithms\s*:\s*\[[^\]]*['"]none['"]/gi,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "jwt-weak-secret",
|
|
25
|
+
name: "JWT Weak or Hardcoded Secret",
|
|
26
|
+
description: "JWT signed with weak or hardcoded secret",
|
|
27
|
+
cwe: "CWE-798",
|
|
28
|
+
severity: "high",
|
|
29
|
+
regex: /jwt\.sign\s*\([^)]*,\s*['"](secret|password|key|123|test|dev)['"]/gi,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "jwt-hs256-public-key",
|
|
33
|
+
name: "JWT Algorithm Confusion",
|
|
34
|
+
description: "JWT may be vulnerable to RS256 to HS256 algorithm confusion",
|
|
35
|
+
cwe: "CWE-327",
|
|
36
|
+
severity: "high",
|
|
37
|
+
regex: /algorithms\s*:\s*\[\s*['"](?:RS|HS)256['"]\s*\]|algorithm\s*:\s*['"](?:RS|HS)256['"]/gi,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "jwt-no-expiry",
|
|
41
|
+
name: "JWT Without Expiration",
|
|
42
|
+
description: "JWT created without expiration claim",
|
|
43
|
+
cwe: "CWE-613",
|
|
44
|
+
severity: "medium",
|
|
45
|
+
regex: /jwt\.sign\s*\([^)]*\)(?![^;]*expiresIn)/gi,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "jwt-decode-no-verify",
|
|
49
|
+
name: "JWT Decoded Without Verification",
|
|
50
|
+
description: "JWT decoded without signature verification",
|
|
51
|
+
cwe: "CWE-345",
|
|
52
|
+
severity: "high",
|
|
53
|
+
regex: /jwt\.decode\s*\([^)]*\)(?![^;]*verify)/gi,
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
const SESSION_PATTERNS = [
|
|
57
|
+
{
|
|
58
|
+
id: "session-no-regenerate",
|
|
59
|
+
name: "Session Not Regenerated on Login",
|
|
60
|
+
description: "Session ID not regenerated after authentication",
|
|
61
|
+
cwe: "CWE-384",
|
|
62
|
+
severity: "high",
|
|
63
|
+
regex: /(?:login|authenticate|signIn)\s*(?:=|:)\s*async[^{]*\{(?![^}]*regenerate|[^}]*destroy)/gis,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "session-insecure-cookie",
|
|
67
|
+
name: "Insecure Session Cookie",
|
|
68
|
+
description: "Session cookie missing secure attributes",
|
|
69
|
+
cwe: "CWE-614",
|
|
70
|
+
severity: "medium",
|
|
71
|
+
regex: /cookie\s*:\s*\{[^}]*(?!secure\s*:\s*true)[^}]*\}|httpOnly\s*:\s*false/gi,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: "session-predictable",
|
|
75
|
+
name: "Predictable Session ID",
|
|
76
|
+
description: "Session ID generated with weak randomness",
|
|
77
|
+
cwe: "CWE-330",
|
|
78
|
+
severity: "high",
|
|
79
|
+
regex: /session(?:Id|Token)\s*=\s*(?:Date\.now\(\)|Math\.random\(\)|uuid\.v1\(\))/gi,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "session-in-url",
|
|
83
|
+
name: "Session Token in URL",
|
|
84
|
+
description: "Session token passed in URL parameters",
|
|
85
|
+
cwe: "CWE-598",
|
|
86
|
+
severity: "medium",
|
|
87
|
+
regex: /\?.*(?:session|token|sid)=|redirect\s*\([^)]*(?:session|token)/gi,
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
const OAUTH_PATTERNS = [
|
|
91
|
+
{
|
|
92
|
+
id: "oauth-open-redirect",
|
|
93
|
+
name: "OAuth Open Redirect",
|
|
94
|
+
description: "OAuth redirect URI not properly validated",
|
|
95
|
+
cwe: "CWE-601",
|
|
96
|
+
severity: "high",
|
|
97
|
+
regex: /redirect_uri\s*[:=]\s*(?:req\.|params\.|query\.)|redirect_uri.*(?:!==|!=)\s*(?:undefined|null)/gi,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: "oauth-no-state",
|
|
101
|
+
name: "OAuth Missing State Parameter",
|
|
102
|
+
description: "OAuth flow missing CSRF protection via state parameter",
|
|
103
|
+
cwe: "CWE-352",
|
|
104
|
+
severity: "high",
|
|
105
|
+
regex: /(?:authorize|oauth)\s*\([^)]*\)(?![^;]*state)|state\s*:\s*(?:null|undefined|['"][\s]*['"])/gi,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: "oauth-token-leak",
|
|
109
|
+
name: "OAuth Token Leakage",
|
|
110
|
+
description: "OAuth tokens logged or exposed in error messages",
|
|
111
|
+
cwe: "CWE-532",
|
|
112
|
+
severity: "high",
|
|
113
|
+
regex: /console\.(?:log|error|warn)\s*\([^)]*(?:access_token|refresh_token|id_token)/gi,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "oauth-implicit-flow",
|
|
117
|
+
name: "OAuth Implicit Flow",
|
|
118
|
+
description: "Using deprecated OAuth implicit flow",
|
|
119
|
+
cwe: "CWE-319",
|
|
120
|
+
severity: "medium",
|
|
121
|
+
regex: /response_type\s*[:=]\s*['"]token['"]/gi,
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
const CREDENTIAL_PATTERNS = [
|
|
125
|
+
{
|
|
126
|
+
id: "hardcoded-password",
|
|
127
|
+
name: "Hardcoded Password",
|
|
128
|
+
description: "Password hardcoded in source code",
|
|
129
|
+
cwe: "CWE-798",
|
|
130
|
+
severity: "critical",
|
|
131
|
+
regex: /(?:password|passwd|pwd)\s*[:=]\s*['"][^'"]{4,}['"]/gi,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "hardcoded-api-key",
|
|
135
|
+
name: "Hardcoded API Key",
|
|
136
|
+
description: "API key hardcoded in source code",
|
|
137
|
+
cwe: "CWE-798",
|
|
138
|
+
severity: "critical",
|
|
139
|
+
regex: /(?:api[_-]?key|apikey|api[_-]?secret)\s*[:=]\s*['"][a-zA-Z0-9_\-]{16,}['"]/gi,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: "hardcoded-db-creds",
|
|
143
|
+
name: "Hardcoded Database Credentials",
|
|
144
|
+
description: "Database credentials hardcoded in source code",
|
|
145
|
+
cwe: "CWE-798",
|
|
146
|
+
severity: "critical",
|
|
147
|
+
regex: /(?:mysql|postgres|mongodb|redis):\/\/[^:]+:[^@]+@/gi,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "default-credentials",
|
|
151
|
+
name: "Default Credentials",
|
|
152
|
+
description: "Default username/password combination",
|
|
153
|
+
cwe: "CWE-1392",
|
|
154
|
+
severity: "high",
|
|
155
|
+
regex: /(?:admin|root|test|demo)['"]?\s*[:=]\s*['"](?:admin|root|password|123456|test)['"]/gi,
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
const RATE_LIMITING_PATTERNS = [
|
|
159
|
+
{
|
|
160
|
+
id: "login-no-rate-limit",
|
|
161
|
+
name: "Login Without Rate Limiting",
|
|
162
|
+
description: "Authentication endpoint without rate limiting",
|
|
163
|
+
cwe: "CWE-307",
|
|
164
|
+
severity: "medium",
|
|
165
|
+
regex: /(?:login|signin|authenticate)\s*[:=]\s*(?:async\s*)?\([^)]*\)\s*(?:=>|{)(?![^}]*(?:rateLimit|throttle|limiter))/gis,
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: "password-reset-no-limit",
|
|
169
|
+
name: "Password Reset Without Rate Limiting",
|
|
170
|
+
description: "Password reset endpoint vulnerable to enumeration",
|
|
171
|
+
cwe: "CWE-640",
|
|
172
|
+
severity: "medium",
|
|
173
|
+
regex: /(?:forgot|reset|recover)(?:Password|Pass)\s*[:=](?![^}]*(?:rateLimit|throttle))/gi,
|
|
174
|
+
},
|
|
175
|
+
];
|
|
176
|
+
const MFA_PATTERNS = [
|
|
177
|
+
{
|
|
178
|
+
id: "mfa-bypass",
|
|
179
|
+
name: "MFA Bypass Possibility",
|
|
180
|
+
description: "MFA check may be bypassed through alternative flow",
|
|
181
|
+
cwe: "CWE-287",
|
|
182
|
+
severity: "high",
|
|
183
|
+
regex: /(?:skip|bypass|disable)(?:Mfa|2fa|TwoFactor)|mfa(?:Required|Enabled)\s*[:=]\s*false/gi,
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
id: "mfa-backup-enumerable",
|
|
187
|
+
name: "MFA Backup Codes Enumerable",
|
|
188
|
+
description: "MFA backup codes may be enumerable or predictable",
|
|
189
|
+
cwe: "CWE-330",
|
|
190
|
+
severity: "medium",
|
|
191
|
+
regex: /backupCode\s*=\s*(?:Math\.random|Date\.now|uuid\.v1)/gi,
|
|
192
|
+
},
|
|
193
|
+
];
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// Tactic Implementation
|
|
196
|
+
// ============================================================================
|
|
197
|
+
const authTactic = {
|
|
198
|
+
focusArea: "auth",
|
|
199
|
+
name: "Authentication",
|
|
200
|
+
description: "Detects authentication, session, and credential vulnerabilities",
|
|
201
|
+
patterns: [
|
|
202
|
+
...JWT_PATTERNS,
|
|
203
|
+
...SESSION_PATTERNS,
|
|
204
|
+
...OAUTH_PATTERNS,
|
|
205
|
+
...CREDENTIAL_PATTERNS,
|
|
206
|
+
...RATE_LIMITING_PATTERNS,
|
|
207
|
+
...MFA_PATTERNS,
|
|
208
|
+
],
|
|
209
|
+
async analyzeFile(file, config) {
|
|
210
|
+
const findings = [];
|
|
211
|
+
for (const pattern of this.patterns) {
|
|
212
|
+
if (!pattern.regex)
|
|
213
|
+
continue;
|
|
214
|
+
// Reset regex state
|
|
215
|
+
pattern.regex.lastIndex = 0;
|
|
216
|
+
let match;
|
|
217
|
+
while ((match = pattern.regex.exec(file.content)) !== null) {
|
|
218
|
+
// Calculate line number
|
|
219
|
+
const beforeMatch = file.content.substring(0, match.index);
|
|
220
|
+
const lineNum = (beforeMatch.match(/\n/g) || []).length + 1;
|
|
221
|
+
// Skip if in comment
|
|
222
|
+
const line = file.lines[lineNum - 1] || "";
|
|
223
|
+
if (isInComment(line)) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
// Skip known false positives
|
|
227
|
+
if (isFalsePositive(match[0], pattern.id, file)) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const finding = {
|
|
231
|
+
id: generateFindingId("auth", file.relativePath, lineNum, pattern.id),
|
|
232
|
+
tacticName: "auth",
|
|
233
|
+
focusArea: "auth",
|
|
234
|
+
patternId: pattern.id,
|
|
235
|
+
file: file.relativePath,
|
|
236
|
+
line: lineNum,
|
|
237
|
+
message: `${pattern.name}: ${pattern.description}`,
|
|
238
|
+
severity: pattern.severity,
|
|
239
|
+
confidence: calculateConfidence(match[0], pattern, file),
|
|
240
|
+
evidence: redactSensitive(match[0].substring(0, 200)),
|
|
241
|
+
cweIds: [pattern.cwe],
|
|
242
|
+
mitreIds: getMitreIds(pattern.id),
|
|
243
|
+
suggestedFix: getSuggestedFix(pattern.id),
|
|
244
|
+
};
|
|
245
|
+
findings.push(finding);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return findings;
|
|
249
|
+
},
|
|
250
|
+
async generatePoC(finding) {
|
|
251
|
+
const pocMap = {
|
|
252
|
+
"jwt-none-algorithm": () => jwtNoneAlgorithmPoC(finding),
|
|
253
|
+
"jwt-weak-secret": () => jwtWeakSecretPoC(finding),
|
|
254
|
+
"jwt-hs256-public-key": () => jwtAlgorithmConfusionPoC(finding),
|
|
255
|
+
"session-no-regenerate": () => sessionFixationPoC(finding),
|
|
256
|
+
"oauth-open-redirect": () => oauthRedirectPoC(finding),
|
|
257
|
+
"oauth-no-state": () => oauthCsrfPoC(finding),
|
|
258
|
+
"hardcoded-password": () => hardcodedCredsPoC(finding),
|
|
259
|
+
"login-no-rate-limit": () => bruteForcePoC(finding),
|
|
260
|
+
};
|
|
261
|
+
const generator = pocMap[finding.patternId];
|
|
262
|
+
return generator ? generator() : null;
|
|
263
|
+
},
|
|
264
|
+
getPromptEnhancement() {
|
|
265
|
+
return `When analyzing for authentication vulnerabilities, focus on:
|
|
266
|
+
|
|
267
|
+
1. **JWT Security**:
|
|
268
|
+
- Verify algorithm is explicitly specified and not 'none'
|
|
269
|
+
- Check if secret is hardcoded or weak
|
|
270
|
+
- Look for algorithm confusion (RS256 → HS256 attacks)
|
|
271
|
+
- Verify expiration is set and validated
|
|
272
|
+
|
|
273
|
+
2. **Session Management**:
|
|
274
|
+
- Session ID regeneration after authentication
|
|
275
|
+
- Secure cookie attributes (HttpOnly, Secure, SameSite)
|
|
276
|
+
- Session timeout and invalidation
|
|
277
|
+
- Protection against session fixation
|
|
278
|
+
|
|
279
|
+
3. **OAuth/OIDC Flows**:
|
|
280
|
+
- State parameter for CSRF protection
|
|
281
|
+
- Redirect URI validation (strict matching, no open redirects)
|
|
282
|
+
- Token storage and handling
|
|
283
|
+
- Avoiding implicit flow
|
|
284
|
+
|
|
285
|
+
4. **Credential Handling**:
|
|
286
|
+
- No hardcoded credentials in source
|
|
287
|
+
- Secure password storage (bcrypt, argon2)
|
|
288
|
+
- Rate limiting on authentication endpoints
|
|
289
|
+
- Account lockout mechanisms
|
|
290
|
+
|
|
291
|
+
5. **MFA Implementation**:
|
|
292
|
+
- MFA cannot be bypassed through alternative flows
|
|
293
|
+
- Backup codes are cryptographically random
|
|
294
|
+
- Rate limiting on MFA attempts
|
|
295
|
+
|
|
296
|
+
For each finding, determine:
|
|
297
|
+
- Can an attacker bypass authentication entirely?
|
|
298
|
+
- Can session hijacking be achieved?
|
|
299
|
+
- Are there credential exposure risks?`;
|
|
300
|
+
},
|
|
301
|
+
getRelevantFilePatterns() {
|
|
302
|
+
return [
|
|
303
|
+
"**/auth/**",
|
|
304
|
+
"**/authentication/**",
|
|
305
|
+
"**/login/**",
|
|
306
|
+
"**/session/**",
|
|
307
|
+
"**/jwt/**",
|
|
308
|
+
"**/oauth/**",
|
|
309
|
+
"**/middleware/**",
|
|
310
|
+
"**/config/**",
|
|
311
|
+
"**/*auth*",
|
|
312
|
+
"**/*session*",
|
|
313
|
+
"**/*jwt*",
|
|
314
|
+
];
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
// ============================================================================
|
|
318
|
+
// Helper Functions
|
|
319
|
+
// ============================================================================
|
|
320
|
+
function isInComment(line) {
|
|
321
|
+
const trimmed = line.trim();
|
|
322
|
+
return trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("#");
|
|
323
|
+
}
|
|
324
|
+
function isFalsePositive(match, patternId, file) {
|
|
325
|
+
// Skip test files for credential patterns
|
|
326
|
+
if (patternId.startsWith("hardcoded-") || patternId === "default-credentials") {
|
|
327
|
+
if (file.relativePath.includes("test") ||
|
|
328
|
+
file.relativePath.includes("spec") ||
|
|
329
|
+
file.relativePath.includes("mock") ||
|
|
330
|
+
file.relativePath.includes("fixture")) {
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Skip example/placeholder values
|
|
335
|
+
const lowerMatch = match.toLowerCase();
|
|
336
|
+
if (lowerMatch.includes("example") ||
|
|
337
|
+
lowerMatch.includes("placeholder") ||
|
|
338
|
+
lowerMatch.includes("your-") ||
|
|
339
|
+
lowerMatch.includes("xxx")) {
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
// Skip environment variable references
|
|
343
|
+
if (match.includes("process.env") || match.includes("${")) {
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
function calculateConfidence(match, pattern, file) {
|
|
349
|
+
let confidence = 70;
|
|
350
|
+
// Higher confidence for production-looking code
|
|
351
|
+
if (!file.relativePath.includes("test") && !file.relativePath.includes("example")) {
|
|
352
|
+
confidence += 10;
|
|
353
|
+
}
|
|
354
|
+
// Higher confidence for critical patterns
|
|
355
|
+
if (pattern.severity === "critical") {
|
|
356
|
+
confidence += 10;
|
|
357
|
+
}
|
|
358
|
+
// Lower confidence for config files (might be overridden)
|
|
359
|
+
if (file.relativePath.includes("config") || file.relativePath.includes(".env")) {
|
|
360
|
+
confidence -= 10;
|
|
361
|
+
}
|
|
362
|
+
return Math.min(95, Math.max(50, confidence));
|
|
363
|
+
}
|
|
364
|
+
function redactSensitive(text) {
|
|
365
|
+
// Redact potential secrets while keeping context
|
|
366
|
+
return text
|
|
367
|
+
.replace(/(['"])[a-zA-Z0-9_\-/+=]{20,}(['"])/g, "$1[REDACTED]$2")
|
|
368
|
+
.replace(/:\/\/[^:]+:[^@]+@/g, "://[USER]:[PASS]@");
|
|
369
|
+
}
|
|
370
|
+
function getMitreIds(patternId) {
|
|
371
|
+
const mapping = {
|
|
372
|
+
"jwt-none-algorithm": ["T1078", "T1556"],
|
|
373
|
+
"jwt-weak-secret": ["T1552", "T1078"],
|
|
374
|
+
"session-no-regenerate": ["T1539", "T1550"],
|
|
375
|
+
"oauth-open-redirect": ["T1078", "T1566"],
|
|
376
|
+
"hardcoded-password": ["T1552", "T1078"],
|
|
377
|
+
"login-no-rate-limit": ["T1110"],
|
|
378
|
+
};
|
|
379
|
+
return mapping[patternId] || ["T1078"];
|
|
380
|
+
}
|
|
381
|
+
function getSuggestedFix(patternId) {
|
|
382
|
+
const fixes = {
|
|
383
|
+
"jwt-none-algorithm": "Explicitly specify allowed algorithms in verification options: algorithms: ['RS256']",
|
|
384
|
+
"jwt-weak-secret": "Use a cryptographically strong secret (256+ bits) stored in environment variables",
|
|
385
|
+
"jwt-hs256-public-key": "Use asymmetric algorithms (RS256) and validate algorithm in verification",
|
|
386
|
+
"jwt-no-expiry": "Always set token expiration: jwt.sign(payload, secret, { expiresIn: '1h' })",
|
|
387
|
+
"session-no-regenerate": "Regenerate session ID after authentication: req.session.regenerate()",
|
|
388
|
+
"session-insecure-cookie": "Set secure cookie options: { httpOnly: true, secure: true, sameSite: 'strict' }",
|
|
389
|
+
"oauth-open-redirect": "Whitelist allowed redirect URIs and validate against exact match",
|
|
390
|
+
"oauth-no-state": "Generate cryptographically random state parameter and validate on callback",
|
|
391
|
+
"hardcoded-password": "Move credentials to environment variables or secrets manager",
|
|
392
|
+
"login-no-rate-limit": "Implement rate limiting: express-rate-limit or similar middleware",
|
|
393
|
+
"mfa-bypass": "Ensure MFA check cannot be skipped through any code path",
|
|
394
|
+
};
|
|
395
|
+
return fixes[patternId] || "Review authentication implementation for security best practices";
|
|
396
|
+
}
|
|
397
|
+
// ============================================================================
|
|
398
|
+
// PoC Generators
|
|
399
|
+
// ============================================================================
|
|
400
|
+
function jwtNoneAlgorithmPoC(finding) {
|
|
401
|
+
const steps = [
|
|
402
|
+
{
|
|
403
|
+
order: 1,
|
|
404
|
+
action: "capture-token",
|
|
405
|
+
description: "Capture a valid JWT from the application",
|
|
406
|
+
command: "Authenticate and capture the JWT from response/cookies",
|
|
407
|
+
expectedResult: "Valid JWT token captured",
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
order: 2,
|
|
411
|
+
action: "decode-token",
|
|
412
|
+
description: "Decode the JWT to examine its structure",
|
|
413
|
+
command: "echo '<token>' | cut -d'.' -f1,2 | base64 -d",
|
|
414
|
+
expectedResult: "JWT header and payload visible",
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
order: 3,
|
|
418
|
+
action: "modify-header",
|
|
419
|
+
description: "Change algorithm to 'none' and modify claims",
|
|
420
|
+
command: "Modify header to {\"alg\":\"none\",\"typ\":\"JWT\"}, change user to admin",
|
|
421
|
+
expectedResult: "Modified JWT ready",
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
order: 4,
|
|
425
|
+
action: "use-token",
|
|
426
|
+
description: "Use the modified token without signature",
|
|
427
|
+
command: "curl -H 'Authorization: Bearer <modified_token>.' https://target/api/admin",
|
|
428
|
+
expectedResult: "Access granted with modified claims",
|
|
429
|
+
},
|
|
430
|
+
];
|
|
431
|
+
return {
|
|
432
|
+
id: `poc-${finding.id}`,
|
|
433
|
+
findingId: finding.id,
|
|
434
|
+
prerequisites: ["Valid user account", "Ability to capture JWT"],
|
|
435
|
+
steps,
|
|
436
|
+
payload: '{"alg":"none","typ":"JWT"}',
|
|
437
|
+
expectedResult: "Authentication bypass via algorithm manipulation",
|
|
438
|
+
safeTestInstructions: "Test on development environment. Verify the application properly rejects 'none' algorithm after fix.",
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
function jwtWeakSecretPoC(finding) {
|
|
442
|
+
const steps = [
|
|
443
|
+
{
|
|
444
|
+
order: 1,
|
|
445
|
+
action: "capture-token",
|
|
446
|
+
description: "Capture a valid JWT",
|
|
447
|
+
command: "Authenticate and capture JWT",
|
|
448
|
+
expectedResult: "Valid JWT captured",
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
order: 2,
|
|
452
|
+
action: "crack-secret",
|
|
453
|
+
description: "Attempt to crack the JWT secret",
|
|
454
|
+
command: "hashcat -a 0 -m 16500 jwt.txt wordlist.txt",
|
|
455
|
+
expectedResult: "Secret key revealed if weak",
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
order: 3,
|
|
459
|
+
action: "forge-token",
|
|
460
|
+
description: "Forge a new token with cracked secret",
|
|
461
|
+
command: "jwt.sign({ sub: 'admin', role: 'admin' }, 'cracked_secret')",
|
|
462
|
+
expectedResult: "Forged admin token",
|
|
463
|
+
},
|
|
464
|
+
];
|
|
465
|
+
return {
|
|
466
|
+
id: `poc-${finding.id}`,
|
|
467
|
+
findingId: finding.id,
|
|
468
|
+
prerequisites: ["Valid JWT to crack", "Hashcat or similar tool"],
|
|
469
|
+
steps,
|
|
470
|
+
payload: "jwt.sign(adminPayload, crackedSecret)",
|
|
471
|
+
expectedResult: "Privilege escalation via forged token",
|
|
472
|
+
safeTestInstructions: "Use offline cracking only. Test forged tokens on isolated environment.",
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function jwtAlgorithmConfusionPoC(finding) {
|
|
476
|
+
const steps = [
|
|
477
|
+
{
|
|
478
|
+
order: 1,
|
|
479
|
+
action: "obtain-public-key",
|
|
480
|
+
description: "Obtain the server's public key",
|
|
481
|
+
command: "curl https://target/.well-known/jwks.json",
|
|
482
|
+
expectedResult: "Public key obtained",
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
order: 2,
|
|
486
|
+
action: "forge-token",
|
|
487
|
+
description: "Sign token with public key using HS256",
|
|
488
|
+
command: "jwt.sign(payload, publicKey, { algorithm: 'HS256' })",
|
|
489
|
+
expectedResult: "Token signed with public key as HMAC secret",
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
order: 3,
|
|
493
|
+
action: "use-token",
|
|
494
|
+
description: "Use forged token",
|
|
495
|
+
command: "curl -H 'Authorization: Bearer <forged>' https://target/api",
|
|
496
|
+
expectedResult: "Server validates with public key as HMAC secret",
|
|
497
|
+
},
|
|
498
|
+
];
|
|
499
|
+
return {
|
|
500
|
+
id: `poc-${finding.id}`,
|
|
501
|
+
findingId: finding.id,
|
|
502
|
+
prerequisites: ["Access to public key", "Knowledge of expected claims"],
|
|
503
|
+
steps,
|
|
504
|
+
payload: "jwt.sign(claims, publicKey, { algorithm: 'HS256' })",
|
|
505
|
+
expectedResult: "Authentication bypass via algorithm confusion",
|
|
506
|
+
safeTestInstructions: "Test on isolated environment. Verify fix explicitly validates algorithm.",
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function sessionFixationPoC(finding) {
|
|
510
|
+
const steps = [
|
|
511
|
+
{
|
|
512
|
+
order: 1,
|
|
513
|
+
action: "obtain-session",
|
|
514
|
+
description: "Obtain a session ID from the application",
|
|
515
|
+
command: "curl -c cookies.txt https://target/",
|
|
516
|
+
expectedResult: "Session ID captured",
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
order: 2,
|
|
520
|
+
action: "set-victim-session",
|
|
521
|
+
description: "Set victim's session to attacker's session ID",
|
|
522
|
+
command: "Send victim link with session parameter or set cookie via XSS",
|
|
523
|
+
expectedResult: "Victim uses attacker's session",
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
order: 3,
|
|
527
|
+
action: "wait-for-auth",
|
|
528
|
+
description: "Wait for victim to authenticate",
|
|
529
|
+
expectedResult: "Victim logs in with attacker's session ID",
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
order: 4,
|
|
533
|
+
action: "hijack-session",
|
|
534
|
+
description: "Use the now-authenticated session",
|
|
535
|
+
command: "curl -b 'sessionId=<attacker_session>' https://target/dashboard",
|
|
536
|
+
expectedResult: "Access victim's authenticated session",
|
|
537
|
+
},
|
|
538
|
+
];
|
|
539
|
+
return {
|
|
540
|
+
id: `poc-${finding.id}`,
|
|
541
|
+
findingId: finding.id,
|
|
542
|
+
prerequisites: ["Ability to set victim's session cookie", "Victim interaction"],
|
|
543
|
+
steps,
|
|
544
|
+
payload: "sessionId=attacker_controlled_session",
|
|
545
|
+
expectedResult: "Session hijacking after victim authentication",
|
|
546
|
+
safeTestInstructions: "Test with two test accounts. Never involve real user sessions.",
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function oauthRedirectPoC(finding) {
|
|
550
|
+
const steps = [
|
|
551
|
+
{
|
|
552
|
+
order: 1,
|
|
553
|
+
action: "identify-redirect",
|
|
554
|
+
description: "Identify the redirect URI parameter",
|
|
555
|
+
command: "Examine OAuth authorization URL",
|
|
556
|
+
expectedResult: "redirect_uri parameter identified",
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
order: 2,
|
|
560
|
+
action: "craft-redirect",
|
|
561
|
+
description: "Craft malicious redirect URI",
|
|
562
|
+
command: "Modify redirect_uri to attacker-controlled domain",
|
|
563
|
+
expectedResult: "Malicious authorization URL ready",
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
order: 3,
|
|
567
|
+
action: "phish-victim",
|
|
568
|
+
description: "Send malicious link to victim",
|
|
569
|
+
command: "https://target/oauth/authorize?redirect_uri=https://evil.com/callback",
|
|
570
|
+
expectedResult: "Victim redirected to attacker site with auth code",
|
|
571
|
+
},
|
|
572
|
+
];
|
|
573
|
+
return {
|
|
574
|
+
id: `poc-${finding.id}`,
|
|
575
|
+
findingId: finding.id,
|
|
576
|
+
prerequisites: ["Understanding of OAuth flow", "Attacker-controlled domain"],
|
|
577
|
+
steps,
|
|
578
|
+
payload: "redirect_uri=https://attacker.com/steal",
|
|
579
|
+
expectedResult: "OAuth token/code stolen via redirect",
|
|
580
|
+
safeTestInstructions: "Use controlled test accounts. Never redirect real user tokens.",
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function oauthCsrfPoC(finding) {
|
|
584
|
+
const steps = [
|
|
585
|
+
{
|
|
586
|
+
order: 1,
|
|
587
|
+
action: "verify-missing-state",
|
|
588
|
+
description: "Verify state parameter is not validated",
|
|
589
|
+
command: "Complete OAuth flow without state parameter",
|
|
590
|
+
expectedResult: "OAuth succeeds without state validation",
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
order: 2,
|
|
594
|
+
action: "link-attacker-account",
|
|
595
|
+
description: "Force victim to link attacker's OAuth account",
|
|
596
|
+
command: "<img src='https://target/oauth/callback?code=attacker_code'>",
|
|
597
|
+
expectedResult: "Victim's account linked to attacker's OAuth",
|
|
598
|
+
},
|
|
599
|
+
];
|
|
600
|
+
return {
|
|
601
|
+
id: `poc-${finding.id}`,
|
|
602
|
+
findingId: finding.id,
|
|
603
|
+
prerequisites: ["OAuth integration", "Ability to serve content to victim"],
|
|
604
|
+
steps,
|
|
605
|
+
payload: "<img src='https://target/oauth/callback?code=ATTACKER_CODE'>",
|
|
606
|
+
expectedResult: "Account hijacking via OAuth CSRF",
|
|
607
|
+
safeTestInstructions: "Test with controlled accounts only.",
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function hardcodedCredsPoC(finding) {
|
|
611
|
+
const steps = [
|
|
612
|
+
{
|
|
613
|
+
order: 1,
|
|
614
|
+
action: "extract-creds",
|
|
615
|
+
description: "Extract credentials from source code",
|
|
616
|
+
command: `grep -n "password" ${finding.file}`,
|
|
617
|
+
expectedResult: "Credentials visible in source",
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
order: 2,
|
|
621
|
+
action: "use-creds",
|
|
622
|
+
description: "Attempt to authenticate with extracted credentials",
|
|
623
|
+
command: "Use credentials at application login or service endpoint",
|
|
624
|
+
expectedResult: "Authentication successful",
|
|
625
|
+
},
|
|
626
|
+
];
|
|
627
|
+
return {
|
|
628
|
+
id: `poc-${finding.id}`,
|
|
629
|
+
findingId: finding.id,
|
|
630
|
+
prerequisites: ["Access to source code"],
|
|
631
|
+
steps,
|
|
632
|
+
payload: "[EXTRACTED_CREDENTIALS]",
|
|
633
|
+
expectedResult: "Unauthorized access using hardcoded credentials",
|
|
634
|
+
safeTestInstructions: "Report finding immediately. Do not use credentials on production systems.",
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function bruteForcePoC(finding) {
|
|
638
|
+
const steps = [
|
|
639
|
+
{
|
|
640
|
+
order: 1,
|
|
641
|
+
action: "identify-endpoint",
|
|
642
|
+
description: "Identify the authentication endpoint",
|
|
643
|
+
command: "Examine login form or API endpoint",
|
|
644
|
+
expectedResult: "Endpoint identified",
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
order: 2,
|
|
648
|
+
action: "test-rate-limit",
|
|
649
|
+
description: "Test for rate limiting",
|
|
650
|
+
command: "for i in {1..100}; do curl -X POST https://target/login -d 'user=test&pass=wrong$i'; done",
|
|
651
|
+
expectedResult: "Requests not blocked after multiple failures",
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
order: 3,
|
|
655
|
+
action: "brute-force",
|
|
656
|
+
description: "Perform credential stuffing or brute force",
|
|
657
|
+
command: "hydra -l admin -P wordlist.txt https://target/login",
|
|
658
|
+
expectedResult: "Valid credentials discovered",
|
|
659
|
+
},
|
|
660
|
+
];
|
|
661
|
+
return {
|
|
662
|
+
id: `poc-${finding.id}`,
|
|
663
|
+
findingId: finding.id,
|
|
664
|
+
prerequisites: ["Username enumeration or known usernames", "Password wordlist"],
|
|
665
|
+
steps,
|
|
666
|
+
payload: "hydra -l admin -P passwords.txt target http-post-form",
|
|
667
|
+
expectedResult: "Credentials compromised via brute force",
|
|
668
|
+
safeTestInstructions: "Test on isolated environment with test accounts. Never brute force production systems.",
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
// ============================================================================
|
|
672
|
+
// Register Tactic
|
|
673
|
+
// ============================================================================
|
|
674
|
+
registerTactic(authTactic);
|
|
675
|
+
export { authTactic };
|
|
676
|
+
//# sourceMappingURL=auth.js.map
|