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.
Files changed (321) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +111 -7
  3. package/dist/__tests__/agents/adversary/tactics/api.test.d.ts +5 -0
  4. package/dist/__tests__/agents/adversary/tactics/api.test.d.ts.map +1 -0
  5. package/dist/__tests__/agents/adversary/tactics/api.test.js +369 -0
  6. package/dist/__tests__/agents/adversary/tactics/api.test.js.map +1 -0
  7. package/dist/__tests__/agents/adversary/tactics/llm.test.d.ts +5 -0
  8. package/dist/__tests__/agents/adversary/tactics/llm.test.d.ts.map +1 -0
  9. package/dist/__tests__/agents/adversary/tactics/llm.test.js +409 -0
  10. package/dist/__tests__/agents/adversary/tactics/llm.test.js.map +1 -0
  11. package/dist/__tests__/agents/adversary/tactics/registry.test.d.ts +7 -0
  12. package/dist/__tests__/agents/adversary/tactics/registry.test.d.ts.map +1 -0
  13. package/dist/__tests__/agents/adversary/tactics/registry.test.js +74 -0
  14. package/dist/__tests__/agents/adversary/tactics/registry.test.js.map +1 -0
  15. package/dist/__tests__/agents/adversary/tactics/web-app.test.d.ts +7 -0
  16. package/dist/__tests__/agents/adversary/tactics/web-app.test.d.ts.map +1 -0
  17. package/dist/__tests__/agents/adversary/tactics/web-app.test.js +374 -0
  18. package/dist/__tests__/agents/adversary/tactics/web-app.test.js.map +1 -0
  19. package/dist/__tests__/compliance-bundle.test.d.ts +9 -0
  20. package/dist/__tests__/compliance-bundle.test.d.ts.map +1 -0
  21. package/dist/__tests__/compliance-bundle.test.js +344 -0
  22. package/dist/__tests__/compliance-bundle.test.js.map +1 -0
  23. package/dist/__tests__/healthcare-compliance.test.d.ts +9 -0
  24. package/dist/__tests__/healthcare-compliance.test.d.ts.map +1 -0
  25. package/dist/__tests__/healthcare-compliance.test.js +233 -0
  26. package/dist/__tests__/healthcare-compliance.test.js.map +1 -0
  27. package/dist/action/diff-mode.d.ts +124 -8
  28. package/dist/action/diff-mode.d.ts.map +1 -1
  29. package/dist/action/diff-mode.js +384 -65
  30. package/dist/action/diff-mode.js.map +1 -1
  31. package/dist/action/diff-mode.test.js +3 -3
  32. package/dist/action/diff-mode.test.js.map +1 -1
  33. package/dist/action/pr-comment.test.js +1 -0
  34. package/dist/action/pr-comment.test.js.map +1 -1
  35. package/dist/action/sarif-upload.test.js +1 -0
  36. package/dist/action/sarif-upload.test.js.map +1 -1
  37. package/dist/agents/adversary/config.d.ts +113 -0
  38. package/dist/agents/adversary/config.d.ts.map +1 -0
  39. package/dist/agents/adversary/config.js +391 -0
  40. package/dist/agents/adversary/config.js.map +1 -0
  41. package/dist/agents/adversary/index.d.ts +41 -0
  42. package/dist/agents/adversary/index.d.ts.map +1 -0
  43. package/dist/agents/adversary/index.js +838 -0
  44. package/dist/agents/adversary/index.js.map +1 -0
  45. package/dist/agents/adversary/reporting/compliance-mapper.d.ts +108 -0
  46. package/dist/agents/adversary/reporting/compliance-mapper.d.ts.map +1 -0
  47. package/dist/agents/adversary/reporting/compliance-mapper.js +391 -0
  48. package/dist/agents/adversary/reporting/compliance-mapper.js.map +1 -0
  49. package/dist/agents/adversary/reporting/index.d.ts +10 -0
  50. package/dist/agents/adversary/reporting/index.d.ts.map +1 -0
  51. package/dist/agents/adversary/reporting/index.js +10 -0
  52. package/dist/agents/adversary/reporting/index.js.map +1 -0
  53. package/dist/agents/adversary/reporting/poc-generator.d.ts +44 -0
  54. package/dist/agents/adversary/reporting/poc-generator.d.ts.map +1 -0
  55. package/dist/agents/adversary/reporting/poc-generator.js +308 -0
  56. package/dist/agents/adversary/reporting/poc-generator.js.map +1 -0
  57. package/dist/agents/adversary/tactics/api.d.ts +13 -0
  58. package/dist/agents/adversary/tactics/api.d.ts.map +1 -0
  59. package/dist/agents/adversary/tactics/api.js +815 -0
  60. package/dist/agents/adversary/tactics/api.js.map +1 -0
  61. package/dist/agents/adversary/tactics/auth.d.ts +13 -0
  62. package/dist/agents/adversary/tactics/auth.d.ts.map +1 -0
  63. package/dist/agents/adversary/tactics/auth.js +676 -0
  64. package/dist/agents/adversary/tactics/auth.js.map +1 -0
  65. package/dist/agents/adversary/tactics/index.d.ts +129 -0
  66. package/dist/agents/adversary/tactics/index.d.ts.map +1 -0
  67. package/dist/agents/adversary/tactics/index.js +199 -0
  68. package/dist/agents/adversary/tactics/index.js.map +1 -0
  69. package/dist/agents/adversary/tactics/infra.d.ts +13 -0
  70. package/dist/agents/adversary/tactics/infra.d.ts.map +1 -0
  71. package/dist/agents/adversary/tactics/infra.js +827 -0
  72. package/dist/agents/adversary/tactics/infra.js.map +1 -0
  73. package/dist/agents/adversary/tactics/injection.d.ts +12 -0
  74. package/dist/agents/adversary/tactics/injection.d.ts.map +1 -0
  75. package/dist/agents/adversary/tactics/injection.js +549 -0
  76. package/dist/agents/adversary/tactics/injection.js.map +1 -0
  77. package/dist/agents/adversary/tactics/llm.d.ts +13 -0
  78. package/dist/agents/adversary/tactics/llm.d.ts.map +1 -0
  79. package/dist/agents/adversary/tactics/llm.js +767 -0
  80. package/dist/agents/adversary/tactics/llm.js.map +1 -0
  81. package/dist/agents/adversary/tactics/web-app.d.ts +13 -0
  82. package/dist/agents/adversary/tactics/web-app.d.ts.map +1 -0
  83. package/dist/agents/adversary/tactics/web-app.js +717 -0
  84. package/dist/agents/adversary/tactics/web-app.js.map +1 -0
  85. package/dist/agents/adversary/types.d.ts +407 -0
  86. package/dist/agents/adversary/types.d.ts.map +1 -0
  87. package/dist/agents/adversary/types.js +12 -0
  88. package/dist/agents/adversary/types.js.map +1 -0
  89. package/dist/agents/index.d.ts +1 -0
  90. package/dist/agents/index.d.ts.map +1 -1
  91. package/dist/agents/index.js +2 -0
  92. package/dist/agents/index.js.map +1 -1
  93. package/dist/agents/zero-day-hunter.d.ts +1 -1
  94. package/dist/agents/zero-day-hunter.d.ts.map +1 -1
  95. package/dist/analysis/data-flow.d.ts +154 -0
  96. package/dist/analysis/data-flow.d.ts.map +1 -0
  97. package/dist/analysis/data-flow.js +393 -0
  98. package/dist/analysis/data-flow.js.map +1 -0
  99. package/dist/analysis/index.d.ts +9 -0
  100. package/dist/analysis/index.d.ts.map +1 -0
  101. package/dist/analysis/index.js +9 -0
  102. package/dist/analysis/index.js.map +1 -0
  103. package/dist/badge-service/index.d.ts +144 -0
  104. package/dist/badge-service/index.d.ts.map +1 -0
  105. package/dist/badge-service/index.js +206 -0
  106. package/dist/badge-service/index.js.map +1 -0
  107. package/dist/certification/consensus.test.js +2 -0
  108. package/dist/certification/consensus.test.js.map +1 -1
  109. package/dist/certification/store.d.ts.map +1 -1
  110. package/dist/certification/store.js +4 -0
  111. package/dist/certification/store.js.map +1 -1
  112. package/dist/certification/types.d.ts +3 -3
  113. package/dist/certification/types.d.ts.map +1 -1
  114. package/dist/certification/types.js +2 -0
  115. package/dist/certification/types.js.map +1 -1
  116. package/dist/commands/certification/certify.d.ts.map +1 -1
  117. package/dist/commands/certification/certify.js +18 -4
  118. package/dist/commands/certification/certify.js.map +1 -1
  119. package/dist/compliance/attestation.d.ts +39 -0
  120. package/dist/compliance/attestation.d.ts.map +1 -0
  121. package/dist/compliance/attestation.js +364 -0
  122. package/dist/compliance/attestation.js.map +1 -0
  123. package/dist/compliance/cfr42-part2.d.ts +42 -0
  124. package/dist/compliance/cfr42-part2.d.ts.map +1 -0
  125. package/dist/compliance/cfr42-part2.js +408 -0
  126. package/dist/compliance/cfr42-part2.js.map +1 -0
  127. package/dist/compliance/compliance-bundle.d.ts +100 -0
  128. package/dist/compliance/compliance-bundle.d.ts.map +1 -0
  129. package/dist/compliance/compliance-bundle.js +210 -0
  130. package/dist/compliance/compliance-bundle.js.map +1 -0
  131. package/dist/compliance/healthcare-bundle.d.ts +68 -0
  132. package/dist/compliance/healthcare-bundle.d.ts.map +1 -0
  133. package/dist/compliance/healthcare-bundle.js +104 -0
  134. package/dist/compliance/healthcare-bundle.js.map +1 -0
  135. package/dist/compliance/hipaa.d.ts.map +1 -1
  136. package/dist/compliance/hipaa.js +14 -11
  137. package/dist/compliance/hipaa.js.map +1 -1
  138. package/dist/compliance/index.d.ts +10 -2
  139. package/dist/compliance/index.d.ts.map +1 -1
  140. package/dist/compliance/index.js +9 -3
  141. package/dist/compliance/index.js.map +1 -1
  142. package/dist/compliance/mapper.d.ts.map +1 -1
  143. package/dist/compliance/mapper.js +3 -17
  144. package/dist/compliance/mapper.js.map +1 -1
  145. package/dist/compliance/nist-800-53.d.ts +22 -6
  146. package/dist/compliance/nist-800-53.d.ts.map +1 -1
  147. package/dist/compliance/nist-800-53.js +264 -272
  148. package/dist/compliance/nist-800-53.js.map +1 -1
  149. package/dist/compliance/report.d.ts +31 -2
  150. package/dist/compliance/report.d.ts.map +1 -1
  151. package/dist/compliance/report.js +255 -4
  152. package/dist/compliance/report.js.map +1 -1
  153. package/dist/compliance/types.d.ts +1 -1
  154. package/dist/compliance/types.d.ts.map +1 -1
  155. package/dist/config/flags.d.ts +12 -12
  156. package/dist/cost/index.d.ts +1 -1
  157. package/dist/cost/index.d.ts.map +1 -1
  158. package/dist/cost/index.js +1 -1
  159. package/dist/cost/index.js.map +1 -1
  160. package/dist/cost/tracker.d.ts +64 -0
  161. package/dist/cost/tracker.d.ts.map +1 -1
  162. package/dist/cost/tracker.js +165 -0
  163. package/dist/cost/tracker.js.map +1 -1
  164. package/dist/eval/fixtures/healthcare/audit-gaps.d.ts +28 -0
  165. package/dist/eval/fixtures/healthcare/audit-gaps.d.ts.map +1 -0
  166. package/dist/eval/fixtures/healthcare/audit-gaps.js +90 -0
  167. package/dist/eval/fixtures/healthcare/audit-gaps.js.map +1 -0
  168. package/dist/eval/fixtures/healthcare/consent-bypass.d.ts +31 -0
  169. package/dist/eval/fixtures/healthcare/consent-bypass.d.ts.map +1 -0
  170. package/dist/eval/fixtures/healthcare/consent-bypass.js +61 -0
  171. package/dist/eval/fixtures/healthcare/consent-bypass.js.map +1 -0
  172. package/dist/eval/fixtures/healthcare/phi-in-logs.d.ts +24 -0
  173. package/dist/eval/fixtures/healthcare/phi-in-logs.d.ts.map +1 -0
  174. package/dist/eval/fixtures/healthcare/phi-in-logs.js +41 -0
  175. package/dist/eval/fixtures/healthcare/phi-in-logs.js.map +1 -0
  176. package/dist/evidence/collector.d.ts +21 -0
  177. package/dist/evidence/collector.d.ts.map +1 -0
  178. package/dist/evidence/collector.js +340 -0
  179. package/dist/evidence/collector.js.map +1 -0
  180. package/dist/evidence/index.d.ts +11 -0
  181. package/dist/evidence/index.d.ts.map +1 -0
  182. package/dist/evidence/index.js +12 -0
  183. package/dist/evidence/index.js.map +1 -0
  184. package/dist/evidence/store.d.ts +39 -0
  185. package/dist/evidence/store.d.ts.map +1 -0
  186. package/dist/evidence/store.js +173 -0
  187. package/dist/evidence/store.js.map +1 -0
  188. package/dist/evidence/types.d.ts +175 -0
  189. package/dist/evidence/types.d.ts.map +1 -0
  190. package/dist/evidence/types.js +9 -0
  191. package/dist/evidence/types.js.map +1 -0
  192. package/dist/exporters/checkmarx.d.ts +18 -0
  193. package/dist/exporters/checkmarx.d.ts.map +1 -0
  194. package/dist/exporters/checkmarx.js +203 -0
  195. package/dist/exporters/checkmarx.js.map +1 -0
  196. package/dist/exporters/index.d.ts +22 -0
  197. package/dist/exporters/index.d.ts.map +1 -0
  198. package/dist/exporters/index.js +41 -0
  199. package/dist/exporters/index.js.map +1 -0
  200. package/dist/exporters/snyk.d.ts +18 -0
  201. package/dist/exporters/snyk.d.ts.map +1 -0
  202. package/dist/exporters/snyk.js +119 -0
  203. package/dist/exporters/snyk.js.map +1 -0
  204. package/dist/exporters/sonarqube.d.ts +18 -0
  205. package/dist/exporters/sonarqube.d.ts.map +1 -0
  206. package/dist/exporters/sonarqube.js +125 -0
  207. package/dist/exporters/sonarqube.js.map +1 -0
  208. package/dist/exporters/types.d.ts +190 -0
  209. package/dist/exporters/types.d.ts.map +1 -0
  210. package/dist/exporters/types.js +9 -0
  211. package/dist/exporters/types.js.map +1 -0
  212. package/dist/frontier/index.d.ts +12 -0
  213. package/dist/frontier/index.d.ts.map +1 -0
  214. package/dist/frontier/index.js +12 -0
  215. package/dist/frontier/index.js.map +1 -0
  216. package/dist/frontier/orchestrator.d.ts +73 -0
  217. package/dist/frontier/orchestrator.d.ts.map +1 -0
  218. package/dist/frontier/orchestrator.js +312 -0
  219. package/dist/frontier/orchestrator.js.map +1 -0
  220. package/dist/frontier/providers/stub.d.ts +32 -0
  221. package/dist/frontier/providers/stub.d.ts.map +1 -0
  222. package/dist/frontier/providers/stub.js +66 -0
  223. package/dist/frontier/providers/stub.js.map +1 -0
  224. package/dist/frontier/types.d.ts +318 -0
  225. package/dist/frontier/types.d.ts.map +1 -0
  226. package/dist/frontier/types.js +27 -0
  227. package/dist/frontier/types.js.map +1 -0
  228. package/dist/history/index.d.ts +13 -0
  229. package/dist/history/index.d.ts.map +1 -0
  230. package/dist/history/index.js +15 -0
  231. package/dist/history/index.js.map +1 -0
  232. package/dist/history/store.d.ts +74 -0
  233. package/dist/history/store.d.ts.map +1 -0
  234. package/dist/history/store.js +399 -0
  235. package/dist/history/store.js.map +1 -0
  236. package/dist/history/types.d.ts +282 -0
  237. package/dist/history/types.d.ts.map +1 -0
  238. package/dist/history/types.js +41 -0
  239. package/dist/history/types.js.map +1 -0
  240. package/dist/history/verify.d.ts +44 -0
  241. package/dist/history/verify.d.ts.map +1 -0
  242. package/dist/history/verify.js +230 -0
  243. package/dist/history/verify.js.map +1 -0
  244. package/dist/index.d.ts.map +1 -1
  245. package/dist/index.js +431 -18
  246. package/dist/index.js.map +1 -1
  247. package/dist/multimodel/index.d.ts +1 -0
  248. package/dist/multimodel/index.d.ts.map +1 -1
  249. package/dist/multimodel/index.js +2 -0
  250. package/dist/multimodel/index.js.map +1 -1
  251. package/dist/multimodel/leaderboard.d.ts +116 -0
  252. package/dist/multimodel/leaderboard.d.ts.map +1 -0
  253. package/dist/multimodel/leaderboard.js +262 -0
  254. package/dist/multimodel/leaderboard.js.map +1 -0
  255. package/dist/observability/otel.d.ts.map +1 -1
  256. package/dist/observability/otel.js +1 -3
  257. package/dist/observability/otel.js.map +1 -1
  258. package/dist/plugins/loader.js +1 -1
  259. package/dist/plugins/loader.js.map +1 -1
  260. package/dist/sbom/provenance.test.js +2 -2
  261. package/dist/sbom/provenance.test.js.map +1 -1
  262. package/dist/scanners/agent/agent-chain-analysis.d.ts +152 -0
  263. package/dist/scanners/agent/agent-chain-analysis.d.ts.map +1 -0
  264. package/dist/scanners/agent/agent-chain-analysis.js +438 -0
  265. package/dist/scanners/agent/agent-chain-analysis.js.map +1 -0
  266. package/dist/scanners/agent/manifest-audit.d.ts.map +1 -1
  267. package/dist/scanners/agent/manifest-audit.js +30 -18
  268. package/dist/scanners/agent/manifest-audit.js.map +1 -1
  269. package/dist/scanners/agent/payloads/index.d.ts +2 -1
  270. package/dist/scanners/agent/payloads/index.d.ts.map +1 -1
  271. package/dist/scanners/agent/payloads/index.js +25 -6
  272. package/dist/scanners/agent/payloads/index.js.map +1 -1
  273. package/dist/scanners/agent/prompt-injection-fuzzer.d.ts.map +1 -1
  274. package/dist/scanners/agent/prompt-injection-fuzzer.js +14 -0
  275. package/dist/scanners/agent/prompt-injection-fuzzer.js.map +1 -1
  276. package/dist/scanners/agent/types.d.ts +5 -5
  277. package/dist/scanners/agent/types.d.ts.map +1 -1
  278. package/dist/scanners/agent/types.js.map +1 -1
  279. package/dist/scanners/cache.d.ts +156 -0
  280. package/dist/scanners/cache.d.ts.map +1 -0
  281. package/dist/scanners/cache.js +462 -0
  282. package/dist/scanners/cache.js.map +1 -0
  283. package/dist/scanners/dependencies.d.ts.map +1 -1
  284. package/dist/scanners/dependencies.js +5 -6
  285. package/dist/scanners/dependencies.js.map +1 -1
  286. package/dist/scanners/gosec.d.ts.map +1 -1
  287. package/dist/scanners/gosec.js +47 -9
  288. package/dist/scanners/gosec.js.map +1 -1
  289. package/dist/scanners/healthcare.d.ts +29 -0
  290. package/dist/scanners/healthcare.d.ts.map +1 -0
  291. package/dist/scanners/healthcare.js +526 -0
  292. package/dist/scanners/healthcare.js.map +1 -0
  293. package/dist/scanners/index.d.ts +1 -0
  294. package/dist/scanners/index.d.ts.map +1 -1
  295. package/dist/scanners/index.js +33 -0
  296. package/dist/scanners/index.js.map +1 -1
  297. package/dist/scanners/index.test.js +6 -6
  298. package/dist/scanners/index.test.js.map +1 -1
  299. package/dist/scanners/secrets.js +4 -4
  300. package/dist/scanners/secrets.js.map +1 -1
  301. package/dist/scanners/semgrep.js +5 -5
  302. package/dist/scanners/semgrep.js.map +1 -1
  303. package/dist/scanners/types.d.ts +1 -1
  304. package/dist/scanners/types.d.ts.map +1 -1
  305. package/dist/scanners/types.js +1 -0
  306. package/dist/scanners/types.js.map +1 -1
  307. package/dist/scanners/typescript.test.js +1 -1
  308. package/dist/scanners/typescript.test.js.map +1 -1
  309. package/dist/telemetry/index.d.ts +10 -0
  310. package/dist/telemetry/index.d.ts.map +1 -0
  311. package/dist/telemetry/index.js +10 -0
  312. package/dist/telemetry/index.js.map +1 -0
  313. package/dist/telemetry/registry.d.ts +178 -0
  314. package/dist/telemetry/registry.d.ts.map +1 -0
  315. package/dist/telemetry/registry.js +297 -0
  316. package/dist/telemetry/registry.js.map +1 -0
  317. package/dist/telemetry/usage.d.ts +197 -0
  318. package/dist/telemetry/usage.d.ts.map +1 -0
  319. package/dist/telemetry/usage.js +244 -0
  320. package/dist/telemetry/usage.js.map +1 -0
  321. package/package.json +11 -2
