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,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governance rules over IaC *shape* — the two Phase-3 finding classes that are
|
|
3
|
+
* about how a configuration behaves as a change-control artefact, not about
|
|
4
|
+
* whether a resource is misconfigured.
|
|
5
|
+
*
|
|
6
|
+
* Config scanners can't produce these. Checkov and Trivy answer "is this setting
|
|
7
|
+
* wrong?", which presumes a setting with a right answer. Neither of these has
|
|
8
|
+
* one:
|
|
9
|
+
*
|
|
10
|
+
* 1. **Imperative side-effects** (Terraform 1.14 `action` blocks invoked from
|
|
11
|
+
* `lifecycle.action_trigger`). An apply now runs work — restart a VM, invoke a
|
|
12
|
+
* Lambda — that is nowhere in the resource graph and leaves no trace in state.
|
|
13
|
+
* That is a legitimate feature and also a change-control fact an auditor needs:
|
|
14
|
+
* "what does an apply do?" stopped being answerable from the plan alone. So
|
|
15
|
+
* this lands in the REVIEW-REQUIRED band. Calling it a failure would be wrong;
|
|
16
|
+
* staying silent would let the one thing a plan can't show you go unmentioned.
|
|
17
|
+
*
|
|
18
|
+
* 2. **Input-bound destroy protection** (OpenTofu 1.12 dynamic `prevent_destroy`).
|
|
19
|
+
* Terraform requires a literal here; OpenTofu allows an expression, which is
|
|
20
|
+
* the documented way to protect production and not ephemeral environments.
|
|
21
|
+
* Whether THIS resource is protected therefore depends on values supplied at
|
|
22
|
+
* plan time. The honest verdict is UNVERIFIABLE — and the rule that matters is
|
|
23
|
+
* the one written into the roadmap: any static rule asserting on
|
|
24
|
+
* `prevent_destroy` must return unverifiable when the value is an unresolved
|
|
25
|
+
* expression, never pass and never fail. This rule is that assertion, made
|
|
26
|
+
* explicit and reported in the ADVISORY band, scoped to stateful resources so
|
|
27
|
+
* it stays signal rather than noise.
|
|
28
|
+
*
|
|
29
|
+
* Pure: HCL text in, findings out. No filesystem, no subprocess, no schema.
|
|
30
|
+
*/
|
|
31
|
+
import { parseBlocks, stripTrailingComment, topLevelAssignments } from "#app/mcp/terraform/hcl";
|
|
32
|
+
import {
|
|
33
|
+
BAND_SEVERITY,
|
|
34
|
+
DYNAMIC_DESTROY_PROTECTION,
|
|
35
|
+
IMPERATIVE_ACTION_TRIGGER,
|
|
36
|
+
NATIVE_SOURCE,
|
|
37
|
+
} from "#app/mcp/terraform/nativeRules";
|
|
38
|
+
import { buildConcern, type Concern } from "#app/mcp/terraform/types";
|
|
39
|
+
|
|
40
|
+
// --- imperative side-effects (action blocks + action_trigger) ----------------
|
|
41
|
+
|
|
42
|
+
export interface ActionTriggerSite {
|
|
43
|
+
/** the resource the trigger hangs off, `<type>.<name>`. */
|
|
44
|
+
resourceAddress: string;
|
|
45
|
+
/** 1-based line of the `action_trigger` block. */
|
|
46
|
+
line: number;
|
|
47
|
+
/** lifecycle events the trigger fires on, as written (`after_create`, …). */
|
|
48
|
+
events: string[];
|
|
49
|
+
/** the action references invoked, as written (`action.aws_lambda_invoke.x`). */
|
|
50
|
+
actions: string[];
|
|
51
|
+
/** true when the trigger is conditional — the side-effect may or may not run,
|
|
52
|
+
* which is strictly harder to reason about, not easier. */
|
|
53
|
+
conditional: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Split an HCL list literal's elements. Tolerant: an unbracketed or multi-line
|
|
57
|
+
* value yields whatever elements are recognisable, and an unrecognisable one
|
|
58
|
+
* yields none — the finding still fires on the trigger itself. */
|
|
59
|
+
function listElements(raw: string): string[] {
|
|
60
|
+
const v = stripTrailingComment(raw);
|
|
61
|
+
const inner = v.startsWith("[") ? v.slice(1, v.endsWith("]") ? -1 : undefined) : v;
|
|
62
|
+
return inner
|
|
63
|
+
.split(",")
|
|
64
|
+
.map((s) => s.trim().replace(/^"|"$/g, ""))
|
|
65
|
+
.filter(Boolean);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Every `lifecycle { action_trigger { … } }` in `hcl`, with the resource it
|
|
70
|
+
* belongs to. Walks resource → lifecycle → action_trigger explicitly rather than
|
|
71
|
+
* matching `action_trigger` anywhere, so a same-named argument elsewhere can't
|
|
72
|
+
* fabricate a governance finding.
|
|
73
|
+
*/
|
|
74
|
+
export function findActionTriggers(hcl: string): ActionTriggerSite[] {
|
|
75
|
+
const out: ActionTriggerSite[] = [];
|
|
76
|
+
for (const resource of parseBlocks(hcl, ["resource"])) {
|
|
77
|
+
const [type = "", name = ""] = resource.labels;
|
|
78
|
+
for (const lifecycle of parseBlocks(resource.body, ["lifecycle"])) {
|
|
79
|
+
const lifecycleBase = resource.bodyLine + lifecycle.bodyLine - 1;
|
|
80
|
+
for (const trigger of parseBlocks(lifecycle.body, ["action_trigger"])) {
|
|
81
|
+
const args = topLevelAssignments(trigger.body);
|
|
82
|
+
const get = (key: string): string | undefined => args.find((a) => a.name === key)?.value;
|
|
83
|
+
out.push({
|
|
84
|
+
resourceAddress: `${type}.${name}`,
|
|
85
|
+
line: lifecycleBase + trigger.line - 1,
|
|
86
|
+
events: listElements(get("events") ?? ""),
|
|
87
|
+
actions: listElements(get("actions") ?? ""),
|
|
88
|
+
// the docs spell it both ways across pages; accept either rather than
|
|
89
|
+
// silently mis-report a conditional trigger as unconditional.
|
|
90
|
+
conditional: get("condition") !== undefined || get("conditions") !== undefined,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Declared `action "<type>" "<name>" {}` blocks — the definitions the triggers
|
|
99
|
+
* reference. Reported as inventory, never as a finding: declaring an action is
|
|
100
|
+
* inert; only invoking one from a lifecycle event has an effect. */
|
|
101
|
+
export function findActionDeclarations(hcl: string): { address: string; line: number }[] {
|
|
102
|
+
return parseBlocks(hcl, ["action"]).map((b) => ({
|
|
103
|
+
address: `action.${b.labels[0] ?? ""}.${b.labels[1] ?? ""}`,
|
|
104
|
+
line: b.line,
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function actionTriggersToConcerns(sites: ActionTriggerSite[], file: string): Concern[] {
|
|
109
|
+
return sites.map((site) =>
|
|
110
|
+
buildConcern({
|
|
111
|
+
source: NATIVE_SOURCE,
|
|
112
|
+
rule: IMPERATIVE_ACTION_TRIGGER,
|
|
113
|
+
file,
|
|
114
|
+
line: site.line,
|
|
115
|
+
severity: BAND_SEVERITY["review-required"],
|
|
116
|
+
category: "correctness",
|
|
117
|
+
evidence:
|
|
118
|
+
`${site.resourceAddress}: an apply invokes ${site.actions.length || "one or more"} provider ` +
|
|
119
|
+
`action(s) on ${site.events.join(", ") || "a lifecycle event"}` +
|
|
120
|
+
(site.conditional ? ", conditionally" : "") +
|
|
121
|
+
(site.actions.length ? ` — ${site.actions.join(", ")}` : "") +
|
|
122
|
+
". The side-effect is outside the resource graph and leaves no record in state",
|
|
123
|
+
remediation_hint:
|
|
124
|
+
"Not a defect to fix — a change to record. Confirm the invoked action is covered by your change " +
|
|
125
|
+
"approval process, that its effect is idempotent (Terraform will re-run it on the next matching " +
|
|
126
|
+
"event), and that whoever reads the plan knows the apply does more than the plan shows.",
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// --- input-bound destroy protection (OpenTofu dynamic prevent_destroy) -------
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Resource types whose destruction loses data rather than just capacity. Only
|
|
135
|
+
* these are worth an advisory: a dynamic `prevent_destroy` on a security group is
|
|
136
|
+
* a deployment-shape choice, on a database it is the difference between an
|
|
137
|
+
* environment teardown and an incident. Matched on TYPE SUFFIX so the list stays
|
|
138
|
+
* provider-agnostic (`aws_db_instance`, `azurerm_mssql_database`,
|
|
139
|
+
* `google_sql_database_instance` all end in a stateful noun).
|
|
140
|
+
*/
|
|
141
|
+
const STATEFUL_TYPE_SIGNAL =
|
|
142
|
+
/(?:^|_)(?:db|database|sql|instance|cluster|bucket|volume|disk|filesystem|file_system|table|vault|keyvault|key_vault|secret|snapshot|backup|registry|repository|share|queue|topic|stream|warehouse)$/;
|
|
143
|
+
|
|
144
|
+
export function isStatefulResourceType(resourceType: string): boolean {
|
|
145
|
+
return STATEFUL_TYPE_SIGNAL.test(resourceType);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** `true` / `false` — the only values standard Terraform accepts for
|
|
149
|
+
* `prevent_destroy`. Anything else is an expression whose value we cannot know. */
|
|
150
|
+
function isBooleanLiteral(value: string): boolean {
|
|
151
|
+
const v = stripTrailingComment(value);
|
|
152
|
+
return v === "true" || v === "false";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface DynamicDestroyProtectionSite {
|
|
156
|
+
resourceAddress: string;
|
|
157
|
+
resourceType: string;
|
|
158
|
+
/** 1-based line of the `prevent_destroy` assignment. */
|
|
159
|
+
line: number;
|
|
160
|
+
/** the expression as written, e.g. `var.protect`. */
|
|
161
|
+
expression: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Every `prevent_destroy` on a STATEFUL resource whose value is an expression
|
|
166
|
+
* rather than a literal. A literal `true` or `false` is decidable from the code
|
|
167
|
+
* and reported by nobody here; a non-stateful resource is out of scope by design.
|
|
168
|
+
*/
|
|
169
|
+
export function findDynamicDestroyProtection(hcl: string): DynamicDestroyProtectionSite[] {
|
|
170
|
+
const out: DynamicDestroyProtectionSite[] = [];
|
|
171
|
+
for (const resource of parseBlocks(hcl, ["resource"])) {
|
|
172
|
+
const [type = "", name = ""] = resource.labels;
|
|
173
|
+
if (!isStatefulResourceType(type)) continue;
|
|
174
|
+
for (const lifecycle of parseBlocks(resource.body, ["lifecycle"])) {
|
|
175
|
+
const lifecycleBase = resource.bodyLine + lifecycle.bodyLine - 1;
|
|
176
|
+
for (const a of topLevelAssignments(lifecycle.body)) {
|
|
177
|
+
if (a.name !== "prevent_destroy" || isBooleanLiteral(a.value)) continue;
|
|
178
|
+
out.push({
|
|
179
|
+
resourceAddress: `${type}.${name}`,
|
|
180
|
+
resourceType: type,
|
|
181
|
+
line: lifecycleBase + a.bodyLine - 1,
|
|
182
|
+
expression: stripTrailingComment(a.value),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function dynamicDestroyProtectionToConcerns(
|
|
191
|
+
sites: DynamicDestroyProtectionSite[],
|
|
192
|
+
file: string,
|
|
193
|
+
): Concern[] {
|
|
194
|
+
return sites.map((site) =>
|
|
195
|
+
buildConcern({
|
|
196
|
+
source: NATIVE_SOURCE,
|
|
197
|
+
rule: DYNAMIC_DESTROY_PROTECTION,
|
|
198
|
+
file,
|
|
199
|
+
line: site.line,
|
|
200
|
+
severity: BAND_SEVERITY.advisory,
|
|
201
|
+
category: "correctness",
|
|
202
|
+
evidence:
|
|
203
|
+
`${site.resourceAddress} is stateful and its destroy protection is bound to \`${site.expression}\` ` +
|
|
204
|
+
"— whether a destroy is blocked depends on plan-time input, so it cannot be determined from code",
|
|
205
|
+
remediation_hint:
|
|
206
|
+
"Legitimate for multi-environment stacks (protect production, not ephemeral environments) and " +
|
|
207
|
+
"reported as advisory for exactly that reason. If this resource should always be protected, a " +
|
|
208
|
+
"literal `prevent_destroy = true` makes the guarantee readable from the code; if it varies, record " +
|
|
209
|
+
"which environments resolve it to true so the protection claim is auditable.",
|
|
210
|
+
}),
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// --- combined pack ------------------------------------------------------------
|
|
215
|
+
|
|
216
|
+
export interface GovernanceScan {
|
|
217
|
+
concerns: Concern[];
|
|
218
|
+
/** action declarations found — inventory for the run summary, not findings. */
|
|
219
|
+
actionDeclarations: { address: string; line: number; file: string }[];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Both governance rules over one file's HCL. Pure. */
|
|
223
|
+
export function scanGovernanceFile(hcl: string, file: string): GovernanceScan {
|
|
224
|
+
return {
|
|
225
|
+
concerns: [
|
|
226
|
+
...actionTriggersToConcerns(findActionTriggers(hcl), file),
|
|
227
|
+
...dynamicDestroyProtectionToConcerns(findDynamicDestroyProtection(hcl), file),
|
|
228
|
+
],
|
|
229
|
+
actionDeclarations: findActionDeclarations(hcl).map((d) => ({ ...d, file })),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, tolerant HCL block scanning.
|
|
3
|
+
*
|
|
4
|
+
* Every native rule in this engine ([[writeOnly]], [[governance]], [[tfquery]],
|
|
5
|
+
* and the provider-schema cross-check in [[scanners]]) needs the same primitive:
|
|
6
|
+
* walk a file's `<keyword> "label" "label" { … }` blocks and read their top-level
|
|
7
|
+
* arguments, without being fooled by a `}` inside a string, a commented-out brace,
|
|
8
|
+
* or a heredoc body. Getting that wrong doesn't produce a parse error — it
|
|
9
|
+
* produces a WRONG FINDING at a wrong line, which is the failure mode this
|
|
10
|
+
* product can least afford. So there is exactly one implementation of it.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately NOT a real HCL parser. We never evaluate expressions, resolve
|
|
13
|
+
* variables, or validate syntax — `terraform validate` (the customer's own
|
|
14
|
+
* binary) owns that. This reads structure only, and every consumer is written to
|
|
15
|
+
* degrade to "no finding" rather than guess when the structure is unclear.
|
|
16
|
+
*
|
|
17
|
+
* Pure: string in, structure out. No filesystem, no subprocess.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A non-code span in HCL — a `"…"` string, a `#`/`//` line comment, or a
|
|
22
|
+
* `<<EOF` heredoc — that the brace/argument scanners must skip wholesale (an
|
|
23
|
+
* interpolation's `${…}`, a commented `}`, or a heredoc's `key = value` lines
|
|
24
|
+
* would otherwise corrupt brace depth or fabricate an argument). Returned for a
|
|
25
|
+
* span starting at `i`: `end` is the index of its LAST char (caller resumes at
|
|
26
|
+
* `end + 1`), and `endsLine` is true when the span finished at a line boundary
|
|
27
|
+
* (comment / heredoc — the next token begins a fresh statement). null when `i`
|
|
28
|
+
* isn't the start of a non-code span.
|
|
29
|
+
*/
|
|
30
|
+
export function skipNonCode(s: string, i: number): { end: number; endsLine: boolean } | null {
|
|
31
|
+
const ch = s[i];
|
|
32
|
+
// double-quoted string (with `\` escapes). Ends mid-line.
|
|
33
|
+
if (ch === '"') {
|
|
34
|
+
let j = i + 1;
|
|
35
|
+
while (j < s.length && s[j] !== '"') {
|
|
36
|
+
if (s[j] === "\\") j++;
|
|
37
|
+
j++;
|
|
38
|
+
}
|
|
39
|
+
return { end: j, endsLine: false };
|
|
40
|
+
}
|
|
41
|
+
// heredoc body (`<<EOF` / `<<-EOF` / `<<~EOF`) — arbitrary text.
|
|
42
|
+
if (ch === "<" && s[i + 1] === "<") {
|
|
43
|
+
const m = /^<<[-~]?([A-Za-z_][A-Za-z0-9_]*)/.exec(s.slice(i, i + 80));
|
|
44
|
+
if (m) {
|
|
45
|
+
const delim = m[1];
|
|
46
|
+
const openNl = s.indexOf("\n", i);
|
|
47
|
+
if (openNl === -1) return { end: s.length - 1, endsLine: true };
|
|
48
|
+
// the closing delimiter sits alone on its own line (optionally indented).
|
|
49
|
+
const closeRe = new RegExp(`\\n[ \\t]*${delim}[ \\t]*(?=\\n|$)`);
|
|
50
|
+
const cm = closeRe.exec(s.slice(openNl));
|
|
51
|
+
const end = cm ? openNl + cm.index + cm[0].length - 1 : s.length - 1;
|
|
52
|
+
return { end, endsLine: true };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// `#` or `//` line comment.
|
|
56
|
+
if (ch === "#" || (ch === "/" && s[i + 1] === "/")) {
|
|
57
|
+
const nl = s.indexOf("\n", i);
|
|
58
|
+
return { end: nl === -1 ? s.length - 1 : nl, endsLine: true };
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Brace-match from an opening `{` at `open`; returns the inner text + end index
|
|
65
|
+
* (the matching `}`), string/comment/heredoc-aware so interpolation braces, a
|
|
66
|
+
* commented `}`, or a heredoc body don't fool it. null when the block never
|
|
67
|
+
* closes (a truncated or malformed file) — callers stop rather than guess.
|
|
68
|
+
*/
|
|
69
|
+
export function matchBraceBody(hcl: string, open: number): { text: string; end: number } | null {
|
|
70
|
+
if (open < 0) return null;
|
|
71
|
+
let depth = 0;
|
|
72
|
+
for (let i = open; i < hcl.length; i++) {
|
|
73
|
+
const span = skipNonCode(hcl, i);
|
|
74
|
+
if (span) {
|
|
75
|
+
i = span.end;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const ch = hcl[i];
|
|
79
|
+
if (ch === "{") depth++;
|
|
80
|
+
else if (ch === "}") {
|
|
81
|
+
depth--;
|
|
82
|
+
if (depth === 0) return { text: hcl.slice(open + 1, i), end: i };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** 1-based line number of `index` in `hcl`. */
|
|
89
|
+
export function lineOf(hcl: string, index: number): number {
|
|
90
|
+
let line = 1;
|
|
91
|
+
for (let i = 0; i < index && i < hcl.length; i++) if (hcl[i] === "\n") line++;
|
|
92
|
+
return line;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** A top-level block (`resource "aws_s3_bucket" "b" { … }`) with its source span. */
|
|
96
|
+
export interface HclBlock {
|
|
97
|
+
/** the block keyword — `resource`, `action`, `list`, … */
|
|
98
|
+
keyword: string;
|
|
99
|
+
/** the quoted labels after the keyword, in order. */
|
|
100
|
+
labels: string[];
|
|
101
|
+
/** the block's inner text (between the braces). */
|
|
102
|
+
body: string;
|
|
103
|
+
/** 1-based line of the block header. */
|
|
104
|
+
line: number;
|
|
105
|
+
/** 1-based line of the body's opening `{`. Body-relative lines add to this
|
|
106
|
+
* (`absolute = bodyLine + bodyRelativeLine - 1`), so a caller never needs a
|
|
107
|
+
* second `lineOf` scan to place an assignment. */
|
|
108
|
+
bodyLine: number;
|
|
109
|
+
/** 1-based line of the matching `}` — the block's last line. */
|
|
110
|
+
endLine: number;
|
|
111
|
+
/** index in `hcl` of the header's first char. */
|
|
112
|
+
start: number;
|
|
113
|
+
/** index in `hcl` of the body's opening `{`. */
|
|
114
|
+
bodyStart: number;
|
|
115
|
+
/** index in `hcl` of the matching `}`. */
|
|
116
|
+
end: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Every TOP-LEVEL block in `hcl` whose keyword is in `keywords`.
|
|
121
|
+
*
|
|
122
|
+
* Anchored to a line start so a `resource` mentioned inside a string or a nested
|
|
123
|
+
* body can't be mistaken for a declaration, and each match resumes past the
|
|
124
|
+
* block's closing brace so nested blocks of the same keyword are never
|
|
125
|
+
* double-reported. A block that doesn't close terminates the scan (see
|
|
126
|
+
* `matchBraceBody`) — an unparseable tail yields fewer findings, never wrong ones.
|
|
127
|
+
*
|
|
128
|
+
* Line numbers are counted forward as the scan advances rather than by rescanning
|
|
129
|
+
* from the start of the file for each one. Matches arrive in increasing index
|
|
130
|
+
* order, so this is exact — and it keeps a file with many blocks linear instead of
|
|
131
|
+
* quadratic, which matters on the monorepos this walks 2000 files of.
|
|
132
|
+
*/
|
|
133
|
+
export function parseBlocks(hcl: string, keywords: readonly string[]): HclBlock[] {
|
|
134
|
+
const out: HclBlock[] = [];
|
|
135
|
+
if (keywords.length === 0) return out;
|
|
136
|
+
const alternation = keywords.join("|");
|
|
137
|
+
const re = new RegExp(`(?:^|\\n)[ \\t]*(${alternation})((?:[ \\t]+"[^"\\n]*")*)[ \\t]*\\{`, "g");
|
|
138
|
+
|
|
139
|
+
let cursor = 0; // index up to which newlines have been counted
|
|
140
|
+
let line = 1;
|
|
141
|
+
/** the 1-based line of `index`; `index` must not go backwards. */
|
|
142
|
+
const lineAt = (index: number): number => {
|
|
143
|
+
for (; cursor < index && cursor < hcl.length; cursor++) {
|
|
144
|
+
if (hcl[cursor] === "\n") line++;
|
|
145
|
+
}
|
|
146
|
+
return line;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
let m: RegExpExecArray | null;
|
|
150
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: idiomatic regex-exec iteration
|
|
151
|
+
while ((m = re.exec(hcl)) !== null) {
|
|
152
|
+
const keyword = m[1] ?? "";
|
|
153
|
+
const labels = [...(m[2] ?? "").matchAll(/"([^"\n]*)"/g)].map((l) => l[1] ?? "");
|
|
154
|
+
const bodyStart = m.index + m[0].length - 1; // the `{` the header ends on
|
|
155
|
+
const body = matchBraceBody(hcl, bodyStart);
|
|
156
|
+
if (!body) break;
|
|
157
|
+
const start = m.index + (hcl[m.index] === "\n" ? 1 : 0);
|
|
158
|
+
out.push({
|
|
159
|
+
keyword,
|
|
160
|
+
labels,
|
|
161
|
+
body: body.text,
|
|
162
|
+
line: lineAt(start),
|
|
163
|
+
bodyLine: lineAt(bodyStart),
|
|
164
|
+
endLine: lineAt(body.end),
|
|
165
|
+
start,
|
|
166
|
+
bodyStart,
|
|
167
|
+
end: body.end,
|
|
168
|
+
});
|
|
169
|
+
re.lastIndex = body.end + 1;
|
|
170
|
+
}
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** One `name = <value>` assignment found at the top level of a block body. */
|
|
175
|
+
export interface HclAssignment {
|
|
176
|
+
name: string;
|
|
177
|
+
/** the raw right-hand side, trimmed — an expression, not an evaluated value. */
|
|
178
|
+
value: string;
|
|
179
|
+
/** 1-based line of the assignment, RELATIVE TO THE BODY text. */
|
|
180
|
+
bodyLine: number;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Top-level `name = value` assignments in a block body, string/comment/heredoc-
|
|
185
|
+
* aware and depth-0 only (a `password` inside a nested block belongs to that
|
|
186
|
+
* block, not this one). Nested BLOCKS are ignored here — use `parseBlocks` on the
|
|
187
|
+
* body for those.
|
|
188
|
+
*
|
|
189
|
+
* The value is captured raw to the end of its line, which is right for the
|
|
190
|
+
* single-line assignments every rule here inspects (`password = var.x`,
|
|
191
|
+
* `prevent_destroy = true`). A multi-line value (a list or object literal
|
|
192
|
+
* spanning lines) is captured only as far as its first line; no rule asserts on
|
|
193
|
+
* such a value, and each is written to skip what it cannot read.
|
|
194
|
+
*/
|
|
195
|
+
export function topLevelAssignments(body: string): HclAssignment[] {
|
|
196
|
+
const out: HclAssignment[] = [];
|
|
197
|
+
let depth = 0;
|
|
198
|
+
let atStmtStart = true;
|
|
199
|
+
for (let i = 0; i < body.length; i++) {
|
|
200
|
+
const span = skipNonCode(body, i);
|
|
201
|
+
if (span) {
|
|
202
|
+
i = span.end;
|
|
203
|
+
atStmtStart = span.endsLine;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const ch = body[i] as string;
|
|
207
|
+
if (ch === "{" || ch === "[" || ch === "(") {
|
|
208
|
+
if (ch === "{") depth++;
|
|
209
|
+
atStmtStart = false;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (ch === "}" || ch === "]" || ch === ")") {
|
|
213
|
+
if (ch === "}") depth--;
|
|
214
|
+
atStmtStart = false;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (ch === "\n") {
|
|
218
|
+
atStmtStart = true;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (ch === " " || ch === "\t" || ch === "\r") continue;
|
|
222
|
+
if (depth === 0 && atStmtStart && /[A-Za-z_]/.test(ch)) {
|
|
223
|
+
let j = i;
|
|
224
|
+
while (j < body.length && /[A-Za-z0-9_-]/.test(body[j] as string)) j++;
|
|
225
|
+
const name = body.slice(i, j);
|
|
226
|
+
let k = j;
|
|
227
|
+
while (k < body.length && (body[k] === " " || body[k] === "\t")) k++;
|
|
228
|
+
// `=` but not `==` — an equality inside a bare expression is not an assignment.
|
|
229
|
+
if (body[k] === "=" && body[k + 1] !== "=") {
|
|
230
|
+
const eol = body.indexOf("\n", k);
|
|
231
|
+
const value = body.slice(k + 1, eol === -1 ? body.length : eol).trim();
|
|
232
|
+
out.push({ name, value, bodyLine: lineOf(body, i) });
|
|
233
|
+
}
|
|
234
|
+
i = j - 1;
|
|
235
|
+
atStmtStart = false;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
atStmtStart = false;
|
|
239
|
+
}
|
|
240
|
+
return out;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Strip a trailing `# …` / `// …` comment from a captured right-hand side.
|
|
245
|
+
*
|
|
246
|
+
* Deliberately handles ONLY strings and comments, not the full `skipNonCode` set:
|
|
247
|
+
* a heredoc opener (`<<EOF`) is part of the value, and treating it as a span to
|
|
248
|
+
* stop at would silently truncate the value to the empty string — which then
|
|
249
|
+
* flows into a proposed edit as `password_wo = ` and produces a broken rewrite.
|
|
250
|
+
*/
|
|
251
|
+
export function stripTrailingComment(value: string): string {
|
|
252
|
+
for (let i = 0; i < value.length; i++) {
|
|
253
|
+
const ch = value[i];
|
|
254
|
+
if (ch === '"') {
|
|
255
|
+
const span = skipNonCode(value, i);
|
|
256
|
+
if (span) i = span.end;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (ch === "#" || (ch === "/" && value[i + 1] === "/")) return value.slice(0, i).trim();
|
|
260
|
+
}
|
|
261
|
+
return value.trim();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** True when a right-hand side is a bare quoted string LITERAL with no
|
|
265
|
+
* interpolation — i.e. the value is written into the config, not referenced. */
|
|
266
|
+
export function isStringLiteral(value: string): boolean {
|
|
267
|
+
const v = stripTrailingComment(value);
|
|
268
|
+
if (v.length < 2 || !v.startsWith('"') || !v.endsWith('"')) return false;
|
|
269
|
+
// one span only: the closing quote must be the last character.
|
|
270
|
+
const span = skipNonCode(v, 0);
|
|
271
|
+
if (!span || span.end !== v.length - 1) return false;
|
|
272
|
+
return !/\$\{/.test(v);
|
|
273
|
+
}
|