@intentius/chant 0.57.0 → 0.58.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/dist/audit/catalog.d.ts +51 -1
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/core.d.ts +20 -1
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +14 -8
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/lineage.d.ts +10 -0
- package/dist/audit/lineage.d.ts.map +1 -0
- package/dist/audit/prior-art.d.ts +385 -0
- package/dist/audit/prior-art.d.ts.map +1 -0
- package/dist/audit/proof.d.ts +16 -0
- package/dist/audit/proof.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +8 -1
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/report.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts +3 -0
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/audit/secrets.d.ts +15 -0
- package/dist/audit/secrets.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +46 -0
- package/dist/audit/terraform-state.d.ts.map +1 -0
- package/dist/build.d.ts +17 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/claimed-fields.d.ts +161 -0
- package/dist/claimed-fields.d.ts.map +1 -0
- package/dist/cli/build-params-cli.d.ts +2 -2
- package/dist/cli/command-group.d.ts +14 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +17 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +2 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +82 -16
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +49 -42
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts +28 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts +16 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/resources/context.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +26 -9
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
- package/dist/components/auto-release.d.ts +10 -20
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/capability.d.ts +4 -4
- package/dist/components/cli-support.d.ts +20 -21
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +3 -3
- package/dist/components/component.d.ts.map +1 -1
- package/dist/components/driver-output.d.ts.map +1 -1
- package/dist/components/driver.d.ts +66 -36
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/manifest-persistence.d.ts +1 -9
- package/dist/components/manifest-persistence.d.ts.map +1 -1
- package/dist/components/run-progress.d.ts +6 -6
- package/dist/components/run-progress.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts +9 -15
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/components/verbs/verify.d.ts +2 -2
- package/dist/deep-observation.d.ts +17 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/discovery/files.d.ts +4 -4
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +33 -0
- package/dist/discovery/param-deps.d.ts.map +1 -1
- package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
- package/dist/discovery/sandbox/config-wire.d.ts +2 -2
- package/dist/fold/foldable-helpers.d.ts +5 -1
- package/dist/fold/foldable-helpers.d.ts.map +1 -1
- package/dist/fold-provenance.d.ts +236 -0
- package/dist/fold-provenance.d.ts.map +1 -0
- package/dist/graph-ops.d.ts +1 -1
- package/dist/held-elsewhere.d.ts +85 -0
- package/dist/held-elsewhere.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/kubectl-context.d.ts +3 -3
- package/dist/lexicon-config.d.ts +4 -4
- package/dist/lexicon-plugin-helpers.d.ts +1 -1
- package/dist/lexicon.d.ts +104 -14
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/change-set.d.ts +13 -0
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/deep-diff.d.ts +133 -6
- package/dist/lifecycle/deep-diff.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +71 -3
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +2 -2
- package/dist/lifecycle/run-ledger.d.ts +53 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -0
- package/dist/lifecycle/symptoms.d.ts +3 -3
- package/dist/lifecycle/symptoms.d.ts.map +1 -1
- package/dist/lint/config.d.ts +81 -2
- package/dist/lint/config.d.ts.map +1 -1
- package/dist/lint/post-synth.d.ts +55 -2
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
- package/dist/lint/rules/comp/index.d.ts +1 -1
- package/dist/lint/rules/comp/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.d.ts +16 -0
- package/dist/lint/rules/op/index.d.ts.map +1 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
- package/dist/lint/rules/op/support.d.ts +12 -0
- package/dist/lint/rules/op/support.d.ts.map +1 -0
- package/dist/lint/suppressions.d.ts +108 -0
- package/dist/lint/suppressions.d.ts.map +1 -0
- package/dist/live-endpoint.d.ts +1 -1
- package/dist/op/activities/activity-contracts.d.ts +54 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -0
- package/dist/op/activities/apply.d.ts +262 -0
- package/dist/op/activities/apply.d.ts.map +1 -0
- package/dist/op/activities/build.d.ts +13 -0
- package/dist/op/activities/build.d.ts.map +1 -0
- package/dist/op/activities/converge.d.ts +110 -0
- package/dist/op/activities/converge.d.ts.map +1 -0
- package/dist/op/activities/env-teardown.d.ts +76 -0
- package/dist/op/activities/env-teardown.d.ts.map +1 -0
- package/dist/op/activities/guard-validate.d.ts +64 -0
- package/dist/op/activities/guard-validate.d.ts.map +1 -0
- package/dist/op/activities/http-check.d.ts +34 -0
- package/dist/op/activities/http-check.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +31 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
- package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
- package/dist/op/activities/lifecycle.d.ts +42 -0
- package/dist/op/activities/lifecycle.d.ts.map +1 -0
- package/dist/op/activities/pipeline-audit.d.ts +84 -0
- package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
- package/dist/op/activities/policy.d.ts +17 -0
- package/dist/op/activities/policy.d.ts.map +1 -0
- package/dist/op/activities/reconcile.d.ts +89 -0
- package/dist/op/activities/reconcile.d.ts.map +1 -0
- package/dist/op/activities/shell.d.ts +13 -0
- package/dist/op/activities/shell.d.ts.map +1 -0
- package/dist/op/activities/teardown.d.ts +10 -0
- package/dist/op/activities/teardown.d.ts.map +1 -0
- package/dist/op/activities/util.d.ts +7 -0
- package/dist/op/activities/util.d.ts.map +1 -0
- package/dist/op/activities/wait.d.ts +28 -0
- package/dist/op/activities/wait.d.ts.map +1 -0
- package/dist/op/activities/workflow-audit.d.ts +92 -0
- package/dist/op/activities/workflow-audit.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts +76 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -0
- package/dist/op/activity-contract.d.ts +25 -18
- package/dist/op/activity-contract.d.ts.map +1 -1
- package/dist/op/activity-failure.d.ts +28 -0
- package/dist/op/activity-failure.d.ts.map +1 -0
- package/dist/op/activity-profiles.d.ts +118 -0
- package/dist/op/activity-profiles.d.ts.map +1 -0
- package/dist/op/activity-registry.d.ts +22 -38
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-runtime.d.ts +3 -14
- package/dist/op/activity-runtime.d.ts.map +1 -1
- package/dist/op/builders.d.ts +84 -29
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/composites/apply-op.d.ts +113 -0
- package/dist/op/composites/apply-op.d.ts.map +1 -0
- package/dist/op/composites/converge-op.d.ts +120 -0
- package/dist/op/composites/converge-op.d.ts.map +1 -0
- package/dist/op/composites/index.d.ts +26 -0
- package/dist/op/composites/index.d.ts.map +1 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
- package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
- package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
- package/dist/op/composites/reconcile-op.d.ts +57 -0
- package/dist/op/composites/reconcile-op.d.ts.map +1 -0
- package/dist/op/composites/watch-op.d.ts +60 -0
- package/dist/op/composites/watch-op.d.ts.map +1 -0
- package/dist/op/composites/workflow-audit-op.d.ts +57 -0
- package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
- package/dist/op/converge-rule.d.ts +12 -10
- package/dist/op/converge-rule.d.ts.map +1 -1
- package/dist/op/cron.d.ts +51 -0
- package/dist/op/cron.d.ts.map +1 -0
- package/dist/op/discover.d.ts +9 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/duration.d.ts +11 -0
- package/dist/op/duration.d.ts.map +1 -0
- package/dist/op/emulator-lifecycle.d.ts.map +1 -1
- package/dist/op/gate.d.ts +90 -0
- package/dist/op/gate.d.ts.map +1 -0
- package/dist/op/generate-pipeline.d.ts +9 -0
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/index.d.ts +23 -7
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +114 -28
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-output.d.ts +11 -1
- package/dist/op/local-output.d.ts.map +1 -1
- package/dist/op/op-ir.d.ts +184 -0
- package/dist/op/op-ir.d.ts.map +1 -0
- package/dist/op/op-verb-class.d.ts +10 -2
- package/dist/op/op-verb-class.d.ts.map +1 -1
- package/dist/op/operator.d.ts +30 -5
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/receipt-store.d.ts +1 -1
- package/dist/op/receipt-store.d.ts.map +1 -1
- package/dist/op/resource.d.ts +15 -1
- package/dist/op/resource.d.ts.map +1 -1
- package/dist/op/runtime.d.ts +200 -0
- package/dist/op/runtime.d.ts.map +1 -0
- package/dist/op/runtimes/local.d.ts +29 -0
- package/dist/op/runtimes/local.d.ts.map +1 -0
- package/dist/op/step-output-ref.d.ts +15 -13
- package/dist/op/step-output-ref.d.ts.map +1 -1
- package/dist/op/types.d.ts +79 -27
- package/dist/op/types.d.ts.map +1 -1
- package/dist/ownership.d.ts +1 -1
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -1
- package/dist/serializer-walker.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/audit/catalog.test.ts +79 -3
- package/src/audit/catalog.ts +111 -13
- package/src/audit/core.test.ts +26 -0
- package/src/audit/core.ts +61 -17
- package/src/audit/discover.test.ts +39 -0
- package/src/audit/discover.ts +91 -15
- package/src/audit/lineage.ts +100 -0
- package/src/audit/prior-art.ts +153 -0
- package/src/audit/proof.test.ts +67 -0
- package/src/audit/proof.ts +70 -0
- package/src/audit/report-html.ts +9 -4
- package/src/audit/report-model.ts +16 -2
- package/src/audit/report.test.ts +33 -0
- package/src/audit/report.ts +5 -3
- package/src/audit/rules-doc.test.ts +34 -2
- package/src/audit/rules-doc.ts +82 -9
- package/src/audit/secrets.ts +15 -3
- package/src/audit/terraform-state.test.ts +144 -0
- package/src/audit/terraform-state.ts +131 -0
- package/src/build.ts +31 -1
- package/src/claimed-fields.test.ts +145 -0
- package/src/claimed-fields.ts +208 -0
- package/src/cli/build-params-cli.ts +2 -2
- package/src/cli/command-group.ts +14 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
- package/src/cli/commands/audit.test.ts +189 -11
- package/src/cli/commands/audit.ts +73 -27
- package/src/cli/commands/build.test.ts +292 -9
- package/src/cli/commands/build.ts +156 -24
- package/src/cli/commands/check-lexicon-docs.ts +1 -1
- package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
- package/src/cli/commands/check-lexicon-examples.ts +42 -10
- package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
- package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
- package/src/cli/commands/init-lexicon.test.ts +97 -2
- package/src/cli/commands/init-lexicon.ts +4 -2
- package/src/cli/commands/init.test.ts +35 -1
- package/src/cli/commands/init.ts +14 -3
- package/src/cli/commands/lint.test.ts +48 -0
- package/src/cli/commands/lint.ts +123 -0
- package/src/cli/commands/onboard.test.ts +75 -0
- package/src/cli/commands/onboard.ts +54 -23
- package/src/cli/handlers/build.test.ts +81 -1
- package/src/cli/handlers/build.ts +15 -1
- package/src/cli/handlers/graph.test.ts +3 -3
- package/src/cli/handlers/lifecycle.test.ts +195 -12
- package/src/cli/handlers/lifecycle.ts +123 -6
- package/src/cli/handlers/misc.ts +1 -1
- package/src/cli/handlers/operator.test.ts +183 -11
- package/src/cli/handlers/operator.ts +260 -55
- package/src/cli/handlers/run.test.ts +431 -1287
- package/src/cli/handlers/run.ts +365 -915
- package/src/cli/main.test.ts +23 -15
- package/src/cli/main.ts +88 -33
- package/src/cli/mcp/op-tools.ts +147 -88
- package/src/cli/mcp/resource-handlers.ts +10 -37
- package/src/cli/mcp/resources/context.ts +11 -12
- package/src/cli/mcp/server.test.ts +25 -27
- package/src/cli/mcp/server.ts +2 -2
- package/src/cli/registry.ts +26 -9
- package/src/codegen/docs.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.ts +1 -1
- package/src/components/README.md +3 -2
- package/src/components/auto-release.test.ts +4 -20
- package/src/components/auto-release.ts +17 -42
- package/src/components/capability.ts +4 -4
- package/src/components/cli-support.test.ts +10 -5
- package/src/components/cli-support.ts +52 -44
- package/src/components/component.schema.json +2 -2
- package/src/components/component.ts +3 -3
- package/src/components/driver-output.ts +14 -2
- package/src/components/driver.test.ts +92 -22
- package/src/components/driver.ts +193 -83
- package/src/components/index.ts +1 -6
- package/src/components/manifest-persistence.test.ts +2 -20
- package/src/components/manifest-persistence.ts +4 -24
- package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
- package/src/components/run-progress.ts +6 -6
- package/src/components/verbs/run-agent.ts +11 -17
- package/src/components/verbs/verify.ts +2 -2
- package/src/deep-observation.test.ts +75 -0
- package/src/deep-observation.ts +48 -0
- package/src/discovery/collect.test.ts +3 -3
- package/src/discovery/files.ts +4 -4
- package/src/discovery/fold-import.ts +105 -2
- package/src/discovery/param-deps.test.ts +112 -1
- package/src/discovery/param-deps.ts +193 -0
- package/src/discovery/sandbox/bundle.ts +15 -1
- package/src/discovery/sandbox/config-wire.test.ts +2 -2
- package/src/discovery/sandbox/config-wire.ts +2 -2
- package/src/fold/fold.test.ts +56 -0
- package/src/fold/foldable-helpers.ts +81 -1
- package/src/fold-provenance-exclusion.test.ts +186 -0
- package/src/fold-provenance.test.ts +495 -0
- package/src/fold-provenance.ts +441 -0
- package/src/graph-ops.test.ts +2 -2
- package/src/graph-ops.ts +1 -1
- package/src/held-elsewhere.test.ts +102 -0
- package/src/held-elsewhere.ts +114 -0
- package/src/index.ts +3 -0
- package/src/kubectl-context.ts +3 -3
- package/src/lexicon-config.test.ts +5 -5
- package/src/lexicon-config.ts +4 -4
- package/src/lexicon-plugin-helpers.ts +1 -1
- package/src/lexicon.ts +120 -14
- package/src/lifecycle/change-set.test.ts +49 -0
- package/src/lifecycle/change-set.ts +67 -0
- package/src/lifecycle/converge-ledger.ts +1 -1
- package/src/lifecycle/deep-diff.test.ts +267 -14
- package/src/lifecycle/deep-diff.ts +217 -14
- package/src/lifecycle/deep-observe.test.ts +3 -1
- package/src/lifecycle/deep-observe.ts +16 -6
- package/src/lifecycle/gate-ledger.test.ts +74 -2
- package/src/lifecycle/gate-ledger.ts +143 -36
- package/src/lifecycle/index.ts +1 -0
- package/src/lifecycle/release-ledger.ts +2 -2
- package/src/lifecycle/run-ledger.test.ts +268 -0
- package/src/lifecycle/run-ledger.ts +201 -0
- package/src/lifecycle/symptoms.ts +3 -3
- package/src/lint/config.test.ts +82 -1
- package/src/lint/config.ts +112 -3
- package/src/lint/policy.test.ts +1 -1
- package/src/lint/post-synth.test.ts +31 -0
- package/src/lint/post-synth.ts +54 -1
- package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
- package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
- package/src/lint/rules/comp/comp.test.ts +1 -1
- package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +74 -0
- package/src/lint/rules/comp/index.ts +3 -3
- package/src/lint/rules/op/index.ts +23 -0
- package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
- package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
- package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
- package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
- package/src/lint/rules/op/support.ts +11 -0
- package/src/lint/suppressions.test.ts +163 -0
- package/src/lint/suppressions.ts +204 -0
- package/src/live-endpoint.ts +1 -1
- package/src/op/activities/activity-contracts.ts +66 -0
- package/src/op/activities/apply.test.ts +567 -0
- package/src/op/activities/apply.ts +662 -0
- package/src/op/activities/build.ts +26 -0
- package/src/op/activities/converge.test.ts +330 -0
- package/src/op/activities/converge.ts +449 -0
- package/src/op/activities/env-teardown.test.ts +187 -0
- package/src/op/activities/env-teardown.ts +171 -0
- package/src/op/activities/guard-validate.test.ts +139 -0
- package/src/op/activities/guard-validate.ts +223 -0
- package/src/op/activities/http-check.test.ts +48 -0
- package/src/op/activities/http-check.ts +55 -0
- package/src/op/activities/index.ts +119 -0
- package/src/op/activities/lexicon-upgrade.test.ts +492 -0
- package/src/op/activities/lexicon-upgrade.ts +673 -0
- package/src/op/activities/lifecycle.ts +85 -0
- package/src/op/activities/pipeline-audit.test.ts +67 -0
- package/src/op/activities/pipeline-audit.ts +201 -0
- package/src/op/activities/policy.test.ts +19 -0
- package/src/op/activities/policy.ts +37 -0
- package/src/op/activities/reconcile.test.ts +81 -0
- package/src/op/activities/reconcile.ts +187 -0
- package/src/op/activities/shell.ts +26 -0
- package/src/op/activities/teardown.ts +22 -0
- package/src/op/activities/util.ts +6 -0
- package/src/op/activities/wait.ts +69 -0
- package/src/op/activities/workflow-audit.test.ts +90 -0
- package/src/op/activities/workflow-audit.ts +203 -0
- package/src/op/activity-contract-registry.test.ts +98 -0
- package/src/op/activity-contract-registry.ts +114 -0
- package/src/op/activity-contract.test.ts +1 -1
- package/src/op/activity-contract.ts +37 -33
- package/src/op/activity-failure.ts +34 -0
- package/src/op/activity-profiles.test.ts +56 -0
- package/src/op/activity-profiles.ts +116 -0
- package/src/op/activity-registry.test.ts +37 -28
- package/src/op/activity-registry.ts +27 -56
- package/src/op/activity-runtime.ts +3 -47
- package/src/op/builders-exports.test.ts +0 -49
- package/src/op/builders-typed.test.ts +164 -0
- package/src/op/builders.ts +123 -39
- package/src/op/composites/apply-op.ts +208 -0
- package/src/op/composites/composites.test.ts +366 -0
- package/src/op/composites/converge-op.test.ts +168 -0
- package/src/op/composites/converge-op.ts +214 -0
- package/src/op/composites/index.ts +26 -0
- package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
- package/src/op/composites/lexicon-upgrade-op.ts +134 -0
- package/src/op/composites/pipeline-audit-op.test.ts +25 -0
- package/src/op/composites/pipeline-audit-op.ts +78 -0
- package/src/op/composites/reconcile-op.test.ts +51 -0
- package/src/op/composites/reconcile-op.ts +107 -0
- package/src/op/composites/watch-op.ts +108 -0
- package/src/op/composites/workflow-audit-op.test.ts +25 -0
- package/src/op/composites/workflow-audit-op.ts +88 -0
- package/src/op/converge-rule.ts +12 -10
- package/src/op/cron.test.ts +128 -0
- package/src/op/cron.ts +149 -0
- package/src/op/discover-root.test.ts +105 -1
- package/src/op/discover.test.ts +6 -2
- package/src/op/discover.ts +84 -16
- package/src/op/duration.ts +21 -0
- package/src/op/effect-step.test.ts +26 -11
- package/src/op/emulator-lifecycle.ts +1 -2
- package/src/op/gate.ts +155 -0
- package/src/op/generate-pipeline.test.ts +65 -3
- package/src/op/generate-pipeline.ts +19 -2
- package/src/op/index.ts +39 -6
- package/src/op/local-executor.test.ts +163 -34
- package/src/op/local-executor.ts +370 -102
- package/src/op/local-output.test.ts +95 -11
- package/src/op/local-output.ts +33 -4
- package/src/op/op-ir.test.ts +281 -0
- package/src/op/op-ir.ts +417 -0
- package/src/op/op-verb-class.ts +19 -5
- package/src/op/op.test.ts +26 -5
- package/src/op/operator.test.ts +175 -6
- package/src/op/operator.ts +63 -16
- package/src/op/receipt-store.ts +1 -1
- package/src/op/resource.ts +20 -2
- package/src/op/runtime.ts +203 -0
- package/src/op/runtimes/local.test.ts +181 -0
- package/src/op/runtimes/local.ts +212 -0
- package/src/op/step-output-ref.ts +15 -13
- package/src/op/types.ts +84 -24
- package/src/ownership.ts +1 -1
- package/src/provenance.test.ts +6 -0
- package/src/provenance.ts +21 -1
- package/src/serializer-walker.test.ts +42 -0
- package/src/serializer-walker.ts +21 -2
- package/dist/cli/handlers/op-progress.d.ts +0 -90
- package/dist/cli/handlers/op-progress.d.ts.map +0 -1
- package/dist/cli/handlers/run-client.d.ts +0 -127
- package/dist/cli/handlers/run-client.d.ts.map +0 -1
- package/dist/cli/handlers/run-report.d.ts +0 -5
- package/dist/cli/handlers/run-report.d.ts.map +0 -1
- package/dist/components/temporal-codegen-loader.d.ts +0 -27
- package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
- package/src/cli/handlers/op-progress.test.ts +0 -268
- package/src/cli/handlers/op-progress.ts +0 -248
- package/src/cli/handlers/run-client.test.ts +0 -82
- package/src/cli/handlers/run-client.ts +0 -219
- package/src/cli/handlers/run-report.test.ts +0 -62
- package/src/cli/handlers/run-report.ts +0 -122
- package/src/components/temporal-codegen-loader.ts +0 -41
- package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
package/src/audit/core.ts
CHANGED
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
|
|
23
23
|
import { basename } from "path";
|
|
24
24
|
import type { Severity } from "../lint/rule";
|
|
25
|
-
import type { PostSynthCheck, PostSynthContext } from "../lint/post-synth";
|
|
25
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "../lint/post-synth";
|
|
26
|
+
import { applyInlineSuppressions, type SuppressionMetaFinding } from "../lint/suppressions";
|
|
26
27
|
import type { SerializerResult } from "../serializer";
|
|
27
28
|
import type { LexiconPlugin } from "../lexicon";
|
|
28
29
|
import type { Declarable } from "../declarable";
|
|
@@ -82,6 +83,8 @@ export interface AuditFinding {
|
|
|
82
83
|
* itself.
|
|
83
84
|
*/
|
|
84
85
|
fingerprint?: string;
|
|
86
|
+
/** Carried verbatim from the producing `PostSynthDiagnostic.missing` (chant #2113) — see its doc comment. */
|
|
87
|
+
missing?: { kind: string; scope: string };
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
/**
|
|
@@ -94,8 +97,10 @@ export type ChecksProvider = (lexicon: AuditLexicon) => Promise<PostSynthCheck[]
|
|
|
94
97
|
* Parse one classified file's content into the lexicon's entity graph — the
|
|
95
98
|
* parse-to-graph half of the audit (#1567). The returned map is what
|
|
96
99
|
* `ctx.entities` holds during a build, so entity-reading checks run unchanged.
|
|
100
|
+
* May return a `Promise` for a lexicon whose parser is inherently async (e.g.
|
|
101
|
+
* terraform's HCL parser); `auditLexicon`'s `entitiesFor` awaits it.
|
|
97
102
|
*/
|
|
98
|
-
export type EntitiesParser = (content: string) => Map<string, Declarable
|
|
103
|
+
export type EntitiesParser = (content: string) => Map<string, Declarable> | Promise<Map<string, Declarable>>;
|
|
99
104
|
|
|
100
105
|
/**
|
|
101
106
|
* Resolve the entities parser for a lexicon (its plugin's `auditEntities`,
|
|
@@ -179,13 +184,25 @@ async function defaultEntitiesProvider(lexicon: AuditLexicon): Promise<EntitiesP
|
|
|
179
184
|
return parser;
|
|
180
185
|
}
|
|
181
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Mutable out-param for the inline (`# chant-ignore*`) suppression count
|
|
189
|
+
* (chant #2111): `auditFiles`'s return type stays `AuditFinding[]` for every
|
|
190
|
+
* existing caller, so a caller that wants the count (`chant audit`'s CLI, for
|
|
191
|
+
* its summary line) passes this in and reads `.count` back afterward, rather
|
|
192
|
+
* than `auditFiles` growing a `{findings, suppressedCount}` shape that would
|
|
193
|
+
* touch every existing call site.
|
|
194
|
+
*/
|
|
195
|
+
export interface SuppressionStats {
|
|
196
|
+
count: number;
|
|
197
|
+
}
|
|
198
|
+
|
|
182
199
|
/**
|
|
183
200
|
* Audit a set of CI files and return all findings. Pure with respect to the
|
|
184
201
|
* filesystem and network — callers supply file contents.
|
|
185
202
|
*/
|
|
186
203
|
export async function auditFiles(
|
|
187
204
|
inputs: AuditInput[],
|
|
188
|
-
opts: { checksProvider?: ChecksProvider; entitiesProvider?: EntitiesProvider } = {},
|
|
205
|
+
opts: { checksProvider?: ChecksProvider; entitiesProvider?: EntitiesProvider; suppressionStats?: SuppressionStats } = {},
|
|
189
206
|
): Promise<AuditFinding[]> {
|
|
190
207
|
const provider = opts.checksProvider ?? defaultChecksProvider;
|
|
191
208
|
const entitiesProvider = opts.entitiesProvider ?? defaultEntitiesProvider;
|
|
@@ -203,7 +220,7 @@ export async function auditFiles(
|
|
|
203
220
|
const checks = await provider(lexicon);
|
|
204
221
|
if (checks.length === 0) continue;
|
|
205
222
|
const parseEntities = await entitiesProvider(lexicon);
|
|
206
|
-
findings.push(...auditLexicon(lexicon, files, checks, parseEntities));
|
|
223
|
+
findings.push(...(await auditLexicon(lexicon, files, checks, parseEntities, opts.suppressionStats)));
|
|
207
224
|
}
|
|
208
225
|
|
|
209
226
|
return findings;
|
|
@@ -224,14 +241,20 @@ function toOutput(file: AuditInput): string | SerializerResult {
|
|
|
224
241
|
return { primary: file.content, files: { [basename(file.path)]: file.content } };
|
|
225
242
|
}
|
|
226
243
|
|
|
244
|
+
interface CheckRunResult {
|
|
245
|
+
diagnostics: PostSynthDiagnostic[];
|
|
246
|
+
suppressed: PostSynthDiagnostic[];
|
|
247
|
+
meta: SuppressionMetaFinding[];
|
|
248
|
+
}
|
|
249
|
+
|
|
227
250
|
function runChecks(
|
|
228
251
|
checks: PostSynthCheck[],
|
|
229
252
|
outputs: Map<string, string | SerializerResult>,
|
|
230
253
|
entities: Map<string, Declarable> = new Map(),
|
|
231
|
-
):
|
|
254
|
+
): CheckRunResult {
|
|
232
255
|
const buildResult: PostSynthContext["buildResult"] = { outputs, entities, warnings: [], errors: [], sourceFileCount: outputs.size };
|
|
233
256
|
const ctx: PostSynthContext = { outputs, entities: buildResult.entities, buildResult };
|
|
234
|
-
const diags = [];
|
|
257
|
+
const diags: PostSynthDiagnostic[] = [];
|
|
235
258
|
for (const check of checks) {
|
|
236
259
|
try {
|
|
237
260
|
diags.push(...check.check(ctx));
|
|
@@ -239,7 +262,11 @@ function runChecks(
|
|
|
239
262
|
// A check that throws on unusual external YAML must not abort the audit.
|
|
240
263
|
}
|
|
241
264
|
}
|
|
242
|
-
|
|
265
|
+
// chant #2111: `rules` (for `ignorable: false`) is omitted, since `chant audit`
|
|
266
|
+
// has no project `lint.rules` to read here (see `applyInlineSuppressions`'s
|
|
267
|
+
// doc comment for why that's a deliberate scope line, not an oversight), so
|
|
268
|
+
// every rule id reads as ignorable, same as an unconfigured one everywhere.
|
|
269
|
+
return applyInlineSuppressions(diags, entities);
|
|
243
270
|
}
|
|
244
271
|
|
|
245
272
|
function diagKey(d: { checkId: string; entity?: string; message: string }): string {
|
|
@@ -281,40 +308,57 @@ function mergeEntities(maps: Array<Map<string, Declarable>>): Map<string, Declar
|
|
|
281
308
|
* the entity graph parsed from its files — per file for the per-file pass, all
|
|
282
309
|
* files merged for the all-files pass — so entity-reading checks fire and
|
|
283
310
|
* cross-file facts (an Agent's Environment declared elsewhere, a name declared
|
|
284
|
-
* twice across the apply directory) resolve in the all-files pass.
|
|
311
|
+
* twice across the apply directory) resolve in the all-files pass. The parser
|
|
312
|
+
* may be async (a lexicon's own choice, e.g. a wasm-backed HCL parser); this
|
|
313
|
+
* function awaits each file's entities before either pass runs.
|
|
285
314
|
*/
|
|
286
|
-
function auditLexicon(
|
|
287
|
-
|
|
315
|
+
async function auditLexicon(
|
|
316
|
+
lexicon: AuditLexicon,
|
|
317
|
+
files: AuditInput[],
|
|
318
|
+
checks: PostSynthCheck[],
|
|
319
|
+
parseEntities?: EntitiesParser,
|
|
320
|
+
suppressionStats?: SuppressionStats,
|
|
321
|
+
): Promise<AuditFinding[]> {
|
|
322
|
+
const entitiesFor = async (file: AuditInput): Promise<Map<string, Declarable>> => {
|
|
288
323
|
if (!parseEntities) return new Map();
|
|
289
324
|
try {
|
|
290
|
-
return parseEntities(file.content);
|
|
325
|
+
return await parseEntities(file.content);
|
|
291
326
|
} catch {
|
|
292
327
|
// The audit contract is "runs against any repo" — unparseable content
|
|
293
328
|
// contributes no entities, never a crash.
|
|
294
329
|
return new Map();
|
|
295
330
|
}
|
|
296
331
|
};
|
|
297
|
-
const perEntities = new Map(files.map((f) => [f.path, entitiesFor(f)]));
|
|
332
|
+
const perEntities = new Map(await Promise.all(files.map(async (f) => [f.path, await entitiesFor(f)] as const)));
|
|
298
333
|
|
|
299
334
|
const perFindings: AuditFinding[] = [];
|
|
300
335
|
const perKeys = new Set<string>();
|
|
336
|
+
// chant #2111: meta findings (an expired/misplaced/denied suppression
|
|
337
|
+
// directive) are collected from the per-file pass only: the all-files pass
|
|
338
|
+
// below sees the same entities merged, so it would report the identical
|
|
339
|
+
// directive again. Counted the same way, once, here too.
|
|
340
|
+
const metaFindings: AuditFinding[] = [];
|
|
301
341
|
for (const file of files) {
|
|
302
|
-
const diags = runChecks(checks, new Map([[file.path, toOutput(file)]]), perEntities.get(file.path));
|
|
342
|
+
const { diagnostics: diags, suppressed, meta } = runChecks(checks, new Map([[file.path, toOutput(file)]]), perEntities.get(file.path));
|
|
343
|
+
if (suppressionStats) suppressionStats.count += suppressed.length;
|
|
344
|
+
for (const m of meta) {
|
|
345
|
+
metaFindings.push({ checkId: m.checkId, severity: m.severity, message: m.message, file: m.file, lexicon, line: m.line });
|
|
346
|
+
}
|
|
303
347
|
for (const d of diags) {
|
|
304
|
-
perFindings.push({ checkId: d.checkId, severity: d.severity, message: d.message, file: file.path, lexicon: d.lexicon ?? lexicon, entity: d.entity });
|
|
348
|
+
perFindings.push({ checkId: d.checkId, severity: d.severity, message: d.message, file: file.path, lexicon: d.lexicon ?? lexicon, entity: d.entity, missing: d.missing });
|
|
305
349
|
perKeys.add(diagKey(d));
|
|
306
350
|
}
|
|
307
351
|
}
|
|
308
352
|
|
|
309
353
|
const allOutputs = new Map<string, string | SerializerResult>(files.map((f) => [f.path, toOutput(f)]));
|
|
310
|
-
const allDiags = runChecks(checks, allOutputs, mergeEntities([...perEntities.values()]));
|
|
354
|
+
const { diagnostics: allDiags } = runChecks(checks, allOutputs, mergeEntities([...perEntities.values()]));
|
|
311
355
|
const allKeys = new Set(allDiags.map(diagKey));
|
|
312
356
|
|
|
313
357
|
const out: AuditFinding[] = perFindings.filter((f) => allKeys.has(diagKey(f)));
|
|
314
358
|
for (const d of allDiags) {
|
|
315
359
|
if (!perKeys.has(diagKey(d))) {
|
|
316
|
-
out.push({ checkId: d.checkId, severity: d.severity, message: d.message, file: CROSS_FILE, lexicon: d.lexicon ?? lexicon, entity: d.entity });
|
|
360
|
+
out.push({ checkId: d.checkId, severity: d.severity, message: d.message, file: CROSS_FILE, lexicon: d.lexicon ?? lexicon, entity: d.entity, missing: d.missing });
|
|
317
361
|
}
|
|
318
362
|
}
|
|
319
|
-
return out;
|
|
363
|
+
return [...out, ...metaFindings];
|
|
320
364
|
}
|
|
@@ -92,6 +92,45 @@ describe("discoverByDetection (unified, detectTemplate-driven)", () => {
|
|
|
92
92
|
const plugins = await loadAuditPlugins(["github", "definitely-not-a-real-lexicon"]);
|
|
93
93
|
expect(plugins.map((p) => p.name)).toEqual(["github"]);
|
|
94
94
|
});
|
|
95
|
+
|
|
96
|
+
test("loadAuditPlugins skips a lexicon whose package is not installed, quietly", async () => {
|
|
97
|
+
const plugins = await loadAuditPlugins(["github", "not-a-lexicon"]);
|
|
98
|
+
expect(plugins.map((p) => p.name)).toEqual(["github"]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("Terraform directories as bundles, modeled on classifyHelm", () => {
|
|
102
|
+
// No real @intentius/chant-lexicon-terraform package exists yet (#2085), so
|
|
103
|
+
// these tests supply a bare stand-in plugin rather than loadAuditPlugins().
|
|
104
|
+
const terraform = { name: "terraform" };
|
|
105
|
+
|
|
106
|
+
test("a directory with two .tf files, and a nested subdirectory with its own .tf file, classify as two separate terraform inputs", () => {
|
|
107
|
+
const found = discoverByDetection(fixture("audit-terraform"), [terraform]);
|
|
108
|
+
const tf = found.filter((i) => i.lexicon === "terraform");
|
|
109
|
+
expect(targets(tf)).toEqual(["terraform:infra", "terraform:infra/modules/foo"]);
|
|
110
|
+
const root = tf.find((i) => i.path === "infra")!;
|
|
111
|
+
expect(Object.keys(root.files!).sort()).toEqual(["main.tf", "variables.tf"]);
|
|
112
|
+
const nested = tf.find((i) => i.path === "infra/modules/foo")!;
|
|
113
|
+
expect(Object.keys(nested.files!).sort()).toEqual(["main.tf"]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("a bundle's content is the whole root module in filename order, each file behind a `# file:` marker", () => {
|
|
117
|
+
const found = discoverByDetection(fixture("audit-terraform"), [terraform]);
|
|
118
|
+
const root = found.find((i) => i.lexicon === "terraform" && i.path === "infra")!;
|
|
119
|
+
expect(root.content).toContain("# file: main.tf\nresource \"null_resource\" \"root\" {}");
|
|
120
|
+
expect(root.content).toContain("# file: variables.tf\nvariable \"region\"");
|
|
121
|
+
expect(root.content.indexOf("# file: main.tf")).toBeLessThan(root.content.indexOf("# file: variables.tf"));
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test(".terraform/ contributes no bundle (walkFiles already skips dot-directories outside WALK_DOT_DIRS)", () => {
|
|
125
|
+
const found = discoverByDetection(fixture("audit-terraform"), [terraform]);
|
|
126
|
+
expect(found.some((i) => i.path.includes(".terraform"))).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("without the terraform plugin, .tf files are classified into nothing (skipped, same as an unloaded helm)", () => {
|
|
130
|
+
const found = discoverByDetection(fixture("audit-terraform"), []);
|
|
131
|
+
expect(found).toEqual([]);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
95
134
|
});
|
|
96
135
|
|
|
97
136
|
describe("lexicon hints for unclaimed files (#1623)", () => {
|
package/src/audit/discover.ts
CHANGED
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
* `detectTemplate` (which only knows Compose's `services:`) can't see them.
|
|
22
22
|
* - Helm charts are a directory BUNDLE keyed by `Chart.yaml`; the helm checks
|
|
23
23
|
* read `output.files`, so the whole chart is one AuditInput.
|
|
24
|
+
* - Terraform is also a directory BUNDLE, keyed by that directory's own
|
|
25
|
+
* `.tf` filenames, not recursively: a nested directory with its own
|
|
26
|
+
* `.tf` files (e.g. `modules/foo`) is a separate root module and gets its
|
|
27
|
+
* own AuditInput. `.tf` isn't YAML/JSON either, so `detectTemplate` can't
|
|
28
|
+
* see it.
|
|
24
29
|
* - k8s `detectTemplate` matches any `apiVersion`+`kind`, including GCP Config
|
|
25
30
|
* Connector (`cnrm.cloud.google.com`) resources and fountain
|
|
26
31
|
* (`fountain.dev/v1`) manifests, so gcp and fountain must be tried first.
|
|
@@ -34,9 +39,10 @@ import { parseYAML } from "../yaml";
|
|
|
34
39
|
import type { LexiconPlugin } from "../lexicon";
|
|
35
40
|
import type { AuditInput, AuditLexicon } from "./core";
|
|
36
41
|
import { isNginxConfigPath } from "./nginx";
|
|
42
|
+
import { gitignoreCoversTerraformState, isTerraformStatePath } from "./terraform-state";
|
|
37
43
|
|
|
38
44
|
/** Lexicons the auditor knows how to detect and run checks for. */
|
|
39
|
-
export const AUDIT_LEXICONS = ["github", "gitlab", "forgejo", "k8s", "docker", "aws", "azure", "gcp", "helm", "fountain"] as const;
|
|
45
|
+
export const AUDIT_LEXICONS = ["github", "gitlab", "forgejo", "k8s", "docker", "aws", "azure", "gcp", "helm", "fountain", "terraform"] as const;
|
|
40
46
|
|
|
41
47
|
/**
|
|
42
48
|
* Load the plugins used for detection. Each is loaded in isolation (no
|
|
@@ -63,6 +69,8 @@ export async function loadAuditPlugins(names: readonly string[] = AUDIT_LEXICONS
|
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
const WALK_SKIP = new Set(["node_modules", ".git", "dist"]);
|
|
72
|
+
/** Terraform's local working directory: recorded by the walk, never entered (TF023). */
|
|
73
|
+
const TERRAFORM_WORK_DIR = ".terraform";
|
|
66
74
|
/** Dot-directories the walk descends into anyway (CI lives here). */
|
|
67
75
|
const WALK_DOT_DIRS = new Set([".github", ".forgejo"]);
|
|
68
76
|
const MAX_WALK_FILES = 1000;
|
|
@@ -80,7 +88,15 @@ function walkFiles(dir: string, out: string[]): void {
|
|
|
80
88
|
}
|
|
81
89
|
for (const e of entries.sort((a, b) => (a.name < b.name ? -1 : 1))) {
|
|
82
90
|
if (out.length >= MAX_WALK_FILES) return;
|
|
83
|
-
if (e.name.startsWith(".") && e.isDirectory() && !WALK_DOT_DIRS.has(e.name))
|
|
91
|
+
if (e.name.startsWith(".") && e.isDirectory() && !WALK_DOT_DIRS.has(e.name)) {
|
|
92
|
+
// `.terraform/` is never descended into: it is machine-generated,
|
|
93
|
+
// routinely hundreds of megabytes of provider binaries, and nothing in
|
|
94
|
+
// it is worth reading. Its presence is itself the finding (TF023), so
|
|
95
|
+
// the directory path alone is recorded and `collectCandidates` hands it
|
|
96
|
+
// on with no content.
|
|
97
|
+
if (e.name === TERRAFORM_WORK_DIR) out.push(join(dir, e.name));
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
84
100
|
if (WALK_SKIP.has(e.name)) continue;
|
|
85
101
|
const full = join(dir, e.name);
|
|
86
102
|
if (e.isDirectory()) walkFiles(full, out);
|
|
@@ -105,6 +121,10 @@ function isDockerfileName(name: string): boolean {
|
|
|
105
121
|
return name === "Dockerfile" || name.startsWith("Dockerfile.") || name.endsWith(".Dockerfile") || name.endsWith(".dockerfile");
|
|
106
122
|
}
|
|
107
123
|
|
|
124
|
+
function isTerraformFileName(name: string): boolean {
|
|
125
|
+
return /\.tf$/i.test(name);
|
|
126
|
+
}
|
|
127
|
+
|
|
108
128
|
/**
|
|
109
129
|
* Filenames the secrets scan (#443) wants that no lexicon otherwise reads —
|
|
110
130
|
* dotenv files and key/cert material. Included in `isCandidatePath` so both
|
|
@@ -294,11 +314,11 @@ export function isCandidatePath(path: string): boolean {
|
|
|
294
314
|
* (path, filename, a few content markers) with no lexicon plugin involved.
|
|
295
315
|
* Names the lexicon that would have claimed a file when that lexicon isn't
|
|
296
316
|
* installed (#1623). Deliberately coarse: precision comes from the plugin's
|
|
297
|
-
* `detectTemplate`, which is exactly what's missing in that case.
|
|
298
|
-
*
|
|
299
|
-
*
|
|
317
|
+
* `detectTemplate`, which is exactly what's missing in that case. `terraform`
|
|
318
|
+
* is a lexicon like any other here: a `.tf` file hints at it by name, the
|
|
319
|
+
* same way `Chart.yaml` hints at helm.
|
|
300
320
|
*/
|
|
301
|
-
export type LexiconHint = AuditLexicon
|
|
321
|
+
export type LexiconHint = AuditLexicon;
|
|
302
322
|
|
|
303
323
|
export function hintLexiconForFile(path: string, content: string): LexiconHint | undefined {
|
|
304
324
|
const name = basename(path);
|
|
@@ -306,7 +326,7 @@ export function hintLexiconForFile(path: string, content: string): LexiconHint |
|
|
|
306
326
|
if (ci) return ci;
|
|
307
327
|
if (isDockerfileName(name)) return "docker";
|
|
308
328
|
if (name === "Chart.yaml") return "helm";
|
|
309
|
-
if (
|
|
329
|
+
if (isTerraformFileName(name)) return "terraform";
|
|
310
330
|
const head = content.slice(0, 64 * 1024);
|
|
311
331
|
if (/cnrm\.cloud\.google\.com/.test(head)) return "gcp";
|
|
312
332
|
if (/fountain\.dev\/v1/.test(head)) return "fountain";
|
|
@@ -371,13 +391,53 @@ function classifyHelm(files: RepoFile[], plugin: DetectPlugin | undefined): { in
|
|
|
371
391
|
return { inputs, prefixes };
|
|
372
392
|
}
|
|
373
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Collect Terraform root modules as directory bundles from an in-memory file
|
|
396
|
+
* set, modeled on `classifyHelm`. Unlike a Helm chart (its whole subtree is
|
|
397
|
+
* one bundle), a Terraform root module references only its own sibling
|
|
398
|
+
* files, so grouping is by each `.tf` file's immediate directory, not
|
|
399
|
+
* recursive: a nested directory with its own `.tf` files (`modules/foo`) is
|
|
400
|
+
* a separate root module and gets its own AuditInput. Returns inputs plus
|
|
401
|
+
* the exact `.tf` paths claimed, so the loose-file pass in `classifyFiles`
|
|
402
|
+
* can skip them.
|
|
403
|
+
*/
|
|
404
|
+
function classifyTerraform(files: RepoFile[], plugin: DetectPlugin | undefined): { inputs: AuditInput[]; claimed: Set<string> } {
|
|
405
|
+
const inputs: AuditInput[] = [];
|
|
406
|
+
const claimed = new Set<string>();
|
|
407
|
+
if (!plugin) return { inputs, claimed };
|
|
408
|
+
const byDir = new Map<string, Record<string, string>>();
|
|
409
|
+
for (const f of files) {
|
|
410
|
+
if (!isTerraformFileName(basename(f.path))) continue;
|
|
411
|
+
const slash = f.path.lastIndexOf("/");
|
|
412
|
+
const dir = slash === -1 ? "" : f.path.slice(0, slash);
|
|
413
|
+
const name = slash === -1 ? f.path : f.path.slice(slash + 1);
|
|
414
|
+
const bundle = byDir.get(dir) ?? {};
|
|
415
|
+
bundle[name] = f.content;
|
|
416
|
+
byDir.set(dir, bundle);
|
|
417
|
+
claimed.add(f.path);
|
|
418
|
+
}
|
|
419
|
+
for (const [dir, bundle] of byDir) {
|
|
420
|
+
// `content` is the whole root module, every `.tf` in filename order with a
|
|
421
|
+
// `# file:` line comment (legal HCL) marking each boundary, so a lexicon's
|
|
422
|
+
// `auditEntities(content)` sees the module Terraform itself would load,
|
|
423
|
+
// not one arbitrary file of it. `files` keeps the per-file split.
|
|
424
|
+
const content = Object.keys(bundle)
|
|
425
|
+
.sort()
|
|
426
|
+
.map((name) => `# file: ${name}\n${bundle[name]}`)
|
|
427
|
+
.join("\n");
|
|
428
|
+
inputs.push({ path: dir === "" ? "." : dir, content, lexicon: "terraform", files: bundle });
|
|
429
|
+
}
|
|
430
|
+
return { inputs, claimed };
|
|
431
|
+
}
|
|
432
|
+
|
|
374
433
|
/**
|
|
375
434
|
* Classify an in-memory set of repo files into per-lexicon audit inputs. Pure
|
|
376
435
|
* (no fs, no network) so the local walk and the remote tree-fetch share exactly
|
|
377
436
|
* one detection path. Each file maps to at most one lexicon: CI by path,
|
|
378
|
-
* Dockerfiles by name, Helm charts
|
|
379
|
-
* precedence-ordered content detectors
|
|
380
|
-
* `detectTemplate`). Lexicons whose plugin isn't
|
|
437
|
+
* Dockerfiles by name, Helm charts and Terraform root modules as directory
|
|
438
|
+
* bundles, everything else by the precedence-ordered content detectors
|
|
439
|
+
* (delegating to each plugin's `detectTemplate`). Lexicons whose plugin isn't
|
|
440
|
+
* provided are skipped.
|
|
381
441
|
*/
|
|
382
442
|
export function classifyFiles(files: RepoFile[], plugins: DetectPlugin[]): AuditInput[] {
|
|
383
443
|
const byName = new Map(plugins.map((p) => [p.name, p]));
|
|
@@ -387,9 +447,12 @@ export function classifyFiles(files: RepoFile[], plugins: DetectPlugin[]): Audit
|
|
|
387
447
|
const helm = classifyHelm(files, byName.get("helm"));
|
|
388
448
|
const underChart = (p: string): boolean => helm.prefixes.some((pre) => (pre === "" ? true : p.startsWith(pre)));
|
|
389
449
|
|
|
390
|
-
const
|
|
450
|
+
const terraform = classifyTerraform(files, byName.get("terraform"));
|
|
451
|
+
|
|
452
|
+
const inputs: AuditInput[] = [...helm.inputs, ...terraform.inputs];
|
|
391
453
|
for (const { path, content } of files) {
|
|
392
454
|
if (underChart(path)) continue;
|
|
455
|
+
if (terraform.claimed.has(path)) continue;
|
|
393
456
|
const name = basename(path);
|
|
394
457
|
|
|
395
458
|
const ci = ciLexiconForPath(path);
|
|
@@ -427,13 +490,26 @@ export function discoverByDetection(root: string, plugins: DetectPlugin[]): Audi
|
|
|
427
490
|
export function collectCandidates(root: string): RepoFile[] {
|
|
428
491
|
const all: string[] = [];
|
|
429
492
|
walkFiles(root, all);
|
|
493
|
+
// TF023 reads paths, not content: a `.tfstate` can be tens of megabytes of
|
|
494
|
+
// machine-generated JSON, and the finding is that it is in the repository at
|
|
495
|
+
// all. The root `.gitignore` is what separates "committed" from "merely on
|
|
496
|
+
// disk" here — a Terraform working tree almost always has an ignored
|
|
497
|
+
// `.terraform/` in it, and reporting that would make TF023 fire on every
|
|
498
|
+
// local audit. A fetched repository has no such ambiguity (its file list is
|
|
499
|
+
// the tracked files) and is not filtered.
|
|
500
|
+
const gitignore = readSafe(join(root, ".gitignore")) ?? "";
|
|
430
501
|
const files: RepoFile[] = [];
|
|
431
502
|
for (const full of all) {
|
|
432
503
|
const path = relative(root, full);
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
504
|
+
if (isTerraformStatePath(path)) {
|
|
505
|
+
if (!gitignoreCoversTerraformState(gitignore, path)) files.push({ path, content: "" });
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
// `.tf` is read locally so `classifyTerraform` can bundle real content.
|
|
509
|
+
// It stays out of `isCandidatePath` so remote fetches still never pull HCL.
|
|
510
|
+
if (isTerraformFileName(basename(path))) {
|
|
511
|
+
const content = readSafe(full);
|
|
512
|
+
if (content !== undefined) files.push({ path, content });
|
|
437
513
|
continue;
|
|
438
514
|
}
|
|
439
515
|
if (!isCandidatePath(path)) continue;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prior art for these audit rules: the open-source tools whose checks cover the
|
|
3
|
+
* same condition, credited per rule. See packages/core/src/audit/prior-art.ts for
|
|
4
|
+
* the registry, the relation vocabulary, and why this is credit rather than
|
|
5
|
+
* authority. Kept by hand; the prior-art sweep (scripts/prior-art-sweep.ts) reports
|
|
6
|
+
* when a credited tool's index no longer lists a rule cited here.
|
|
7
|
+
*/
|
|
8
|
+
import type { Lineage } from "./prior-art";
|
|
9
|
+
|
|
10
|
+
export const coreAuditLineage: Record<string, Lineage[]> = {
|
|
11
|
+
AGT002: [
|
|
12
|
+
{ tool: "agent-audit", rule: "auth-bypass/env-secret-in-config", url: "https://raw.githubusercontent.com/piiiico/agent-audit/main/README.md", relation: "equivalent" },
|
|
13
|
+
{ tool: "mcp-audit", rule: "Secrets Detection", url: "https://raw.githubusercontent.com/apisec-inc/mcp-audit/main/README.md", relation: "overlaps" },
|
|
14
|
+
{ tool: "agent-scan", rule: "W008", url: "https://raw.githubusercontent.com/invariantlabs-ai/mcp-scan/main/docs/issue-codes.md", relation: "overlaps" },
|
|
15
|
+
],
|
|
16
|
+
COR020: [
|
|
17
|
+
{ tool: "cfn-lint", rule: "E3004", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#E3004", relation: "equivalent" },
|
|
18
|
+
],
|
|
19
|
+
EXT001: [
|
|
20
|
+
{ tool: "cfn-lint", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/cfn-schema-specification.md#extending-the-schemas-with-new-keywords", relation: "equivalent" },
|
|
21
|
+
{ tool: "cfn-lint", rule: "E3014", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#E3014", relation: "equivalent" },
|
|
22
|
+
{ tool: "cfn-lint", rule: "E3021", url: "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/rules.md#E3021", relation: "equivalent" },
|
|
23
|
+
],
|
|
24
|
+
NGX001: [
|
|
25
|
+
{ tool: "gixy-ng", rule: "weak_ssl_tls", url: "https://gixy.getpagespeed.com/plugins/weak_ssl_tls/", relation: "overlaps" },
|
|
26
|
+
],
|
|
27
|
+
NGX002: [
|
|
28
|
+
{ tool: "gixy-ng", rule: "weak_ssl_tls", url: "https://gixy.getpagespeed.com/plugins/weak_ssl_tls/", relation: "overlaps" },
|
|
29
|
+
],
|
|
30
|
+
NGX004: [
|
|
31
|
+
{ tool: "gixy", rule: "alias_traversal", url: "https://github.com/yandex/gixy", relation: "equivalent" },
|
|
32
|
+
{ tool: "gixy-ng", rule: "alias_traversal", url: "https://gixy.getpagespeed.com/plugins/aliastraversal/", relation: "equivalent" },
|
|
33
|
+
],
|
|
34
|
+
NGX005: [
|
|
35
|
+
{ tool: "gixy-ng", rule: "status_page_exposed", url: "https://gixy.getpagespeed.com/checks/status-page-exposed/", relation: "overlaps" },
|
|
36
|
+
],
|
|
37
|
+
NGX006: [
|
|
38
|
+
{ tool: "gixy-ng", rule: "version_disclosure", url: "https://gixy.getpagespeed.com/plugins/version_disclosure/", relation: "equivalent" },
|
|
39
|
+
],
|
|
40
|
+
SEC001: [
|
|
41
|
+
{ tool: "gitleaks", rule: "aws-access-token", url: "https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml", relation: "equivalent" },
|
|
42
|
+
{ tool: "trufflehog", rule: "aws", url: "https://github.com/trufflesecurity/trufflehog/blob/main/pkg/detectors/aws/access_keys/accesskey.go", relation: "overlaps" },
|
|
43
|
+
{ tool: "detect-secrets", rule: "AWSKeyDetector", url: "https://github.com/Yelp/detect-secrets/blob/master/detect_secrets/plugins/aws.py", relation: "equivalent" },
|
|
44
|
+
],
|
|
45
|
+
SEC002: [
|
|
46
|
+
{ tool: "gitleaks", rule: "generic-api-key", url: "https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml", relation: "overlaps" },
|
|
47
|
+
{ tool: "trufflehog", rule: "aws", url: "https://github.com/trufflesecurity/trufflehog/blob/main/pkg/detectors/aws/access_keys/accesskey.go", relation: "overlaps" },
|
|
48
|
+
{ tool: "detect-secrets", rule: "AWSKeyDetector", url: "https://github.com/Yelp/detect-secrets/blob/master/detect_secrets/plugins/aws.py", relation: "overlaps" },
|
|
49
|
+
],
|
|
50
|
+
SEC003: [
|
|
51
|
+
{ tool: "gitleaks", rule: "github-pat", url: "https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml", relation: "overlaps" },
|
|
52
|
+
{ tool: "trufflehog", rule: "github", url: "https://github.com/trufflesecurity/trufflehog/blob/main/pkg/detectors/github/v2/github.go", relation: "equivalent" },
|
|
53
|
+
{ tool: "detect-secrets", rule: "GitHubTokenDetector", url: "https://github.com/Yelp/detect-secrets/blob/master/detect_secrets/plugins/github_token.py", relation: "overlaps" },
|
|
54
|
+
],
|
|
55
|
+
SEC004: [
|
|
56
|
+
{ tool: "gitleaks", rule: "slack-bot-token", url: "https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml", relation: "overlaps" },
|
|
57
|
+
{ tool: "trufflehog", rule: "slack", url: "https://github.com/trufflesecurity/trufflehog/blob/main/pkg/detectors/slack/slack.go", relation: "overlaps" },
|
|
58
|
+
{ tool: "detect-secrets", rule: "SlackDetector", url: "https://github.com/Yelp/detect-secrets/blob/master/detect_secrets/plugins/slack.py", relation: "overlaps" },
|
|
59
|
+
],
|
|
60
|
+
SEC005: [
|
|
61
|
+
{ tool: "gitleaks", rule: "gcp-api-key", url: "https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml", relation: "equivalent" },
|
|
62
|
+
{ tool: "trufflehog", rule: "googlegemini", url: "https://github.com/trufflesecurity/trufflehog/blob/main/pkg/detectors/googlegemini/googlegemini.go", relation: "overlaps" },
|
|
63
|
+
],
|
|
64
|
+
SEC006: [
|
|
65
|
+
{ tool: "gitleaks", rule: "stripe-access-token", url: "https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml", relation: "overlaps" },
|
|
66
|
+
{ tool: "trufflehog", rule: "stripe", url: "https://github.com/trufflesecurity/trufflehog/blob/main/pkg/detectors/stripe/stripe.go", relation: "overlaps" },
|
|
67
|
+
{ tool: "detect-secrets", rule: "StripeDetector", url: "https://github.com/Yelp/detect-secrets/blob/master/detect_secrets/plugins/stripe.py", relation: "overlaps" },
|
|
68
|
+
],
|
|
69
|
+
SEC007: [
|
|
70
|
+
{ tool: "gitleaks", rule: "private-key", url: "https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml", relation: "equivalent" },
|
|
71
|
+
{ tool: "trufflehog", rule: "privatekey", url: "https://github.com/trufflesecurity/trufflehog/blob/main/pkg/detectors/privatekey/privatekey.go", relation: "equivalent" },
|
|
72
|
+
{ tool: "detect-secrets", rule: "PrivateKeyDetector", url: "https://github.com/Yelp/detect-secrets/blob/master/detect_secrets/plugins/private_key.py", relation: "equivalent" },
|
|
73
|
+
],
|
|
74
|
+
SEC008: [
|
|
75
|
+
{ tool: "gitleaks", rule: "curl-auth-header", url: "https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml", relation: "overlaps" },
|
|
76
|
+
],
|
|
77
|
+
SEC009: [
|
|
78
|
+
{ tool: "detect-secrets", rule: "BasicAuthDetector", url: "https://github.com/Yelp/detect-secrets/blob/master/detect_secrets/plugins/basic_auth.py", relation: "equivalent" },
|
|
79
|
+
{ tool: "trufflehog", rule: "uri", url: "https://github.com/trufflesecurity/trufflehog/blob/main/pkg/detectors/uri/uri.go", relation: "overlaps" },
|
|
80
|
+
{ tool: "gitleaks", rule: "curl-auth-user", url: "https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml", relation: "overlaps" },
|
|
81
|
+
],
|
|
82
|
+
SEC010: [
|
|
83
|
+
{ tool: "detect-secrets", rule: "Base64HighEntropyString", url: "https://github.com/Yelp/detect-secrets/blob/master/detect_secrets/plugins/high_entropy_strings.py", relation: "overlaps" },
|
|
84
|
+
{ tool: "detect-secrets", rule: "HexHighEntropyString", url: "https://github.com/Yelp/detect-secrets/blob/master/detect_secrets/plugins/high_entropy_strings.py", relation: "overlaps" },
|
|
85
|
+
{ tool: "gitleaks", rule: "generic-api-key", url: "https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml", relation: "overlaps" },
|
|
86
|
+
],
|
|
87
|
+
// TF023 is the one TF id core owns (./terraform-state.ts); the rest of the
|
|
88
|
+
// family credits its prior art from lexicons/terraform/src/lint/audit-lineage.ts.
|
|
89
|
+
// No linter or scanner in #2107's survey checks for committed state, but the
|
|
90
|
+
// style guide's .gitignore section names the exact files, so the document is
|
|
91
|
+
// both the authority and the first written statement of the rule.
|
|
92
|
+
TF023: [
|
|
93
|
+
{
|
|
94
|
+
tool: "hashicorp-style-guide",
|
|
95
|
+
rule: ".gitignore: do not commit terraform.tfstate, terraform.tfstate.* backups, or the .terraform directory",
|
|
96
|
+
url: "https://developer.hashicorp.com/terraform/language/style#gitignore",
|
|
97
|
+
relation: "equivalent",
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|