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.
- package/LICENSE +661 -0
- package/README.md +244 -0
- package/dist/agents/claude.d.ts +113 -0
- package/dist/agents/claudePretoolGate.d.ts +137 -0
- package/dist/agents/dispatchMetrics.d.ts +77 -0
- package/dist/agents/gateServer.d.ts +7 -0
- package/dist/agents/index.d.ts +6 -0
- package/dist/agents/nativeFsDenies.d.ts +46 -0
- package/dist/agents/opencode.d.ts +284 -0
- package/dist/agents/opencodePlugin.d.ts +96 -0
- package/dist/agents/opencodeShared.d.ts +40 -0
- package/dist/agents/postRun.d.ts +126 -0
- package/dist/agents/reviewer.d.ts +41 -0
- package/dist/agents/sessionLabeler.d.ts +97 -0
- package/dist/agents/shared.d.ts +246 -0
- package/dist/agents/subagentModels.d.ts +19 -0
- package/dist/agents/tokenQuota.d.ts +118 -0
- package/dist/agents/writeGateSource.d.ts +22 -0
- package/dist/agents/writePolicy.d.ts +79 -0
- package/dist/brand.d.ts +67 -0
- package/dist/cli.mjs +247846 -0
- package/dist/external.d.ts +227 -0
- package/dist/i18n/scaffolding.d.ts +59 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +247061 -0
- package/dist/internal/index.d.ts +18 -0
- package/dist/internal.js +2398 -0
- package/dist/lifecycle.d.ts +2 -0
- package/dist/main.d.ts +8 -0
- package/dist/mcp/arkConfig.d.ts +1 -0
- package/dist/mcp/assess.d.ts +157 -0
- package/dist/mcp/capabilityContext.d.ts +71 -0
- package/dist/mcp/changeSummary.d.ts +52 -0
- package/dist/mcp/checkSuite.d.ts +27 -0
- package/dist/mcp/checkout.d.ts +92 -0
- package/dist/mcp/comment.d.ts +127 -0
- package/dist/mcp/commitInfo.d.ts +11 -0
- package/dist/mcp/crosswalk.d.ts +178 -0
- package/dist/mcp/crosswalkDigest.d.ts +1 -0
- package/dist/mcp/cyberEssentials.d.ts +24 -0
- package/dist/mcp/dashboard.d.ts +125 -0
- package/dist/mcp/dependencies.d.ts +12 -0
- package/dist/mcp/frameworks.d.ts +74 -0
- package/dist/mcp/geminiSanitizer.d.ts +28 -0
- package/dist/mcp/git.d.ts +60 -0
- package/dist/mcp/guardrails.d.ts +222 -0
- package/dist/mcp/issue.d.ts +20 -0
- package/dist/mcp/issueComments.d.ts +11 -0
- package/dist/mcp/issueEvents.d.ts +11 -0
- package/dist/mcp/issueInfo.d.ts +11 -0
- package/dist/mcp/labels.d.ts +14 -0
- package/dist/mcp/localContext.d.ts +26 -0
- package/dist/mcp/moduleExtraction.d.ts +79 -0
- package/dist/mcp/moduleTests.d.ts +106 -0
- package/dist/mcp/modules.d.ts +198 -0
- package/dist/mcp/output.d.ts +16 -0
- package/dist/mcp/pathSafety.d.ts +14 -0
- package/dist/mcp/policy.d.ts +50 -0
- package/dist/mcp/pr.d.ts +64 -0
- package/dist/mcp/prInfo.d.ts +11 -0
- package/dist/mcp/providerSchema.d.ts +52 -0
- package/dist/mcp/review.d.ts +212 -0
- package/dist/mcp/reviewComments.d.ts +245 -0
- package/dist/mcp/roots.d.ts +60 -0
- package/dist/mcp/scope.d.ts +26 -0
- package/dist/mcp/selectMode.d.ts +20 -0
- package/dist/mcp/server.d.ts +61 -0
- package/dist/mcp/shared.d.ts +62 -0
- package/dist/mcp/shell.d.ts +58 -0
- package/dist/mcp/staleFix.d.ts +138 -0
- package/dist/mcp/terraform/azurePrices.d.ts +8 -0
- package/dist/mcp/terraform/baseline.d.ts +57 -0
- package/dist/mcp/terraform/concernResult.d.ts +38 -0
- package/dist/mcp/terraform/cost.d.ts +55 -0
- package/dist/mcp/terraform/cspm.d.ts +80 -0
- package/dist/mcp/terraform/currency.d.ts +110 -0
- package/dist/mcp/terraform/decisions.d.ts +178 -0
- package/dist/mcp/terraform/eidas.d.ts +100 -0
- package/dist/mcp/terraform/evidence.d.ts +343 -0
- package/dist/mcp/terraform/findings.d.ts +76 -0
- package/dist/mcp/terraform/fixMemory.d.ts +81 -0
- package/dist/mcp/terraform/governance.d.ts +56 -0
- package/dist/mcp/terraform/hcl.d.ts +115 -0
- package/dist/mcp/terraform/iacLanguages.d.ts +120 -0
- package/dist/mcp/terraform/idleFloor.d.ts +78 -0
- package/dist/mcp/terraform/keyless.d.ts +68 -0
- package/dist/mcp/terraform/moduleDocs.d.ts +56 -0
- package/dist/mcp/terraform/nativeRules.d.ts +38 -0
- package/dist/mcp/terraform/nativeScan.d.ts +11 -0
- package/dist/mcp/terraform/noiseProfile.d.ts +100 -0
- package/dist/mcp/terraform/normalization.d.ts +52 -0
- package/dist/mcp/terraform/oscal.d.ts +103 -0
- package/dist/mcp/terraform/packs/azurerm-3-4.d.ts +2 -0
- package/dist/mcp/terraform/packs/cloudflare-4-5.d.ts +2 -0
- package/dist/mcp/terraform/paths.d.ts +28 -0
- package/dist/mcp/terraform/plan.d.ts +157 -0
- package/dist/mcp/terraform/planDrift.d.ts +199 -0
- package/dist/mcp/terraform/policyAuthor.d.ts +191 -0
- package/dist/mcp/terraform/prowlerOcsf.d.ts +136 -0
- package/dist/mcp/terraform/refactor.d.ts +183 -0
- package/dist/mcp/terraform/registry.d.ts +108 -0
- package/dist/mcp/terraform/risk.d.ts +41 -0
- package/dist/mcp/terraform/scanSession.d.ts +116 -0
- package/dist/mcp/terraform/scannerCache.d.ts +15 -0
- package/dist/mcp/terraform/scannerEgress.d.ts +72 -0
- package/dist/mcp/terraform/scannerJson.d.ts +13 -0
- package/dist/mcp/terraform/scanners.d.ts +205 -0
- package/dist/mcp/terraform/signing.d.ts +61 -0
- package/dist/mcp/terraform/stateLocking.d.ts +18 -0
- package/dist/mcp/terraform/subprocess.d.ts +43 -0
- package/dist/mcp/terraform/suppressions.d.ts +90 -0
- package/dist/mcp/terraform/taxonomy.d.ts +39 -0
- package/dist/mcp/terraform/tfquery.d.ts +47 -0
- package/dist/mcp/terraform/toolchainIntegrity.d.ts +82 -0
- package/dist/mcp/terraform/tools/authorPolicy.d.ts +59 -0
- package/dist/mcp/terraform/tools/detectPlanDrift.d.ts +20 -0
- package/dist/mcp/terraform/tools/emitCost.d.ts +11 -0
- package/dist/mcp/terraform/tools/emitSarif.d.ts +14 -0
- package/dist/mcp/terraform/tools/fixMemory.d.ts +19 -0
- package/dist/mcp/terraform/tools/infracostDiff.d.ts +22 -0
- package/dist/mcp/terraform/tools/ingestExternalFindings.d.ts +21 -0
- package/dist/mcp/terraform/tools/moduleLookup.d.ts +14 -0
- package/dist/mcp/terraform/tools/moduleSearch.d.ts +23 -0
- package/dist/mcp/terraform/tools/plan.d.ts +59 -0
- package/dist/mcp/terraform/tools/providerUpgrade.d.ts +55 -0
- package/dist/mcp/terraform/tools/readFindings.d.ts +17 -0
- package/dist/mcp/terraform/tools/scan.d.ts +114 -0
- package/dist/mcp/terraform/tools/validate.d.ts +31 -0
- package/dist/mcp/terraform/tools/verifyRemediation.d.ts +31 -0
- package/dist/mcp/terraform/tools/versionCurrency.d.ts +5 -0
- package/dist/mcp/terraform/tools.d.ts +16 -0
- package/dist/mcp/terraform/transformPacks.d.ts +267 -0
- package/dist/mcp/terraform/types.d.ts +226 -0
- package/dist/mcp/terraform/verification.d.ts +81 -0
- package/dist/mcp/terraform/vex.d.ts +115 -0
- package/dist/mcp/terraform/writeOnly.d.ts +67 -0
- package/dist/mcp/terraform.d.ts +51 -0
- package/dist/mcp/terratest.d.ts +85 -0
- package/dist/mcp/untrustedContent.d.ts +76 -0
- package/dist/mcp/upload.d.ts +8 -0
- package/dist/models.d.ts +181 -0
- package/dist/modes/address-reviews.d.ts +2 -0
- package/dist/modes/assess.d.ts +2 -0
- package/dist/modes/build.d.ts +2 -0
- package/dist/modes/compliance-audit.d.ts +2 -0
- package/dist/modes/cost-optimization.d.ts +2 -0
- package/dist/modes/drift-detect.d.ts +2 -0
- package/dist/modes/fix.d.ts +2 -0
- package/dist/modes/incremental-review.d.ts +2 -0
- package/dist/modes/index.d.ts +24 -0
- package/dist/modes/modernize-deprecated.d.ts +2 -0
- package/dist/modes/plan.d.ts +2 -0
- package/dist/modes/policy-gate.d.ts +2 -0
- package/dist/modes/prFormats.d.ts +3 -0
- package/dist/modes/refactor.d.ts +2 -0
- package/dist/modes/refresh-remediation.d.ts +2 -0
- package/dist/modes/remediate-and-refactor.d.ts +2 -0
- package/dist/modes/remediate.d.ts +2 -0
- package/dist/modes/resolve-conflicts.d.ts +2 -0
- package/dist/modes/review.d.ts +2 -0
- package/dist/modes/summarize-pr.d.ts +2 -0
- package/dist/modes/task.d.ts +2 -0
- package/dist/modes/terraform-code-review.d.ts +2 -0
- package/dist/modes/types.d.ts +9 -0
- package/dist/modes/update-dependencies.d.ts +2 -0
- package/dist/prep/index.d.ts +7 -0
- package/dist/prep/installNodeDependencies.d.ts +2 -0
- package/dist/prep/installPythonDependencies.d.ts +2 -0
- package/dist/prep/types.d.ts +31 -0
- package/dist/reviewQuality.d.ts +107 -0
- package/dist/skills/terraform-best-practices/SKILL.md +379 -0
- package/dist/toolState.d.ts +123 -0
- package/dist/utils/activity.d.ts +40 -0
- package/dist/utils/agent.d.ts +47 -0
- package/dist/utils/agentHangReport.d.ts +38 -0
- package/dist/utils/annotations.d.ts +17 -0
- package/dist/utils/apiFetch.d.ts +11 -0
- package/dist/utils/apiKeys.d.ts +48 -0
- package/dist/utils/apiUrl.d.ts +28 -0
- package/dist/utils/assets.d.ts +8 -0
- package/dist/utils/baseRefConfig.d.ts +121 -0
- package/dist/utils/billingErrors.d.ts +85 -0
- package/dist/utils/body.d.ts +34 -0
- package/dist/utils/buildInfraweaverFooter.d.ts +29 -0
- package/dist/utils/byokFallback.d.ts +85 -0
- package/dist/utils/changeImpact.d.ts +101 -0
- package/dist/utils/claudeSubscription.d.ts +30 -0
- package/dist/utils/cli.d.ts +10 -0
- package/dist/utils/cloudClient.d.ts +33 -0
- package/dist/utils/cloudFetchCore.d.ts +42 -0
- package/dist/utils/cloudReport.d.ts +114 -0
- package/dist/utils/codexHome.d.ts +29 -0
- package/dist/utils/codexOAuth.d.ts +60 -0
- package/dist/utils/diffCoverage.d.ts +63 -0
- package/dist/utils/env.d.ts +46 -0
- package/dist/utils/errorReport.d.ts +17 -0
- package/dist/utils/exitHandler.d.ts +8 -0
- package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
- package/dist/utils/gitAuth.d.ts +84 -0
- package/dist/utils/gitAuthServer.d.ts +24 -0
- package/dist/utils/github.d.ts +104 -0
- package/dist/utils/globals.d.ts +3 -0
- package/dist/utils/infraweaverConfig.d.ts +56 -0
- package/dist/utils/install.d.ts +37 -0
- package/dist/utils/instructions.d.ts +48 -0
- package/dist/utils/keylessOidc.d.ts +28 -0
- package/dist/utils/leapingComment.d.ts +11 -0
- package/dist/utils/learnings.d.ts +62 -0
- package/dist/utils/learningsTruncate.d.ts +25 -0
- package/dist/utils/lifecycle.d.ts +57 -0
- package/dist/utils/log.d.ts +111 -0
- package/dist/utils/modelResolver.d.ts +63 -0
- package/dist/utils/moduleFetch.d.ts +39 -0
- package/dist/utils/normalizeEnv.d.ts +30 -0
- package/dist/utils/openCodeModels.d.ts +11 -0
- package/dist/utils/openaiCompatible.d.ts +26 -0
- package/dist/utils/opentofuMcp.d.ts +50 -0
- package/dist/utils/overrides.d.ts +40 -0
- package/dist/utils/packageManager.d.ts +49 -0
- package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
- package/dist/utils/payload.d.ts +249 -0
- package/dist/utils/prSummary.d.ts +61 -0
- package/dist/utils/presets.d.ts +36 -0
- package/dist/utils/progressComment.d.ts +146 -0
- package/dist/utils/providerErrors.d.ts +31 -0
- package/dist/utils/proxyModel.d.ts +76 -0
- package/dist/utils/rangeDiff.d.ts +51 -0
- package/dist/utils/redact.d.ts +33 -0
- package/dist/utils/registryAuth.d.ts +52 -0
- package/dist/utils/remediationCommand.d.ts +58 -0
- package/dist/utils/retry.d.ts +38 -0
- package/dist/utils/reviewCleanup.d.ts +14 -0
- package/dist/utils/run.d.ts +9 -0
- package/dist/utils/runContext.d.ts +112 -0
- package/dist/utils/runContextData.d.ts +42 -0
- package/dist/utils/runErrorRenderer.d.ts +64 -0
- package/dist/utils/runLifecycle.d.ts +89 -0
- package/dist/utils/runStartupLog.d.ts +15 -0
- package/dist/utils/secrets.d.ts +34 -0
- package/dist/utils/setup.d.ts +90 -0
- package/dist/utils/shell.d.ts +44 -0
- package/dist/utils/skills.d.ts +10 -0
- package/dist/utils/subprocess.d.ts +81 -0
- package/dist/utils/terraformMcp.d.ts +46 -0
- package/dist/utils/time.d.ts +15 -0
- package/dist/utils/timer.d.ts +23 -0
- package/dist/utils/todoTracking.d.ts +16 -0
- package/dist/utils/token.d.ts +52 -0
- package/dist/utils/toolLicensing.d.ts +56 -0
- package/dist/utils/toolSelection.d.ts +73 -0
- package/dist/utils/toon.d.ts +16 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/versioning.d.ts +7 -0
- package/dist/utils/vertex.d.ts +16 -0
- package/dist/utils/workflow.d.ts +13 -0
- package/package.json +132 -0
- package/src/agents/claude.ts +1588 -0
- package/src/agents/claudePretoolGate.ts +286 -0
- package/src/agents/dispatchMetrics.ts +162 -0
- package/src/agents/gateServer.ts +178 -0
- package/src/agents/index.ts +10 -0
- package/src/agents/nativeFsDenies.ts +164 -0
- package/src/agents/opencode.ts +1600 -0
- package/src/agents/opencodePlugin.ts +304 -0
- package/src/agents/opencodeShared.ts +134 -0
- package/src/agents/postRun.ts +615 -0
- package/src/agents/reviewer.ts +134 -0
- package/src/agents/sessionLabeler.ts +185 -0
- package/src/agents/shared.ts +377 -0
- package/src/agents/subagentModels.ts +40 -0
- package/src/agents/tokenQuota.ts +203 -0
- package/src/agents/writeGateSource.ts +148 -0
- package/src/agents/writePolicy.ts +132 -0
- package/src/brand.ts +84 -0
- package/src/cli.ts +117 -0
- package/src/commands/gha.ts +188 -0
- package/src/commands/mcp.ts +126 -0
- package/src/commands/verifyEvidence.ts +100 -0
- package/src/entry.ts +7 -0
- package/src/entryPost.ts +109 -0
- package/src/external.ts +308 -0
- package/src/i18n/scaffolding.ts +191 -0
- package/src/index.ts +7 -0
- package/src/internal/index.ts +74 -0
- package/src/lifecycle.ts +2 -0
- package/src/main.ts +815 -0
- package/src/mcp/__fixtures__/infraweaver-scratch-pr-49-review-3485940013.json +110 -0
- package/src/mcp/__fixtures__/infraweaver-scratch-pr-64-review-3531000326.json +14 -0
- package/src/mcp/__fixtures__/infraweaver-test-repo-pr-1.diff.json +67 -0
- package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
- package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
- package/src/mcp/arkConfig.ts +7 -0
- package/src/mcp/assess.ts +723 -0
- package/src/mcp/capabilityContext.ts +80 -0
- package/src/mcp/changeSummary.ts +145 -0
- package/src/mcp/checkSuite.ts +261 -0
- package/src/mcp/checkout.ts +1015 -0
- package/src/mcp/comment.ts +683 -0
- package/src/mcp/commitInfo.ts +57 -0
- package/src/mcp/crosswalk.ts +1253 -0
- package/src/mcp/crosswalkDigest.ts +5 -0
- package/src/mcp/cyberEssentials.ts +60 -0
- package/src/mcp/dashboard.ts +232 -0
- package/src/mcp/dependencies.ts +190 -0
- package/src/mcp/frameworks.ts +236 -0
- package/src/mcp/geminiSanitizer.ts +212 -0
- package/src/mcp/git.ts +1116 -0
- package/src/mcp/guardrails.ts +771 -0
- package/src/mcp/issue.ts +74 -0
- package/src/mcp/issueComments.ts +48 -0
- package/src/mcp/issueEvents.ts +100 -0
- package/src/mcp/issueInfo.ts +72 -0
- package/src/mcp/labels.ts +35 -0
- package/src/mcp/localContext.ts +65 -0
- package/src/mcp/localServer.ts +217 -0
- package/src/mcp/moduleExtraction.ts +368 -0
- package/src/mcp/moduleTests.ts +421 -0
- package/src/mcp/modules.ts +752 -0
- package/src/mcp/output.ts +71 -0
- package/src/mcp/pathSafety.ts +28 -0
- package/src/mcp/policy.ts +226 -0
- package/src/mcp/pr.ts +238 -0
- package/src/mcp/prInfo.ts +91 -0
- package/src/mcp/providerSchema.ts +175 -0
- package/src/mcp/review.ts +1081 -0
- package/src/mcp/reviewComments.ts +1137 -0
- package/src/mcp/roots.ts +217 -0
- package/src/mcp/scope.ts +82 -0
- package/src/mcp/selectMode.ts +207 -0
- package/src/mcp/server.ts +498 -0
- package/src/mcp/shared.ts +120 -0
- package/src/mcp/shell.ts +631 -0
- package/src/mcp/staleFix.ts +557 -0
- package/src/mcp/terraform/__snapshots__/moduleDocs.test.ts.snap +40 -0
- package/src/mcp/terraform/azurePrices.ts +38 -0
- package/src/mcp/terraform/baseline.ts +172 -0
- package/src/mcp/terraform/concernResult.ts +111 -0
- package/src/mcp/terraform/cost.ts +175 -0
- package/src/mcp/terraform/cspm.ts +326 -0
- package/src/mcp/terraform/currency.ts +354 -0
- package/src/mcp/terraform/decisions.ts +590 -0
- package/src/mcp/terraform/eidas.ts +134 -0
- package/src/mcp/terraform/evidence.ts +771 -0
- package/src/mcp/terraform/findings.ts +383 -0
- package/src/mcp/terraform/fixMemory.ts +253 -0
- package/src/mcp/terraform/governance.ts +231 -0
- package/src/mcp/terraform/hcl.ts +273 -0
- package/src/mcp/terraform/iacLanguages.ts +431 -0
- package/src/mcp/terraform/idleFloor.ts +304 -0
- package/src/mcp/terraform/keyless.ts +123 -0
- package/src/mcp/terraform/moduleDocs.ts +303 -0
- package/src/mcp/terraform/nativeRules.ts +152 -0
- package/src/mcp/terraform/nativeScan.ts +67 -0
- package/src/mcp/terraform/noiseProfile.ts +183 -0
- package/src/mcp/terraform/normalization.ts +265 -0
- package/src/mcp/terraform/oscal.ts +283 -0
- package/src/mcp/terraform/packs/azurerm-3-4.ts +382 -0
- package/src/mcp/terraform/packs/cloudflare-4-5.ts +114 -0
- package/src/mcp/terraform/paths.ts +59 -0
- package/src/mcp/terraform/plan.ts +377 -0
- package/src/mcp/terraform/planDrift.ts +520 -0
- package/src/mcp/terraform/policyAuthor.ts +886 -0
- package/src/mcp/terraform/prowlerOcsf.ts +380 -0
- package/src/mcp/terraform/refactor.ts +879 -0
- package/src/mcp/terraform/registry.ts +317 -0
- package/src/mcp/terraform/risk.ts +99 -0
- package/src/mcp/terraform/scanSession.ts +242 -0
- package/src/mcp/terraform/scannerCache.ts +86 -0
- package/src/mcp/terraform/scannerEgress.ts +141 -0
- package/src/mcp/terraform/scannerJson.ts +22 -0
- package/src/mcp/terraform/scanners.ts +1134 -0
- package/src/mcp/terraform/signing.ts +151 -0
- package/src/mcp/terraform/stateLocking.ts +106 -0
- package/src/mcp/terraform/subprocess.ts +102 -0
- package/src/mcp/terraform/suppressions.ts +551 -0
- package/src/mcp/terraform/taxonomy.ts +0 -0
- package/src/mcp/terraform/tfquery.ts +159 -0
- package/src/mcp/terraform/toolchainIntegrity.ts +151 -0
- package/src/mcp/terraform/tools/authorPolicy.ts +185 -0
- package/src/mcp/terraform/tools/detectPlanDrift.ts +279 -0
- package/src/mcp/terraform/tools/emitCost.ts +130 -0
- package/src/mcp/terraform/tools/emitSarif.ts +108 -0
- package/src/mcp/terraform/tools/fixMemory.ts +82 -0
- package/src/mcp/terraform/tools/infracostDiff.ts +110 -0
- package/src/mcp/terraform/tools/ingestExternalFindings.ts +259 -0
- package/src/mcp/terraform/tools/moduleLookup.ts +86 -0
- package/src/mcp/terraform/tools/moduleSearch.ts +46 -0
- package/src/mcp/terraform/tools/plan.ts +384 -0
- package/src/mcp/terraform/tools/providerUpgrade.ts +217 -0
- package/src/mcp/terraform/tools/readFindings.ts +138 -0
- package/src/mcp/terraform/tools/scan.ts +303 -0
- package/src/mcp/terraform/tools/validate.ts +160 -0
- package/src/mcp/terraform/tools/verifyRemediation.ts +190 -0
- package/src/mcp/terraform/tools/versionCurrency.ts +64 -0
- package/src/mcp/terraform/tools.ts +62 -0
- package/src/mcp/terraform/transformPacks.ts +594 -0
- package/src/mcp/terraform/types.ts +382 -0
- package/src/mcp/terraform/verification.ts +150 -0
- package/src/mcp/terraform/vex.ts +367 -0
- package/src/mcp/terraform/writeOnly.ts +252 -0
- package/src/mcp/terraform.ts +52 -0
- package/src/mcp/terratest.ts +215 -0
- package/src/mcp/untrustedContent.ts +126 -0
- package/src/mcp/upload.ts +123 -0
- package/src/models.ts +753 -0
- package/src/modes/address-reviews.ts +35 -0
- package/src/modes/assess.ts +27 -0
- package/src/modes/build.ts +86 -0
- package/src/modes/compliance-audit.ts +25 -0
- package/src/modes/cost-optimization.ts +36 -0
- package/src/modes/drift-detect.ts +31 -0
- package/src/modes/fix.ts +29 -0
- package/src/modes/incremental-review.ts +103 -0
- package/src/modes/index.ts +94 -0
- package/src/modes/modernize-deprecated.ts +36 -0
- package/src/modes/plan.ts +21 -0
- package/src/modes/policy-gate.ts +27 -0
- package/src/modes/prFormats.ts +325 -0
- package/src/modes/refactor.ts +74 -0
- package/src/modes/refresh-remediation.ts +40 -0
- package/src/modes/remediate-and-refactor.ts +42 -0
- package/src/modes/remediate.ts +78 -0
- package/src/modes/resolve-conflicts.ts +32 -0
- package/src/modes/review.ts +135 -0
- package/src/modes/summarize-pr.ts +42 -0
- package/src/modes/task.ts +26 -0
- package/src/modes/terraform-code-review.ts +68 -0
- package/src/modes/types.ts +17 -0
- package/src/modes/update-dependencies.ts +44 -0
- package/src/prep/index.ts +96 -0
- package/src/prep/installNodeDependencies.ts +251 -0
- package/src/prep/installPythonDependencies.ts +235 -0
- package/src/prep/types.ts +38 -0
- package/src/reviewQuality.ts +237 -0
- package/src/runCli.ts +335 -0
- package/src/skills/terraform-best-practices/SKILL.md +379 -0
- package/src/toolState.ts +241 -0
- package/src/utils/activity.ts +210 -0
- package/src/utils/agent.ts +245 -0
- package/src/utils/agentHangReport.ts +180 -0
- package/src/utils/annotations.ts +56 -0
- package/src/utils/apiFetch.ts +19 -0
- package/src/utils/apiKeys.ts +305 -0
- package/src/utils/apiUrl.ts +42 -0
- package/src/utils/assets.ts +123 -0
- package/src/utils/baseRefConfig.ts +254 -0
- package/src/utils/billingErrors.ts +210 -0
- package/src/utils/body.ts +168 -0
- package/src/utils/buildInfraweaverFooter.ts +104 -0
- package/src/utils/byokFallback.ts +137 -0
- package/src/utils/changeImpact.ts +330 -0
- package/src/utils/claudeSubscription.ts +93 -0
- package/src/utils/cli.ts +36 -0
- package/src/utils/cloudClient.ts +57 -0
- package/src/utils/cloudFetchCore.ts +139 -0
- package/src/utils/cloudReport.ts +316 -0
- package/src/utils/codexHome.ts +204 -0
- package/src/utils/codexOAuth.ts +154 -0
- package/src/utils/codexRefreshDetect.ts +36 -0
- package/src/utils/diffCoverage.ts +404 -0
- package/src/utils/env.ts +84 -0
- package/src/utils/errorReport.ts +94 -0
- package/src/utils/exitHandler.ts +35 -0
- package/src/utils/fixDoubleEscapedString.ts +9 -0
- package/src/utils/ghaCore.ts +13 -0
- package/src/utils/gitAuth.ts +268 -0
- package/src/utils/gitAuthServer.ts +186 -0
- package/src/utils/github.ts +676 -0
- package/src/utils/globals.ts +9 -0
- package/src/utils/humanEditCapture.ts +198 -0
- package/src/utils/infraweaverConfig.ts +202 -0
- package/src/utils/install.ts +279 -0
- package/src/utils/instructions.ts +640 -0
- package/src/utils/keylessOidc.ts +47 -0
- package/src/utils/leapingComment.ts +20 -0
- package/src/utils/learnings.ts +145 -0
- package/src/utils/learningsTruncate.ts +42 -0
- package/src/utils/lifecycle.ts +198 -0
- package/src/utils/log.ts +452 -0
- package/src/utils/modelResolver.ts +105 -0
- package/src/utils/moduleFetch.ts +138 -0
- package/src/utils/normalizeEnv.ts +106 -0
- package/src/utils/openCodeModels.ts +86 -0
- package/src/utils/openaiCompatible.ts +52 -0
- package/src/utils/opentofuMcp.ts +61 -0
- package/src/utils/overrides.ts +100 -0
- package/src/utils/packageManager.ts +257 -0
- package/src/utils/patchWorkflowRunFields.ts +173 -0
- package/src/utils/payload.ts +1206 -0
- package/src/utils/prSummary.ts +148 -0
- package/src/utils/presets.ts +105 -0
- package/src/utils/progressComment.ts +266 -0
- package/src/utils/providerErrors.ts +189 -0
- package/src/utils/proxyModel.ts +239 -0
- package/src/utils/rangeDiff.ts +182 -0
- package/src/utils/redact.ts +86 -0
- package/src/utils/registryAuth.ts +115 -0
- package/src/utils/remediationCommand.ts +144 -0
- package/src/utils/retry.ts +180 -0
- package/src/utils/reviewCleanup.ts +116 -0
- package/src/utils/run.ts +100 -0
- package/src/utils/runContext.ts +276 -0
- package/src/utils/runContextData.ts +121 -0
- package/src/utils/runErrorRenderer.ts +282 -0
- package/src/utils/runFixture.ts +91 -0
- package/src/utils/runLifecycle.ts +360 -0
- package/src/utils/runStartupLog.ts +67 -0
- package/src/utils/secrets.ts +208 -0
- package/src/utils/setup.ts +368 -0
- package/src/utils/shell.ts +132 -0
- package/src/utils/skills.ts +67 -0
- package/src/utils/subprocess.ts +474 -0
- package/src/utils/terraformMcp.ts +99 -0
- package/src/utils/time.ts +59 -0
- package/src/utils/timer.ts +72 -0
- package/src/utils/todoTracking.ts +168 -0
- package/src/utils/token.ts +294 -0
- package/src/utils/toolLicensing.ts +152 -0
- package/src/utils/toolSelection.ts +239 -0
- package/src/utils/toon.ts +76 -0
- package/src/utils/version.ts +10 -0
- package/src/utils/versioning.ts +44 -0
- package/src/utils/vertex.ts +94 -0
- 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";
|