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.
Files changed (303) hide show
  1. package/CHANGELOG.md +109 -7
  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 +25 -4
  38. package/dist/agents/adversary/config.d.ts.map +1 -1
  39. package/dist/agents/adversary/config.js +38 -8
  40. package/dist/agents/adversary/config.js.map +1 -1
  41. package/dist/agents/adversary/index.d.ts +7 -0
  42. package/dist/agents/adversary/index.d.ts.map +1 -1
  43. package/dist/agents/adversary/index.js +83 -1
  44. package/dist/agents/adversary/index.js.map +1 -1
  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 +66 -10
  86. package/dist/agents/adversary/types.d.ts.map +1 -1
  87. package/dist/agents/zero-day-hunter.d.ts +1 -1
  88. package/dist/agents/zero-day-hunter.d.ts.map +1 -1
  89. package/dist/analysis/data-flow.d.ts +154 -0
  90. package/dist/analysis/data-flow.d.ts.map +1 -0
  91. package/dist/analysis/data-flow.js +393 -0
  92. package/dist/analysis/data-flow.js.map +1 -0
  93. package/dist/analysis/index.d.ts +9 -0
  94. package/dist/analysis/index.d.ts.map +1 -0
  95. package/dist/analysis/index.js +9 -0
  96. package/dist/analysis/index.js.map +1 -0
  97. package/dist/badge-service/index.d.ts +144 -0
  98. package/dist/badge-service/index.d.ts.map +1 -0
  99. package/dist/badge-service/index.js +206 -0
  100. package/dist/badge-service/index.js.map +1 -0
  101. package/dist/certification/types.d.ts +1 -1
  102. package/dist/certification/types.d.ts.map +1 -1
  103. package/dist/certification/types.js.map +1 -1
  104. package/dist/commands/certification/certify.d.ts.map +1 -1
  105. package/dist/commands/certification/certify.js +18 -4
  106. package/dist/commands/certification/certify.js.map +1 -1
  107. package/dist/compliance/attestation.d.ts +39 -0
  108. package/dist/compliance/attestation.d.ts.map +1 -0
  109. package/dist/compliance/attestation.js +364 -0
  110. package/dist/compliance/attestation.js.map +1 -0
  111. package/dist/compliance/cfr42-part2.d.ts +42 -0
  112. package/dist/compliance/cfr42-part2.d.ts.map +1 -0
  113. package/dist/compliance/cfr42-part2.js +408 -0
  114. package/dist/compliance/cfr42-part2.js.map +1 -0
  115. package/dist/compliance/compliance-bundle.d.ts +100 -0
  116. package/dist/compliance/compliance-bundle.d.ts.map +1 -0
  117. package/dist/compliance/compliance-bundle.js +210 -0
  118. package/dist/compliance/compliance-bundle.js.map +1 -0
  119. package/dist/compliance/healthcare-bundle.d.ts +68 -0
  120. package/dist/compliance/healthcare-bundle.d.ts.map +1 -0
  121. package/dist/compliance/healthcare-bundle.js +104 -0
  122. package/dist/compliance/healthcare-bundle.js.map +1 -0
  123. package/dist/compliance/hipaa.d.ts.map +1 -1
  124. package/dist/compliance/hipaa.js +14 -11
  125. package/dist/compliance/hipaa.js.map +1 -1
  126. package/dist/compliance/index.d.ts +10 -2
  127. package/dist/compliance/index.d.ts.map +1 -1
  128. package/dist/compliance/index.js +9 -3
  129. package/dist/compliance/index.js.map +1 -1
  130. package/dist/compliance/mapper.d.ts.map +1 -1
  131. package/dist/compliance/mapper.js +3 -17
  132. package/dist/compliance/mapper.js.map +1 -1
  133. package/dist/compliance/nist-800-53.d.ts +22 -6
  134. package/dist/compliance/nist-800-53.d.ts.map +1 -1
  135. package/dist/compliance/nist-800-53.js +264 -272
  136. package/dist/compliance/nist-800-53.js.map +1 -1
  137. package/dist/compliance/report.d.ts +31 -2
  138. package/dist/compliance/report.d.ts.map +1 -1
  139. package/dist/compliance/report.js +255 -4
  140. package/dist/compliance/report.js.map +1 -1
  141. package/dist/compliance/types.d.ts +1 -1
  142. package/dist/compliance/types.d.ts.map +1 -1
  143. package/dist/config/flags.d.ts +12 -12
  144. package/dist/cost/index.d.ts +1 -1
  145. package/dist/cost/index.d.ts.map +1 -1
  146. package/dist/cost/index.js +1 -1
  147. package/dist/cost/index.js.map +1 -1
  148. package/dist/cost/tracker.d.ts +64 -0
  149. package/dist/cost/tracker.d.ts.map +1 -1
  150. package/dist/cost/tracker.js +165 -0
  151. package/dist/cost/tracker.js.map +1 -1
  152. package/dist/eval/fixtures/healthcare/audit-gaps.d.ts +28 -0
  153. package/dist/eval/fixtures/healthcare/audit-gaps.d.ts.map +1 -0
  154. package/dist/eval/fixtures/healthcare/audit-gaps.js +90 -0
  155. package/dist/eval/fixtures/healthcare/audit-gaps.js.map +1 -0
  156. package/dist/eval/fixtures/healthcare/consent-bypass.d.ts +31 -0
  157. package/dist/eval/fixtures/healthcare/consent-bypass.d.ts.map +1 -0
  158. package/dist/eval/fixtures/healthcare/consent-bypass.js +61 -0
  159. package/dist/eval/fixtures/healthcare/consent-bypass.js.map +1 -0
  160. package/dist/eval/fixtures/healthcare/phi-in-logs.d.ts +24 -0
  161. package/dist/eval/fixtures/healthcare/phi-in-logs.d.ts.map +1 -0
  162. package/dist/eval/fixtures/healthcare/phi-in-logs.js +41 -0
  163. package/dist/eval/fixtures/healthcare/phi-in-logs.js.map +1 -0
  164. package/dist/evidence/collector.d.ts +21 -0
  165. package/dist/evidence/collector.d.ts.map +1 -0
  166. package/dist/evidence/collector.js +340 -0
  167. package/dist/evidence/collector.js.map +1 -0
  168. package/dist/evidence/index.d.ts +11 -0
  169. package/dist/evidence/index.d.ts.map +1 -0
  170. package/dist/evidence/index.js +12 -0
  171. package/dist/evidence/index.js.map +1 -0
  172. package/dist/evidence/store.d.ts +39 -0
  173. package/dist/evidence/store.d.ts.map +1 -0
  174. package/dist/evidence/store.js +173 -0
  175. package/dist/evidence/store.js.map +1 -0
  176. package/dist/evidence/types.d.ts +175 -0
  177. package/dist/evidence/types.d.ts.map +1 -0
  178. package/dist/evidence/types.js +9 -0
  179. package/dist/evidence/types.js.map +1 -0
  180. package/dist/exporters/checkmarx.d.ts +18 -0
  181. package/dist/exporters/checkmarx.d.ts.map +1 -0
  182. package/dist/exporters/checkmarx.js +203 -0
  183. package/dist/exporters/checkmarx.js.map +1 -0
  184. package/dist/exporters/index.d.ts +22 -0
  185. package/dist/exporters/index.d.ts.map +1 -0
  186. package/dist/exporters/index.js +41 -0
  187. package/dist/exporters/index.js.map +1 -0
  188. package/dist/exporters/snyk.d.ts +18 -0
  189. package/dist/exporters/snyk.d.ts.map +1 -0
  190. package/dist/exporters/snyk.js +119 -0
  191. package/dist/exporters/snyk.js.map +1 -0
  192. package/dist/exporters/sonarqube.d.ts +18 -0
  193. package/dist/exporters/sonarqube.d.ts.map +1 -0
  194. package/dist/exporters/sonarqube.js +125 -0
  195. package/dist/exporters/sonarqube.js.map +1 -0
  196. package/dist/exporters/types.d.ts +190 -0
  197. package/dist/exporters/types.d.ts.map +1 -0
  198. package/dist/exporters/types.js +9 -0
  199. package/dist/exporters/types.js.map +1 -0
  200. package/dist/frontier/index.d.ts +12 -0
  201. package/dist/frontier/index.d.ts.map +1 -0
  202. package/dist/frontier/index.js +12 -0
  203. package/dist/frontier/index.js.map +1 -0
  204. package/dist/frontier/orchestrator.d.ts +73 -0
  205. package/dist/frontier/orchestrator.d.ts.map +1 -0
  206. package/dist/frontier/orchestrator.js +312 -0
  207. package/dist/frontier/orchestrator.js.map +1 -0
  208. package/dist/frontier/providers/stub.d.ts +32 -0
  209. package/dist/frontier/providers/stub.d.ts.map +1 -0
  210. package/dist/frontier/providers/stub.js +66 -0
  211. package/dist/frontier/providers/stub.js.map +1 -0
  212. package/dist/frontier/types.d.ts +318 -0
  213. package/dist/frontier/types.d.ts.map +1 -0
  214. package/dist/frontier/types.js +27 -0
  215. package/dist/frontier/types.js.map +1 -0
  216. package/dist/history/index.d.ts +13 -0
  217. package/dist/history/index.d.ts.map +1 -0
  218. package/dist/history/index.js +15 -0
  219. package/dist/history/index.js.map +1 -0
  220. package/dist/history/store.d.ts +74 -0
  221. package/dist/history/store.d.ts.map +1 -0
  222. package/dist/history/store.js +399 -0
  223. package/dist/history/store.js.map +1 -0
  224. package/dist/history/types.d.ts +282 -0
  225. package/dist/history/types.d.ts.map +1 -0
  226. package/dist/history/types.js +41 -0
  227. package/dist/history/types.js.map +1 -0
  228. package/dist/history/verify.d.ts +44 -0
  229. package/dist/history/verify.d.ts.map +1 -0
  230. package/dist/history/verify.js +230 -0
  231. package/dist/history/verify.js.map +1 -0
  232. package/dist/index.d.ts.map +1 -1
  233. package/dist/index.js +515 -19
  234. package/dist/index.js.map +1 -1
  235. package/dist/multimodel/index.d.ts +1 -0
  236. package/dist/multimodel/index.d.ts.map +1 -1
  237. package/dist/multimodel/index.js +2 -0
  238. package/dist/multimodel/index.js.map +1 -1
  239. package/dist/multimodel/leaderboard.d.ts +116 -0
  240. package/dist/multimodel/leaderboard.d.ts.map +1 -0
  241. package/dist/multimodel/leaderboard.js +262 -0
  242. package/dist/multimodel/leaderboard.js.map +1 -0
  243. package/dist/observability/otel.d.ts.map +1 -1
  244. package/dist/observability/otel.js +1 -3
  245. package/dist/observability/otel.js.map +1 -1
  246. package/dist/plugins/loader.js +1 -1
  247. package/dist/plugins/loader.js.map +1 -1
  248. package/dist/scanners/agent/agent-chain-analysis.d.ts +152 -0
  249. package/dist/scanners/agent/agent-chain-analysis.d.ts.map +1 -0
  250. package/dist/scanners/agent/agent-chain-analysis.js +438 -0
  251. package/dist/scanners/agent/agent-chain-analysis.js.map +1 -0
  252. package/dist/scanners/agent/payloads/index.d.ts +2 -1
  253. package/dist/scanners/agent/payloads/index.d.ts.map +1 -1
  254. package/dist/scanners/agent/payloads/index.js +25 -6
  255. package/dist/scanners/agent/payloads/index.js.map +1 -1
  256. package/dist/scanners/agent/prompt-injection-fuzzer.d.ts.map +1 -1
  257. package/dist/scanners/agent/prompt-injection-fuzzer.js +14 -0
  258. package/dist/scanners/agent/prompt-injection-fuzzer.js.map +1 -1
  259. package/dist/scanners/agent/types.d.ts +5 -5
  260. package/dist/scanners/agent/types.d.ts.map +1 -1
  261. package/dist/scanners/agent/types.js.map +1 -1
  262. package/dist/scanners/cache.d.ts +156 -0
  263. package/dist/scanners/cache.d.ts.map +1 -0
  264. package/dist/scanners/cache.js +462 -0
  265. package/dist/scanners/cache.js.map +1 -0
  266. package/dist/scanners/dependencies.js +4 -4
  267. package/dist/scanners/dependencies.js.map +1 -1
  268. package/dist/scanners/gosec.d.ts.map +1 -1
  269. package/dist/scanners/gosec.js +47 -9
  270. package/dist/scanners/gosec.js.map +1 -1
  271. package/dist/scanners/healthcare.d.ts +29 -0
  272. package/dist/scanners/healthcare.d.ts.map +1 -0
  273. package/dist/scanners/healthcare.js +526 -0
  274. package/dist/scanners/healthcare.js.map +1 -0
  275. package/dist/scanners/index.d.ts +1 -0
  276. package/dist/scanners/index.d.ts.map +1 -1
  277. package/dist/scanners/index.js +33 -0
  278. package/dist/scanners/index.js.map +1 -1
  279. package/dist/scanners/index.test.js +6 -6
  280. package/dist/scanners/index.test.js.map +1 -1
  281. package/dist/scanners/secrets.js +4 -4
  282. package/dist/scanners/secrets.js.map +1 -1
  283. package/dist/scanners/semgrep.js +5 -5
  284. package/dist/scanners/semgrep.js.map +1 -1
  285. package/dist/scanners/types.d.ts +1 -1
  286. package/dist/scanners/types.d.ts.map +1 -1
  287. package/dist/scanners/types.js +1 -0
  288. package/dist/scanners/types.js.map +1 -1
  289. package/dist/scanners/typescript.test.js +1 -1
  290. package/dist/scanners/typescript.test.js.map +1 -1
  291. package/dist/telemetry/index.d.ts +10 -0
  292. package/dist/telemetry/index.d.ts.map +1 -0
  293. package/dist/telemetry/index.js +10 -0
  294. package/dist/telemetry/index.js.map +1 -0
  295. package/dist/telemetry/registry.d.ts +178 -0
  296. package/dist/telemetry/registry.d.ts.map +1 -0
  297. package/dist/telemetry/registry.js +297 -0
  298. package/dist/telemetry/registry.js.map +1 -0
  299. package/dist/telemetry/usage.d.ts +197 -0
  300. package/dist/telemetry/usage.d.ts.map +1 -0
  301. package/dist/telemetry/usage.js +252 -0
  302. package/dist/telemetry/usage.js.map +1 -0
  303. package/package.json +2 -6
