infraweaver 0.1.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 (524) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +244 -0
  3. package/dist/agents/claude.d.ts +113 -0
  4. package/dist/agents/claudePretoolGate.d.ts +137 -0
  5. package/dist/agents/dispatchMetrics.d.ts +77 -0
  6. package/dist/agents/gateServer.d.ts +7 -0
  7. package/dist/agents/index.d.ts +6 -0
  8. package/dist/agents/nativeFsDenies.d.ts +46 -0
  9. package/dist/agents/opencode.d.ts +284 -0
  10. package/dist/agents/opencodePlugin.d.ts +96 -0
  11. package/dist/agents/opencodeShared.d.ts +40 -0
  12. package/dist/agents/postRun.d.ts +126 -0
  13. package/dist/agents/reviewer.d.ts +41 -0
  14. package/dist/agents/sessionLabeler.d.ts +97 -0
  15. package/dist/agents/shared.d.ts +246 -0
  16. package/dist/agents/subagentModels.d.ts +19 -0
  17. package/dist/agents/tokenQuota.d.ts +118 -0
  18. package/dist/agents/writeGateSource.d.ts +22 -0
  19. package/dist/agents/writePolicy.d.ts +79 -0
  20. package/dist/brand.d.ts +67 -0
  21. package/dist/cli.mjs +247846 -0
  22. package/dist/external.d.ts +227 -0
  23. package/dist/i18n/scaffolding.d.ts +59 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.js +247061 -0
  26. package/dist/internal/index.d.ts +18 -0
  27. package/dist/internal.js +2398 -0
  28. package/dist/lifecycle.d.ts +2 -0
  29. package/dist/main.d.ts +8 -0
  30. package/dist/mcp/arkConfig.d.ts +1 -0
  31. package/dist/mcp/assess.d.ts +157 -0
  32. package/dist/mcp/capabilityContext.d.ts +71 -0
  33. package/dist/mcp/changeSummary.d.ts +52 -0
  34. package/dist/mcp/checkSuite.d.ts +27 -0
  35. package/dist/mcp/checkout.d.ts +92 -0
  36. package/dist/mcp/comment.d.ts +127 -0
  37. package/dist/mcp/commitInfo.d.ts +11 -0
  38. package/dist/mcp/crosswalk.d.ts +178 -0
  39. package/dist/mcp/crosswalkDigest.d.ts +1 -0
  40. package/dist/mcp/cyberEssentials.d.ts +24 -0
  41. package/dist/mcp/dashboard.d.ts +125 -0
  42. package/dist/mcp/dependencies.d.ts +12 -0
  43. package/dist/mcp/frameworks.d.ts +74 -0
  44. package/dist/mcp/geminiSanitizer.d.ts +28 -0
  45. package/dist/mcp/git.d.ts +60 -0
  46. package/dist/mcp/guardrails.d.ts +222 -0
  47. package/dist/mcp/issue.d.ts +20 -0
  48. package/dist/mcp/issueComments.d.ts +11 -0
  49. package/dist/mcp/issueEvents.d.ts +11 -0
  50. package/dist/mcp/issueInfo.d.ts +11 -0
  51. package/dist/mcp/labels.d.ts +14 -0
  52. package/dist/mcp/localContext.d.ts +26 -0
  53. package/dist/mcp/moduleExtraction.d.ts +79 -0
  54. package/dist/mcp/moduleTests.d.ts +106 -0
  55. package/dist/mcp/modules.d.ts +198 -0
  56. package/dist/mcp/output.d.ts +16 -0
  57. package/dist/mcp/pathSafety.d.ts +14 -0
  58. package/dist/mcp/policy.d.ts +50 -0
  59. package/dist/mcp/pr.d.ts +64 -0
  60. package/dist/mcp/prInfo.d.ts +11 -0
  61. package/dist/mcp/providerSchema.d.ts +52 -0
  62. package/dist/mcp/review.d.ts +212 -0
  63. package/dist/mcp/reviewComments.d.ts +245 -0
  64. package/dist/mcp/roots.d.ts +60 -0
  65. package/dist/mcp/scope.d.ts +26 -0
  66. package/dist/mcp/selectMode.d.ts +20 -0
  67. package/dist/mcp/server.d.ts +61 -0
  68. package/dist/mcp/shared.d.ts +62 -0
  69. package/dist/mcp/shell.d.ts +58 -0
  70. package/dist/mcp/staleFix.d.ts +138 -0
  71. package/dist/mcp/terraform/azurePrices.d.ts +8 -0
  72. package/dist/mcp/terraform/baseline.d.ts +57 -0
  73. package/dist/mcp/terraform/concernResult.d.ts +38 -0
  74. package/dist/mcp/terraform/cost.d.ts +55 -0
  75. package/dist/mcp/terraform/cspm.d.ts +80 -0
  76. package/dist/mcp/terraform/currency.d.ts +110 -0
  77. package/dist/mcp/terraform/decisions.d.ts +178 -0
  78. package/dist/mcp/terraform/eidas.d.ts +100 -0
  79. package/dist/mcp/terraform/evidence.d.ts +343 -0
  80. package/dist/mcp/terraform/findings.d.ts +76 -0
  81. package/dist/mcp/terraform/fixMemory.d.ts +81 -0
  82. package/dist/mcp/terraform/governance.d.ts +56 -0
  83. package/dist/mcp/terraform/hcl.d.ts +115 -0
  84. package/dist/mcp/terraform/iacLanguages.d.ts +120 -0
  85. package/dist/mcp/terraform/idleFloor.d.ts +78 -0
  86. package/dist/mcp/terraform/keyless.d.ts +68 -0
  87. package/dist/mcp/terraform/moduleDocs.d.ts +56 -0
  88. package/dist/mcp/terraform/nativeRules.d.ts +38 -0
  89. package/dist/mcp/terraform/nativeScan.d.ts +11 -0
  90. package/dist/mcp/terraform/noiseProfile.d.ts +100 -0
  91. package/dist/mcp/terraform/normalization.d.ts +52 -0
  92. package/dist/mcp/terraform/oscal.d.ts +103 -0
  93. package/dist/mcp/terraform/packs/azurerm-3-4.d.ts +2 -0
  94. package/dist/mcp/terraform/packs/cloudflare-4-5.d.ts +2 -0
  95. package/dist/mcp/terraform/paths.d.ts +28 -0
  96. package/dist/mcp/terraform/plan.d.ts +157 -0
  97. package/dist/mcp/terraform/planDrift.d.ts +199 -0
  98. package/dist/mcp/terraform/policyAuthor.d.ts +191 -0
  99. package/dist/mcp/terraform/prowlerOcsf.d.ts +136 -0
  100. package/dist/mcp/terraform/refactor.d.ts +183 -0
  101. package/dist/mcp/terraform/registry.d.ts +108 -0
  102. package/dist/mcp/terraform/risk.d.ts +41 -0
  103. package/dist/mcp/terraform/scanSession.d.ts +116 -0
  104. package/dist/mcp/terraform/scannerCache.d.ts +15 -0
  105. package/dist/mcp/terraform/scannerEgress.d.ts +72 -0
  106. package/dist/mcp/terraform/scannerJson.d.ts +13 -0
  107. package/dist/mcp/terraform/scanners.d.ts +205 -0
  108. package/dist/mcp/terraform/signing.d.ts +61 -0
  109. package/dist/mcp/terraform/stateLocking.d.ts +18 -0
  110. package/dist/mcp/terraform/subprocess.d.ts +43 -0
  111. package/dist/mcp/terraform/suppressions.d.ts +90 -0
  112. package/dist/mcp/terraform/taxonomy.d.ts +39 -0
  113. package/dist/mcp/terraform/tfquery.d.ts +47 -0
  114. package/dist/mcp/terraform/toolchainIntegrity.d.ts +82 -0
  115. package/dist/mcp/terraform/tools/authorPolicy.d.ts +59 -0
  116. package/dist/mcp/terraform/tools/detectPlanDrift.d.ts +20 -0
  117. package/dist/mcp/terraform/tools/emitCost.d.ts +11 -0
  118. package/dist/mcp/terraform/tools/emitSarif.d.ts +14 -0
  119. package/dist/mcp/terraform/tools/fixMemory.d.ts +19 -0
  120. package/dist/mcp/terraform/tools/infracostDiff.d.ts +22 -0
  121. package/dist/mcp/terraform/tools/ingestExternalFindings.d.ts +21 -0
  122. package/dist/mcp/terraform/tools/moduleLookup.d.ts +14 -0
  123. package/dist/mcp/terraform/tools/moduleSearch.d.ts +23 -0
  124. package/dist/mcp/terraform/tools/plan.d.ts +59 -0
  125. package/dist/mcp/terraform/tools/providerUpgrade.d.ts +55 -0
  126. package/dist/mcp/terraform/tools/readFindings.d.ts +17 -0
  127. package/dist/mcp/terraform/tools/scan.d.ts +114 -0
  128. package/dist/mcp/terraform/tools/validate.d.ts +31 -0
  129. package/dist/mcp/terraform/tools/verifyRemediation.d.ts +31 -0
  130. package/dist/mcp/terraform/tools/versionCurrency.d.ts +5 -0
  131. package/dist/mcp/terraform/tools.d.ts +16 -0
  132. package/dist/mcp/terraform/transformPacks.d.ts +267 -0
  133. package/dist/mcp/terraform/types.d.ts +226 -0
  134. package/dist/mcp/terraform/verification.d.ts +81 -0
  135. package/dist/mcp/terraform/vex.d.ts +115 -0
  136. package/dist/mcp/terraform/writeOnly.d.ts +67 -0
  137. package/dist/mcp/terraform.d.ts +51 -0
  138. package/dist/mcp/terratest.d.ts +85 -0
  139. package/dist/mcp/untrustedContent.d.ts +76 -0
  140. package/dist/mcp/upload.d.ts +8 -0
  141. package/dist/models.d.ts +181 -0
  142. package/dist/modes/address-reviews.d.ts +2 -0
  143. package/dist/modes/assess.d.ts +2 -0
  144. package/dist/modes/build.d.ts +2 -0
  145. package/dist/modes/compliance-audit.d.ts +2 -0
  146. package/dist/modes/cost-optimization.d.ts +2 -0
  147. package/dist/modes/drift-detect.d.ts +2 -0
  148. package/dist/modes/fix.d.ts +2 -0
  149. package/dist/modes/incremental-review.d.ts +2 -0
  150. package/dist/modes/index.d.ts +24 -0
  151. package/dist/modes/modernize-deprecated.d.ts +2 -0
  152. package/dist/modes/plan.d.ts +2 -0
  153. package/dist/modes/policy-gate.d.ts +2 -0
  154. package/dist/modes/prFormats.d.ts +3 -0
  155. package/dist/modes/refactor.d.ts +2 -0
  156. package/dist/modes/refresh-remediation.d.ts +2 -0
  157. package/dist/modes/remediate-and-refactor.d.ts +2 -0
  158. package/dist/modes/remediate.d.ts +2 -0
  159. package/dist/modes/resolve-conflicts.d.ts +2 -0
  160. package/dist/modes/review.d.ts +2 -0
  161. package/dist/modes/summarize-pr.d.ts +2 -0
  162. package/dist/modes/task.d.ts +2 -0
  163. package/dist/modes/terraform-code-review.d.ts +2 -0
  164. package/dist/modes/types.d.ts +9 -0
  165. package/dist/modes/update-dependencies.d.ts +2 -0
  166. package/dist/prep/index.d.ts +7 -0
  167. package/dist/prep/installNodeDependencies.d.ts +2 -0
  168. package/dist/prep/installPythonDependencies.d.ts +2 -0
  169. package/dist/prep/types.d.ts +31 -0
  170. package/dist/reviewQuality.d.ts +107 -0
  171. package/dist/skills/terraform-best-practices/SKILL.md +379 -0
  172. package/dist/toolState.d.ts +123 -0
  173. package/dist/utils/activity.d.ts +40 -0
  174. package/dist/utils/agent.d.ts +47 -0
  175. package/dist/utils/agentHangReport.d.ts +38 -0
  176. package/dist/utils/annotations.d.ts +17 -0
  177. package/dist/utils/apiFetch.d.ts +11 -0
  178. package/dist/utils/apiKeys.d.ts +48 -0
  179. package/dist/utils/apiUrl.d.ts +28 -0
  180. package/dist/utils/assets.d.ts +8 -0
  181. package/dist/utils/baseRefConfig.d.ts +121 -0
  182. package/dist/utils/billingErrors.d.ts +85 -0
  183. package/dist/utils/body.d.ts +34 -0
  184. package/dist/utils/buildInfraweaverFooter.d.ts +29 -0
  185. package/dist/utils/byokFallback.d.ts +85 -0
  186. package/dist/utils/changeImpact.d.ts +101 -0
  187. package/dist/utils/claudeSubscription.d.ts +30 -0
  188. package/dist/utils/cli.d.ts +10 -0
  189. package/dist/utils/cloudClient.d.ts +33 -0
  190. package/dist/utils/cloudFetchCore.d.ts +42 -0
  191. package/dist/utils/cloudReport.d.ts +114 -0
  192. package/dist/utils/codexHome.d.ts +29 -0
  193. package/dist/utils/codexOAuth.d.ts +60 -0
  194. package/dist/utils/diffCoverage.d.ts +63 -0
  195. package/dist/utils/env.d.ts +46 -0
  196. package/dist/utils/errorReport.d.ts +17 -0
  197. package/dist/utils/exitHandler.d.ts +8 -0
  198. package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
  199. package/dist/utils/gitAuth.d.ts +84 -0
  200. package/dist/utils/gitAuthServer.d.ts +24 -0
  201. package/dist/utils/github.d.ts +104 -0
  202. package/dist/utils/globals.d.ts +3 -0
  203. package/dist/utils/infraweaverConfig.d.ts +56 -0
  204. package/dist/utils/install.d.ts +37 -0
  205. package/dist/utils/instructions.d.ts +48 -0
  206. package/dist/utils/keylessOidc.d.ts +28 -0
  207. package/dist/utils/leapingComment.d.ts +11 -0
  208. package/dist/utils/learnings.d.ts +62 -0
  209. package/dist/utils/learningsTruncate.d.ts +25 -0
  210. package/dist/utils/lifecycle.d.ts +57 -0
  211. package/dist/utils/log.d.ts +111 -0
  212. package/dist/utils/modelResolver.d.ts +63 -0
  213. package/dist/utils/moduleFetch.d.ts +39 -0
  214. package/dist/utils/normalizeEnv.d.ts +30 -0
  215. package/dist/utils/openCodeModels.d.ts +11 -0
  216. package/dist/utils/openaiCompatible.d.ts +26 -0
  217. package/dist/utils/opentofuMcp.d.ts +50 -0
  218. package/dist/utils/overrides.d.ts +40 -0
  219. package/dist/utils/packageManager.d.ts +49 -0
  220. package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
  221. package/dist/utils/payload.d.ts +249 -0
  222. package/dist/utils/prSummary.d.ts +61 -0
  223. package/dist/utils/presets.d.ts +36 -0
  224. package/dist/utils/progressComment.d.ts +146 -0
  225. package/dist/utils/providerErrors.d.ts +31 -0
  226. package/dist/utils/proxyModel.d.ts +76 -0
  227. package/dist/utils/rangeDiff.d.ts +51 -0
  228. package/dist/utils/redact.d.ts +33 -0
  229. package/dist/utils/registryAuth.d.ts +52 -0
  230. package/dist/utils/remediationCommand.d.ts +58 -0
  231. package/dist/utils/retry.d.ts +38 -0
  232. package/dist/utils/reviewCleanup.d.ts +14 -0
  233. package/dist/utils/run.d.ts +9 -0
  234. package/dist/utils/runContext.d.ts +112 -0
  235. package/dist/utils/runContextData.d.ts +42 -0
  236. package/dist/utils/runErrorRenderer.d.ts +64 -0
  237. package/dist/utils/runLifecycle.d.ts +89 -0
  238. package/dist/utils/runStartupLog.d.ts +15 -0
  239. package/dist/utils/secrets.d.ts +34 -0
  240. package/dist/utils/setup.d.ts +90 -0
  241. package/dist/utils/shell.d.ts +44 -0
  242. package/dist/utils/skills.d.ts +10 -0
  243. package/dist/utils/subprocess.d.ts +81 -0
  244. package/dist/utils/terraformMcp.d.ts +46 -0
  245. package/dist/utils/time.d.ts +15 -0
  246. package/dist/utils/timer.d.ts +23 -0
  247. package/dist/utils/todoTracking.d.ts +16 -0
  248. package/dist/utils/token.d.ts +52 -0
  249. package/dist/utils/toolLicensing.d.ts +56 -0
  250. package/dist/utils/toolSelection.d.ts +73 -0
  251. package/dist/utils/toon.d.ts +16 -0
  252. package/dist/utils/version.d.ts +2 -0
  253. package/dist/utils/versioning.d.ts +7 -0
  254. package/dist/utils/vertex.d.ts +16 -0
  255. package/dist/utils/workflow.d.ts +13 -0
  256. package/package.json +132 -0
  257. package/src/agents/claude.ts +1588 -0
  258. package/src/agents/claudePretoolGate.ts +286 -0
  259. package/src/agents/dispatchMetrics.ts +162 -0
  260. package/src/agents/gateServer.ts +178 -0
  261. package/src/agents/index.ts +10 -0
  262. package/src/agents/nativeFsDenies.ts +164 -0
  263. package/src/agents/opencode.ts +1600 -0
  264. package/src/agents/opencodePlugin.ts +304 -0
  265. package/src/agents/opencodeShared.ts +134 -0
  266. package/src/agents/postRun.ts +615 -0
  267. package/src/agents/reviewer.ts +134 -0
  268. package/src/agents/sessionLabeler.ts +185 -0
  269. package/src/agents/shared.ts +377 -0
  270. package/src/agents/subagentModels.ts +40 -0
  271. package/src/agents/tokenQuota.ts +203 -0
  272. package/src/agents/writeGateSource.ts +148 -0
  273. package/src/agents/writePolicy.ts +132 -0
  274. package/src/brand.ts +84 -0
  275. package/src/cli.ts +117 -0
  276. package/src/commands/gha.ts +188 -0
  277. package/src/commands/mcp.ts +126 -0
  278. package/src/commands/verifyEvidence.ts +100 -0
  279. package/src/entry.ts +7 -0
  280. package/src/entryPost.ts +109 -0
  281. package/src/external.ts +308 -0
  282. package/src/i18n/scaffolding.ts +191 -0
  283. package/src/index.ts +7 -0
  284. package/src/internal/index.ts +74 -0
  285. package/src/lifecycle.ts +2 -0
  286. package/src/main.ts +815 -0
  287. package/src/mcp/__fixtures__/infraweaver-scratch-pr-49-review-3485940013.json +110 -0
  288. package/src/mcp/__fixtures__/infraweaver-scratch-pr-64-review-3531000326.json +14 -0
  289. package/src/mcp/__fixtures__/infraweaver-test-repo-pr-1.diff.json +67 -0
  290. package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
  291. package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
  292. package/src/mcp/arkConfig.ts +7 -0
  293. package/src/mcp/assess.ts +723 -0
  294. package/src/mcp/capabilityContext.ts +80 -0
  295. package/src/mcp/changeSummary.ts +145 -0
  296. package/src/mcp/checkSuite.ts +261 -0
  297. package/src/mcp/checkout.ts +1015 -0
  298. package/src/mcp/comment.ts +683 -0
  299. package/src/mcp/commitInfo.ts +57 -0
  300. package/src/mcp/crosswalk.ts +1253 -0
  301. package/src/mcp/crosswalkDigest.ts +5 -0
  302. package/src/mcp/cyberEssentials.ts +60 -0
  303. package/src/mcp/dashboard.ts +232 -0
  304. package/src/mcp/dependencies.ts +190 -0
  305. package/src/mcp/frameworks.ts +236 -0
  306. package/src/mcp/geminiSanitizer.ts +212 -0
  307. package/src/mcp/git.ts +1116 -0
  308. package/src/mcp/guardrails.ts +771 -0
  309. package/src/mcp/issue.ts +74 -0
  310. package/src/mcp/issueComments.ts +48 -0
  311. package/src/mcp/issueEvents.ts +100 -0
  312. package/src/mcp/issueInfo.ts +72 -0
  313. package/src/mcp/labels.ts +35 -0
  314. package/src/mcp/localContext.ts +65 -0
  315. package/src/mcp/localServer.ts +217 -0
  316. package/src/mcp/moduleExtraction.ts +368 -0
  317. package/src/mcp/moduleTests.ts +421 -0
  318. package/src/mcp/modules.ts +752 -0
  319. package/src/mcp/output.ts +71 -0
  320. package/src/mcp/pathSafety.ts +28 -0
  321. package/src/mcp/policy.ts +226 -0
  322. package/src/mcp/pr.ts +238 -0
  323. package/src/mcp/prInfo.ts +91 -0
  324. package/src/mcp/providerSchema.ts +175 -0
  325. package/src/mcp/review.ts +1081 -0
  326. package/src/mcp/reviewComments.ts +1137 -0
  327. package/src/mcp/roots.ts +217 -0
  328. package/src/mcp/scope.ts +82 -0
  329. package/src/mcp/selectMode.ts +207 -0
  330. package/src/mcp/server.ts +498 -0
  331. package/src/mcp/shared.ts +120 -0
  332. package/src/mcp/shell.ts +631 -0
  333. package/src/mcp/staleFix.ts +557 -0
  334. package/src/mcp/terraform/__snapshots__/moduleDocs.test.ts.snap +40 -0
  335. package/src/mcp/terraform/azurePrices.ts +38 -0
  336. package/src/mcp/terraform/baseline.ts +172 -0
  337. package/src/mcp/terraform/concernResult.ts +111 -0
  338. package/src/mcp/terraform/cost.ts +175 -0
  339. package/src/mcp/terraform/cspm.ts +326 -0
  340. package/src/mcp/terraform/currency.ts +354 -0
  341. package/src/mcp/terraform/decisions.ts +590 -0
  342. package/src/mcp/terraform/eidas.ts +134 -0
  343. package/src/mcp/terraform/evidence.ts +771 -0
  344. package/src/mcp/terraform/findings.ts +383 -0
  345. package/src/mcp/terraform/fixMemory.ts +253 -0
  346. package/src/mcp/terraform/governance.ts +231 -0
  347. package/src/mcp/terraform/hcl.ts +273 -0
  348. package/src/mcp/terraform/iacLanguages.ts +431 -0
  349. package/src/mcp/terraform/idleFloor.ts +304 -0
  350. package/src/mcp/terraform/keyless.ts +123 -0
  351. package/src/mcp/terraform/moduleDocs.ts +303 -0
  352. package/src/mcp/terraform/nativeRules.ts +152 -0
  353. package/src/mcp/terraform/nativeScan.ts +67 -0
  354. package/src/mcp/terraform/noiseProfile.ts +183 -0
  355. package/src/mcp/terraform/normalization.ts +265 -0
  356. package/src/mcp/terraform/oscal.ts +283 -0
  357. package/src/mcp/terraform/packs/azurerm-3-4.ts +382 -0
  358. package/src/mcp/terraform/packs/cloudflare-4-5.ts +114 -0
  359. package/src/mcp/terraform/paths.ts +59 -0
  360. package/src/mcp/terraform/plan.ts +377 -0
  361. package/src/mcp/terraform/planDrift.ts +520 -0
  362. package/src/mcp/terraform/policyAuthor.ts +886 -0
  363. package/src/mcp/terraform/prowlerOcsf.ts +380 -0
  364. package/src/mcp/terraform/refactor.ts +879 -0
  365. package/src/mcp/terraform/registry.ts +317 -0
  366. package/src/mcp/terraform/risk.ts +99 -0
  367. package/src/mcp/terraform/scanSession.ts +242 -0
  368. package/src/mcp/terraform/scannerCache.ts +86 -0
  369. package/src/mcp/terraform/scannerEgress.ts +141 -0
  370. package/src/mcp/terraform/scannerJson.ts +22 -0
  371. package/src/mcp/terraform/scanners.ts +1134 -0
  372. package/src/mcp/terraform/signing.ts +151 -0
  373. package/src/mcp/terraform/stateLocking.ts +106 -0
  374. package/src/mcp/terraform/subprocess.ts +102 -0
  375. package/src/mcp/terraform/suppressions.ts +551 -0
  376. package/src/mcp/terraform/taxonomy.ts +0 -0
  377. package/src/mcp/terraform/tfquery.ts +159 -0
  378. package/src/mcp/terraform/toolchainIntegrity.ts +151 -0
  379. package/src/mcp/terraform/tools/authorPolicy.ts +185 -0
  380. package/src/mcp/terraform/tools/detectPlanDrift.ts +279 -0
  381. package/src/mcp/terraform/tools/emitCost.ts +130 -0
  382. package/src/mcp/terraform/tools/emitSarif.ts +108 -0
  383. package/src/mcp/terraform/tools/fixMemory.ts +82 -0
  384. package/src/mcp/terraform/tools/infracostDiff.ts +110 -0
  385. package/src/mcp/terraform/tools/ingestExternalFindings.ts +259 -0
  386. package/src/mcp/terraform/tools/moduleLookup.ts +86 -0
  387. package/src/mcp/terraform/tools/moduleSearch.ts +46 -0
  388. package/src/mcp/terraform/tools/plan.ts +384 -0
  389. package/src/mcp/terraform/tools/providerUpgrade.ts +217 -0
  390. package/src/mcp/terraform/tools/readFindings.ts +138 -0
  391. package/src/mcp/terraform/tools/scan.ts +303 -0
  392. package/src/mcp/terraform/tools/validate.ts +160 -0
  393. package/src/mcp/terraform/tools/verifyRemediation.ts +190 -0
  394. package/src/mcp/terraform/tools/versionCurrency.ts +64 -0
  395. package/src/mcp/terraform/tools.ts +62 -0
  396. package/src/mcp/terraform/transformPacks.ts +594 -0
  397. package/src/mcp/terraform/types.ts +382 -0
  398. package/src/mcp/terraform/verification.ts +150 -0
  399. package/src/mcp/terraform/vex.ts +367 -0
  400. package/src/mcp/terraform/writeOnly.ts +252 -0
  401. package/src/mcp/terraform.ts +52 -0
  402. package/src/mcp/terratest.ts +215 -0
  403. package/src/mcp/untrustedContent.ts +126 -0
  404. package/src/mcp/upload.ts +123 -0
  405. package/src/models.ts +753 -0
  406. package/src/modes/address-reviews.ts +35 -0
  407. package/src/modes/assess.ts +27 -0
  408. package/src/modes/build.ts +86 -0
  409. package/src/modes/compliance-audit.ts +25 -0
  410. package/src/modes/cost-optimization.ts +36 -0
  411. package/src/modes/drift-detect.ts +31 -0
  412. package/src/modes/fix.ts +29 -0
  413. package/src/modes/incremental-review.ts +103 -0
  414. package/src/modes/index.ts +94 -0
  415. package/src/modes/modernize-deprecated.ts +36 -0
  416. package/src/modes/plan.ts +21 -0
  417. package/src/modes/policy-gate.ts +27 -0
  418. package/src/modes/prFormats.ts +325 -0
  419. package/src/modes/refactor.ts +74 -0
  420. package/src/modes/refresh-remediation.ts +40 -0
  421. package/src/modes/remediate-and-refactor.ts +42 -0
  422. package/src/modes/remediate.ts +78 -0
  423. package/src/modes/resolve-conflicts.ts +32 -0
  424. package/src/modes/review.ts +135 -0
  425. package/src/modes/summarize-pr.ts +42 -0
  426. package/src/modes/task.ts +26 -0
  427. package/src/modes/terraform-code-review.ts +68 -0
  428. package/src/modes/types.ts +17 -0
  429. package/src/modes/update-dependencies.ts +44 -0
  430. package/src/prep/index.ts +96 -0
  431. package/src/prep/installNodeDependencies.ts +251 -0
  432. package/src/prep/installPythonDependencies.ts +235 -0
  433. package/src/prep/types.ts +38 -0
  434. package/src/reviewQuality.ts +237 -0
  435. package/src/runCli.ts +335 -0
  436. package/src/skills/terraform-best-practices/SKILL.md +379 -0
  437. package/src/toolState.ts +241 -0
  438. package/src/utils/activity.ts +210 -0
  439. package/src/utils/agent.ts +245 -0
  440. package/src/utils/agentHangReport.ts +180 -0
  441. package/src/utils/annotations.ts +56 -0
  442. package/src/utils/apiFetch.ts +19 -0
  443. package/src/utils/apiKeys.ts +305 -0
  444. package/src/utils/apiUrl.ts +42 -0
  445. package/src/utils/assets.ts +123 -0
  446. package/src/utils/baseRefConfig.ts +254 -0
  447. package/src/utils/billingErrors.ts +210 -0
  448. package/src/utils/body.ts +168 -0
  449. package/src/utils/buildInfraweaverFooter.ts +104 -0
  450. package/src/utils/byokFallback.ts +137 -0
  451. package/src/utils/changeImpact.ts +330 -0
  452. package/src/utils/claudeSubscription.ts +93 -0
  453. package/src/utils/cli.ts +36 -0
  454. package/src/utils/cloudClient.ts +57 -0
  455. package/src/utils/cloudFetchCore.ts +139 -0
  456. package/src/utils/cloudReport.ts +316 -0
  457. package/src/utils/codexHome.ts +204 -0
  458. package/src/utils/codexOAuth.ts +154 -0
  459. package/src/utils/codexRefreshDetect.ts +36 -0
  460. package/src/utils/diffCoverage.ts +404 -0
  461. package/src/utils/env.ts +84 -0
  462. package/src/utils/errorReport.ts +94 -0
  463. package/src/utils/exitHandler.ts +35 -0
  464. package/src/utils/fixDoubleEscapedString.ts +9 -0
  465. package/src/utils/ghaCore.ts +13 -0
  466. package/src/utils/gitAuth.ts +268 -0
  467. package/src/utils/gitAuthServer.ts +186 -0
  468. package/src/utils/github.ts +676 -0
  469. package/src/utils/globals.ts +9 -0
  470. package/src/utils/humanEditCapture.ts +198 -0
  471. package/src/utils/infraweaverConfig.ts +202 -0
  472. package/src/utils/install.ts +279 -0
  473. package/src/utils/instructions.ts +640 -0
  474. package/src/utils/keylessOidc.ts +47 -0
  475. package/src/utils/leapingComment.ts +20 -0
  476. package/src/utils/learnings.ts +145 -0
  477. package/src/utils/learningsTruncate.ts +42 -0
  478. package/src/utils/lifecycle.ts +198 -0
  479. package/src/utils/log.ts +452 -0
  480. package/src/utils/modelResolver.ts +105 -0
  481. package/src/utils/moduleFetch.ts +138 -0
  482. package/src/utils/normalizeEnv.ts +106 -0
  483. package/src/utils/openCodeModels.ts +86 -0
  484. package/src/utils/openaiCompatible.ts +52 -0
  485. package/src/utils/opentofuMcp.ts +61 -0
  486. package/src/utils/overrides.ts +100 -0
  487. package/src/utils/packageManager.ts +257 -0
  488. package/src/utils/patchWorkflowRunFields.ts +173 -0
  489. package/src/utils/payload.ts +1206 -0
  490. package/src/utils/prSummary.ts +148 -0
  491. package/src/utils/presets.ts +105 -0
  492. package/src/utils/progressComment.ts +266 -0
  493. package/src/utils/providerErrors.ts +189 -0
  494. package/src/utils/proxyModel.ts +239 -0
  495. package/src/utils/rangeDiff.ts +182 -0
  496. package/src/utils/redact.ts +86 -0
  497. package/src/utils/registryAuth.ts +115 -0
  498. package/src/utils/remediationCommand.ts +144 -0
  499. package/src/utils/retry.ts +180 -0
  500. package/src/utils/reviewCleanup.ts +116 -0
  501. package/src/utils/run.ts +100 -0
  502. package/src/utils/runContext.ts +276 -0
  503. package/src/utils/runContextData.ts +121 -0
  504. package/src/utils/runErrorRenderer.ts +282 -0
  505. package/src/utils/runFixture.ts +91 -0
  506. package/src/utils/runLifecycle.ts +360 -0
  507. package/src/utils/runStartupLog.ts +67 -0
  508. package/src/utils/secrets.ts +208 -0
  509. package/src/utils/setup.ts +368 -0
  510. package/src/utils/shell.ts +132 -0
  511. package/src/utils/skills.ts +67 -0
  512. package/src/utils/subprocess.ts +474 -0
  513. package/src/utils/terraformMcp.ts +99 -0
  514. package/src/utils/time.ts +59 -0
  515. package/src/utils/timer.ts +72 -0
  516. package/src/utils/todoTracking.ts +168 -0
  517. package/src/utils/token.ts +294 -0
  518. package/src/utils/toolLicensing.ts +152 -0
  519. package/src/utils/toolSelection.ts +239 -0
  520. package/src/utils/toon.ts +76 -0
  521. package/src/utils/version.ts +10 -0
  522. package/src/utils/versioning.ts +44 -0
  523. package/src/utils/vertex.ts +94 -0
  524. package/src/utils/workflow.ts +25 -0
