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,771 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { basename, dirname } from "node:path";
|
|
3
|
+
import { type } from "arktype";
|
|
4
|
+
import { EVIDENCE_FILE, PRODUCT_NAME, TOOL_NAME } from "#app/brand";
|
|
5
|
+
import {
|
|
6
|
+
type AssessmentScorecard,
|
|
7
|
+
type AssessWaiver,
|
|
8
|
+
runAssessmentPipeline,
|
|
9
|
+
} from "#app/mcp/assess";
|
|
10
|
+
import { CROSSWALK_DIGEST, type CrosswalkReport } from "#app/mcp/crosswalk";
|
|
11
|
+
import type { LocalToolContext } from "#app/mcp/localContext";
|
|
12
|
+
import { resolveWithinCwd } from "#app/mcp/pathSafety";
|
|
13
|
+
import { execute, tool, toolOk } from "#app/mcp/shared";
|
|
14
|
+
import { type EidasSeal, eidasSealFor } from "#app/mcp/terraform/eidas";
|
|
15
|
+
import {
|
|
16
|
+
IDLE_FLOOR_ASSERTS,
|
|
17
|
+
IDLE_FLOOR_DOES_NOT_ASSERT,
|
|
18
|
+
type IdleFloorReport,
|
|
19
|
+
} from "#app/mcp/terraform/idleFloor";
|
|
20
|
+
import {
|
|
21
|
+
buildInTotoStatement,
|
|
22
|
+
keylessBundlePath,
|
|
23
|
+
keylessSigningPossible,
|
|
24
|
+
keylessSignStatement,
|
|
25
|
+
} from "#app/mcp/terraform/keyless";
|
|
26
|
+
import type { DriftEvidenceStatement } from "#app/mcp/terraform/planDrift";
|
|
27
|
+
import {
|
|
28
|
+
collectWorkspaceProvenance,
|
|
29
|
+
type ModuleProvenance,
|
|
30
|
+
} from "#app/mcp/terraform/refactor";
|
|
31
|
+
import {
|
|
32
|
+
scanSessionOf,
|
|
33
|
+
upgradeStatementOf,
|
|
34
|
+
} from "#app/mcp/terraform/scanSession";
|
|
35
|
+
import {
|
|
36
|
+
loadSigningKey,
|
|
37
|
+
type SignatureMetadata,
|
|
38
|
+
signatureMetadata,
|
|
39
|
+
signBytes,
|
|
40
|
+
} from "#app/mcp/terraform/signing";
|
|
41
|
+
import { probedToolVersions } from "#app/mcp/terraform/toolchainIntegrity";
|
|
42
|
+
import type { UpgradeEvidenceStatement } from "#app/mcp/terraform/transformPacks";
|
|
43
|
+
import {
|
|
44
|
+
type Concern,
|
|
45
|
+
concernKeyOf,
|
|
46
|
+
type Severity,
|
|
47
|
+
} from "#app/mcp/terraform/types";
|
|
48
|
+
import {
|
|
49
|
+
VERIFICATION_STATUS_LABEL,
|
|
50
|
+
type VerificationStatus,
|
|
51
|
+
type VerificationSummary,
|
|
52
|
+
} from "#app/mcp/terraform/verification";
|
|
53
|
+
import { log } from "#app/utils/cli";
|
|
54
|
+
import { githubContext } from "#app/utils/env";
|
|
55
|
+
import packageJson from "#package.json" with { type: "json" };
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Backend-free compliance evidence bundle (the WS4a wedge — an auditor-facing
|
|
59
|
+
* artifact the OSS action can emit with **zero cloud and no backend**, committed
|
|
60
|
+
* to a `compliance/` path). It packages the read-only assessment — posture,
|
|
61
|
+
* per-control statements with their five-status verdict ([[verification]]), the
|
|
62
|
+
* crosswalk index, and the scanner coverage — into one deterministic JSON file.
|
|
63
|
+
*
|
|
64
|
+
* SCHEMA / HONESTY: this is Infraweaver's OWN structured schema, NOT OSCAL. A
|
|
65
|
+
* strict OSCAL (or compliance-trestle / C2P) emitter is a deliberate follow-up,
|
|
66
|
+
* gated on a buyer who actually needs OSCAL — emitting OSCAL nobody consumes is
|
|
67
|
+
* cost without value. The bundle is INDICATIVE alignment guidance, never an audit
|
|
68
|
+
* verdict, and it never claims `pass` for an unfired control (absence of a
|
|
69
|
+
* finding is not proof). Pure builder + a thin file-writing tool.
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
// v0.2 was ADDITIVE over v0.1 (the `signature` field — Ed25519 signing metadata,
|
|
73
|
+
// or an explicit null when no signing key is configured — see [[signing]]).
|
|
74
|
+
// v0.3 is ADDITIVE over v0.2: each control statement gains `concern_key`, the
|
|
75
|
+
// line-independent anchor (sha1(source|rule|file)) that stays stable across a fix
|
|
76
|
+
// that shifts lines — the deep-link key a dashboard finding uses to locate its
|
|
77
|
+
// entry in this bundle. Every prior field is unchanged, so older consumers parse.
|
|
78
|
+
// v0.4 is ADDITIVE over v0.3: an optional `cost` block carrying the §5.19
|
|
79
|
+
// credential-free IDLE FLOOR (what the configuration bills at zero traffic) with
|
|
80
|
+
// its explicit asserts / does_not_assert pair. Present only when the cost tool
|
|
81
|
+
// ran; every prior field is unchanged.
|
|
82
|
+
// v0.6 is ADDITIVE over v0.5: an always-present `suppressions` block recording
|
|
83
|
+
// what the posture EXCLUDES because a human accepted it (count, per-source
|
|
84
|
+
// tally, never-expiring count, and every entry with its reason/expiry). The
|
|
85
|
+
// human-readable report always listed waivers; the signed artifact did not, so
|
|
86
|
+
// a repo with thirty active waivers signed identically to a clean one. Always
|
|
87
|
+
// present — including `count: 0` — because an omitted section is ambiguous.
|
|
88
|
+
// v0.7 is ADDITIVE over v0.6: an always-present `seal` block declaring the
|
|
89
|
+
// eIDAS electronic-seal tier the `signature` reaches — today `simple` (or
|
|
90
|
+
// `none` when unsigned), never `advanced`/`qualified`. Machine-readable rather
|
|
91
|
+
// than prose, because "signed ed25519" is easy to over-read as a legal seal in
|
|
92
|
+
// a procurement context and the difference is an Art. 35(2) presumption. The
|
|
93
|
+
// qualified seal itself (§5.16: ASiC-E/CAdES-LTA + an Art. 41 timestamp from a
|
|
94
|
+
// QTSP) is NOT built — it is blocked on trust-service onboarding, and this
|
|
95
|
+
// block is the schema slot it lands in without a breaking change.
|
|
96
|
+
// v0.5 was ADDITIVE over v0.4, two blocks:
|
|
97
|
+
// - `provenance` (always present) — WHO produced this evidence: the resolved
|
|
98
|
+
// model, the runtime, and the exact scanner versions the integrity gate
|
|
99
|
+
// observed before executing them (A.3 — the layer the Trivy compromise
|
|
100
|
+
// exploited; "evidence over trust" now covers the actor, not just the
|
|
101
|
+
// result).
|
|
102
|
+
// - `drift` (present only when detect_plan_drift ran) — the TC-2.1 statement:
|
|
103
|
+
// drift state, the `forgotten` blind-spot list, provider-noise filtering
|
|
104
|
+
// (auditable, field-by-field), and every coverage gap, each bound stated in
|
|
105
|
+
// the signed artifact itself.
|
|
106
|
+
// v0.8 is ADDITIVE over v0.7, one block:
|
|
107
|
+
// - `upgrade` (present only when terraform_provider_upgrade matched a pack) —
|
|
108
|
+
// the TC-2.2 statement: which provider major boundary was crossed, which
|
|
109
|
+
// codified transforms the configuration contained, and the plan that backs
|
|
110
|
+
// it. Carries the pack's own non-exhaustiveness into the signed bytes so a
|
|
111
|
+
// reader cannot mistake the transform list for a complete migration.
|
|
112
|
+
export const EVIDENCE_SCHEMA = "infraweaver-evidence/v0.8" as const;
|
|
113
|
+
export const DEFAULT_EVIDENCE_PATH = `compliance/${EVIDENCE_FILE}`;
|
|
114
|
+
|
|
115
|
+
export interface EvidenceControlStatement {
|
|
116
|
+
concern_id: string;
|
|
117
|
+
/** line-independent anchor (sha1(source|rule|file), see `concernKeyOf`) — the
|
|
118
|
+
* stable deep-link key into this bundle: a dashboard finding carries the same
|
|
119
|
+
* key, so its entry here is locatable even after a fix shifts line numbers.
|
|
120
|
+
* Absent when the backing concern wasn't supplied to the builder. */
|
|
121
|
+
concern_key?: string | undefined;
|
|
122
|
+
rule_id: string;
|
|
123
|
+
/** the five-status verdict for this statement (fail / not-code-verifiable). */
|
|
124
|
+
status: VerificationStatus;
|
|
125
|
+
severity?: string | undefined;
|
|
126
|
+
file?: string | undefined;
|
|
127
|
+
line?: number | null | undefined;
|
|
128
|
+
controls: { framework: string; control: string; title: string }[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* A refactor's evidence statement — emitted only for a refactor run. Carries the
|
|
133
|
+
* verdict, the equivalence sub-results that BACK it (when a keys-free proof ran),
|
|
134
|
+
* the adopted-module provenance, and — per §8 — BOTH what it asserts and what it
|
|
135
|
+
* explicitly does NOT. A `proposed-unproven` verdict (a b.4 third-party adoption
|
|
136
|
+
* shipped without a plan) asserts nothing about runtime behaviour.
|
|
137
|
+
*/
|
|
138
|
+
export interface EvidenceRefactorStatement {
|
|
139
|
+
verdict: "equivalence-proven" | "equivalence-failed" | "proposed-unproven";
|
|
140
|
+
equivalence?:
|
|
141
|
+
| {
|
|
142
|
+
resource_set_preserved: boolean;
|
|
143
|
+
arguments_preserved: boolean;
|
|
144
|
+
moves_exhaustive: boolean;
|
|
145
|
+
moves_detected: number;
|
|
146
|
+
uncovered_moves: number;
|
|
147
|
+
validate_passed: boolean;
|
|
148
|
+
fmt_clean: boolean;
|
|
149
|
+
}
|
|
150
|
+
| undefined;
|
|
151
|
+
/** every external module the config adopts, with its pinned version (provenance
|
|
152
|
+
* discipline — the analog to a SHA-pinned action). */
|
|
153
|
+
module_provenance: ModuleProvenance[];
|
|
154
|
+
/** true when the change is proposed but unproven — the unmissable b.4 marker. */
|
|
155
|
+
proposed_unproven: boolean;
|
|
156
|
+
asserts: string;
|
|
157
|
+
does_not_assert: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The §5.19 cost statement: the credential-free IDLE FLOOR — what this
|
|
162
|
+
* configuration bills at zero traffic — with the same asserts/does_not_assert
|
|
163
|
+
* honesty pair the refactor statement carries. Deliberately NOT a monthly total:
|
|
164
|
+
* usage-dependent lines are not derivable from Terraform, so the floor is the
|
|
165
|
+
* only cost figure this bundle is willing to sign.
|
|
166
|
+
*/
|
|
167
|
+
export interface EvidenceCostStatement {
|
|
168
|
+
/** Currency of every figure below (from the bundled price snapshot). */
|
|
169
|
+
currency: string;
|
|
170
|
+
/** Cloud region the list prices were captured for. */
|
|
171
|
+
region: string;
|
|
172
|
+
/** ISO date the bundled price snapshot was captured — makes staleness visible. */
|
|
173
|
+
priced_at: string;
|
|
174
|
+
/** Monthly cost that accrues at zero traffic. */
|
|
175
|
+
monthly_floor: number;
|
|
176
|
+
/** One entry per resource contributing to the floor. */
|
|
177
|
+
resources: {
|
|
178
|
+
address: string;
|
|
179
|
+
resource_type: string;
|
|
180
|
+
file: string;
|
|
181
|
+
line: number;
|
|
182
|
+
monthly_cost: number;
|
|
183
|
+
}[];
|
|
184
|
+
/** Catalogued resources left unpriced because their tier was not identifiable
|
|
185
|
+
* — the signed floor is a lower bound by exactly these. */
|
|
186
|
+
unpriced: {
|
|
187
|
+
address: string;
|
|
188
|
+
resource_type: string;
|
|
189
|
+
file: string;
|
|
190
|
+
line: number;
|
|
191
|
+
reason: string;
|
|
192
|
+
}[];
|
|
193
|
+
asserts: string;
|
|
194
|
+
does_not_assert: string;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Build the suppression summary (pure). Unlike `cost` and `drift`, this block is
|
|
199
|
+
* **always present** — including with `count: 0`. An omitted section would be
|
|
200
|
+
* ambiguous ("nothing silenced" vs "this version didn't record it"), and the
|
|
201
|
+
* whole point is that a reader can tell a clean repo from a quiet one without
|
|
202
|
+
* having to trust that we'd have mentioned it.
|
|
203
|
+
*/
|
|
204
|
+
export function buildSuppressionSummary(
|
|
205
|
+
waivers: ReadonlyArray<AssessWaiver>,
|
|
206
|
+
): EvidenceSuppressionSummary {
|
|
207
|
+
const by_source: Record<string, number> = {};
|
|
208
|
+
let never_expire = 0;
|
|
209
|
+
for (const w of waivers) {
|
|
210
|
+
by_source[w.source] = (by_source[w.source] ?? 0) + 1;
|
|
211
|
+
if (!w.expires) never_expire++;
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
count: waivers.length,
|
|
215
|
+
by_source,
|
|
216
|
+
never_expire,
|
|
217
|
+
entries: waivers.map((w) => ({
|
|
218
|
+
rule_id: w.rule_id,
|
|
219
|
+
severity: w.severity,
|
|
220
|
+
file: w.file,
|
|
221
|
+
line: w.line,
|
|
222
|
+
reason: w.reason,
|
|
223
|
+
source: w.source,
|
|
224
|
+
expires: w.expires ?? null,
|
|
225
|
+
})),
|
|
226
|
+
asserts:
|
|
227
|
+
waivers.length === 0
|
|
228
|
+
? "no finding was suppressed — the posture above covers everything the scanners reported."
|
|
229
|
+
: `${waivers.length} finding(s) were excluded from the posture above by an explicit, ` +
|
|
230
|
+
"reasoned suppression. Each is listed with its reason, source and expiry.",
|
|
231
|
+
does_not_assert:
|
|
232
|
+
"that a suppressed finding was fixed, or that its acceptance was correct — a waiver records " +
|
|
233
|
+
"a human decision, not a remediation. An expired waiver resurfaces the finding on the next " +
|
|
234
|
+
"run. An entry whose reason carries a `[via <tool> skip]` marker came from another scanner's " +
|
|
235
|
+
"own inline directive: honoured here, but neither authored nor reviewed here.",
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Build the cost evidence statement (pure). Returns undefined when no cost
|
|
241
|
+
* analysis ran or nothing priced, so the bundle omits the section entirely
|
|
242
|
+
* rather than signing a zero that could be read as "this costs nothing".
|
|
243
|
+
*/
|
|
244
|
+
export function buildCostStatement(
|
|
245
|
+
report: IdleFloorReport | undefined,
|
|
246
|
+
): EvidenceCostStatement | undefined {
|
|
247
|
+
if (!report || report.items.length === 0) return undefined;
|
|
248
|
+
return {
|
|
249
|
+
currency: report.currency,
|
|
250
|
+
region: report.region,
|
|
251
|
+
priced_at: report.priced_at,
|
|
252
|
+
monthly_floor: report.monthly_floor,
|
|
253
|
+
resources: report.items.map((i) => ({
|
|
254
|
+
address: i.address,
|
|
255
|
+
resource_type: i.resourceType,
|
|
256
|
+
file: i.file,
|
|
257
|
+
line: i.line,
|
|
258
|
+
monthly_cost: i.monthly_cost,
|
|
259
|
+
})),
|
|
260
|
+
/** Catalogued resources left unpriced because their tier was not
|
|
261
|
+
* identifiable — the signed floor is a lower bound by exactly these. */
|
|
262
|
+
unpriced: report.unpriced.map((u) => ({
|
|
263
|
+
address: u.address,
|
|
264
|
+
resource_type: u.resourceType,
|
|
265
|
+
file: u.file,
|
|
266
|
+
line: u.line,
|
|
267
|
+
reason: u.reason,
|
|
268
|
+
})),
|
|
269
|
+
asserts: IDLE_FLOOR_ASSERTS,
|
|
270
|
+
does_not_assert: IDLE_FLOOR_DOES_NOT_ASSERT,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** the equivalence sub-results buildRefactorStatement reads (the shape recorded
|
|
275
|
+
* in `scanSession.refactorEquivalence`). */
|
|
276
|
+
export interface RefactorEquivalenceInput {
|
|
277
|
+
equivalent: boolean;
|
|
278
|
+
resource_set_equivalent: boolean;
|
|
279
|
+
arg_names_equivalent: boolean;
|
|
280
|
+
moves_exhaustive: boolean;
|
|
281
|
+
moves_detected: number;
|
|
282
|
+
validate_passed: boolean;
|
|
283
|
+
fmt_clean: boolean;
|
|
284
|
+
uncovered_moves: { from: string; to: string }[];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Build the refactor evidence statement (pure). Returns undefined when nothing
|
|
289
|
+
* refactor-related happened (no equivalence proof, not a proposed-unproven b.4,
|
|
290
|
+
* no external module) so the bundle omits the section entirely. Infraweaver asserts
|
|
291
|
+
* only what the inputs prove — a failed or absent proof asserts nothing.
|
|
292
|
+
*/
|
|
293
|
+
export function buildRefactorStatement(args: {
|
|
294
|
+
equivalence?: RefactorEquivalenceInput | undefined;
|
|
295
|
+
provenance: ModuleProvenance[];
|
|
296
|
+
proposedUnproven: boolean;
|
|
297
|
+
}): EvidenceRefactorStatement | undefined {
|
|
298
|
+
const { equivalence, provenance, proposedUnproven } = args;
|
|
299
|
+
if (!equivalence && !proposedUnproven && provenance.length === 0)
|
|
300
|
+
return undefined;
|
|
301
|
+
|
|
302
|
+
let verdict: EvidenceRefactorStatement["verdict"];
|
|
303
|
+
let asserts: string;
|
|
304
|
+
let doesNotAssert: string;
|
|
305
|
+
if (equivalence?.equivalent) {
|
|
306
|
+
verdict = "equivalence-proven";
|
|
307
|
+
asserts =
|
|
308
|
+
"structure changed, behaviour preserved — same resource set, identical arguments, every relocation covered by a `moved {}` block, terraform validate + fmt clean.";
|
|
309
|
+
doesNotAssert =
|
|
310
|
+
"that unrelated resources are correct or the configuration is otherwise optimal; completeness is never claimed.";
|
|
311
|
+
} else if (equivalence) {
|
|
312
|
+
verdict = "equivalence-failed";
|
|
313
|
+
asserts =
|
|
314
|
+
"nothing — the equivalence proof did NOT pass, so this change must not be treated as behaviour-preserving.";
|
|
315
|
+
doesNotAssert =
|
|
316
|
+
"behaviour-equivalence (the proof failed; see the run reasons).";
|
|
317
|
+
} else {
|
|
318
|
+
// proposed-unproven b.4 (or provenance recorded with no proof context).
|
|
319
|
+
verdict = "proposed-unproven";
|
|
320
|
+
asserts =
|
|
321
|
+
"nothing about runtime behaviour — this third-party-module adoption is PROPOSED only and was not verified against a plan.";
|
|
322
|
+
doesNotAssert =
|
|
323
|
+
"behaviour-equivalence (it alters behaviour and is not claimed equivalent); no terraform plan was run.";
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
verdict,
|
|
328
|
+
equivalence: equivalence
|
|
329
|
+
? {
|
|
330
|
+
resource_set_preserved: equivalence.resource_set_equivalent,
|
|
331
|
+
arguments_preserved: equivalence.arg_names_equivalent,
|
|
332
|
+
moves_exhaustive: equivalence.moves_exhaustive,
|
|
333
|
+
moves_detected: equivalence.moves_detected,
|
|
334
|
+
uncovered_moves: equivalence.uncovered_moves.length,
|
|
335
|
+
validate_passed: equivalence.validate_passed,
|
|
336
|
+
fmt_clean: equivalence.fmt_clean,
|
|
337
|
+
}
|
|
338
|
+
: undefined,
|
|
339
|
+
module_provenance: provenance,
|
|
340
|
+
proposed_unproven: verdict === "proposed-unproven",
|
|
341
|
+
asserts,
|
|
342
|
+
does_not_assert: doesNotAssert,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* What this run did NOT judge, because a human had already accepted it.
|
|
348
|
+
*
|
|
349
|
+
* WHY THIS IS IN THE SIGNED BYTES. `posture` and `control_statements` describe
|
|
350
|
+
* the OPEN findings only — suppressed ones are excluded by design, because a
|
|
351
|
+
* waived finding genuinely is an accepted risk. But that means a repo with
|
|
352
|
+
* thirty active waivers produced a bundle byte-indistinguishable from a
|
|
353
|
+
* genuinely clean one, and the difference is exactly what an assessor is there
|
|
354
|
+
* to examine. The human-readable report has always listed waivers; the SIGNED
|
|
355
|
+
* artifact did not, which made the signature cover the flattering half of the
|
|
356
|
+
* story.
|
|
357
|
+
*
|
|
358
|
+
* Each entry is what was silenced, where it is, why (a reason is mandatory
|
|
359
|
+
* everywhere), by which mechanism, and until when.
|
|
360
|
+
*
|
|
361
|
+
* A note on `source`. It records the two things the suppression pipeline
|
|
362
|
+
* actually models: `inline` (a directive in the source file) and `policy` (the
|
|
363
|
+
* `.infraweaver.yml` block). A skip written for ANOTHER scanner
|
|
364
|
+
* (`# checkov:skip=…`) is honoured and lands under `inline`, with the pipeline's
|
|
365
|
+
* own `[via <tool> skip]` marker appended to its reason — so the distinction is
|
|
366
|
+
* visible in the entry without this block claiming a structured tally the
|
|
367
|
+
* concern model does not carry.
|
|
368
|
+
*/
|
|
369
|
+
export interface EvidenceSuppressionRecord {
|
|
370
|
+
rule_id: string;
|
|
371
|
+
severity: string;
|
|
372
|
+
file: string;
|
|
373
|
+
line?: number | null | undefined;
|
|
374
|
+
/** mandatory everywhere; carries the `[via <tool> skip]` marker when the
|
|
375
|
+
* directive was another scanner's rather than ours. */
|
|
376
|
+
reason: string;
|
|
377
|
+
/** `inline` = a directive in the source file; `policy` = the `.infraweaver.yml` block. */
|
|
378
|
+
source: string;
|
|
379
|
+
/** ISO date the waiver lapses, or null for an open-ended one. */
|
|
380
|
+
expires: string | null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export interface EvidenceSuppressionSummary {
|
|
384
|
+
/** how many findings this bundle's posture excludes. 0 ⇒ nothing silenced. */
|
|
385
|
+
count: number;
|
|
386
|
+
/** count by mechanism (`inline` / `policy`) — how the silencing was applied. */
|
|
387
|
+
by_source: Record<string, number>;
|
|
388
|
+
/** open-ended waivers — the ones with no review date. */
|
|
389
|
+
never_expire: number;
|
|
390
|
+
entries: EvidenceSuppressionRecord[];
|
|
391
|
+
asserts: string;
|
|
392
|
+
does_not_assert: string;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* WHO produced this evidence (A.3). The bundle always named the engine; after
|
|
397
|
+
* the Trivy compromise that is not enough — a poisoned scanner produces
|
|
398
|
+
* poisoned findings under a clean engine version. This block records the whole
|
|
399
|
+
* acting chain: the model that drove the agent, the runtime, and the exact
|
|
400
|
+
* scanner builds the integrity gate observed (`toolchainIntegrity`) before
|
|
401
|
+
* executing them. Nothing here is fabricated: a tool that never ran this
|
|
402
|
+
* session is simply absent from `scanners`, and an unresolved model is null.
|
|
403
|
+
*/
|
|
404
|
+
export interface EvidenceProvenance {
|
|
405
|
+
/** resolved model specifier that drove the agent, or null (auto/unknown —
|
|
406
|
+
* e.g. a local `infraweaver mcp` session has no agent of its own). */
|
|
407
|
+
model: string | null;
|
|
408
|
+
/** the Node.js runtime the engine executed on (`process.version`). */
|
|
409
|
+
node: string;
|
|
410
|
+
/** exact scanner versions probed by the integrity gate before execution —
|
|
411
|
+
* only tools that actually ran this session appear. */
|
|
412
|
+
scanners: { tool: string; version: string | null }[];
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export interface EvidenceBundle {
|
|
416
|
+
/** Infraweaver's own schema id — explicitly NOT OSCAL (see module note). */
|
|
417
|
+
schema: typeof EVIDENCE_SCHEMA;
|
|
418
|
+
/** caller-supplied ISO timestamp (kept out of the builder so it stays pure). */
|
|
419
|
+
generated_at: string;
|
|
420
|
+
tool: { name: typeof TOOL_NAME; version: string };
|
|
421
|
+
/** who produced this evidence — model + runtime + observed scanner builds. */
|
|
422
|
+
provenance: EvidenceProvenance;
|
|
423
|
+
subject: {
|
|
424
|
+
scanned_dir: string;
|
|
425
|
+
repo?: string | undefined;
|
|
426
|
+
ref?: string | undefined;
|
|
427
|
+
commit?: string | undefined;
|
|
428
|
+
};
|
|
429
|
+
posture: AssessmentScorecard["posture"];
|
|
430
|
+
summary: {
|
|
431
|
+
total: number;
|
|
432
|
+
by_severity: AssessmentScorecard["by_severity"];
|
|
433
|
+
verification: VerificationSummary["counts"];
|
|
434
|
+
};
|
|
435
|
+
/** one statement per mapped concern, each carrying its status + controls. */
|
|
436
|
+
control_statements: EvidenceControlStatement[];
|
|
437
|
+
/** which scanners code-verified vs which were inconclusive (coverage gaps). */
|
|
438
|
+
coverage: VerificationSummary["coverage"];
|
|
439
|
+
/** what the posture above EXCLUDES because a human accepted it. Always
|
|
440
|
+
* present (0 when nothing was silenced) so a clean repo and a quiet one are
|
|
441
|
+
* distinguishable from the signed bytes alone. */
|
|
442
|
+
suppressions: EvidenceSuppressionSummary;
|
|
443
|
+
crosswalk: {
|
|
444
|
+
version: string;
|
|
445
|
+
reviewed: string;
|
|
446
|
+
/** sha256 of the reviewed crosswalk tables that produced these verdicts —
|
|
447
|
+
* binds the exact (integrity-gated) control mapping into the signed bundle. */
|
|
448
|
+
digest: string;
|
|
449
|
+
by_framework: CrosswalkReport["by_framework"];
|
|
450
|
+
};
|
|
451
|
+
/** the five-status legend, so the bundle is self-describing for an assessor. */
|
|
452
|
+
legend: Record<VerificationStatus, string>;
|
|
453
|
+
/** present only on a refactor run — the equivalence/proposed-unproven statement
|
|
454
|
+
* with the proof that backs it (omitted entirely otherwise). */
|
|
455
|
+
refactor?: EvidenceRefactorStatement | undefined;
|
|
456
|
+
/** present only when the credential-free cost analysis ran — the §5.19 idle
|
|
457
|
+
* floor (what this bills at zero traffic), never a monthly total. */
|
|
458
|
+
cost?: EvidenceCostStatement | undefined;
|
|
459
|
+
/** present only when detect_plan_drift ran — the TC-2.1 drift statement:
|
|
460
|
+
* signed, bounded proof of whether live infrastructure matched the committed
|
|
461
|
+
* configuration when the plan artifact was produced, with the `forgotten`
|
|
462
|
+
* blind-spot list and every coverage gap carried in the claim itself. */
|
|
463
|
+
drift?: DriftEvidenceStatement | undefined;
|
|
464
|
+
/** present only when a provider-major upgrade was detected (TC-2.2) — which
|
|
465
|
+
* boundary was crossed, the codified transforms found in this configuration,
|
|
466
|
+
* and the plan that proves (or fails to prove) the result. A `no-plan` verdict
|
|
467
|
+
* is recorded rather than omitted: an upgrade shipped without a plan is
|
|
468
|
+
* exactly the thing a reader must be able to see. */
|
|
469
|
+
upgrade?: UpgradeEvidenceStatement | undefined;
|
|
470
|
+
/** Ed25519 signing metadata (public key + fingerprint + the sibling `.sig`
|
|
471
|
+
* carrying the detached signature over this file's exact bytes), or an
|
|
472
|
+
* explicit null when no INFRAWEAVER_SIGNING_KEY is configured — never fake. */
|
|
473
|
+
signature: SignatureMetadata | null;
|
|
474
|
+
/** §5.16 always-present eIDAS assurance declaration: which electronic-seal
|
|
475
|
+
* tier the `signature` above actually reaches, and — the load-bearing half —
|
|
476
|
+
* which it does not. `signature` says HOW the bytes were signed; this says
|
|
477
|
+
* WHAT THAT IS WORTH legally, so a reader cannot over-read an Ed25519
|
|
478
|
+
* signature as a qualified seal. Present even when unsigned (`level: "none"`),
|
|
479
|
+
* because an omitted section is ambiguous. See [[eidas]]. */
|
|
480
|
+
seal: EidasSeal;
|
|
481
|
+
disclaimer: string;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const DISCLAIMER =
|
|
485
|
+
"Indicative alignment guidance from a deterministic starter rule-pack — NOT an " +
|
|
486
|
+
"audit verdict. Statuses are code-verified only; an inconclusive check is a " +
|
|
487
|
+
"coverage gap (not a pass) and absence of a finding is not proof of compliance.";
|
|
488
|
+
|
|
489
|
+
export interface EvidenceSubject {
|
|
490
|
+
scanned_dir: string;
|
|
491
|
+
repo?: string | undefined;
|
|
492
|
+
ref?: string | undefined;
|
|
493
|
+
commit?: string | undefined;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Build the evidence bundle from an assessment's scorecard + crosswalk. Pure —
|
|
498
|
+
* `generatedAt` and the subject identifiers are passed in so the same inputs
|
|
499
|
+
* always produce the same bytes (and tests don't need a clock). Control
|
|
500
|
+
* statements come from the crosswalk entries (which already carry the
|
|
501
|
+
* verification status), enriched with the concern's severity/location when the
|
|
502
|
+
* scanned concerns are supplied (the crosswalk entry itself carries neither).
|
|
503
|
+
*/
|
|
504
|
+
export function buildEvidenceBundle(args: {
|
|
505
|
+
scorecard: AssessmentScorecard;
|
|
506
|
+
crosswalk: CrosswalkReport;
|
|
507
|
+
subject: EvidenceSubject;
|
|
508
|
+
generatedAt: string;
|
|
509
|
+
version?: string;
|
|
510
|
+
/** the scanned concerns backing the crosswalk entries — the source of each
|
|
511
|
+
* statement's severity/file/line enrichment. */
|
|
512
|
+
concerns?: Concern[];
|
|
513
|
+
refactor?: EvidenceRefactorStatement | undefined;
|
|
514
|
+
/** the §5.19 idle-floor statement; omitted when no cost analysis ran. */
|
|
515
|
+
cost?: EvidenceCostStatement | undefined;
|
|
516
|
+
/** the TC-2.1 drift statement; omitted when detect_plan_drift didn't run. */
|
|
517
|
+
drift?: DriftEvidenceStatement | undefined;
|
|
518
|
+
/** the TC-2.2 upgrade statement; omitted when no provider-major pack applied. */
|
|
519
|
+
upgrade?: UpgradeEvidenceStatement | undefined;
|
|
520
|
+
/** who produced this evidence (A.3). REQUIRED — an evidence bundle that
|
|
521
|
+
* doesn't name its actor is the gap this block exists to close. Callers pass
|
|
522
|
+
* real values (the builder stays pure; no ambient reads here). */
|
|
523
|
+
provenance: EvidenceProvenance;
|
|
524
|
+
/** signing metadata when a key is configured; omitted/null ⇒ explicit
|
|
525
|
+
* `"signature": null` (the honest-unsigned marker). */
|
|
526
|
+
signature?: SignatureMetadata | null;
|
|
527
|
+
}): EvidenceBundle {
|
|
528
|
+
const {
|
|
529
|
+
scorecard,
|
|
530
|
+
crosswalk,
|
|
531
|
+
subject,
|
|
532
|
+
generatedAt,
|
|
533
|
+
refactor,
|
|
534
|
+
cost,
|
|
535
|
+
drift,
|
|
536
|
+
upgrade,
|
|
537
|
+
} = args;
|
|
538
|
+
const concernById = new Map((args.concerns ?? []).map((c) => [c.id, c]));
|
|
539
|
+
const control_statements: EvidenceControlStatement[] = crosswalk.entries.map(
|
|
540
|
+
(e) => {
|
|
541
|
+
const concern = concernById.get(e.concern_id);
|
|
542
|
+
return {
|
|
543
|
+
concern_id: e.concern_id,
|
|
544
|
+
rule_id: e.rule_id,
|
|
545
|
+
status: e.status,
|
|
546
|
+
...(concern
|
|
547
|
+
? {
|
|
548
|
+
concern_key: concernKeyOf(concern),
|
|
549
|
+
severity: concern.severity,
|
|
550
|
+
file: concern.location.file,
|
|
551
|
+
line: concern.location.line,
|
|
552
|
+
}
|
|
553
|
+
: {}),
|
|
554
|
+
controls: e.controls,
|
|
555
|
+
};
|
|
556
|
+
},
|
|
557
|
+
);
|
|
558
|
+
return {
|
|
559
|
+
schema: EVIDENCE_SCHEMA,
|
|
560
|
+
generated_at: generatedAt,
|
|
561
|
+
tool: { name: TOOL_NAME, version: args.version ?? packageJson.version },
|
|
562
|
+
provenance: args.provenance,
|
|
563
|
+
subject,
|
|
564
|
+
posture: scorecard.posture,
|
|
565
|
+
summary: {
|
|
566
|
+
total: scorecard.total,
|
|
567
|
+
by_severity: scorecard.by_severity,
|
|
568
|
+
verification: scorecard.verification.counts,
|
|
569
|
+
},
|
|
570
|
+
control_statements,
|
|
571
|
+
coverage: scorecard.verification.coverage,
|
|
572
|
+
// Derived from the scorecard the report already renders, so the signed
|
|
573
|
+
// bundle and the human-readable report can never disagree about what was
|
|
574
|
+
// silenced.
|
|
575
|
+
suppressions: buildSuppressionSummary(scorecard.waivers),
|
|
576
|
+
crosswalk: {
|
|
577
|
+
version: crosswalk.version,
|
|
578
|
+
reviewed: crosswalk.reviewed,
|
|
579
|
+
digest: CROSSWALK_DIGEST,
|
|
580
|
+
by_framework: crosswalk.by_framework,
|
|
581
|
+
},
|
|
582
|
+
legend: VERIFICATION_STATUS_LABEL,
|
|
583
|
+
...(refactor ? { refactor } : {}),
|
|
584
|
+
...(cost ? { cost } : {}),
|
|
585
|
+
...(drift ? { drift } : {}),
|
|
586
|
+
...(upgrade ? { upgrade } : {}),
|
|
587
|
+
signature: args.signature ?? null,
|
|
588
|
+
// Derived from the signature actually present, never from configuration, so
|
|
589
|
+
// the declared level can't drift from what the bundle really carries.
|
|
590
|
+
seal: eidasSealFor(args.signature ?? null),
|
|
591
|
+
disclaimer: DISCLAIMER,
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
export const TerraformEmitEvidenceParams = type({
|
|
596
|
+
"output_path?": type.string.describe(
|
|
597
|
+
`where to write the evidence bundle (default: ./${DEFAULT_EVIDENCE_PATH} in the workspace). Commit it so the compliance/ path is the auditable evidence trail.`,
|
|
598
|
+
),
|
|
599
|
+
"severity_threshold?": type(
|
|
600
|
+
"'critical' | 'high' | 'medium' | 'low' | 'info'",
|
|
601
|
+
).describe(
|
|
602
|
+
"minimum severity to include (default: the run's configured threshold, else low).",
|
|
603
|
+
),
|
|
604
|
+
"proposed_unproven?": type("boolean").describe(
|
|
605
|
+
"refactor b.4 only: set true when emitting evidence for a third-party-module adoption shipped WITHOUT a plan, so the bundle records the unmissable `proposed — unproven` marker (it asserts nothing about runtime behaviour). Ignored when a keys-free equivalence proof was already recorded this run.",
|
|
606
|
+
),
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
export function TerraformEmitEvidenceTool(ctx: LocalToolContext) {
|
|
610
|
+
return tool({
|
|
611
|
+
name: "terraform_emit_evidence",
|
|
612
|
+
description:
|
|
613
|
+
"Emit a backend-free compliance EVIDENCE BUNDLE (auditor-facing JSON) for the workspace — the wedge that " +
|
|
614
|
+
"produces assessor-ready evidence with no cloud and no backend. Runs the read-only assessment and writes " +
|
|
615
|
+
`a deterministic bundle (default \`${DEFAULT_EVIDENCE_PATH}\`): overall \`posture\`, per-control ` +
|
|
616
|
+
"statements each with a five-status verdict (fail / not-code-verifiable), the scanner `coverage` " +
|
|
617
|
+
"(verified vs inconclusive), and the indicative crosswalk index. Commit the file so `compliance/` is the " +
|
|
618
|
+
`auditable trail. It NEVER modifies Terraform or opens a PR. The schema is ${PRODUCT_NAME}'s own (not OSCAL); ` +
|
|
619
|
+
"the bundle is indicative alignment guidance, never an audit verdict, and never claims a pass it can't " +
|
|
620
|
+
"code-verify. Pairs with `terraform_assess` (the human-readable report) and `terraform_emit_sarif`.",
|
|
621
|
+
parameters: TerraformEmitEvidenceParams,
|
|
622
|
+
execute: execute(
|
|
623
|
+
async ({ output_path, severity_threshold, proposed_unproven }) => {
|
|
624
|
+
const configured = ctx.payload.severityThreshold as
|
|
625
|
+
| Severity
|
|
626
|
+
| undefined;
|
|
627
|
+
const threshold: Severity = severity_threshold ?? configured ?? "low";
|
|
628
|
+
const { cwd, open, scorecard, crosswalk } = runAssessmentPipeline(
|
|
629
|
+
ctx,
|
|
630
|
+
threshold,
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
// refactor statement (omitted on a non-refactor run): the recorded keys-free
|
|
634
|
+
// equivalence proof + adopted-module provenance + the proposed-unproven marker.
|
|
635
|
+
const session = scanSessionOf(ctx.toolState);
|
|
636
|
+
const refactor = buildRefactorStatement({
|
|
637
|
+
equivalence: session.refactorEquivalence,
|
|
638
|
+
provenance: collectWorkspaceProvenance(cwd),
|
|
639
|
+
proposedUnproven: proposed_unproven ?? false,
|
|
640
|
+
});
|
|
641
|
+
// surface the proposed-unproven verdict to the Cloud report (cloudReport.ts):
|
|
642
|
+
// an equivalence proof, when present, takes precedence, so only record the
|
|
643
|
+
// flag when the statement actually resolved to proposed-unproven.
|
|
644
|
+
if (refactor?.proposed_unproven)
|
|
645
|
+
session.refactorProposedUnproven = true;
|
|
646
|
+
// cost statement (omitted unless terraform_emit_cost ran): the SAME idle-floor
|
|
647
|
+
// numbers cost.md shows, so the artifact and the signed bundle can't disagree.
|
|
648
|
+
const cost = buildCostStatement(session.lastIdleFloor);
|
|
649
|
+
// drift statement (omitted unless detect_plan_drift ran): drift — with the
|
|
650
|
+
// forgotten blind-spot list and any coverage gap — enters the SIGNED bundle
|
|
651
|
+
// instead of dying as a run verdict (TC-2.1).
|
|
652
|
+
const drift = session.lastDrift;
|
|
653
|
+
// upgrade statement (TC-2.2; omitted unless terraform_provider_upgrade
|
|
654
|
+
// matched a pack): binds the detected transforms to the plan that came
|
|
655
|
+
// after them. The plan facts are read from the session rather than taken
|
|
656
|
+
// from the agent, so a "proven" verdict cannot be self-reported. A run with
|
|
657
|
+
// no plan becomes the `no-plan` verdict rather than a silent omission.
|
|
658
|
+
const upgrade = upgradeStatementOf(session);
|
|
659
|
+
|
|
660
|
+
// SECURITY: confine the agent-supplied path to the workspace (same guard as
|
|
661
|
+
// terraform_emit_sarif) so it can't clobber arbitrary files on the runner.
|
|
662
|
+
const target = resolveWithinCwd(
|
|
663
|
+
cwd,
|
|
664
|
+
output_path ?? DEFAULT_EVIDENCE_PATH,
|
|
665
|
+
);
|
|
666
|
+
// Ed25519 signing (opt-in via INFRAWEAVER_SIGNING_KEY). A set-but-malformed
|
|
667
|
+
// key throws here — a configured signer must never silently emit unsigned.
|
|
668
|
+
const signingKey = loadSigningKey();
|
|
669
|
+
const sigPath = `${target}.sig`;
|
|
670
|
+
const gh = githubContext();
|
|
671
|
+
const bundle = buildEvidenceBundle({
|
|
672
|
+
scorecard,
|
|
673
|
+
crosswalk,
|
|
674
|
+
concerns: open,
|
|
675
|
+
subject: {
|
|
676
|
+
scanned_dir: cwd,
|
|
677
|
+
repo: gh.repository,
|
|
678
|
+
ref: gh.refName,
|
|
679
|
+
commit: gh.sha,
|
|
680
|
+
},
|
|
681
|
+
generatedAt: new Date().toISOString(),
|
|
682
|
+
refactor,
|
|
683
|
+
cost,
|
|
684
|
+
drift,
|
|
685
|
+
upgrade,
|
|
686
|
+
// WHO produced this evidence (A.3): the resolved model (absent on a
|
|
687
|
+
// local MCP session), the runtime, and the exact scanner builds the
|
|
688
|
+
// integrity gate observed before executing them this session.
|
|
689
|
+
provenance: {
|
|
690
|
+
model: ctx.resolvedModel ?? null,
|
|
691
|
+
node: process.version,
|
|
692
|
+
scanners: probedToolVersions(),
|
|
693
|
+
},
|
|
694
|
+
signature: signingKey
|
|
695
|
+
? signatureMetadata(signingKey, basename(sigPath))
|
|
696
|
+
: null,
|
|
697
|
+
});
|
|
698
|
+
const bytes = `${JSON.stringify(bundle, null, 2)}\n`;
|
|
699
|
+
// the default compliance/ dir may not exist yet on a first run.
|
|
700
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
701
|
+
writeFileSync(target, bytes);
|
|
702
|
+
// detached signature over the EXACT bytes written (builder is deterministic,
|
|
703
|
+
// so no canonicaliser) — verify with `infraweaver verify-evidence`.
|
|
704
|
+
if (signingKey)
|
|
705
|
+
writeFileSync(sigPath, `${signBytes(bytes, signingKey)}\n`);
|
|
706
|
+
|
|
707
|
+
// Second tier: keyless Sigstore attestation (opt-in via INFRAWEAVER_KEYLESS_SIGNING,
|
|
708
|
+
// CI-only). Best-effort — a Fulcio/Rekor failure logs and leaves the evidence
|
|
709
|
+
// (+ Ed25519 sig) intact rather than aborting the emit. The in-toto statement's
|
|
710
|
+
// subject is this file (by sha256 digest), so the attestation binds the exact
|
|
711
|
+
// bytes to the workflow's OIDC identity, auditable in the public Rekor log.
|
|
712
|
+
let keylessBundleFile: string | undefined;
|
|
713
|
+
if (keylessSigningPossible()) {
|
|
714
|
+
try {
|
|
715
|
+
const statement = buildInTotoStatement({
|
|
716
|
+
subjectName: basename(target),
|
|
717
|
+
artifactBytes: bytes,
|
|
718
|
+
predicate: {
|
|
719
|
+
posture: bundle.posture,
|
|
720
|
+
control_statements: bundle.control_statements.length,
|
|
721
|
+
verification: bundle.summary.verification,
|
|
722
|
+
tool: bundle.tool,
|
|
723
|
+
generated_at: bundle.generated_at,
|
|
724
|
+
subject: bundle.subject,
|
|
725
|
+
},
|
|
726
|
+
});
|
|
727
|
+
const sigstoreBundle = await keylessSignStatement(statement);
|
|
728
|
+
keylessBundleFile = keylessBundlePath(target);
|
|
729
|
+
writeFileSync(
|
|
730
|
+
keylessBundleFile,
|
|
731
|
+
`${JSON.stringify(sigstoreBundle, null, 2)}\n`,
|
|
732
|
+
);
|
|
733
|
+
log.info(
|
|
734
|
+
`» terraform_emit_evidence: keyless Sigstore attestation → ${keylessBundleFile}`,
|
|
735
|
+
);
|
|
736
|
+
} catch (e) {
|
|
737
|
+
log.warning(
|
|
738
|
+
`» terraform_emit_evidence: keyless signing failed (evidence still emitted): ${e instanceof Error ? e.message : String(e)}`,
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
log.info(
|
|
743
|
+
`» terraform_emit_evidence: ${scorecard.posture} — ${bundle.control_statements.length} control statement(s) → ${target}` +
|
|
744
|
+
(signingKey
|
|
745
|
+
? ` (signed ed25519, key ${signingKey.fingerprint.slice(0, 16)}…)`
|
|
746
|
+
: " (unsigned — no INFRAWEAVER_SIGNING_KEY)"),
|
|
747
|
+
);
|
|
748
|
+
return toolOk({
|
|
749
|
+
output_path: target,
|
|
750
|
+
posture: bundle.posture,
|
|
751
|
+
statements: bundle.control_statements.length,
|
|
752
|
+
verification: bundle.summary.verification,
|
|
753
|
+
...(bundle.refactor
|
|
754
|
+
? { refactor_verdict: bundle.refactor.verdict }
|
|
755
|
+
: {}),
|
|
756
|
+
signed: signingKey !== null,
|
|
757
|
+
...(signingKey
|
|
758
|
+
? {
|
|
759
|
+
signature_path: sigPath,
|
|
760
|
+
key_fingerprint_sha256: signingKey.fingerprint,
|
|
761
|
+
}
|
|
762
|
+
: {}),
|
|
763
|
+
keyless_signed: keylessBundleFile !== undefined,
|
|
764
|
+
...(keylessBundleFile
|
|
765
|
+
? { keyless_bundle_path: keylessBundleFile }
|
|
766
|
+
: {}),
|
|
767
|
+
});
|
|
768
|
+
},
|
|
769
|
+
),
|
|
770
|
+
});
|
|
771
|
+
}
|