package/dist/index.js CHANGED
@@ -30,13 +30,25 @@ import { getAIFrameworkControls, AI_FRAMEWORKS_METADATA, } from "./compliance/fr
30
30
  // Evaluation harness
31
31
  import { runEvaluation, runStabilityTest, calculateMetrics, calculateStabilityMetrics, meetsTargets, generateEvaluationReport, formatReportAsMarkdown, generateCompactSummary, generateReadmeBadges, getFixtureStats, ALL_FIXTURES, TARGET_METRICS, } from "./eval/index.js";
32
32
  // Compliance frameworks
33
- import { generateComplianceReport, generateMultiFrameworkReport, formatComplianceReportAsMarkdown, formatMultiFrameworkReportAsMarkdown, generateCompactComplianceSummary, getControlsForFramework, getSOC2Categories, getISO27001Categories, } from "./compliance/index.js";
33
+ import { generateComplianceReport, generateMultiFrameworkReport, formatComplianceReportAsMarkdown, formatMultiFrameworkReportAsMarkdown, generateCompactComplianceSummary, getControlsForFramework,
34
+ // Healthcare compliance
35
+ runHealthcareComplianceAssessment, generateHealthcareComplianceSummary,
36
+ // Universal audit-defensible compliance
37
+ runSingleFrameworkAssessment, runComplianceAssessment, generateComplianceSummary, } from "./compliance/index.js";
38
+ // Evidence collection and audit trail verification
39
+ import { collectEvidence, storeEvidenceBundle, formatEvidenceBundleAsMarkdown, } from "./evidence/index.js";
40
+ import { verifyHistoryIntegrity, formatVerificationResultAsMarkdown, } from "./history/verify.js";
34
41
  // SBOM, Provenance, and Sigstore Signing (uses @sigstore/sign for real signing)