@@ -0,0 +1,367 @@
1
+ /**
2
+ * CycloneDX 1.7 VEX emitter — the same read-only assessment as
3
+ * [[evidence]]/[[oscal]], rendered as a Vulnerability Exploitability eXchange
4
+ * document a downstream security scanner / SBOM consumer ingests directly.
5
+ *
6
+ * Honesty rules (matching the evidence bundle):
7
+ * - Statuses derive from the five-status verdict: a code-verified violation
8
+ * (`fail`) is `exploitable`; a human-decision control the engine can flag
9
+ * but not prove (`not-code-verifiable`) is `in_triage`. We NEVER emit
10
+ * `not_affected` — an unfired control is a coverage gap, not a clearance.
11
+ * - Deterministic: name-based UUIDs (the v5-from-sha256 helper shared with
12
+ * [[oscal]]), clock passed in — same inputs ⇒ byte-identical output.
13
+ * - Scanner rule ids ride in `id`/`source`; file/line in the affected `file`
14
+ * components + per-vulnerability properties.
15
+ *
16
+ * Signed via [[signing]] when INFRAWEAVER_SIGNING_KEY is present, exactly like
17
+ * the evidence bundle (detached `.sig` beside the file).
18
+ */
19
+ import { mkdirSync, writeFileSync } from "node:fs";
20
+ import { basename, dirname } from "node:path";
21
+ import { type } from "arktype";
22
+ import { PRODUCT_NAME, TOOL_NAME, VEX_FILE } from "#app/brand";
23
+ import { runAssessmentPipeline } from "#app/mcp/assess";
24
+ import type { LocalToolContext } from "#app/mcp/localContext";
25
+ import { resolveWithinCwd } from "#app/mcp/pathSafety";
26
+ import { execute, tool, toolOk } from "#app/mcp/shared";
27
+ import type { EvidenceSubject } from "#app/mcp/terraform/evidence";
28
+ import {
29
+ buildInTotoStatement,
30
+ keylessBundlePath,
31
+ keylessSigningPossible,
32
+ keylessSignStatement,
33
+ } from "#app/mcp/terraform/keyless";
34
+ import { deterministicUuid } from "#app/mcp/terraform/oscal";
35
+ import {
36
+ loadSigningKey,
37
+ type SignatureMetadata,
38
+ signatureMetadata,
39
+ signBytes,
40
+ } from "#app/mcp/terraform/signing";
41
+ import type { Concern, Severity } from "#app/mcp/terraform/types";
42
+ import {
43
+ type ConcernVerificationStatus,
44
+ concernVerificationStatus,
45
+ } from "#app/mcp/terraform/verification";
46
+ import { log } from "#app/utils/cli";
47
+ import { githubContext } from "#app/utils/env";
48
+ import { redactSecrets } from "#app/utils/redact";
49
+ import packageJson from "#package.json" with { type: "json" };
50
+
51
+ /**
52
+ * The spec version emitted. CycloneDX 1.7 is ECMA-424 2nd edition — the version
53
+ * SBOM/VEX consumers standardise on, so emitting 1.6 would date the artefact.
54
+ *
55
+ * The document deliberately stays inside the 1.6 STRUCTURAL SUBSET: every field
56
+ * used here exists in both schemas, so a 1.6-only consumer still parses it.
57
+ * `vex.test.ts` validates the same bytes against both schema fixtures, which is
58
+ * what turns that from an intention into a guarantee.
59
+ *
60
+ * The signing path is untouched by this: signatures cover the exact bytes on
61
+ * disk, not the schema they conform to.
62
+ */
63
+ export const CYCLONEDX_SPEC_VERSION = "1.7" as const;
64
+
65
+ /** The older spec version the emitted document remains parseable as. */
66
+ export const CYCLONEDX_COMPATIBLE_SPEC_VERSION = "1.6" as const;
67
+ export const DEFAULT_VEX_PATH = `compliance/${VEX_FILE}`;
68
+
69
+ /** CycloneDX impact-analysis states this emitter may produce. `not_affected`
70
+ * is deliberately NOT in this union: absence of a finding is not proof, so an
71
+ * unfired control never becomes a clearance. */
72
+ export type VexAnalysisState = "exploitable" | "in_triage";
73
+
74
+ interface VexProperty {
75
+ name: string;
76
+ value: string;
77
+ }
78
+
79
+ interface VexComponent {
80
+ type: "file";
81
+ "bom-ref": string;
82
+ name: string;
83
+ }
84
+
85
+ interface VexVulnerability {
86
+ "bom-ref": string;
87
+ /** the scanner's rule id (trivy AVD-*, checkov CKV_*, tflint rule…). */
88
+ id: string;
89
+ source: { name: string };
90
+ description: string;
91
+ ratings: { severity: Severity; method: "other" }[];
92
+ analysis: {
93
+ state: VexAnalysisState;
94
+ detail: string;
95
+ };
96
+ affects: { ref: string }[];
97
+ properties: VexProperty[];
98
+ }
99
+
100
+ export interface CycloneDxVexDocument {
101
+ bomFormat: "CycloneDX";
102
+ specVersion: typeof CYCLONEDX_SPEC_VERSION;
103
+ serialNumber: string;
104
+ version: number;
105
+ metadata: {
106
+ timestamp: string;
107
+ tools: {
108
+ components: {
109
+ type: "application";
110
+ name: typeof TOOL_NAME;
111
+ version: string;
112
+ }[];
113
+ };
114
+ component: { type: "application"; "bom-ref": string; name: string };
115
+ properties: VexProperty[];
116
+ };
117
+ components: VexComponent[];
118
+ vulnerabilities: VexVulnerability[];
119
+ }
120
+
121
+ const SUBJECT_REF = "infraweaver:workspace";
122
+
123
+ const HONESTY_NOTE =
124
+ "Indicative, scanner-derived VEX from a deterministic rule-pack — statuses are code-verified " +
125
+ "only. `not_affected` is never emitted: an unfired control is a coverage gap, not a clearance.";
126
+
127
+ /** the five-status verdict → CycloneDX impact-analysis state (honest subset). */
128
+ export function vexAnalysisState(
129
+ status: ConcernVerificationStatus,
130
+ ): VexAnalysisState {
131
+ return status === "fail" ? "exploitable" : "in_triage";
132
+ }
133
+
134
+ function subjectName(s: EvidenceSubject): string {
135
+ return s.repo ? `${s.repo}${s.ref ? `@${s.ref}` : ""}` : s.scanned_dir;
136
+ }
137
+
138
+ /**
139
+ * Build the CycloneDX 1.7 VEX document from the assessment's OPEN concerns.
140
+ * Pure (clock/identity passed in); UUIDs are name-based, seeded like the OSCAL
141
+ * emitter — same inputs ⇒ identical bytes (the seed includes the generation
142
+ * timestamp, so ids are stable per generation, not across wall-clock re-runs).
143
+ * One vulnerability per concern; each affected file becomes a `file` component
144
+ * the vulnerability `affects` by bom-ref. When `signature` is supplied the
145
+ * public key + fingerprint are embedded in the document's metadata properties
146
+ * (inside the signed bytes), so a consumer holding only the artefact + `.sig`
147
+ * can verify it offline — the same self-contained contract as the evidence
148
+ * bundle.
149
+ */
150
+ export function buildVexDocument(args: {
151
+ concerns: Concern[];
152
+ subject: EvidenceSubject;
153
+ generatedAt: string;
154
+ version?: string;
155
+ signature?: SignatureMetadata | null;
156
+ }): CycloneDxVexDocument {
157
+ const { concerns, subject, generatedAt } = args;
158
+ const seed = `${subject.commit ?? subject.scanned_dir}|${generatedAt}`;
159
+
160
+ const fileRefs = new Map<string, VexComponent>();
161
+ const vulnerabilities: VexVulnerability[] = [];
162
+ for (const c of concerns) {
163
+ const file = c.location.file.replace(/\\/g, "/").replace(/^\.\//, "");
164
+ const fileRef = `file:${file}`;
165
+ if (!fileRefs.has(fileRef)) {
166
+ fileRefs.set(fileRef, { type: "file", "bom-ref": fileRef, name: file });
167
+ }
168
+ const { status, reason } = concernVerificationStatus(c);
169
+ const state = vexAnalysisState(status);
170
+ vulnerabilities.push({
171
+ "bom-ref": deterministicUuid(`vex|${seed}|${c.id}`),
172
+ id: c.rule_id,
173
+ source: { name: c.source },
174
+ // LLM02: evidence + suppression reason are repo/agent-derived — redact before
175
+ // they land in the signed, downstream-ingested VEX document.
176
+ description: redactSecrets(c.evidence),
177
+ ratings: [{ severity: c.severity, method: "other" }],
178
+ analysis: {
179
+ state,
180
+ detail: redactSecrets(
181
+ state === "exploitable"
182
+ ? `code-verified violation (five-status verdict: ${status})`
183
+ : `needs human/process evidence — IaC cannot prove it (five-status verdict: ${status}${reason ? `; ${reason}` : ""})`,
184
+ ),
185
+ },
186
+ affects: [{ ref: fileRef }],
187
+ properties: [
188
+ { name: "infraweaver:concern_id", value: c.id },
189
+ { name: "infraweaver:file", value: file },
190
+ { name: "infraweaver:line", value: String(c.location.line ?? "") },
191
+ { name: "infraweaver:verification_status", value: status },
192
+ ],
193
+ });
194
+ }
195
+
196
+ return {
197
+ bomFormat: "CycloneDX",
198
+ specVersion: CYCLONEDX_SPEC_VERSION,
199
+ serialNumber: `urn:uuid:${deterministicUuid(`vex-serial|${seed}`)}`,
200
+ version: 1,
201
+ metadata: {
202
+ timestamp: generatedAt,
203
+ tools: {
204
+ components: [
205
+ {
206
+ type: "application",
207
+ name: TOOL_NAME,
208
+ version: args.version ?? packageJson.version,
209
+ },
210
+ ],
211
+ },
212
+ component: {
213
+ type: "application",
214
+ "bom-ref": SUBJECT_REF,
215
+ name: subjectName(subject),
216
+ },
217
+ properties: [
218
+ { name: "infraweaver:honesty", value: HONESTY_NOTE },
219
+ ...(args.signature
220
+ ? [
221
+ {
222
+ name: "infraweaver:signature:algorithm",
223
+ value: args.signature.algorithm,
224
+ },
225
+ {
226
+ name: "infraweaver:signature:public_key",
227
+ value: args.signature.public_key,
228
+ },
229
+ {
230
+ name: "infraweaver:signature:fingerprint_sha256",
231
+ value: args.signature.fingerprint_sha256,
232
+ },
233
+ {
234
+ name: "infraweaver:signature:detached_signature",
235
+ value: args.signature.detached_signature,
236
+ },
237
+ ]
238
+ : []),
239
+ ],
240
+ },
241
+ components: [...fileRefs.values()].sort((a, b) =>
242
+ a.name.localeCompare(b.name),
243
+ ),
244
+ vulnerabilities,
245
+ };
246
+ }
247
+
248
+ export const TerraformEmitVexParams = type({
249
+ "output_path?": type.string.describe(
250
+ `where to write the CycloneDX VEX (default: ./${DEFAULT_VEX_PATH}). Commit it so the compliance/ path is the auditable trail.`,
251
+ ),
252
+ "severity_threshold?": type(
253
+ "'critical' | 'high' | 'medium' | 'low' | 'info'",
254
+ ).describe(
255
+ "minimum severity to include (default: the run's configured threshold, else low).",
256
+ ),
257
+ });
258
+
259
+ export function TerraformEmitVexTool(ctx: LocalToolContext) {
260
+ return tool({
261
+ name: "terraform_emit_vex",
262
+ description:
263
+ `Emit the read-only assessment as a CYCLONEDX ${CYCLONEDX_SPEC_VERSION} VEX document (default ` +
264
+ `\`${DEFAULT_VEX_PATH}\`) — the same open concerns as \`terraform_emit_evidence\`, in the ` +
265
+ "vulnerability-exchange format a downstream SBOM/security scanner ingests directly. Each concern " +
266
+ "becomes one `vulnerabilities[]` entry: the scanner rule id in `id`/`source`, file/line in the " +
267
+ "affected `file` component + properties, and an HONEST `analysis.state` from the five-status verdict " +
268
+ "(`exploitable` for a code-verified violation, `in_triage` for a human-decision control) — " +
269
+ "`not_affected` is NEVER emitted for an unfired control. UUIDs are name-based (deterministic for " +
270
+ "the same commit + generation time); when INFRAWEAVER_SIGNING_KEY is set the document is signed " +
271
+ "(detached Ed25519 `.sig` beside the file, public key + fingerprint embedded in the metadata " +
272
+ "properties so it verifies offline). It NEVER modifies Terraform or opens a PR. Pairs with " +
273
+ `\`terraform_emit_evidence\` (${PRODUCT_NAME}'s own schema) and \`terraform_emit_oscal\` (GRC import).`,
274
+ parameters: TerraformEmitVexParams,
275
+ execute: execute(async ({ output_path, severity_threshold }) => {
276
+ const configured = ctx.payload.severityThreshold as Severity | undefined;
277
+ const threshold: Severity = severity_threshold ?? configured ?? "low";
278
+ const { cwd, open } = runAssessmentPipeline(ctx, threshold);
279
+
280
+ // SECURITY: confine the agent-supplied path to the workspace (same guard
281
+ // as terraform_emit_evidence / terraform_emit_sarif).
282
+ const target = resolveWithinCwd(cwd, output_path ?? DEFAULT_VEX_PATH);
283
+ // load (and validate) the key BEFORE any bytes hit disk — a set-but-
284
+ // malformed key must fail the tool without leaving an unsigned artefact
285
+ // behind for a retry/later step to pick up (same order as evidence.ts).
286
+ const signingKey = loadSigningKey();
287
+ const sigPath = `${target}.sig`;
288
+ const gh = githubContext();
289
+ const doc = buildVexDocument({
290
+ concerns: open,
291
+ subject: {
292
+ scanned_dir: cwd,
293
+ repo: gh.repository,
294
+ ref: gh.refName,
295
+ commit: gh.sha,
296
+ },
297
+ generatedAt: new Date().toISOString(),
298
+ signature: signingKey
299
+ ? signatureMetadata(signingKey, basename(sigPath))
300
+ : null,
301
+ });
302
+ const bytes = `${JSON.stringify(doc, null, 2)}\n`;
303
+ // the default compliance/ dir may not exist yet on a first run.
304
+ mkdirSync(dirname(target), { recursive: true });
305
+ writeFileSync(target, bytes);
306
+ // same signing contract as the evidence bundle: detached signature over
307
+ // the exact bytes the artefact holds on disk.
308
+ if (signingKey)
309
+ writeFileSync(sigPath, `${signBytes(bytes, signingKey)}\n`);
310
+
311
+ // Second tier: keyless Sigstore attestation of the VEX (opt-in, CI-only,
312
+ // best-effort — same contract as terraform_emit_evidence). Binds the exact
313
+ // VEX bytes to the workflow OIDC identity, logged to Rekor.
314
+ let keylessBundleFile: string | undefined;
315
+ if (keylessSigningPossible()) {
316
+ try {
317
+ const statement = buildInTotoStatement({
318
+ subjectName: basename(target),
319
+ artifactBytes: bytes,
320
+ predicate: {
321
+ spec_version: CYCLONEDX_SPEC_VERSION,
322
+ vulnerabilities: doc.vulnerabilities.length,
323
+ generated_at: doc.metadata.timestamp,
324
+ subject: { repo: gh.repository, ref: gh.refName, commit: gh.sha },
325
+ },
326
+ });
327
+ const sigstoreBundle = await keylessSignStatement(statement);
328
+ keylessBundleFile = keylessBundlePath(target);
329
+ writeFileSync(
330
+ keylessBundleFile,
331
+ `${JSON.stringify(sigstoreBundle, null, 2)}\n`,
332
+ );
333
+ log.info(
334
+ `» terraform_emit_vex: keyless Sigstore attestation → ${keylessBundleFile}`,
335
+ );
336
+ } catch (e) {
337
+ log.warning(
338
+ `» terraform_emit_vex: keyless signing failed (VEX still emitted): ${e instanceof Error ? e.message : String(e)}`,
339
+ );
340
+ }
341
+ }
342
+ log.info(
343
+ `» terraform_emit_vex: ${doc.vulnerabilities.length} vulnerability statement(s) → ${target}` +
344
+ (signingKey
345
+ ? ` (signed ed25519, key ${signingKey.fingerprint.slice(0, 16)}…)`
346
+ : ""),
347
+ );
348
+ return toolOk({
349
+ output_path: target,
350
+ vulnerabilities: doc.vulnerabilities.length,
351
+ spec_version: CYCLONEDX_SPEC_VERSION,
352
+ signed: signingKey !== null,
353
+ ...(signingKey
354
+ ? {
355
+ signature_path: sigPath,
356
+ key_fingerprint_sha256: signingKey.fingerprint,
357
+ }
358
+ : {}),
359
+ keyless_signed: keylessBundleFile !== undefined,
360
+ ...(keylessBundleFile
361
+ ? { keyless_bundle_path: keylessBundleFile }
362
+ : {}),
363
+ note: `${basename(target)} is CycloneDX ${CYCLONEDX_SPEC_VERSION} (ECMA-424 2nd ed.), written inside the ${CYCLONEDX_COMPATIBLE_SPEC_VERSION} structural subset so older consumers still parse it — attach it to the compliance trail or feed it to an SBOM-aware scanner.`,
364
+ });
365
+ }),
366
+ });
367
+ }
@@ -0,0 +1,252 @@
1
+ /**
2
+ * `secret-persisted-to-state` — the write-only / ephemeral-value finding class
3
+ * (Terraform 1.11+, OpenTofu 1.11+).
4
+ *
5
+ * THE DEFECT. Before write-only arguments, handing a provider a credential meant
6
+ * the credential landed in the state file in plain text — forever, in every
7
+ * historical state version, readable by everyone with state access, and copied
8
+ * into every backup. `terraform plan` could not avoid it: the value had to be in
9
+ * the plan to be sent to the provider. Write-only arguments (`password_wo` and
10
+ * its `password_wo_version` companion) fix this properly: the value is sent to
11
+ * the provider and then discarded, never serialised into state.
12
+ *
13
+ * WHY THE PROVIDER SCHEMA, NOT A RULE TABLE. Whether `password_wo` exists is a
14
+ * fact about the provider version the customer has pinned — providers adopted
15
+ * write-only arguments resource by resource, over several majors. A hardcoded
16
+ * catalogue of "resources that support write-only" would be wrong for somebody on
17
+ * the day it was written and wronger every month after, and it would propose
18
+ * `password_wo` on a provider where that argument does not exist — a "fix" that
19
+ * fails `terraform plan`. So the rule reads `terraform providers schema -json`
20
+ * (already loaded and cached by [[providerSchema]]) and fires ONLY when the
21
+ * installed provider genuinely declares the counterpart. No schema ⇒ no finding,
22
+ * reported as a coverage gap rather than guessed at.
23
+ *
24
+ * That also makes the remediation transform trivially correct: rename the
25
+ * argument to the counterpart the schema named, and add the `_wo_version` the
26
+ * schema named. Both sides come from the same source of truth, so the ✗→✓ re-scan
27
+ * clears the finding exactly when the edit is right.
28
+ *
29
+ * WHAT THIS RULE DELIBERATELY DOES NOT DO. The plan it came from paired it with a
30
+ * suppressor for stale UPSTREAM findings — a resource that has already migrated to
31
+ * `password_wo` can still trip "password stored in plaintext" rules in scanner
32
+ * packs that predate write-only arguments, and being punished for adopting the
33
+ * better pattern is a real incentive inversion. That suppressor is not here,
34
+ * because deciding another tool's finding is stale means pattern-matching its
35
+ * message text, and we have no verified example of such a message to match
36
+ * against. Guessing would risk waving away a real finding, which is a strictly
37
+ * worse failure than leaving a stale one visible. If a concrete offending rule id
38
+ * turns up, suppress that id — by evidence, not by regex.
39
+ *
40
+ * What IS handled here: this rule does not fire on a block that has already
41
+ * migrated (see the `assigned.has(wo)` check), so we never contradict ourselves.
42
+ *
43
+ * Pure except for `scanWriteOnlyFiles`, which consults the cached provider schema.
44
+ */
45
+ import { loadProvidersSchema } from "#app/mcp/providerSchema";
46
+ import {
47
+ isStringLiteral,
48
+ parseBlocks,
49
+ stripTrailingComment,
50
+ topLevelAssignments,
51
+ } from "#app/mcp/terraform/hcl";
52
+ import { NATIVE_SOURCE, SECRET_PERSISTED_TO_STATE } from "#app/mcp/terraform/nativeRules";
53
+ import { buildConcern, type Concern } from "#app/mcp/terraform/types";
54
+
55
+ /** Look up a resource type's attribute names in the installed provider schema.
56
+ * Returns null when the type is unknown — the caller must then make NO claim. */
57
+ export type ResourceAttributes = (resourceType: string) => ReadonlySet<string> | null;
58
+
59
+ /** The write-only counterpart of an argument, by provider convention. */
60
+ function writeOnlyNameFor(argument: string): string {
61
+ return `${argument}_wo`;
62
+ }
63
+
64
+ export interface WriteOnlySite {
65
+ resourceType: string;
66
+ resourceName: string;
67
+ /** the persisted argument as written, e.g. `password`. */
68
+ argument: string;
69
+ /** the counterpart the INSTALLED provider declares, e.g. `password_wo`. */
70
+ writeOnlyArgument: string;
71
+ /** the `_wo_version` companion, when the provider declares one. Null means the
72
+ * provider takes the write-only argument without a version — do not invent it. */
73
+ versionArgument: string | null;
74
+ /** 1-based line of the offending assignment in the file. */
75
+ line: number;
76
+ /** true when the value is a bare string literal — a credential written into the
77
+ * repo, not merely into state. Strictly worse, and not fixable by a rename. */
78
+ literal: boolean;
79
+ /** true when the value opens a heredoc, i.e. it continues past this line. The
80
+ * finding is unaffected (the value is persisted either way) but a single-line
81
+ * rewrite cannot express it, so none is offered. */
82
+ multiline: boolean;
83
+ /** the assignment as written. */
84
+ before: string;
85
+ /** the behaviour-equivalent write-only form. Empty for a `literal` or
86
+ * `multiline` value: there is no correct one-line rewrite for either, and
87
+ * offering one would imply the transform had solved a problem it hasn't. */
88
+ after: string;
89
+ }
90
+
91
+ /** Terraform meta-arguments never carry a provider value, so a `_wo` collision on
92
+ * one could only be a coincidence of naming. Excluded so a future provider
93
+ * attribute named e.g. `count_wo` can't make us rewrite a meta-argument. */
94
+ const META_ARGUMENTS: ReadonlySet<string> = new Set([
95
+ "count",
96
+ "for_each",
97
+ "provider",
98
+ "depends_on",
99
+ "lifecycle",
100
+ "provisioner",
101
+ "connection",
102
+ ]);
103
+
104
+ /**
105
+ * Every argument in `hcl` that is persisted to state while the installed provider
106
+ * offers a write-only counterpart.
107
+ *
108
+ * Skipped without a finding: a resource type the schema doesn't know (we cannot
109
+ * judge it), a block that already assigns the counterpart (mid-migration, or the
110
+ * author set both — a provider-level conflict `terraform validate` reports better
111
+ * than we could), and meta-arguments.
112
+ */
113
+ export function findPersistedSecrets(
114
+ hcl: string,
115
+ attributesFor: ResourceAttributes,
116
+ ): WriteOnlySite[] {
117
+ const out: WriteOnlySite[] = [];
118
+ for (const block of parseBlocks(hcl, ["resource"])) {
119
+ const [resourceType = "", resourceName = ""] = block.labels;
120
+ const attributes = attributesFor(resourceType);
121
+ if (!attributes) continue; // unknown to the installed providers — make no claim
122
+ const assignments = topLevelAssignments(block.body);
123
+ const assigned = new Set(assignments.map((a) => a.name));
124
+ for (const a of assignments) {
125
+ if (META_ARGUMENTS.has(a.name) || a.name.endsWith("_wo")) continue;
126
+ const wo = writeOnlyNameFor(a.name);
127
+ if (!attributes.has(wo)) continue; // provider offers no write-only form
128
+ if (assigned.has(wo)) continue; // already migrated (or both set — not our call)
129
+ const versionArgument = attributes.has(`${wo}_version`) ? `${wo}_version` : null;
130
+ const value = stripTrailingComment(a.value);
131
+ const literal = isStringLiteral(a.value);
132
+ const multiline = value.startsWith("<<");
133
+ out.push({
134
+ resourceType,
135
+ resourceName,
136
+ argument: a.name,
137
+ writeOnlyArgument: wo,
138
+ versionArgument,
139
+ line: block.bodyLine + a.bodyLine - 1,
140
+ literal,
141
+ multiline,
142
+ before: `${a.name} = ${value}`,
143
+ after:
144
+ literal || multiline
145
+ ? ""
146
+ : [`${wo} = ${value}`, ...(versionArgument ? [`${versionArgument} = 1`] : [])].join(
147
+ "\n",
148
+ ),
149
+ });
150
+ }
151
+ }
152
+ return out;
153
+ }
154
+
155
+ /** The remediation hint for one site — the exact edit, or the reason there isn't
156
+ * a mechanical one. */
157
+ export function writeOnlyHint(site: WriteOnlySite): string {
158
+ if (site.literal) {
159
+ return (
160
+ `\`${site.argument}\` holds a literal value, so it is in the repository as well as in state. ` +
161
+ "Rotate it, move it to a secret manager or an ephemeral input, and only then switch to " +
162
+ `\`${site.writeOnlyArgument}\`` +
163
+ (site.versionArgument ? ` + \`${site.versionArgument} = 1\`` : "") +
164
+ " — renaming the argument alone would leave the secret committed."
165
+ );
166
+ }
167
+ if (site.multiline) {
168
+ return (
169
+ `Rename \`${site.argument}\` to \`${site.writeOnlyArgument}\`, keeping the heredoc body exactly as ` +
170
+ `it is` +
171
+ (site.versionArgument
172
+ ? `, and add \`${site.versionArgument} = 1\` after the closing delimiter`
173
+ : "") +
174
+ ". The value spans several lines, so no single-line rewrite is offered — the edit is the " +
175
+ "argument name, not the value."
176
+ );
177
+ }
178
+ return (
179
+ `Replace \`${site.before}\` with \`${site.writeOnlyArgument} = <the same expression>\`` +
180
+ (site.versionArgument
181
+ ? ` and add \`${site.versionArgument} = 1\` (bump it whenever the value changes — that is how the provider is told to re-send it)`
182
+ : "") +
183
+ ". The provider receives the value and discards it instead of writing it to state."
184
+ );
185
+ }
186
+
187
+ /**
188
+ * Turn detected sites into native concerns.
189
+ *
190
+ * Severity is the band floor (`medium`) for the mechanical case and `high` for a
191
+ * literal: a referenced secret in state is a real exposure with a one-line fix,
192
+ * whereas a committed literal needs rotation before anything else and should
193
+ * escalate. That split is also what keeps the mechanical case auto-remediable —
194
+ * `classifyAutonomy` routes high-severity security findings to a human, which is
195
+ * correct for the literal and wrong for the rename.
196
+ */
197
+ export function persistedSecretsToConcerns(sites: WriteOnlySite[], file: string): Concern[] {
198
+ return sites.map((site) =>
199
+ buildConcern({
200
+ source: NATIVE_SOURCE,
201
+ rule: SECRET_PERSISTED_TO_STATE,
202
+ file,
203
+ line: site.line,
204
+ severity: site.literal ? "high" : "medium",
205
+ category: "security",
206
+ evidence:
207
+ `${site.resourceType}.${site.resourceName}: \`${site.argument}\` is written to Terraform state in ` +
208
+ `plain text — the installed provider declares \`${site.writeOnlyArgument}\`, which is not persisted` +
209
+ (site.literal ? ", and the value here is a hardcoded literal" : ""),
210
+ remediation_hint: writeOnlyHint(site),
211
+ // the offending line as written, so a reader sees the code and not just a line number.
212
+ code_snippet: site.before,
213
+ }),
214
+ );
215
+ }
216
+
217
+ // --- workspace scan (I/O composition) ----------------------------------------
218
+
219
+ export interface WriteOnlyScan {
220
+ concerns: Concern[];
221
+ /** set when the provider schema was unavailable, so this rule did NOT run. An
222
+ * honest coverage gap, never a silent pass. */
223
+ coverageGap: string | null;
224
+ }
225
+
226
+ /**
227
+ * The workspace-level entry point, over files ALREADY READ by the caller —
228
+ * `[repo-relative POSIX path, contents]` pairs. The native pack reads the tree
229
+ * once and passes the same text to every rule; re-reading each `.tf` per rule
230
+ * would double the I/O of every scan on exactly the monorepos where it hurts.
231
+ */
232
+ export function scanWriteOnlyFiles(
233
+ cwd: string,
234
+ files: ReadonlyArray<readonly [string, string]>,
235
+ ): WriteOnlyScan {
236
+ const schema = loadProvidersSchema(cwd);
237
+ const attributesFor: ResourceAttributes = (t) => schema?.get(t)?.attributes ?? null;
238
+ const concerns: Concern[] = [];
239
+ if (schema) {
240
+ for (const [file, text] of files) {
241
+ concerns.push(...persistedSecretsToConcerns(findPersistedSecrets(text, attributesFor), file));
242
+ }
243
+ }
244
+ return {
245
+ concerns,
246
+ coverageGap: schema
247
+ ? null
248
+ : "provider schema unavailable (the directory is not `terraform init`-ed, or terraform is not " +
249
+ "installed) — whether a write-only counterpart exists is a fact about the INSTALLED provider, " +
250
+ "so no persisted-secret claim was made either way",
251
+ };
252
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Facade for the Terraform MCP toolchain. The implementation lives in the
3
+ * per-concern modules under `./terraform/`; this file re-exports every symbol so
4
+ * existing importers (`#app/mcp/terraform`) keep resolving unchanged.
5
+ *
6
+ * types — Concern + shared types/helpers (ids, paths, roots, severity)
7
+ * scanners — fmt / validate / tflint / trivy / checkov + provider/arg schema
8
+ * decisions — grouping, autonomy, confidence, refusal, prevention, co-location
9
+ * cost — infracost breakdown / delta / escalation
10
+ * currency — registry version currency (provider + module upgrade intel)
11
+ * registry — published-module lookup/search (public + private registries)
12
+ * findings — reviewer findings + SARIF ingest/emit
13
+ * plan — plan parsing + destroy/blast/stability/aggregation
14
+ * tools — the MCP Tool factories + their *Params schemas
15
+ * verification — the five-status taxonomy (fail / not-code-verifiable / …)
16
+ * evidence — the backend-free compliance evidence bundle + emitter
17
+ * signing — detached Ed25519 signing for the compliance artefacts
18
+ * vex — the CycloneDX 1.7 VEX emitter
19
+ * nativeRules — the native rule pack's registry (ids, bands, honesty pairs)
20
+ * writeOnly — secret-persisted-to-state + its FP suppression
21
+ * governance — action triggers + dynamic destroy protection
22
+ * tfquery — *.tfquery.hcl discovery inventory (never remediation input)
23
+ * hcl — the shared, tolerant HCL block scanner every rule reads through
24
+ */
25
+
26
+ export * from "#app/mcp/terraform/cost";
27
+ export * from "#app/mcp/terraform/currency";
28
+ export * from "#app/mcp/terraform/decisions";
29
+ export * from "#app/mcp/terraform/evidence";
30
+ export * from "#app/mcp/terraform/findings";
31
+ export * from "#app/mcp/terraform/governance";
32
+ export * from "#app/mcp/terraform/hcl";
33
+ export * from "#app/mcp/terraform/moduleDocs";
34
+ export * from "#app/mcp/terraform/nativeRules";
35
+ export * from "#app/mcp/terraform/nativeScan";
36
+ export * from "#app/mcp/terraform/normalization";
37
+ export * from "#app/mcp/terraform/oscal";
38
+ export * from "#app/mcp/terraform/paths";
39
+ export * from "#app/mcp/terraform/plan";
40
+ export * from "#app/mcp/terraform/policyAuthor";
41
+ export * from "#app/mcp/terraform/refactor";
42
+ export * from "#app/mcp/terraform/registry";
43
+ export * from "#app/mcp/terraform/risk";
44
+ export * from "#app/mcp/terraform/scanners";
45
+ export * from "#app/mcp/terraform/signing";
46
+ export * from "#app/mcp/terraform/subprocess";
47
+ export * from "#app/mcp/terraform/tfquery";
48
+ export * from "#app/mcp/terraform/tools";
49
+ export * from "#app/mcp/terraform/types";
50
+ export * from "#app/mcp/terraform/verification";
51
+ export * from "#app/mcp/terraform/vex";
52
+ export * from "#app/mcp/terraform/writeOnly";