@@ -0,0 +1,838 @@
1
+ /**
2
+ * Adversary Agent - Main Orchestrator
3
+ *
4
+ * The Adversary agent is a mythos-class ethical hacker that uses real
5
+ * Claude API reasoning to find vulnerabilities that pattern-based scanners
6
+ * miss. It coordinates four analysis phases:
7
+ *
8
+ * 1. Reconnaissance - Technology stack detection, framework identification
9
+ * 2. Attack Surface - Entry points, trust boundaries, data flows
10
+ * 3. Exploitation - LLM-powered vulnerability discovery with PoCs
11
+ * 4. Chaining - Multi-vulnerability attack path discovery
12
+ *
13
+ * @module agents/adversary
14
+ */
15
+ import { randomUUID } from "crypto";
16
+ import { glob } from "glob";
17
+ import { readFile, stat } from "fs/promises";
18
+ import * as path from "path";
19
+ import Anthropic from "@anthropic-ai/sdk";
20
+ import { DEFAULT_INCLUDE_PATTERNS, DEFAULT_EXCLUDE_PATTERNS, SECURITY_RELEVANT_PATTERNS, MODEL_PRICING, } from "./config.js";
21
+ // Import tactics modules
22
+ import { getTacticsForFocusAreas, runTacticAnalysis, buildCombinedPromptEnhancement, createFileContext, } from "./tactics/index.js";
23
+ // Import tactics (auto-registers on import)
24
+ import "./tactics/injection.js";
25
+ import "./tactics/auth.js";
26
+ import "./tactics/llm.js";
27
+ import "./tactics/api.js";
28
+ import "./tactics/web-app.js";
29
+ import "./tactics/infra.js";
30
+ // Import reporting modules
31
+ import { generatePoC } from "./reporting/poc-generator.js";
32
+ import { mapFindingToCompliance } from "./reporting/compliance-mapper.js";
33
+ // Re-export types and config
34
+ export * from "./types.js";
35
+ export * from "./config.js";
36
+ // Re-export reporting
37
+ export * from "./reporting/index.js";
38
+ // ============================================================================
39
+ // Claude API Integration
40
+ // ============================================================================
41
+ let anthropicClient = null;
42
+ /**
43
+ * Initialize Anthropic client
44
+ */
45
+ function getAnthropicClient() {
46
+ if (!anthropicClient) {
47
+ const apiKey = process.env.ANTHROPIC_API_KEY;
48
+ if (!apiKey) {
49
+ throw new Error("ANTHROPIC_API_KEY environment variable is required for adversary analysis. " +
50
+ "Set it to your Claude API key to enable LLM-powered vulnerability discovery.");
51
+ }
52
+ anthropicClient = new Anthropic({ apiKey });
53
+ }
54
+ return anthropicClient;
55
+ }
56
+ /**
57
+ * Send prompt to Claude and get response
58
+ */
59
+ async function analyzeWithClaude(prompt, model) {
60
+ const client = getAnthropicClient();
61
+ const response = await client.messages.create({
62
+ model: model,
63
+ max_tokens: 8192,
64
+ system: prompt.systemPrompt,
65
+ messages: [
66
+ {
67
+ role: "user",
68
+ content: `${prompt.instructions}\n\n## Code Context\n\`\`\`\n${prompt.codeContext}\n\`\`\`\n\n${prompt.outputFormat}`,
69
+ },
70
+ ],
71
+ });
72
+ const content = response.content[0];
73
+ if (content.type !== "text") {
74
+ throw new Error("Unexpected response type from Claude");
75
+ }
76
+ return {
77
+ content: content.text,
78
+ tokensUsed: {
79
+ input: response.usage.input_tokens,
80
+ output: response.usage.output_tokens,
81
+ },
82
+ model: response.model,
83
+ stopReason: response.stop_reason,
84
+ };
85
+ }
86
+ // ============================================================================
87
+ // File Discovery
88
+ // ============================================================================
89
+ /**
90
+ * Discover files for analysis
91
+ */
92
+ async function discoverFiles(projectPath, config) {
93
+ const includePatterns = config.includePatterns || DEFAULT_INCLUDE_PATTERNS;
94
+ const excludePatterns = config.excludePatterns || DEFAULT_EXCLUDE_PATTERNS;
95
+ const allFiles = [];
96
+ for (const pattern of includePatterns) {
97
+ const matches = await glob(pattern, {
98
+ cwd: projectPath,
99
+ ignore: excludePatterns,
100
+ nodir: true,
101
+ absolute: true,
102
+ });
103
+ allFiles.push(...matches);
104
+ }
105
+ // Deduplicate
106
+ const uniqueFiles = [...new Set(allFiles)];
107
+ // Prioritize security-relevant files
108
+ const prioritized = uniqueFiles.sort((a, b) => {
109
+ const aSecurityRelevant = SECURITY_RELEVANT_PATTERNS.some((p) => a.includes(p.replace("**/*", "").replace("*", "")));
110
+ const bSecurityRelevant = SECURITY_RELEVANT_PATTERNS.some((p) => b.includes(p.replace("**/*", "").replace("*", "")));
111
+ if (aSecurityRelevant && !bSecurityRelevant)
112
+ return -1;
113
+ if (!aSecurityRelevant && bSecurityRelevant)
114
+ return 1;
115
+ return 0;
116
+ });
117
+ // Limit to max files
118
+ const maxFiles = config.maxFiles || 100;
119
+ return prioritized.slice(0, maxFiles);
120
+ }
121
+ /**
122
+ * Read file content safely
123
+ */
124
+ async function readFileContent(filePath) {
125
+ try {
126
+ const content = await readFile(filePath, "utf-8");
127
+ // Truncate very large files
128
+ if (content.length > 50000) {
129
+ return content.slice(0, 50000) + "\n\n... [truncated]";
130
+ }
131
+ return content;
132
+ }
133
+ catch {
134
+ return "";
135
+ }
136
+ }
137
+ // ============================================================================
138
+ // Phase 1: Reconnaissance
139
+ // ============================================================================
140
+ /**
141
+ * Detect technology stack from code
142
+ */
143
+ function detectTechStack(files, contents) {
144
+ const stack = {
145
+ language: "unknown",
146
+ };
147
+ // Language detection by file extensions
148
+ const extCounts = {};
149
+ for (const file of files) {
150
+ const ext = path.extname(file).toLowerCase();
151
+ extCounts[ext] = (extCounts[ext] || 0) + 1;
152
+ }
153
+ const topExt = Object.entries(extCounts).sort((a, b) => b[1] - a[1])[0];
154
+ if (topExt) {
155
+ const langMap = {
156
+ ".ts": "TypeScript",
157
+ ".tsx": "TypeScript",
158
+ ".js": "JavaScript",
159
+ ".jsx": "JavaScript",
160
+ ".py": "Python",
161
+ ".go": "Go",
162
+ ".rs": "Rust",
163
+ ".java": "Java",
164
+ ".rb": "Ruby",
165
+ ".php": "PHP",
166
+ };
167
+ stack.language = langMap[topExt[0]] || "unknown";
168
+ }
169
+ // Framework detection from content
170
+ const allContent = [...contents.values()].join("\n");
171
+ // Node.js/JavaScript frameworks
172
+ if (allContent.includes("from 'next'") || allContent.includes("'next/")) {
173
+ stack.framework = "Next.js";
174
+ stack.runtime = "Node.js";
175
+ }
176
+ else if (allContent.includes("from 'express'") || allContent.includes("require('express')")) {
177
+ stack.framework = "Express";
178
+ stack.runtime = "Node.js";
179
+ }
180
+ else if (allContent.includes("from '@hono/")) {
181
+ stack.framework = "Hono";
182
+ stack.runtime = "Node.js/Bun";
183
+ }
184
+ else if (allContent.includes("from 'fastify'")) {
185
+ stack.framework = "Fastify";
186
+ stack.runtime = "Node.js";
187
+ }
188
+ // Python frameworks
189
+ if (allContent.includes("from fastapi") || allContent.includes("import fastapi")) {
190
+ stack.framework = "FastAPI";
191
+ stack.runtime = "Python";
192
+ }
193
+ else if (allContent.includes("from flask") || allContent.includes("import flask")) {
194
+ stack.framework = "Flask";
195
+ stack.runtime = "Python";
196
+ }
197
+ else if (allContent.includes("from django") || allContent.includes("import django")) {
198
+ stack.framework = "Django";
199
+ stack.runtime = "Python";
200
+ }
201
+ // Database detection
202
+ const databases = [];
203
+ if (allContent.includes("postgres") || allContent.includes("pg.Pool") || allContent.includes("psycopg")) {
204
+ databases.push("PostgreSQL");
205
+ }
206
+ if (allContent.includes("mongodb") || allContent.includes("mongoose")) {
207
+ databases.push("MongoDB");
208
+ }
209
+ if (allContent.includes("redis") || allContent.includes("ioredis")) {
210
+ databases.push("Redis");
211
+ }
212
+ if (allContent.includes("mysql")) {
213
+ databases.push("MySQL");
214
+ }
215
+ if (databases.length > 0) {
216
+ stack.database = databases;
217
+ }
218
+ // Auth detection
219
+ const auth = [];
220
+ if (allContent.includes("jwt") || allContent.includes("jsonwebtoken")) {
221
+ auth.push("JWT");
222
+ }
223
+ if (allContent.includes("oauth") || allContent.includes("OAuth")) {
224
+ auth.push("OAuth");
225
+ }
226
+ if (allContent.includes("session") || allContent.includes("express-session")) {
227
+ auth.push("session-based");
228
+ }
229
+ if (allContent.includes("@clerk/") || allContent.includes("clerk")) {
230
+ auth.push("Clerk");
231
+ }
232
+ if (auth.length > 0) {
233
+ stack.auth = auth;
234
+ }
235
+ // API types
236
+ const apis = [];
237
+ if (allContent.includes("graphql") || allContent.includes("GraphQL")) {
238
+ apis.push("graphql");
239
+ }
240
+ if (allContent.includes("grpc") || allContent.includes("proto")) {
241
+ apis.push("grpc");
242
+ }
243
+ if (allContent.includes("WebSocket") || allContent.includes("ws://") || allContent.includes("socket.io")) {
244
+ apis.push("websocket");
245
+ }
246
+ // REST is assumed as default
247
+ apis.push("rest");
248
+ stack.apis = apis;
249
+ // Cloud detection
250
+ if (allContent.includes("vercel") || allContent.includes("@vercel/")) {
251
+ stack.cloud = "Vercel";
252
+ }
253
+ else if (allContent.includes("aws-sdk") || allContent.includes("@aws-sdk/")) {
254
+ stack.cloud = "AWS";
255
+ }
256
+ else if (allContent.includes("@google-cloud/")) {
257
+ stack.cloud = "GCP";
258
+ }
259
+ else if (allContent.includes("@azure/")) {
260
+ stack.cloud = "Azure";
261
+ }
262
+ return stack;
263
+ }
264
+ /**
265
+ * Run reconnaissance phase
266
+ */
267
+ async function runReconnaissance(projectPath, files, config) {
268
+ const startTime = Date.now();
269
+ // Read file contents
270
+ const contents = new Map();
271
+ for (const file of files.slice(0, 50)) { // Limit for recon
272
+ const content = await readFileContent(file);
273
+ if (content) {
274
+ contents.set(file, content);
275
+ }
276
+ }
277
+ // Detect tech stack
278
+ const techStack = detectTechStack(files, contents);
279
+ // Find entry points (basic pattern matching)
280
+ const entryPoints = [];
281
+ for (const [file, content] of contents) {
282
+ // Express-style routes
283
+ const routePatterns = [
284
+ /app\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]/g,
285
+ /router\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]/g,
286
+ ];
287
+ for (const pattern of routePatterns) {
288
+ let match;
289
+ while ((match = pattern.exec(content)) !== null) {
290
+ const lineNumber = content.slice(0, match.index).split("\n").length;
291
+ entryPoints.push({
292
+ type: "route",
293
+ path: match[2],
294
+ methods: [match[1].toUpperCase()],
295
+ file: file,
296
+ line: lineNumber,
297
+ authRequired: false, // Would need deeper analysis
298
+ inputs: [],
299
+ riskScore: 50,
300
+ });
301
+ }
302
+ }
303
+ // Next.js API routes
304
+ if (file.includes("/api/") && (file.endsWith(".ts") || file.endsWith(".js"))) {
305
+ const methodMatch = content.match(/export\s+(async\s+)?function\s+(GET|POST|PUT|DELETE|PATCH)/g);
306
+ if (methodMatch) {
307
+ const methods = methodMatch.map((m) => m.replace(/export\s+(async\s+)?function\s+/, ""));
308
+ const relativePath = file.replace(projectPath, "").replace(/\.(ts|js)$/, "");
309
+ entryPoints.push({
310
+ type: "endpoint",
311
+ path: relativePath.replace("/src/app", "").replace("/pages", ""),
312
+ methods,
313
+ file,
314
+ line: 1,
315
+ authRequired: false,
316
+ inputs: [],
317
+ riskScore: 50,
318
+ });
319
+ }
320
+ }
321
+ }
322
+ return {
323
+ techStack,
324
+ filesAnalyzed: [...contents.keys()],
325
+ entryPoints,
326
+ trustBoundaries: [], // Would need deeper analysis
327
+ thirdParty: [], // Would need dependency analysis
328
+ duration: Date.now() - startTime,
329
+ };
330
+ }
331
+ // ============================================================================
332
+ // Phase 2: Attack Surface
333
+ // ============================================================================
334
+ /**
335
+ * Run attack surface analysis phase
336
+ */
337
+ async function runAttackSurfaceAnalysis(projectPath, files, recon, config) {
338
+ const startTime = Date.now();
339
+ // For now, use the entry points from recon
340
+ // In full implementation, this would use Claude to analyze data flows
341
+ // Identify high-risk areas
342
+ const highRiskAreas = [];
343
+ // Check for auth-related files
344
+ const authFiles = files.filter((f) => f.includes("auth") || f.includes("login") || f.includes("session"));
345
+ if (authFiles.length > 0) {
346
+ highRiskAreas.push({
347
+ area: "Authentication",
348
+ reason: "Authentication logic is a prime target for bypass attacks",
349
+ files: authFiles,
350
+ });
351
+ }
352
+ // Check for database-related files
353
+ const dbFiles = files.filter((f) => f.includes("db") || f.includes("database") || f.includes("query") || f.includes("sql"));
354
+ if (dbFiles.length > 0) {
355
+ highRiskAreas.push({
356
+ area: "Database Access",
357
+ reason: "Database queries may be vulnerable to injection attacks",
358
+ files: dbFiles,
359
+ });
360
+ }
361
+ // Check for file handling
362
+ const fileHandlingFiles = files.filter((f) => f.includes("upload") || f.includes("download") || f.includes("file"));
363
+ if (fileHandlingFiles.length > 0) {
364
+ highRiskAreas.push({
365
+ area: "File Handling",
366
+ reason: "File operations may lead to path traversal or arbitrary file access",
367
+ files: fileHandlingFiles,
368
+ });
369
+ }
370
+ return {
371
+ entryPoints: recon.entryPoints,
372
+ highRiskAreas,
373
+ dataFlows: [], // Would need Claude analysis
374
+ permissionBoundaries: [], // Would need deeper analysis
375
+ duration: Date.now() - startTime,
376
+ };
377
+ }
378
+ // ============================================================================
379
+ // Phase 3: Exploitation
380
+ // ============================================================================
381
+ /**
382
+ * Build system prompt for adversarial analysis
383
+ */
384
+ function buildAdversarySystemPrompt(config) {
385
+ return `You are an expert ethical hacker and security researcher performing authorized vulnerability assessment.
386
+
387
+ ## Your Mission
388
+ Analyze code with an attacker's mindset. Your goal is to find vulnerabilities that pattern-based scanners miss - logic flaws, subtle injection vectors, authentication bypasses, and novel attack paths.
389
+
390
+ ## Analysis Approach
391
+ 1. **Think like an attacker**: Ask "How would I exploit this?"
392
+ 2. **Consider context**: Understand the application flow and trust boundaries
393
+ 3. **Look for subtle issues**: Not just obvious bugs, but design flaws and edge cases
394
+ 4. **Chain vulnerabilities**: Consider how multiple issues could be combined
395
+
396
+ ## Focus Areas
397
+ ${config.focusAreas.map((area) => `- ${area}: ${getAreaDescription(area)}`).join("\n")}
398
+
399
+ ## Output Requirements
400
+ - Be specific with file paths and line numbers
401
+ - Provide clear attack scenarios that demonstrate exploitability
402
+ - Assign realistic severity and confidence scores
403
+ - Suggest concrete remediations
404
+
405
+ ## Aggressiveness Level: ${config.aggressiveness}
406
+ ${config.aggressiveness === "passive" ? "Focus on finding issues, do not generate exploits." :
407
+ config.aggressiveness === "active" ? "Generate proof-of-concept exploits where possible." :
408
+ "Deep analysis - generate full exploits and attack chains."}
409
+
410
+ Remember: This is authorized security testing. Be thorough and think creatively.`;
411
+ }
412
+ /**
413
+ * Get description for focus area
414
+ */
415
+ function getAreaDescription(area) {
416
+ const descriptions = {
417
+ "web-app": "XSS, CSRF, clickjacking, CORS misconfigurations",
418
+ "api": "GraphQL/REST abuse, rate limiting bypasses, BOLA/IDOR",
419
+ "auth": "Authentication bypass, session fixation, OAuth flaws",
420
+ "injection": "SQL, NoSQL, XXE, SSTI, command injection",
421
+ "llm": "Prompt injection, jailbreaks, data extraction",
422
+ "infra": "Container escapes, privilege escalation, cloud misconfig",
423
+ "crypto": "Weak algorithms, key management, timing attacks",
424
+ "data-flow": "Data exposure, exfiltration paths, logging issues",
425
+ "supply-chain": "Dependency vulnerabilities, build pipeline attacks",
426
+ };
427
+ return descriptions[area] || "General security issues";
428
+ }
429
+ /**
430
+ * Parse findings from Claude response
431
+ */
432
+ function parseFindingsFromResponse(response, file, config) {
433
+ const findings = [];
434
+ try {
435
+ // Try to parse JSON if response is structured
436
+ const jsonMatch = response.match(/\[[\s\S]*\]/);
437
+ if (jsonMatch) {
438
+ const parsed = JSON.parse(jsonMatch[0]);
439
+ if (Array.isArray(parsed)) {
440
+ for (const item of parsed) {
441
+ if (item.title && item.severity) {
442
+ findings.push({
443
+ id: `adv-${randomUUID().slice(0, 8)}`,
444
+ title: item.title,
445
+ description: item.description || item.title,
446
+ severity: item.severity,
447
+ confidence: item.confidence || 70,
448
+ category: item.category || "code-quality",
449
+ focusArea: item.focusArea || config.focusAreas[0] || "web-app",
450
+ file: item.file || file,
451
+ line: item.line || 1,
452
+ codeSnippet: item.codeSnippet || "",
453
+ attackScenario: item.attackScenario || "",
454
+ exploitability: item.exploitability || "moderate",
455
+ chainPotential: item.chainPotential || [],
456
+ mitreAttackTechniques: item.mitreAttackTechniques || [],
457
+ cweIds: item.cweIds || [],
458
+ recommendation: item.recommendation || "Review and fix the identified issue",
459
+ aiReasoning: item.aiReasoning || "",
460
+ modelUsed: config.model,
461
+ });
462
+ }
463
+ }
464
+ }
465
+ }
466
+ }
467
+ catch {
468
+ // If JSON parsing fails, try to extract findings from text
469
+ // This is a fallback for unstructured responses
470
+ }
471
+ return findings;
472
+ }
473
+ /**
474
+ * Run exploitation phase with Claude analysis
475
+ */
476
+ async function runExploitation(projectPath, files, recon, attackSurface, config) {
477
+ const startTime = Date.now();
478
+ const findings = [];
479
+ let totalTokens = 0;
480
+ // Get tactics for configured focus areas
481
+ const tactics = getTacticsForFocusAreas(config.focusAreas);
482
+ const tacticPromptEnhancement = buildCombinedPromptEnhancement(tactics);
483
+ // Prioritize high-risk files
484
+ const prioritizedFiles = [
485
+ ...attackSurface.highRiskAreas.flatMap((area) => area.files),
486
+ ...files.filter((f) => !attackSurface.highRiskAreas.some((area) => area.files.includes(f))),
487
+ ].slice(0, config.maxFiles || 50);
488
+ // Analyze each file with Claude
489
+ for (const file of prioritizedFiles) {
490
+ const content = await readFileContent(file);
491
+ if (!content || content.length < 100)
492
+ continue;
493
+ // Run tactic-based pattern detection first
494
+ const fileContext = createFileContext(projectPath, file, content);
495
+ let tacticFindings = [];
496
+ try {
497
+ tacticFindings = await runTacticAnalysis(fileContext, tactics, config);
498
+ }
499
+ catch {
500
+ // Continue even if tactic analysis fails
501
+ }
502
+ // Convert tactic findings to adversary findings and add to results
503
+ for (const tf of tacticFindings) {
504
+ const adversaryFinding = {
505
+ id: tf.id,
506
+ title: tf.message.split(":")[0] || tf.message,
507
+ description: tf.message,
508
+ severity: tf.severity,
509
+ confidence: tf.confidence,
510
+ category: tf.patternId,
511
+ focusArea: tf.focusArea,
512
+ file: tf.file,
513
+ line: tf.line,
514
+ codeSnippet: tf.evidence,
515
+ attackScenario: "",
516
+ exploitability: tf.severity === "critical" ? "easy" : tf.severity === "high" ? "moderate" : "hard",
517
+ chainPotential: [],
518
+ mitreAttackTechniques: tf.mitreIds,
519
+ cweIds: tf.cweIds,
520
+ recommendation: tf.suggestedFix || "Review and fix the identified issue",
521
+ aiReasoning: "Pattern-based detection",
522
+ modelUsed: "deterministic",
523
+ };
524
+ findings.push(adversaryFinding);
525
+ }
526
+ // Build enhanced prompt with tactic findings context
527
+ const tacticContext = tacticFindings.length > 0
528
+ ? `\n\n## Pre-identified Issues (validate and expand)\n${tacticFindings.map((tf) => `- Line ${tf.line}: ${tf.message}`).join("\n")}`
529
+ : "";
530
+ try {
531
+ const prompt = {
532
+ systemPrompt: buildAdversarySystemPrompt(config) + "\n\n" + tacticPromptEnhancement,
533
+ codeContext: content,
534
+ instructions: `Analyze this file for security vulnerabilities. Focus on ${config.focusAreas.join(", ")}.
535
+
536
+ File: ${path.relative(projectPath, file)}
537
+ Technology Stack: ${JSON.stringify(recon.techStack)}
538
+ ${tacticContext}
539
+
540
+ Look for:
541
+ - Input validation issues
542
+ - Authentication/authorization flaws
543
+ - Injection vulnerabilities
544
+ - Logic errors
545
+ - Cryptographic weaknesses
546
+ - Race conditions
547
+ - Information disclosure`,
548
+ outputFormat: `Return findings as a JSON array:
549
+ [{
550
+ "title": "Vulnerability Title",
551
+ "description": "Detailed description",
552
+ "severity": "critical|high|medium|low",
553
+ "confidence": 0-100,
554
+ "category": "category-name",
555
+ "focusArea": "focus-area",
556
+ "line": line_number,
557
+ "codeSnippet": "relevant code",
558
+ "attackScenario": "Step-by-step attack",
559
+ "exploitability": "trivial|easy|moderate|hard|expert",
560
+ "cweIds": ["CWE-XX"],
561
+ "recommendation": "How to fix"
562
+ }]
563
+
564
+ If no vulnerabilities found, return empty array [].`,
565
+ };
566
+ const response = await analyzeWithClaude(prompt, config.model);
567
+ totalTokens += response.tokensUsed.input + response.tokensUsed.output;
568
+ const fileFindings = parseFindingsFromResponse(response.content, file, config);
569
+ findings.push(...fileFindings);
570
+ // Check time limit
571
+ if (Date.now() - startTime > config.maxAnalysisTime) {
572
+ break;
573
+ }
574
+ }
575
+ catch (error) {
576
+ // Log error but continue with other files
577
+ console.error(`Error analyzing ${file}:`, error);
578
+ }
579
+ }
580
+ // Generate PoCs if enabled
581
+ if (config.generatePoC) {
582
+ const pocConfig = {
583
+ includePayloads: true,
584
+ safeMode: true,
585
+ maxSteps: 6,
586
+ includeRemediation: true,
587
+ };
588
+ for (const finding of findings) {
589
+ if (finding.severity === "critical" || finding.severity === "high") {
590
+ try {
591
+ const poc = await generatePoC(finding, pocConfig);
592
+ if (poc) {
593
+ finding.proofOfConcept = poc;
594
+ }
595
+ }
596
+ catch {
597
+ // Continue if PoC generation fails
598
+ }
599
+ }
600
+ }
601
+ }
602
+ // Add compliance mapping to all findings
603
+ for (const finding of findings) {
604
+ const mapping = mapFindingToCompliance(finding, finding.focusArea);
605
+ finding.complianceMapping = mapping.frameworks;
606
+ }
607
+ return {
608
+ findings,
609
+ filesAnalyzed: prioritizedFiles.length,
610
+ tokensUsed: totalTokens,
611
+ duration: Date.now() - startTime,
612
+ };
613
+ }
614
+ // ============================================================================
615
+ // Phase 4: Chaining
616
+ // ============================================================================
617
+ /**
618
+ * Analyze findings for exploit chains
619
+ */
620
+ async function runChainingAnalysis(findings, config) {
621
+ const startTime = Date.now();
622
+ const chains = [];
623
+ const chainedFindingIds = new Set();
624
+ // Simple chaining logic - look for related findings
625
+ // In full implementation, this would use Claude for deeper analysis
626
+ // Group findings by category
627
+ const byCategory = new Map();
628
+ for (const finding of findings) {
629
+ const category = finding.category;
630
+ if (!byCategory.has(category)) {
631
+ byCategory.set(category, []);
632
+ }
633
+ byCategory.get(category).push(finding);
634
+ }
635
+ // Look for common chains
636
+ // XSS + Session = Session Hijacking
637
+ const xssFindings = findings.filter((f) => f.category === "xss");
638
+ const sessionFindings = findings.filter((f) => f.category === "session-management" || f.category === "authentication");
639
+ if (xssFindings.length > 0 && sessionFindings.length > 0) {
640
+ const chain = {
641
+ id: `chain-${randomUUID().slice(0, 8)}`,
642
+ name: "XSS to Session Hijacking",
643
+ description: "Cross-site scripting can be chained with session vulnerabilities to hijack user sessions",
644
+ findingIds: [...xssFindings.map((f) => f.id), ...sessionFindings.map((f) => f.id)],
645
+ combinedSeverity: "critical",
646
+ impact: "Full account takeover through stolen session credentials",
647
+ mitreChain: ["T1189", "T1539"],
648
+ likelihood: "high",
649
+ };
650
+ chains.push(chain);
651
+ xssFindings.forEach((f) => chainedFindingIds.add(f.id));
652
+ sessionFindings.forEach((f) => chainedFindingIds.add(f.id));
653
+ }
654
+ // SQL Injection + Auth Bypass = Full DB Access
655
+ const sqlFindings = findings.filter((f) => f.category === "sql-injection");
656
+ const authFindings = findings.filter((f) => f.category === "auth-bypass");
657
+ if (sqlFindings.length > 0 && authFindings.length > 0) {
658
+ const chain = {
659
+ id: `chain-${randomUUID().slice(0, 8)}`,
660
+ name: "SQL Injection to Database Compromise",
661
+ description: "SQL injection combined with authentication bypass enables complete database access",
662
+ findingIds: [...sqlFindings.map((f) => f.id), ...authFindings.map((f) => f.id)],
663
+ combinedSeverity: "critical",
664
+ impact: "Complete database compromise including sensitive data exfiltration",
665
+ mitreChain: ["T1190", "T1078", "T1005"],
666
+ likelihood: "high",
667
+ };
668
+ chains.push(chain);
669
+ sqlFindings.forEach((f) => chainedFindingIds.add(f.id));
670
+ authFindings.forEach((f) => chainedFindingIds.add(f.id));
671
+ }
672
+ // SSRF + Internal API = Internal Service Access
673
+ const ssrfFindings = findings.filter((f) => f.category === "ssrf");
674
+ const apiFindings = findings.filter((f) => f.category === "api-security");
675
+ if (ssrfFindings.length > 0 && apiFindings.length > 0) {
676
+ const chain = {
677
+ id: `chain-${randomUUID().slice(0, 8)}`,
678
+ name: "SSRF to Internal Service Compromise",
679
+ description: "Server-side request forgery can access internal APIs and services",
680
+ findingIds: [...ssrfFindings.map((f) => f.id), ...apiFindings.map((f) => f.id)],
681
+ combinedSeverity: "critical",
682
+ impact: "Access to internal services, potential cloud metadata exposure",
683
+ mitreChain: ["T1190", "T1552"],
684
+ likelihood: "medium",
685
+ };
686
+ chains.push(chain);
687
+ ssrfFindings.forEach((f) => chainedFindingIds.add(f.id));
688
+ apiFindings.forEach((f) => chainedFindingIds.add(f.id));
689
+ }
690
+ // Isolated findings (not part of any chain)
691
+ const isolatedFindings = findings
692
+ .filter((f) => !chainedFindingIds.has(f.id))
693
+ .map((f) => f.id);
694
+ return {
695
+ chains,
696
+ isolatedFindings,
697
+ duration: Date.now() - startTime,
698
+ };
699
+ }
700
+ // ============================================================================
701
+ // Main Entry Point
702
+ // ============================================================================
703
+ /**
704
+ * Run full adversary analysis
705
+ */
706
+ export async function runAdversaryAnalysis(projectPath, config) {
707
+ const analysisId = `adv-${randomUUID().slice(0, 12)}`;
708
+ const startTime = Date.now();
709
+ try {
710
+ // Validate project path
711
+ const stats = await stat(projectPath);
712
+ if (!stats.isDirectory()) {
713
+ throw new Error(`Project path is not a directory: ${projectPath}`);
714
+ }
715
+ // Discover files
716
+ const files = await discoverFiles(projectPath, config);
717
+ if (files.length === 0) {
718
+ return {
719
+ success: false,
720
+ analysisId,
721
+ projectPath,
722
+ config,
723
+ findings: [],
724
+ chains: [],
725
+ totalTokensUsed: 0,
726
+ totalDuration: Date.now() - startTime,
727
+ error: "No analyzable files found in project",
728
+ recommendations: [],
729
+ };
730
+ }
731
+ // Phase 1: Reconnaissance
732
+ const recon = await runReconnaissance(projectPath, files, config);
733
+ // Phase 2: Attack Surface
734
+ const attackSurface = await runAttackSurfaceAnalysis(projectPath, files, recon, config);
735
+ // Phase 3: Exploitation
736
+ const exploitation = await runExploitation(projectPath, files, recon, attackSurface, config);
737
+ // Phase 4: Chaining (if enabled)
738
+ let chaining;
739
+ if (config.enableChaining && exploitation.findings.length > 1) {
740
+ chaining = await runChainingAnalysis(exploitation.findings, config);
741
+ }
742
+ // Generate recommendations
743
+ const recommendations = [];
744
+ const criticalCount = exploitation.findings.filter((f) => f.severity === "critical").length;
745
+ const highCount = exploitation.findings.filter((f) => f.severity === "high").length;
746
+ if (criticalCount > 0) {
747
+ recommendations.push(`CRITICAL: ${criticalCount} critical vulnerabilities require immediate remediation.`);
748
+ }
749
+ if (highCount > 0) {
750
+ recommendations.push(`HIGH: ${highCount} high-severity issues should be fixed before deployment.`);
751
+ }
752
+ if (chaining && chaining.chains.length > 0) {
753
+ recommendations.push(`WARNING: ${chaining.chains.length} exploit chains discovered that could amplify impact.`);
754
+ }
755
+ // Focus area specific recommendations
756
+ for (const area of config.focusAreas) {
757
+ const areaFindings = exploitation.findings.filter((f) => f.focusArea === area);
758
+ if (areaFindings.length > 0) {
759
+ recommendations.push(`${area.toUpperCase()}: ${areaFindings.length} issues found. Review ${getAreaDescription(area)}.`);
760
+ }
761
+ }
762
+ return {
763
+ success: true,
764
+ analysisId,
765
+ projectPath,
766
+ config,
767
+ recon,
768
+ attackSurface,
769
+ exploitation,
770
+ chaining,
771
+ findings: exploitation.findings,
772
+ chains: chaining?.chains || [],
773
+ totalTokensUsed: exploitation.tokensUsed,
774
+ totalDuration: Date.now() - startTime,
775
+ recommendations,
776
+ };
777
+ }
778
+ catch (error) {
779
+ return {
780
+ success: false,
781
+ analysisId,
782
+ projectPath,
783
+ config,
784
+ findings: [],
785
+ chains: [],
786
+ totalTokensUsed: 0,
787
+ totalDuration: Date.now() - startTime,
788
+ error: error instanceof Error ? error.message : String(error),
789
+ recommendations: [],
790
+ };
791
+ }
792
+ }
793
+ /**
794
+ * Convert adversary findings to certification findings
795
+ */
796
+ export function adversaryToFindings(result) {
797
+ return result.findings.map((f) => ({
798
+ id: f.id,
799
+ severity: f.severity,
800
+ category: f.category,
801
+ description: `${f.title}: ${f.description}`,
802
+ evidence: `File: ${f.file}:${f.line}\nCode: ${f.codeSnippet.slice(0, 200)}\nAttack: ${f.attackScenario}`,
803
+ confidence: f.confidence,
804
+ verifications: [],
805
+ created_at: new Date().toISOString(),
806
+ scanner_source: "adversary",
807
+ metadata: {
808
+ focusArea: f.focusArea,
809
+ exploitability: f.exploitability,
810
+ cweIds: f.cweIds,
811
+ mitreAttackTechniques: f.mitreAttackTechniques,
812
+ recommendation: f.recommendation,
813
+ aiReasoning: f.aiReasoning,
814
+ modelUsed: f.modelUsed,
815
+ hasPoC: !!f.proofOfConcept,
816
+ },
817
+ }));
818
+ }
819
+ /**
820
+ * Estimate cost for adversary analysis
821
+ */
822
+ export function estimateAdversaryCost(filesCount, config) {
823
+ // Rough estimates based on typical analysis patterns
824
+ const tokensPerFile = config.aggressiveness === "aggressive" ? 8000 :
825
+ config.aggressiveness === "active" ? 5000 : 3000;
826
+ const estimatedTokens = filesCount * tokensPerFile;
827
+ const pricing = MODEL_PRICING[config.model];
828
+ // Assume 70% input, 30% output
829
+ const inputTokens = estimatedTokens * 0.7;
830
+ const outputTokens = estimatedTokens * 0.3;
831
+ const inputCost = (inputTokens / 1_000_000) * pricing.input;
832
+ const outputCost = (outputTokens / 1_000_000) * pricing.output;
833
+ return {
834
+ estimatedCost: Math.round((inputCost + outputCost) * 100) / 100,
835
+ estimatedTokens: Math.round(estimatedTokens),
836
+ };
837
+ }
838
+ //# sourceMappingURL=index.js.map