35
42
  import { generateSBOM, generateSBOMSummary, generateProvenance, generateProvenanceSummary, verifyProvenance, signContent, isSigningAvailable, generateSigningSummary, detectCIEnvironment, } from "./sbom/index.js";
36
43
  // Cost tracking
37
44
  import { getTracker, formatCost, formatTokens, estimateCost, getSupportedModels, MODEL_PRICING, } from "./cost/index.js";
38
45
  // Multi-model consensus
39
46
  import { getRunner, DEFAULT_MODELS, formatProvider, } from "./multimodel/index.js";
47
+ // Path validation utilities
48
+ import { validateProjectPath, PathValidationError } from "./util/paths.js";
49
+ // Telemetry and scan registry
50
+ import { trackCertificationStarted, trackCertificationCompleted, trackScannerRun, } from "./telemetry/usage.js";
51
+ import { getRegistry } from "./telemetry/registry.js";
40
52
  // ---------------------------------------------------------------------------
41
53
  // Config
42
54
  // ---------------------------------------------------------------------------
@@ -221,7 +233,17 @@ server.registerTool("hardening_install", {
221
233
  openWorldHint: false,
222
234
  },
223
235
  }, async ({ project_path }) => {
224
- if (!(await dirExists(project_path))) {
236
+ // Validate path to prevent traversal attacks
237
+ let validatedPath;
238
+ try {
239
+ validatedPath = await validateProjectPath(project_path);
240
+ }
241
+ catch (error) {
242
+ if (error instanceof PathValidationError) {
243
+ return {
244
+ content: [{ type: "text", text: `Error: ${error.message}` }],
245
+ };
246
+ }
225
247
  return {
226
248
  content: [
227
249
  {
@@ -232,7 +254,7 @@ server.registerTool("hardening_install", {
232
254
  };
233
255
  }
234
256
  try {
235
- const commandsDir = join(project_path, ".claude", "commands");
257
+ const commandsDir = join(validatedPath, ".claude", "commands");
236
258
  await mkdir(commandsDir, { recursive: true });
237
259
  const installed = [];
238
260
  const errors = [];
@@ -248,7 +270,7 @@ server.registerTool("hardening_install", {
248
270
  }
249
271
  }
250
272
  const output = {
251
- project: project_path,
273
+ project: validatedPath,
252
274
  commands_dir: commandsDir,
253
275
  installed_commands: installed,
254
276
  errors: errors.length > 0 ? errors : undefined,
@@ -295,7 +317,17 @@ server.registerTool("hardening_install_all", {
295
317
  },
296
318
  }, async ({ base_dir, dry_run = true }) => {
297
319
  const dir = base_dir || DEFAULT_PROJECTS_DIR;
298
- if (!(await dirExists(dir))) {
320
+ // Validate path to prevent traversal attacks
321
+ let validatedDir;
322
+ try {
323
+ validatedDir = await validateProjectPath(dir);
324
+ }
325
+ catch (error) {
326
+ if (error instanceof PathValidationError) {
327
+ return {
328
+ content: [{ type: "text", text: `Error: ${error.message}` }],
329
+ };
330
+ }
299
331
  return {
300
332
  content: [
301
333
  {
@@ -306,7 +338,7 @@ server.registerTool("hardening_install_all", {
306
338
  };
307
339
  }
308
340
  try {
309
- const projects = await discoverProjects(dir);
341
+ const projects = await discoverProjects(validatedDir);
310
342
  const commandCount = Object.keys(COMMANDS).length;
311
343
  const results = [];
312
344
  for (const projectPath of projects) {
@@ -347,7 +379,7 @@ server.registerTool("hardening_install_all", {
347
379
  }
348
380
  const output = {
349
381
  mode: dry_run ? "dry-run" : "applied",
350
- base_dir: dir,
382
+ base_dir: validatedDir,
351
383
  projects_scanned: projects.length,
352
384
  commands_per_project: commandCount,
353
385
  projects_updated: dry_run ? 0 : results.filter((r) => !r.error && r.commands_installed).length,
@@ -726,6 +758,12 @@ server.registerTool("certification_scan", {
726
758
  project: basename(project_path),
727
759
  });
728
760
  scanLogger.info("scanners.starting", { scanners, auto_detect });
761
+ const startTime = Date.now();
762
+ // Track scan start via telemetry
763
+ const scannersToRun = auto_detect
764
+ ? ["auto-detect"]
765
+ : Object.entries(scanners || {}).filter(([, v]) => v).map(([k]) => k);
766
+ await trackCertificationStarted(project_path, scannersToRun, [], auto_detect ? "auto" : "manual");
729
767
  // Use auto-detection or manual scanner selection
730
768
  let result;
731
769
  let detectedLanguages;
@@ -739,6 +777,24 @@ server.registerTool("certification_scan", {
739
777
  else {
740
778
  result = await runAllScanners(project_path, scanners);
741
779
  }
780
+ // Track scanner runs in telemetry
781
+ for (const scanner of Object.keys(result.byScanner)) {
782
+ await trackScannerRun(project_path, scanner, result.totalDuration / Object.keys(result.byScanner).length, // Approximate per-scanner duration
783
+ result.byScanner[scanner] || 0, !result.failedScanners.includes(scanner));
784
+ }
785
+ // Record scan in registry for analytics
786
+ const registry = getRegistry();
787
+ await registry.recordScan({
788
+ certificationId: certification_id,
789
+ projectPath: project_path,
790
+ scanDate: new Date().toISOString(),
791
+ duration: Date.now() - startTime,
792
+ findingsSummary: result.bySeverity,
793
+ totalFindings: result.totalFindings,
794
+ scannersRun: Object.keys(result.byScanner),
795
+ frameworksAssessed: [],
796
+ success: result.allSucceeded,
797
+ });
742
798
  // If certification_id provided and submit_findings is true, submit to certification
743
799
  if (certification_id && submit_findings && result.totalFindings > 0) {
744
800
  const certFindings = scannerFindingsToCertificationFindings(result);
@@ -1247,6 +1303,87 @@ server.registerTool("agent_complete", {
1247
1303
  };
1248
1304
  });
1249
1305
  // ---------------------------------------------------------------------------
1306
+ // Tool: Batch Submit Findings (for subagent JSON output)
1307
+ // ---------------------------------------------------------------------------
1308
+ server.registerTool("agent_batch_submit", {
1309
+ title: "Batch Submit Agent Findings",
1310
+ description: `Submit multiple findings from a subagent's JSON output. Use this when certification agents run as subagents (via Task tool) and output JSON instead of calling agent_submit_finding directly. Parses the agent's JSON output and submits all findings at once.`,
1311
+ inputSchema: {
1312
+ project_path: z.string().describe("Absolute path to the project root"),
1313
+ certification_id: z.string().describe("Certification ID"),
1314
+ agent: z.enum(["security", "reliability", "typesafety", "performance", "quality", "redteam"]),
1315
+ findings: z.array(z.object({
1316
+ id: z.string().describe("Finding ID (e.g., sec-001)"),
1317
+ severity: z.enum(["critical", "high", "medium", "low", "info"]),
1318
+ category: z.string().describe("Category of the finding"),
1319
+ file: z.string().optional().describe("File path where issue found"),
1320
+ line: z.number().optional().describe("Line number"),
1321
+ description: z.string().describe("Description of the issue"),
1322
+ evidence: z.string().describe("Evidence supporting the finding"),
1323
+ confidence: z.number().min(0).max(100).describe("Confidence score 0-100"),
1324
+ })).describe("Array of findings from subagent JSON output"),
1325
+ summary: z.object({
1326
+ total_findings: z.number(),
1327
+ by_severity: z.object({
1328
+ critical: z.number(),
1329
+ high: z.number(),
1330
+ medium: z.number(),
1331
+ low: z.number(),
1332
+ info: z.number(),
1333
+ }),
1334
+ confidence_score: z.number().min(0).max(100),
1335
+ coverage_areas: z.array(z.string()),
1336
+ notes: z.string().optional(),
1337
+ }).optional().describe("Optional summary to complete the agent run"),
1338
+ },
1339
+ annotations: {
1340
+ readOnlyHint: false,
1341
+ destructiveHint: false,
1342
+ idempotentHint: false,
1343
+ openWorldHint: false,
1344
+ },
1345
+ }, async ({ project_path, certification_id, agent, findings, summary }) => {
1346
+ const certLogger = createChildLogger({ certId: certification_id, agent });
1347
+ // Ensure agent is started
1348
+ let agentFindings = await getAgentFindings(project_path, certification_id, agent);
1349
+ if (!agentFindings) {
1350
+ certLogger.info("agent.started");
1351
+ agentFindings = await startAgent(project_path, certification_id, agent);
1352
+ }
1353
+ // Submit all findings
1354
+ const submitted = [];
1355
+ for (const finding of findings) {
1356
+ await submitFinding(project_path, certification_id, agent, finding);
1357
+ submitted.push({ id: finding.id, severity: finding.severity });
1358
+ certLogger.info("finding.submitted", {
1359
+ findingId: finding.id,
1360
+ severity: finding.severity,
1361
+ confidence: finding.confidence,
1362
+ });
1363
+ }
1364
+ // Complete agent if summary provided
1365
+ let completionResult = null;
1366
+ if (summary) {
1367
+ completionResult = await completeAgent(project_path, certification_id, agent, summary);
1368
+ certLogger.info("agent.completed", {
1369
+ totalFindings: summary.total_findings,
1370
+ confidenceScore: summary.confidence_score,
1371
+ });
1372
+ }
1373
+ return {
1374
+ content: [{
1375
+ type: "text",
1376
+ text: JSON.stringify({
1377
+ agent,
1378
+ findingsSubmitted: submitted.length,
1379
+ findings: submitted,
1380
+ completed: !!summary,
1381
+ summary: completionResult?.summary,
1382
+ }, null, 2),
1383
+ }],
1384
+ };
1385
+ });
1386
+ // ---------------------------------------------------------------------------
1250
1387
  // Tool: Cross-Verify Finding
1251
1388
  // ---------------------------------------------------------------------------
1252
1389
  server.registerTool("agent_cross_verify", {
@@ -1469,6 +1606,7 @@ server.registerTool("certification_finalize", {
1469
1606
  },
1470
1607
  }, async ({ project_path, certification_id }) => {
1471
1608
  const certLogger = createChildLogger({ certId: certification_id, project: basename(project_path) });
1609
+ const startTime = Date.now();
1472
1610
  const certification = await getCertification(project_path, certification_id);
1473
1611
  if (!certification) {
1474
1612
  certLogger.warn("certification.not_found");
@@ -1494,6 +1632,36 @@ server.registerTool("certification_finalize", {
1494
1632
  }
1495
1633
  // Generate artifacts
1496
1634
  const artifacts = await writeCertificationArtifacts(project_path, finalCert);
1635
+ // Track certification completion via telemetry
1636
+ const severityCounts = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
1637
+ let totalFindings = 0;
1638
+ for (const agentType of Object.keys(certification.agents || {})) {
1639
+ const agent = certification.agents[agentType];
1640
+ if (agent?.findings) {
1641
+ for (const finding of agent.findings) {
1642
+ severityCounts[finding.severity]++;
1643
+ totalFindings++;
1644
+ }
1645
+ }
1646
+ }
1647
+ await trackCertificationCompleted(project_path, certification_id, finalCert.consensus?.certification_level || "BLOCKED", finalCert.consensus?.overall_score || 0, Date.now() - new Date(certification.metadata.started_at).getTime(), severityCounts, totalFindings, [] // frameworks
1648
+ );
1649
+ // Record in registry
1650
+ const registry = getRegistry();
1651
+ await registry.recordScan({
1652
+ certificationId: certification_id,
1653
+ projectPath: project_path,
1654
+ scanDate: new Date().toISOString(),
1655
+ level: finalCert.consensus?.certification_level || "BLOCKED",
1656
+ score: finalCert.consensus?.overall_score || 0,
1657
+ duration: Date.now() - startTime,
1658
+ findingsSummary: severityCounts,
1659
+ totalFindings,
1660
+ scannersRun: Object.keys(certification.agents || {}),
1661
+ frameworksAssessed: [],
1662
+ success: true,
1663
+ tags: ["certification-finalized"],
1664
+ });
1497
1665
  certLogger.info("certification.finalized", {
1498
1666
  level: finalCert.consensus?.certification_level,
1499
1667
  score: finalCert.consensus?.overall_score,
@@ -2142,13 +2310,17 @@ server.registerTool("certification_eval_fixtures", {
2142
2310
  // ---------------------------------------------------------------------------
2143
2311
  server.registerTool("compliance_report", {
2144
2312
  title: "Generate Compliance Report",
2145
- description: `Generate a compliance report mapping certification findings to framework controls (SOC 2, ISO 27001). Shows which controls are at risk or non-compliant based on security findings.`,
2313
+ description: `Generate a compliance report mapping certification findings to framework controls. Supports all major frameworks (SOC 2, ISO 27001, PCI-DSS, HIPAA, 42 CFR Part 2, GDPR, NIST 800-53, CIS). Enable audit-defensibility options for evidence collection and audit trail verification.`,
2146
2314
  inputSchema: {
2147
2315
  project_path: z.string().describe("Absolute path to the project root"),
2148
2316
  certification_id: z.string().describe("Certification ID to assess"),
2149
2317
  framework: z
2150
- .enum(["SOC2", "ISO27001"])
2318
+ .enum(["SOC2", "ISO27001", "PCI-DSS", "HIPAA", "42-CFR-PART-2", "GDPR", "NIST-800-53", "CIS"])
2151
2319
  .describe("Compliance framework to assess"),
2320
+ collect_evidence: z.boolean().optional().describe("Collect evidence bundle for audit defensibility. Default: false"),
2321
+ verify_audit_trail: z.boolean().optional().describe("Verify audit trail integrity. Default: false"),
2322
+ store_evidence: z.boolean().optional().describe("Store evidence bundle to disk. Default: true if collecting"),
2323
+ include_attestation: z.boolean().optional().describe("Include attestation section with methodology and scope. Default: true when audit features enabled"),
2152
2324
  output_format: z
2153
2325
  .enum(["markdown", "json", "summary"])
2154
2326
  .optional()
@@ -2160,7 +2332,7 @@ server.registerTool("compliance_report", {
2160
2332
  idempotentHint: true,
2161
2333
  openWorldHint: false,
2162
2334
  },
2163
- }, async ({ project_path, certification_id, framework, output_format }) => {
2335
+ }, async ({ project_path, certification_id, framework, collect_evidence, verify_audit_trail, store_evidence, include_attestation, output_format }) => {
2164
2336
  const certification = await getCertification(project_path, certification_id);
2165
2337
  if (!certification) {
2166
2338
  return errorResponse(`Certification ${certification_id} not found`);
@@ -2172,6 +2344,46 @@ server.registerTool("compliance_report", {
2172
2344
  if (allFindings.length === 0) {
2173
2345
  return errorResponse("No findings to assess. Run certification agents first.");
2174
2346
  }
2347
+ // Check if audit-defensibility features are requested
2348
+ const useAuditFeatures = collect_evidence || verify_audit_trail;
2349
+ if (useAuditFeatures) {
2350
+ // Use the full compliance bundle for audit-defensible reports
2351
+ const result = await runSingleFrameworkAssessment({
2352
+ projectPath: project_path,
2353
+ findings: allFindings,
2354
+ framework: framework,
2355
+ certificationId: certification_id,
2356
+ collectEvidence: collect_evidence ?? false,
2357
+ verifyAuditTrail: verify_audit_trail ?? false,
2358
+ storeEvidence: store_evidence !== false,
2359
+ includeAttestation: include_attestation !== false,
2360
+ });
2361
+ if (output_format === "json") {
2362
+ return jsonResponse({
2363
+ status: result.status,
2364
+ complianceScore: result.report.status.complianceScore,
2365
+ riskScore: result.report.status.riskScore,
2366
+ auditVerification: result.auditVerification ? {
2367
+ verified: result.auditVerification.verified,
2368
+ chainIntegrity: result.auditVerification.chainIntegrity,
2369
+ } : undefined,
2370
+ evidenceBundle: result.evidenceBundle ? {
2371
+ id: result.evidenceBundle.id,
2372
+ bundleDigest: result.evidenceBundle.bundleDigest,
2373
+ signed: !!result.evidenceBundle.signature,
2374
+ } : undefined,
2375
+ evidencePath: result.evidencePath,
2376
+ report: result.report,
2377
+ });
2378
+ }
2379
+ else if (output_format === "summary") {
2380
+ return textResponse(generateCompactComplianceSummary(result.report));
2381
+ }
2382
+ else {
2383
+ return textResponse(result.markdownReport);
2384
+ }
2385
+ }
2386
+ // Standard report without audit features
2175
2387
  const report = generateComplianceReport(allFindings, framework, project_path, certification_id);
2176
2388
  if (output_format === "json") {
2177
2389
  return jsonResponse(report);
@@ -2188,16 +2400,20 @@ server.registerTool("compliance_report", {
2188
2400
  // ---------------------------------------------------------------------------
2189
2401
  server.registerTool("compliance_multi_report", {
2190
2402
  title: "Multi-Framework Compliance Report",
2191
- description: `Generate a compliance report across multiple frameworks (SOC 2 and ISO 27001). Shows comparative status and prioritized recommendations.`,
2403
+ description: `Generate a compliance report across multiple frameworks. Supports all major frameworks (SOC 2, ISO 27001, PCI-DSS, HIPAA, 42 CFR Part 2, GDPR, NIST 800-53, CIS). Shows comparative status and prioritized recommendations. Enable audit-defensibility options for evidence collection and audit trail verification.`,
2192
2404
  inputSchema: {
2193
2405
  project_path: z.string().describe("Absolute path to the project root"),
2194
2406
  certification_id: z.string().describe("Certification ID to assess"),
2195
2407
  frameworks: z
2196
- .array(z.enum(["SOC2", "ISO27001"]))
2408
+ .array(z.enum(["SOC2", "ISO27001", "PCI-DSS", "HIPAA", "42-CFR-PART-2", "GDPR", "NIST-800-53", "CIS"]))
2197
2409
  .optional()
2198
- .describe("Frameworks to assess. Default: both"),
2410
+ .describe("Frameworks to assess. Default: SOC2, ISO27001"),
2411
+ collect_evidence: z.boolean().optional().describe("Collect evidence bundle for audit defensibility. Default: false"),
2412
+ verify_audit_trail: z.boolean().optional().describe("Verify audit trail integrity. Default: false"),
2413
+ store_evidence: z.boolean().optional().describe("Store evidence bundle to disk. Default: true if collecting"),
2414
+ include_attestation: z.boolean().optional().describe("Include attestation section with methodology and scope. Default: true when audit features enabled"),
2199
2415
  output_format: z
2200
- .enum(["markdown", "json"])
2416
+ .enum(["markdown", "json", "summary"])
2201
2417
  .optional()
2202
2418
  .describe("Output format. Default: markdown"),
2203
2419
  },
@@ -2207,7 +2423,7 @@ server.registerTool("compliance_multi_report", {
2207
2423
  idempotentHint: true,
2208
2424
  openWorldHint: false,
2209
2425
  },
2210
- }, async ({ project_path, certification_id, frameworks, output_format }) => {
2426
+ }, async ({ project_path, certification_id, frameworks, collect_evidence, verify_audit_trail, store_evidence, include_attestation, output_format }) => {
2211
2427
  const certification = await getCertification(project_path, certification_id);
2212
2428
  if (!certification) {
2213
2429
  return errorResponse(`Certification ${certification_id} not found`);
@@ -2219,6 +2435,53 @@ server.registerTool("compliance_multi_report", {
2219
2435
  return errorResponse("No findings to assess. Run certification agents first.");
2220
2436
  }
2221
2437
  const selectedFrameworks = (frameworks || ["SOC2", "ISO27001"]);
2438
+ // Check if audit-defensibility features are requested
2439
+ const useAuditFeatures = collect_evidence || verify_audit_trail;
2440
+ if (useAuditFeatures) {
2441
+ // Use the full compliance bundle for audit-defensible reports
2442
+ const result = await runComplianceAssessment({
2443
+ projectPath: project_path,
2444
+ findings: allFindings,
2445
+ frameworks: selectedFrameworks,
2446
+ certificationId: certification_id,
2447
+ collectEvidence: collect_evidence ?? false,
2448
+ verifyAuditTrail: verify_audit_trail ?? false,
2449
+ storeEvidence: store_evidence !== false,
2450
+ includeAttestation: include_attestation !== false,
2451
+ });
2452
+ if (output_format === "json") {
2453
+ return jsonResponse({
2454
+ status: result.status,
2455
+ combinedScore: result.combinedScore,
2456
+ frameworks: Object.fromEntries(Object.entries(result.reports).map(([fw, report]) => [
2457
+ fw,
2458
+ report ? {
2459
+ complianceScore: report.status.complianceScore,
2460
+ riskScore: report.status.riskScore,
2461
+ controlsNonCompliant: report.status.controlsNonCompliant,
2462
+ } : null,
2463
+ ])),
2464
+ auditVerification: result.auditVerification ? {
2465
+ verified: result.auditVerification.verified,
2466
+ chainIntegrity: result.auditVerification.chainIntegrity,
2467
+ } : undefined,
2468
+ evidenceBundle: result.evidenceBundle ? {
2469
+ id: result.evidenceBundle.id,
2470
+ bundleDigest: result.evidenceBundle.bundleDigest,
2471
+ signed: !!result.evidenceBundle.signature,
2472
+ } : undefined,
2473
+ evidencePath: result.evidencePath,
2474
+ multiFrameworkReport: result.multiFrameworkReport,
2475
+ });
2476
+ }
2477
+ else if (output_format === "summary") {
2478
+ return textResponse(generateComplianceSummary(result));
2479
+ }
2480
+ else {
2481
+ return textResponse(result.markdownReport);
2482
+ }
2483
+ }
2484
+ // Standard report without audit features
2222
2485
  const report = generateMultiFrameworkReport(allFindings, selectedFrameworks, project_path, certification_id);
2223
2486
  if (output_format === "json") {
2224
2487
  return jsonResponse(report);
@@ -2232,10 +2495,10 @@ server.registerTool("compliance_multi_report", {
2232
2495
  // ---------------------------------------------------------------------------
2233
2496
  server.registerTool("compliance_controls", {
2234
2497
  title: "List Compliance Controls",
2235
- description: `List available compliance controls for a framework. Use to understand what controls are assessed and their mapping to finding categories.`,
2498
+ description: `List available compliance controls for a framework. Use to understand what controls are assessed and their mapping to finding categories. Supports all major frameworks.`,
2236
2499
  inputSchema: {
2237
2500
  framework: z
2238
- .enum(["SOC2", "ISO27001"])
2501
+ .enum(["SOC2", "ISO27001", "PCI-DSS", "HIPAA", "42-CFR-PART-2", "GDPR", "NIST-800-53", "CIS"])
2239
2502
  .describe("Compliance framework"),
2240
2503
  category: z
2241
2504
  .string()
@@ -2253,7 +2516,8 @@ server.registerTool("compliance_controls", {
2253
2516
  if (category) {
2254
2517
  controls = controls.filter((c) => c.category === category);
2255
2518
  }
2256
- const categories = framework === "SOC2" ? getSOC2Categories() : getISO27001Categories();
2519
+ // Get unique categories from controls
2520
+ const categories = [...new Set(controls.map((c) => c.category))].sort();
2257
2521
  return jsonResponse({
2258
2522
  framework,
2259
2523
  totalControls: controls.length,
@@ -2269,6 +2533,213 @@ server.registerTool("compliance_controls", {
2269
2533
  });
2270
2534
  });
2271
2535
  // ---------------------------------------------------------------------------
2536
+ // Tool: Healthcare Compliance Scan (HIPAA + 42 CFR Part 2)
2537
+ // ---------------------------------------------------------------------------
2538
+ server.registerTool("healthcare_compliance_scan", {
2539
+ title: "Healthcare Compliance Assessment",
2540
+ description: `Run a combined HIPAA and 42 CFR Part 2 compliance assessment. Generates audit-defensible reports with evidence collection and audit trail verification. Designed for SUD (Substance Use Disorder) and healthcare applications.`,
2541
+ inputSchema: {
2542
+ project_path: z.string().describe("Absolute path to the project root"),
2543
+ certification_id: z.string().optional().describe("Certification ID to use findings from"),
2544
+ collect_evidence: z.boolean().optional().describe("Collect evidence bundle. Default: true"),
2545
+ verify_audit_trail: z.boolean().optional().describe("Verify audit trail integrity. Default: true"),
2546
+ store_evidence: z.boolean().optional().describe("Store evidence bundle to disk. Default: true"),
2547
+ output_format: z.enum(["markdown", "json", "summary"]).optional().describe("Output format. Default: markdown"),
2548
+ },
2549
+ annotations: {
2550
+ readOnlyHint: true,
2551
+ destructiveHint: false,
2552
+ idempotentHint: true,
2553
+ openWorldHint: false,
2554
+ },
2555
+ }, async ({ project_path, certification_id, collect_evidence, verify_audit_trail, store_evidence, output_format }) => {
2556
+ try {
2557
+ const validatedPath = await validateProjectPath(project_path);
2558
+ const allFindings = [];
2559
+ const certId = certification_id || undefined;
2560
+ let certificationToUse;
2561
+ if (certId) {
2562
+ certificationToUse = await getCertification(validatedPath, certId);
2563
+ }
2564
+ else {
2565
+ certificationToUse = await getLatestCertification(validatedPath);
2566
+ }
2567
+ if (certificationToUse) {
2568
+ const agents = Object.keys(certificationToUse.agents);
2569
+ for (const agent of agents) {
2570
+ const agentData = certificationToUse.agents[agent];
2571
+ if (agentData?.findings) {
2572
+ // Cast to any to avoid type conflicts between local and imported types
2573
+ allFindings.push(...agentData.findings);
2574
+ }
2575
+ }
2576
+ }
2577
+ // Run healthcare compliance assessment
2578
+ const result = await runHealthcareComplianceAssessment({
2579
+ projectPath: validatedPath,
2580
+ // Healthcare bundle expects Finding[] from certification/types
2581
+ findings: allFindings,
2582
+ certificationId: certificationToUse?.metadata.id,
2583
+ collectEvidence: collect_evidence !== false,
2584
+ verifyAuditTrail: verify_audit_trail !== false,
2585
+ storeEvidence: store_evidence !== false,
2586
+ });
2587
+ if (output_format === "json") {
2588
+ return jsonResponse({
2589
+ status: result.status,
2590
+ combinedScore: result.combinedScore,
2591
+ hipaaScore: result.hipaaReport.status.complianceScore,
2592
+ cfr42Score: result.cfr42Report.status.complianceScore,
2593
+ hipaaControls: {
2594
+ total: result.hipaaReport.status.totalControls,
2595
+ compliant: result.hipaaReport.status.controlsCovered,
2596
+ atRisk: result.hipaaReport.status.controlsAtRisk,
2597
+ nonCompliant: result.hipaaReport.status.controlsNonCompliant,
2598
+ },
2599
+ cfr42Controls: {
2600
+ total: result.cfr42Report.status.totalControls,
2601
+ compliant: result.cfr42Report.status.controlsCovered,
2602
+ atRisk: result.cfr42Report.status.controlsAtRisk,
2603
+ nonCompliant: result.cfr42Report.status.controlsNonCompliant,
2604
+ },
2605
+ auditTrailVerified: result.auditVerification?.verified,
2606
+ evidenceBundleId: result.evidenceBundle?.id,
2607
+ evidencePath: result.evidencePath,
2608
+ });
2609
+ }
2610
+ else if (output_format === "summary") {
2611
+ return textResponse(generateHealthcareComplianceSummary(result));
2612
+ }
2613
+ return textResponse(result.markdownReport);
2614
+ }
2615
+ catch (error) {
2616
+ if (error instanceof PathValidationError) {
2617
+ return errorResponse(error.message);
2618
+ }
2619
+ return errorResponse(`Healthcare compliance scan failed: ${error instanceof Error ? error.message : String(error)}`);
2620
+ }
2621
+ });
2622
+ // ---------------------------------------------------------------------------
2623
+ // Tool: Verify Audit Trail
2624
+ // ---------------------------------------------------------------------------
2625
+ server.registerTool("verify_audit_trail", {
2626
+ title: "Verify Audit Trail Integrity",
2627
+ description: `Verify the tamper-evident audit trail by checking hash chains. Detects if any historical entries have been modified. Critical for healthcare compliance (HIPAA, 42 CFR Part 2) audit defensibility.`,
2628
+ inputSchema: {
2629
+ project_path: z.string().describe("Absolute path to the project root"),
2630
+ output_format: z.enum(["markdown", "json"]).optional().describe("Output format. Default: markdown"),
2631
+ },
2632
+ annotations: {
2633
+ readOnlyHint: true,
2634
+ destructiveHint: false,
2635
+ idempotentHint: true,
2636
+ openWorldHint: false,
2637
+ },
2638
+ }, async ({ project_path, output_format }) => {
2639
+ try {
2640
+ const validatedPath = await validateProjectPath(project_path);
2641
+ const result = await verifyHistoryIntegrity(validatedPath);
2642
+ if (output_format === "json") {
2643
+ return jsonResponse({
2644
+ verified: result.verified,
2645
+ projectPath: result.projectPath,
2646
+ verifiedAt: result.verifiedAt,
2647
+ totalEntries: result.totalEntries,
2648
+ entriesVerified: result.entriesVerified,
2649
+ entriesPassed: result.entriesPassed,
2650
+ entriesFailed: result.entriesFailed,
2651
+ chainIntegrity: result.chainIntegrity,
2652
+ genesisHash: result.genesisHash,
2653
+ headHash: result.headHash,
2654
+ failures: result.failures,
2655
+ });
2656
+ }
2657
+ return textResponse(formatVerificationResultAsMarkdown(result));
2658
+ }
2659
+ catch (error) {
2660
+ if (error instanceof PathValidationError) {
2661
+ return errorResponse(error.message);
2662
+ }
2663
+ return errorResponse(`Audit trail verification failed: ${error instanceof Error ? error.message : String(error)}`);
2664
+ }
2665
+ });
2666
+ // ---------------------------------------------------------------------------
2667
+ // Tool: Collect Evidence Bundle
2668
+ // ---------------------------------------------------------------------------
2669
+ server.registerTool("collect_evidence_bundle", {
2670
+ title: "Collect Evidence Bundle",
2671
+ description: `Collect and package audit evidence for compliance. Creates a cryptographically attested bundle of scan results, compliance reports, SBOM, and history snapshots. Essential for audit defensibility.`,
2672
+ inputSchema: {
2673
+ project_path: z.string().describe("Absolute path to the project root"),
2674
+ certification_id: z.string().optional().describe("Certification ID to associate with"),
2675
+ frameworks: z
2676
+ .array(z.enum(["HIPAA", "42-CFR-PART-2", "SOC2", "ISO27001", "PCI-DSS", "GDPR", "NIST-800-53", "CIS"]))
2677
+ .optional()
2678
+ .describe("Compliance frameworks to include reports for"),
2679
+ include_sbom: z.boolean().optional().describe("Include SBOM. Default: true"),
2680
+ include_history: z.boolean().optional().describe("Include history snapshot. Default: true"),
2681
+ include_scans: z.boolean().optional().describe("Include scan results. Default: true"),
2682
+ store: z.boolean().optional().describe("Store bundle to disk. Default: true"),
2683
+ output_format: z.enum(["markdown", "json"]).optional().describe("Output format. Default: markdown"),
2684
+ },
2685
+ annotations: {
2686
+ readOnlyHint: true,
2687
+ destructiveHint: false,
2688
+ idempotentHint: true,
2689
+ openWorldHint: false,
2690
+ },
2691
+ }, async ({ project_path, certification_id, frameworks, include_sbom, include_history, include_scans, store, output_format }) => {
2692
+ try {
2693
+ const validatedPath = await validateProjectPath(project_path);
2694
+ const result = await collectEvidence({
2695
+ projectPath: validatedPath,
2696
+ certificationId: certification_id,
2697
+ frameworks: frameworks,
2698
+ includeSbom: include_sbom !== false,
2699
+ includeHistory: include_history !== false,
2700
+ includeScanResults: include_scans !== false,
2701
+ });
2702
+ if (!result.success || !result.bundle) {
2703
+ return errorResponse(result.error || "Evidence collection failed");
2704
+ }
2705
+ let storedPath;
2706
+ if (store !== false) {
2707
+ storedPath = await storeEvidenceBundle(validatedPath, result.bundle);
2708
+ }
2709
+ if (output_format === "json") {
2710
+ return jsonResponse({
2711
+ success: true,
2712
+ bundleId: result.bundle.id,
2713
+ createdAt: result.bundle.createdAt,
2714
+ artifactCount: result.bundle.artifacts.length,
2715
+ bundleDigest: result.bundle.bundleDigest,
2716
+ storedPath,
2717
+ warnings: result.warnings,
2718
+ artifacts: result.bundle.artifacts.map((a) => ({
2719
+ type: a.type,
2720
+ name: a.name,
2721
+ sizeBytes: a.sizeBytes,
2722
+ digest: a.contentDigest,
2723
+ })),
2724
+ });
2725
+ }
2726
+ let output = formatEvidenceBundleAsMarkdown(result.bundle);
2727
+ if (storedPath) {
2728
+ output += `\n\n**Stored at:** \`${storedPath}\``;
2729
+ }
2730
+ if (result.warnings.length > 0) {
2731
+ output += `\n\n**Warnings:**\n${result.warnings.map((w) => `- ${w}`).join("\n")}`;
2732
+ }
2733
+ return textResponse(output);
2734
+ }
2735
+ catch (error) {
2736
+ if (error instanceof PathValidationError) {
2737
+ return errorResponse(error.message);
2738
+ }
2739
+ return errorResponse(`Evidence collection failed: ${error instanceof Error ? error.message : String(error)}`);
2740
+ }
2741
+ });
2742
+ // ---------------------------------------------------------------------------
2272
2743
  // Tool: Generate SBOM
2273
2744
  // ---------------------------------------------------------------------------
2274
2745
  server.registerTool("sbom_generate", {
@@ -3212,7 +3683,11 @@ Maps findings to AI compliance frameworks (OWASP LLM, NIST AI RMF, EU AI Act).`,
3212
3683
  if (!authorized) {
3213
3684
  return errorResponse("Agent scanning requires explicit authorization. Set authorized=true to confirm you have permission to scan this target.");
3214
3685
  }
3686
+ const startTime = Date.now();
3215
3687
  try {
3688
+ // Track scan start via telemetry
3689
+ const enabledScanners = scanners || AGENT_SCANNER_TYPES;
3690
+ await trackCertificationStarted(target, enabledScanners, frameworks || [], "agent-cert");
3216
3691
  // Build scan target
3217
3692
  const scanTarget = {};
3218
3693
  if (target.startsWith("http://") || target.startsWith("https://")) {
@@ -3228,7 +3703,6 @@ Maps findings to AI compliance frameworks (OWASP LLM, NIST AI RMF, EU AI Act).`,
3228
3703
  scanTarget.npmPackage = target;
3229
3704
  }
3230
3705
  // Build scanner options
3231
- const enabledScanners = scanners || AGENT_SCANNER_TYPES;
3232
3706
  const scannerFlags = {
3233
3707
  manifestAudit: enabledScanners.includes("manifest-audit"),
3234
3708
  toolDrift: enabledScanners.includes("tool-description-drift"),
@@ -3267,6 +3741,28 @@ Maps findings to AI compliance frameworks (OWASP LLM, NIST AI RMF, EU AI Act).`,
3267
3741
  }
3268
3742
  // Generate summary
3269
3743
  const summary = generateAgentScannerSummary(result);
3744
+ // Record scan in registry for analytics
3745
+ const registry = getRegistry();
3746
+ await registry.recordScan({
3747
+ certificationId: certification_id,
3748
+ projectPath: target,
3749
+ scanDate: new Date().toISOString(),
3750
+ level: result.certificationReadiness === "ready" ? "CERTIFIED"
3751
+ : result.certificationReadiness === "needs-review" ? "REVIEW_REQUIRED"
3752
+ : "BLOCKED",
3753
+ score: 100 - result.riskScore,
3754
+ duration: Date.now() - startTime,
3755
+ findingsSummary: result.bySeverity,
3756
+ totalFindings: result.totalFindings,
3757
+ scannersRun: result.scanners.map((s) => s.scanner),
3758
+ frameworksAssessed: frameworks || [],
3759
+ success: result.allSucceeded,
3760
+ tags: ["agent-cert", "mcp-security"],
3761
+ });
3762
+ // Track individual scanner runs
3763
+ for (const scanner of result.scanners) {
3764
+ await trackScannerRun(target, scanner.scanner, scanner.duration || 0, scanner.findings.length, scanner.success);
3765
+ }
3270
3766
  return jsonResponse({
3271
3767
  success: result.allSucceeded,
3272
3768
  target,