@intentius/chant 0.57.0 → 0.59.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 +36 -1
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +26 -9
- 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/carve-bridge.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 +13 -0
- 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 +12 -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/plugins.d.ts +22 -0
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +40 -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 +24 -22
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +21 -6
- 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 +73 -37
- 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 +10 -22
- 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/detectLexicon.d.ts +13 -0
- package/dist/detectLexicon.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 +273 -16
- 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 +79 -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 +149 -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 +85 -30
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/change-signal.d.ts +91 -0
- package/dist/op/change-signal.d.ts.map +1 -0
- package/dist/op/composites/apply-op.d.ts +118 -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-name.d.ts +40 -0
- package/dist/op/gate-name.d.ts.map +1 -0
- package/dist/op/gate-summary.d.ts +43 -0
- package/dist/op/gate-summary.d.ts.map +1 -0
- 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 +29 -9
- 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 +185 -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 +93 -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 +96 -29
- 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/dist/terraform/bridge.d.ts +26 -7
- package/dist/terraform/bridge.d.ts.map +1 -1
- package/dist/terraform/carve-provider.d.ts +28 -2
- package/dist/terraform/carve-provider.d.ts.map +1 -1
- package/dist/terraform/data-source-shape.d.ts +77 -0
- package/dist/terraform/data-source-shape.d.ts.map +1 -0
- package/dist/terraform/graph.d.ts.map +1 -1
- package/dist/terraform/providers/kubernetes.d.ts +5 -0
- package/dist/terraform/providers/kubernetes.d.ts.map +1 -1
- package/dist/terraform/tier-map.d.ts +16 -4
- package/dist/terraform/tier-map.d.ts.map +1 -1
- package/dist/terraform/types.d.ts +8 -0
- package/dist/terraform/types.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 +84 -18
- package/src/audit/discover.test.ts +39 -0
- package/src/audit/discover.ts +202 -18
- 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 +191 -11
- package/src/cli/commands/audit.ts +78 -28
- package/src/cli/commands/build.test.ts +330 -9
- package/src/cli/commands/build.ts +170 -24
- package/src/cli/commands/carve-bridge.test.ts +237 -14
- package/src/cli/commands/carve-bridge.ts +21 -17
- package/src/cli/commands/carve-emit-k8s.test.ts +17 -10
- 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 +194 -1
- package/src/cli/commands/lint.ts +205 -7
- 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 +341 -55
- package/src/cli/handlers/run.test.ts +568 -1295
- package/src/cli/handlers/run.ts +446 -912
- package/src/cli/main.test.ts +22 -16
- package/src/cli/main.ts +66 -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/plugins.test.ts +68 -2
- package/src/cli/plugins.ts +39 -0
- package/src/cli/registry.ts +40 -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 +4 -3
- package/src/components/__fixtures__/neo4j-fanout.json +1 -1
- 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 +28 -12
- package/src/components/cli-support.ts +60 -47
- package/src/components/component-schema.test.ts +18 -2
- package/src/components/component.schema.json +18 -5
- package/src/components/component.test.ts +2 -2
- package/src/components/component.ts +27 -7
- package/src/components/config-defaults.test.ts +2 -2
- package/src/components/driver-output.ts +14 -2
- package/src/components/driver.test.ts +109 -20
- package/src/components/driver.ts +201 -84
- 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/neo4j-fanout.pilot.ts +3 -3
- 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.test.ts +19 -0
- package/src/components/verbs/run-agent.ts +12 -24
- 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/detectLexicon.ts +18 -1
- package/src/discovery/collect.test.ts +3 -3
- package/src/discovery/files.ts +4 -4
- package/src/discovery/fold-import.test.ts +1 -1
- 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 +82 -2
- 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 +3 -3
- 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 +296 -16
- 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 +154 -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/pipeline-change-gate.test.ts +2 -2
- 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 +27 -1
- package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +76 -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 +161 -0
- package/src/op/activities/reconcile.ts +354 -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 +125 -41
- package/src/op/change-signal.test.ts +117 -0
- package/src/op/change-signal.ts +169 -0
- package/src/op/composites/apply-op.ts +218 -0
- package/src/op/composites/composites.test.ts +379 -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 +110 -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 +29 -14
- package/src/op/emulator-lifecycle.ts +1 -2
- package/src/op/gate-name.test.ts +65 -0
- package/src/op/gate-name.ts +60 -0
- package/src/op/gate-summary.ts +84 -0
- 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 +48 -8
- package/src/op/local-executor.test.ts +178 -34
- package/src/op/local-executor.ts +371 -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 +292 -0
- package/src/op/op-ir.ts +419 -0
- package/src/op/op-verb-class.test.ts +2 -2
- package/src/op/op-verb-class.ts +19 -5
- package/src/op/op.test.ts +28 -7
- package/src/op/operator.test.ts +543 -6
- package/src/op/operator.ts +203 -20
- 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 +106 -26
- 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/src/terraform/aws-resources.test.ts +13 -4
- package/src/terraform/bridge.test.ts +22 -9
- package/src/terraform/bridge.ts +89 -28
- package/src/terraform/carve-provider.ts +38 -2
- package/src/terraform/data-source-shape.ts +95 -0
- package/src/terraform/graph.ts +35 -7
- package/src/terraform/providers/kubernetes.ts +48 -2
- package/src/terraform/tier-map.ts +21 -6
- package/src/terraform/types.ts +8 -0
- 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
|
@@ -221,6 +221,35 @@ describe("auditCommand", () => {
|
|
|
221
221
|
expect(none.exitCode).toBe(0);
|
|
222
222
|
});
|
|
223
223
|
|
|
224
|
+
test("--fail-on error is a severity floor, decoupled from tier: a warning-severity finding does not fail it, an error-severity one does (chant #2113)", async () => {
|
|
225
|
+
const onlyWarning = await auditCommand({
|
|
226
|
+
path: REPO,
|
|
227
|
+
failOn: "error",
|
|
228
|
+
checksProvider: async () => [
|
|
229
|
+
{
|
|
230
|
+
id: "TEST900",
|
|
231
|
+
description: "always fires a warning, for the --fail-on error floor test",
|
|
232
|
+
check: () => [{ checkId: "TEST900", severity: "warning" as const, message: "a new rule shipped at warning" }],
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
});
|
|
236
|
+
expect(onlyWarning.findings.some((f) => f.checkId === "TEST900")).toBe(true);
|
|
237
|
+
expect(onlyWarning.exitCode).toBe(0);
|
|
238
|
+
|
|
239
|
+
const withAnError = await auditCommand({
|
|
240
|
+
path: REPO,
|
|
241
|
+
failOn: "error",
|
|
242
|
+
checksProvider: async () => [
|
|
243
|
+
{
|
|
244
|
+
id: "TEST901",
|
|
245
|
+
description: "always fires an error, for the --fail-on error floor test",
|
|
246
|
+
check: () => [{ checkId: "TEST901", severity: "error" as const, message: "an actual error" }],
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
});
|
|
250
|
+
expect(withAnError.exitCode).toBe(1);
|
|
251
|
+
});
|
|
252
|
+
|
|
224
253
|
test("--tier merge-worthy filters out report-only findings", async () => {
|
|
225
254
|
const all = await auditCommand({ path: REPO, tier: "all" });
|
|
226
255
|
const mw = await auditCommand({ path: REPO, tier: "merge-worthy" });
|
|
@@ -267,12 +296,11 @@ describe("auditCommand", () => {
|
|
|
267
296
|
expect(result.output).toContain("k8s/deploy.yaml -> k8s");
|
|
268
297
|
expect(result.output).toContain("infra/stack.json -> aws");
|
|
269
298
|
expect(result.output).toContain("Dockerfile -> docker");
|
|
270
|
-
expect(result.output).toContain("infra/main.tf -> terraform
|
|
271
|
-
// The install line names
|
|
299
|
+
expect(result.output).toContain("infra/main.tf -> terraform");
|
|
300
|
+
// The install line names every lexicon the files wanted, terraform included.
|
|
272
301
|
expect(result.output).toContain(
|
|
273
|
-
`npx -p @intentius/chant -p @intentius/chant-lexicon-github -p @intentius/chant-lexicon-docker -p @intentius/chant-lexicon-aws -p @intentius/chant-lexicon-k8s chant audit ${MIXED}`,
|
|
302
|
+
`npx -p @intentius/chant -p @intentius/chant-lexicon-github -p @intentius/chant-lexicon-docker -p @intentius/chant-lexicon-terraform -p @intentius/chant-lexicon-aws -p @intentius/chant-lexicon-k8s chant audit ${MIXED}`,
|
|
274
303
|
);
|
|
275
|
-
expect(result.output).not.toContain("chant-lexicon-terraform");
|
|
276
304
|
});
|
|
277
305
|
|
|
278
306
|
test("zero lexicons with --json: status no-lexicons plus the unclaimed list and install line", async () => {
|
|
@@ -282,7 +310,7 @@ describe("auditCommand", () => {
|
|
|
282
310
|
const json = JSON.parse(result.output);
|
|
283
311
|
expect(json.status).toBe("no-lexicons");
|
|
284
312
|
expect(json.findings).toEqual([]);
|
|
285
|
-
expect(json.missingLexicons).toEqual(["github", "docker", "aws", "k8s"]);
|
|
313
|
+
expect(json.missingLexicons).toEqual(["github", "docker", "terraform", "aws", "k8s"]);
|
|
286
314
|
expect(json.unclaimed).toContainEqual({ path: "k8s/deploy.yaml", lexicon: "k8s" });
|
|
287
315
|
expect(json.unclaimed).toContainEqual({ path: "infra/main.tf", lexicon: "terraform" });
|
|
288
316
|
expect(json.install).toMatch(/^npx -p @intentius\/chant /);
|
|
@@ -307,9 +335,9 @@ describe("auditCommand", () => {
|
|
|
307
335
|
expect(result.status).toBe("ok");
|
|
308
336
|
expect(result.exitCode).toBe(0);
|
|
309
337
|
expect(result.scanned).toEqual([".github/workflows/ci.yml"]);
|
|
310
|
-
expect(result.output).toMatch(/^Note:
|
|
311
|
-
expect(result.output).toContain("1 Terraform file skipped; the audit does not read HCL (see chant carve).");
|
|
338
|
+
expect(result.output).toMatch(/^Note: 4 files look like docker\/terraform\/aws\/k8s but those lexicons are not installed, so they were skipped \(npm i @intentius\/chant-lexicon-docker @intentius\/chant-lexicon-terraform @intentius\/chant-lexicon-aws @intentius\/chant-lexicon-k8s\)\./);
|
|
312
339
|
expect(result.unclaimed).toContainEqual({ path: "k8s/deploy.yaml", lexicon: "k8s" });
|
|
340
|
+
expect(result.unclaimed).toContainEqual({ path: "infra/main.tf", lexicon: "terraform" });
|
|
313
341
|
});
|
|
314
342
|
|
|
315
343
|
test("partial with --json: status ok and the unclaimed files ride along", async () => {
|
|
@@ -320,12 +348,13 @@ describe("auditCommand", () => {
|
|
|
320
348
|
expect(json.unclaimed.map((u: { lexicon: string }) => u.lexicon).sort()).toEqual(["aws", "docker", "k8s", "terraform"]);
|
|
321
349
|
});
|
|
322
350
|
|
|
323
|
-
test("
|
|
351
|
+
test("every installable lexicon loaded: nothing is unclaimed, and the terraform root is scanned as one bundle (#2083)", async () => {
|
|
324
352
|
const result = await auditCommand({ path: MIXED, plugins: await loadAuditPlugins() });
|
|
325
353
|
expect(result.status).toBe("ok");
|
|
326
|
-
expect(result.unclaimed).toEqual([
|
|
327
|
-
expect(result.output).not.toContain("
|
|
328
|
-
|
|
354
|
+
expect(result.unclaimed).toEqual([]);
|
|
355
|
+
expect(result.output).not.toContain("looks like terraform");
|
|
356
|
+
// `infra` is the terraform root-module bundle (one input per directory of `.tf` files).
|
|
357
|
+
expect(result.scanned.sort()).toEqual([".github/workflows/ci.yml", "Dockerfile", "infra", "infra/stack.json", "k8s/deploy.yaml"]);
|
|
329
358
|
});
|
|
330
359
|
|
|
331
360
|
test("installLine puts every lexicon on the same npx -p path", () => {
|
|
@@ -441,6 +470,53 @@ describe("auditCommand", () => {
|
|
|
441
470
|
expect(valid).toBe(true);
|
|
442
471
|
});
|
|
443
472
|
|
|
473
|
+
// The scope is the discovered root module's own directory (`infra`), which
|
|
474
|
+
// is what the terraform plugin names an audited scope after since #2217.
|
|
475
|
+
describe("TF001's missing-resource shape renders in all three reporters (chant #2113)", () => {
|
|
476
|
+
const dir = join(tmpdir(), `chant-audit-tf-missing-${process.pid}`);
|
|
477
|
+
|
|
478
|
+
function setup(): void {
|
|
479
|
+
mkdirSync(join(dir, "infra"), { recursive: true });
|
|
480
|
+
writeFileSync(join(dir, "infra/main.tf"), 'terraform {\n required_version = ">= 1.5.0"\n}\n');
|
|
481
|
+
}
|
|
482
|
+
function teardown(): void {
|
|
483
|
+
rmSync(dir, { recursive: true, force: true });
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
test("stylish names the missing kind and scope instead of falling back to entity", async () => {
|
|
487
|
+
setup();
|
|
488
|
+
const result = await auditCommand({ path: dir, format: "stylish" });
|
|
489
|
+
expect(result.output).toContain("(missing backend in infra)");
|
|
490
|
+
teardown();
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
test("json carries `missing` on the finding", async () => {
|
|
494
|
+
setup();
|
|
495
|
+
const result = await auditCommand({ path: dir, format: "json" });
|
|
496
|
+
const doc = JSON.parse(result.output) as { findings: Array<{ checkId: string; missing?: { kind: string; scope: string } }> };
|
|
497
|
+
const tf001 = doc.findings.find((f) => f.checkId === "TF001");
|
|
498
|
+
expect(tf001?.missing).toEqual({ kind: "backend", scope: "infra" });
|
|
499
|
+
teardown();
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
test("sarif carries `missing` in the result's properties, and still validates against the SARIF schema", async () => {
|
|
503
|
+
setup();
|
|
504
|
+
const result = await auditCommand({ path: dir, format: "sarif" });
|
|
505
|
+
const doc = JSON.parse(result.output) as {
|
|
506
|
+
runs: Array<{ results: Array<{ ruleId: string; properties?: { missing?: { kind: string; scope: string } } }> }>;
|
|
507
|
+
};
|
|
508
|
+
const tf001 = doc.runs[0].results.find((r) => r.ruleId === "TF001");
|
|
509
|
+
expect(tf001?.properties?.missing).toEqual({ kind: "backend", scope: "infra" });
|
|
510
|
+
|
|
511
|
+
const ajv = new Ajv({ strict: false, allErrors: true });
|
|
512
|
+
const validate = ajv.compile(loadSarifSchema() as object);
|
|
513
|
+
const valid = validate(doc);
|
|
514
|
+
if (!valid) throw new Error(ajv.errorsText(validate.errors));
|
|
515
|
+
expect(valid).toBe(true);
|
|
516
|
+
teardown();
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
|
|
444
520
|
test("sarif schema validation also holds for the empty-result case (no findings)", async () => {
|
|
445
521
|
const repo = fileURLToPath(new URL("./__fixtures__/audit-fountain-clean", import.meta.url));
|
|
446
522
|
const result = await auditCommand({ path: repo, format: "sarif" });
|
|
@@ -555,6 +631,82 @@ describe("secrets detection (#443)", () => {
|
|
|
555
631
|
});
|
|
556
632
|
});
|
|
557
633
|
|
|
634
|
+
describe("Terraform HCL suppression (chant #2111)", () => {
|
|
635
|
+
function tmpRepo(): string {
|
|
636
|
+
const dir = join(tmpdir(), `chant-audit-tf-suppress-${process.pid}-${Math.random().toString(36).slice(2)}`);
|
|
637
|
+
mkdirSync(dir, { recursive: true });
|
|
638
|
+
mkdirSync(join(dir, "infra"), { recursive: true });
|
|
639
|
+
return dir;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const BACKENDLESS = 'terraform {\n required_version = ">= 1.5.0"\n}\n';
|
|
643
|
+
|
|
644
|
+
async function auditTerraform(dir: string): Promise<Awaited<ReturnType<typeof auditCommand>>> {
|
|
645
|
+
const all = await loadAuditPlugins();
|
|
646
|
+
return auditCommand({ path: dir, plugins: all.filter((p) => p.name === "terraform"), format: "json" });
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
test("chant-ignore-block suppresses TF001 through chant audit", async () => {
|
|
650
|
+
const dir = tmpRepo();
|
|
651
|
+
writeFileSync(join(dir, "infra", "main.tf"), `# chant-ignore-block: TF001\n${BACKENDLESS}`);
|
|
652
|
+
const result = await auditTerraform(dir);
|
|
653
|
+
expect(result.findings.map((f) => f.checkId)).not.toContain("TF001");
|
|
654
|
+
expect(result.suppressedCount).toBe(1);
|
|
655
|
+
rmSync(dir, { recursive: true, force: true });
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
test("a plain chant-ignore on the same block also suppresses TF001 through chant audit", async () => {
|
|
659
|
+
const dir = tmpRepo();
|
|
660
|
+
writeFileSync(join(dir, "infra", "main.tf"), `# chant-ignore: TF001\n${BACKENDLESS}`);
|
|
661
|
+
const result = await auditTerraform(dir);
|
|
662
|
+
expect(result.findings.map((f) => f.checkId)).not.toContain("TF001");
|
|
663
|
+
expect(result.suppressedCount).toBe(1);
|
|
664
|
+
rmSync(dir, { recursive: true, force: true });
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
test('"all" suppresses TF001 through chant audit', async () => {
|
|
668
|
+
const dir = tmpRepo();
|
|
669
|
+
writeFileSync(join(dir, "infra", "main.tf"), `# chant-ignore-block: all\n${BACKENDLESS}`);
|
|
670
|
+
const result = await auditTerraform(dir);
|
|
671
|
+
expect(result.findings.map((f) => f.checkId)).not.toContain("TF001");
|
|
672
|
+
rmSync(dir, { recursive: true, force: true });
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
test("chant-ignore-file at the top of the file suppresses TF001 through chant audit", async () => {
|
|
676
|
+
const dir = tmpRepo();
|
|
677
|
+
writeFileSync(join(dir, "infra", "main.tf"), `# chant-ignore-file: TF001\n${BACKENDLESS}`);
|
|
678
|
+
const result = await auditTerraform(dir);
|
|
679
|
+
expect(result.findings.map((f) => f.checkId)).not.toContain("TF001");
|
|
680
|
+
rmSync(dir, { recursive: true, force: true });
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
test("a chant-ignore-file NOT at the top has no effect and is reported through chant audit", async () => {
|
|
684
|
+
const dir = tmpRepo();
|
|
685
|
+
writeFileSync(join(dir, "infra", "main.tf"), `# a header\n# chant-ignore-file: TF001\n${BACKENDLESS}`);
|
|
686
|
+
const result = await auditTerraform(dir);
|
|
687
|
+
expect(result.findings.map((f) => f.checkId)).toContain("TF001"); // still fires
|
|
688
|
+
expect(result.findings.map((f) => f.checkId)).toContain("SUPP002");
|
|
689
|
+
rmSync(dir, { recursive: true, force: true });
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
test("a directive naming a different rule id does not suppress TF001 through chant audit", async () => {
|
|
693
|
+
const dir = tmpRepo();
|
|
694
|
+
writeFileSync(join(dir, "infra", "main.tf"), `# chant-ignore-block: TF010\n${BACKENDLESS}`);
|
|
695
|
+
const result = await auditTerraform(dir);
|
|
696
|
+
expect(result.findings.map((f) => f.checkId)).toContain("TF001");
|
|
697
|
+
rmSync(dir, { recursive: true, force: true });
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
test("an already-expired suppression no longer suppresses TF001 and is reported through chant audit", async () => {
|
|
701
|
+
const dir = tmpRepo();
|
|
702
|
+
writeFileSync(join(dir, "infra", "main.tf"), `# chant-ignore-block: TF001 exp:2020-01-01\n${BACKENDLESS}`);
|
|
703
|
+
const result = await auditTerraform(dir);
|
|
704
|
+
expect(result.findings.map((f) => f.checkId)).toContain("TF001"); // still fires: expired
|
|
705
|
+
expect(result.findings.map((f) => f.checkId)).toContain("SUPP001");
|
|
706
|
+
rmSync(dir, { recursive: true, force: true });
|
|
707
|
+
});
|
|
708
|
+
});
|
|
709
|
+
|
|
558
710
|
describe("Wrangler config audit (#446)", () => {
|
|
559
711
|
function tmpRepo(): string {
|
|
560
712
|
const dir = join(tmpdir(), `chant-audit-wrangler-${process.pid}-${Math.random().toString(36).slice(2)}`);
|
|
@@ -649,3 +801,31 @@ describe("nginx config audit (#1979)", () => {
|
|
|
649
801
|
rmSync(dir, { recursive: true, force: true });
|
|
650
802
|
});
|
|
651
803
|
});
|
|
804
|
+
|
|
805
|
+
describe("committed Terraform state (TF023, #2110)", () => {
|
|
806
|
+
function tmpRepo(): string {
|
|
807
|
+
const dir = join(tmpdir(), `chant-audit-tfstate-${process.pid}-${Math.random().toString(36).slice(2)}`);
|
|
808
|
+
mkdirSync(dir, { recursive: true });
|
|
809
|
+
return dir;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
test("fires end to end on a directory with a tracked state file, with no lexicon installed", async () => {
|
|
813
|
+
const dir = tmpRepo();
|
|
814
|
+
writeFileSync(join(dir, "main.tf"), 'resource "null_resource" "a" {}\n');
|
|
815
|
+
writeFileSync(join(dir, "terraform.tfstate"), JSON.stringify({ version: 4, resources: [] }));
|
|
816
|
+
const result = await auditCommand({ path: dir, plugins: [] });
|
|
817
|
+
expect(result.findings.map((f) => f.checkId)).toContain("TF023");
|
|
818
|
+
rmSync(dir, { recursive: true, force: true });
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
test("says nothing when the state is ignored rather than committed", async () => {
|
|
822
|
+
const dir = tmpRepo();
|
|
823
|
+
writeFileSync(join(dir, ".gitignore"), "*.tfstate\n.terraform/\n");
|
|
824
|
+
writeFileSync(join(dir, "main.tf"), 'resource "null_resource" "a" {}\n');
|
|
825
|
+
writeFileSync(join(dir, "terraform.tfstate"), JSON.stringify({ version: 4, resources: [] }));
|
|
826
|
+
mkdirSync(join(dir, ".terraform"), { recursive: true });
|
|
827
|
+
const result = await auditCommand({ path: dir, plugins: [] });
|
|
828
|
+
expect(result.findings.map((f) => f.checkId)).not.toContain("TF023");
|
|
829
|
+
rmSync(dir, { recursive: true, force: true });
|
|
830
|
+
});
|
|
831
|
+
});
|
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
import { existsSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
9
9
|
import { join } from "path";
|
|
10
|
-
import { auditFiles, type AuditInput, type AuditFinding, type ChecksProvider } from "../../audit/core";
|
|
10
|
+
import { auditFiles, type AuditInput, type AuditFinding, type ChecksProvider, type SuppressionStats } from "../../audit/core";
|
|
11
11
|
import { AUDIT_LEXICONS, classifyFiles, collectCandidates, loadAuditPlugins, unclaimedFiles, type DetectPlugin, type RepoFile, type UnclaimedFile } from "../../audit/discover";
|
|
12
12
|
import { RULE_CATALOG, resolveAuditCatalog, type RuleMeta } from "../../audit/catalog";
|
|
13
13
|
import { scanForSecrets, parseSecretsConfig, type SecretsScanOptions } from "../../audit/secrets";
|
|
14
14
|
import { auditWranglerConfigs } from "../../audit/wrangler";
|
|
15
15
|
import { auditNginxConfigs } from "../../audit/nginx";
|
|
16
|
+
import { auditTerraformState } from "../../audit/terraform-state";
|
|
16
17
|
import { renderMarkdown } from "../../audit/report";
|
|
17
18
|
import { renderHtml, type ReportTheme } from "../../audit/report-html";
|
|
18
19
|
import { buildReportJson, REPORT_SCHEMA_VERSION, type AuditSnapshot } from "../../audit/report-model";
|
|
@@ -23,7 +24,17 @@ import type { Severity } from "../../lint/rule";
|
|
|
23
24
|
|
|
24
25
|
export type AuditFormat = "stylish" | "json" | "sarif" | "markdown" | "html";
|
|
25
26
|
export type AuditTier = "merge-worthy" | "all";
|
|
26
|
-
|
|
27
|
+
/**
|
|
28
|
+
* `"error"` (chant #2113) is the minimum-failure-severity floor tflint's
|
|
29
|
+
* `--minimum-failure-severity` maps onto (`error | warning | notice`): unlike
|
|
30
|
+
* `"merge-worthy"`/`"warning"` above (which key off a rule's catalog `tier`
|
|
31
|
+
* or a finding's severity being at-or-above warning), `"error"` fails only on
|
|
32
|
+
* an `error`-severity finding, decoupling what CI fails a build over from
|
|
33
|
+
* what a rule ships reporting by default. It's the lever that lets a new
|
|
34
|
+
* rule ship at `warning` and be promoted to `error` later without a pipeline
|
|
35
|
+
* pinned to `--fail-on error` breaking the moment the rule starts firing.
|
|
36
|
+
*/
|
|
37
|
+
export type AuditFailOn = "merge-worthy" | "warning" | "error" | "none";
|
|
27
38
|
|
|
28
39
|
export interface AuditCommandOptions {
|
|
29
40
|
/** Repo root/dir to scan, or an https:// repo URL to fetch and audit. */
|
|
@@ -79,6 +90,12 @@ export interface AuditCommandResult {
|
|
|
79
90
|
unclaimed?: UnclaimedFile[];
|
|
80
91
|
/** Where `output` belongs; diagnostics go to stderr, reports to stdout (default). */
|
|
81
92
|
stream?: "stdout" | "stderr";
|
|
93
|
+
/**
|
|
94
|
+
* Findings an inline `# chant-ignore*` comment suppressed (chant #2111),
|
|
95
|
+
* counted, never dropped silently, mirroring `chant build`'s equivalent
|
|
96
|
+
* summary line.
|
|
97
|
+
*/
|
|
98
|
+
suppressedCount?: number;
|
|
82
99
|
}
|
|
83
100
|
|
|
84
101
|
/** Exit code when the audit had no lexicons to look with. Distinct from 1 (findings / failure). */
|
|
@@ -89,9 +106,9 @@ function lexiconPackage(name: string): string {
|
|
|
89
106
|
return `@intentius/chant-lexicon-${name}`;
|
|
90
107
|
}
|
|
91
108
|
|
|
92
|
-
/** Missing audit lexicons the unclaimed files pointed at, in first-seen order.
|
|
109
|
+
/** Missing audit lexicons the unclaimed files pointed at, in first-seen order. */
|
|
93
110
|
function wantedLexicons(unclaimed: UnclaimedFile[]): string[] {
|
|
94
|
-
return [...new Set(unclaimed.map((u) => u.lexicon))]
|
|
111
|
+
return [...new Set(unclaimed.map((u) => u.lexicon))];
|
|
95
112
|
}
|
|
96
113
|
|
|
97
114
|
/**
|
|
@@ -107,17 +124,10 @@ export function installLine(lexicons: string[], target: string): string {
|
|
|
107
124
|
/** One-line coverage hint for the partial case: some lexicons loaded, others wanted by files on disk. */
|
|
108
125
|
function missingLexiconHint(unclaimed: UnclaimedFile[]): string | undefined {
|
|
109
126
|
const wanted = wantedLexicons(unclaimed);
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
parts.push(
|
|
115
|
-
`${n} file${n === 1 ? " looks" : "s look"} like ${wanted.join("/")} but ${wanted.length === 1 ? "that lexicon is" : "those lexicons are"} not installed, so ${n === 1 ? "it was" : "they were"} skipped` +
|
|
116
|
-
` (npm i ${wanted.map(lexiconPackage).join(" ")}).`,
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
if (tf > 0) parts.push(`${tf} Terraform file${tf === 1 ? "" : "s"} skipped; the audit does not read HCL (see chant carve).`);
|
|
120
|
-
return parts.length > 0 ? parts.join(" ") : undefined;
|
|
127
|
+
if (wanted.length === 0) return undefined;
|
|
128
|
+
const n = unclaimed.length;
|
|
129
|
+
return `${n} file${n === 1 ? " looks" : "s look"} like ${wanted.join("/")} but ${wanted.length === 1 ? "that lexicon is" : "those lexicons are"} not installed, so ${n === 1 ? "it was" : "they were"} skipped` +
|
|
130
|
+
` (npm i ${wanted.map(lexiconPackage).join(" ")}).`;
|
|
121
131
|
}
|
|
122
132
|
|
|
123
133
|
/** Human-readable diagnostic for the zero-lexicon case. */
|
|
@@ -129,11 +139,10 @@ function renderNoLexicons(target: string, unclaimed: UnclaimedFile[]): string {
|
|
|
129
139
|
if (unclaimed.length > 0) {
|
|
130
140
|
lines.push("", "Files that wanted a lexicon:");
|
|
131
141
|
for (const u of unclaimed) {
|
|
132
|
-
|
|
133
|
-
lines.push(` ${u.path} -> ${note}`);
|
|
142
|
+
lines.push(` ${u.path} -> ${u.lexicon}`);
|
|
134
143
|
}
|
|
135
144
|
} else {
|
|
136
|
-
lines.push("", "No file under the target looked like CI, Kubernetes, Helm, Docker, CloudFormation, ARM, Config Connector, or
|
|
145
|
+
lines.push("", "No file under the target looked like CI, Kubernetes, Helm, Docker, CloudFormation, ARM, Config Connector, fountain, or Terraform either.");
|
|
137
146
|
}
|
|
138
147
|
lines.push("", "Run it with the lexicons those files need:");
|
|
139
148
|
lines.push(` ${installLine(wanted.length > 0 ? wanted : [...AUDIT_LEXICONS], target)}`);
|
|
@@ -197,6 +206,7 @@ function exitCodeFor(findings: AuditFinding[], failOn: AuditFailOn, catalog: Rec
|
|
|
197
206
|
if (failOn === "warning") {
|
|
198
207
|
return findings.some((f) => f.severity === "error" || f.severity === "warning") ? 1 : 0;
|
|
199
208
|
}
|
|
209
|
+
if (failOn === "error") return findings.some((f) => f.severity === "error") ? 1 : 0;
|
|
200
210
|
return 0;
|
|
201
211
|
}
|
|
202
212
|
|
|
@@ -252,7 +262,15 @@ function renderStylish(findings: AuditFinding[], scanned: string[], notes: strin
|
|
|
252
262
|
lines.push("", title);
|
|
253
263
|
for (const f of list) {
|
|
254
264
|
const loc = f.line ? `${f.file}:${f.line}` : f.file;
|
|
255
|
-
|
|
265
|
+
// A `missing` finding (chant #2113) has nothing present to anchor
|
|
266
|
+
// `entity` to. The whole point is that `f.missing.kind` does not
|
|
267
|
+
// exist in `f.missing.scope`, so it renders that absence directly
|
|
268
|
+
// instead of falling back to `entity` (which such a check rarely sets).
|
|
269
|
+
const where = f.missing
|
|
270
|
+
? `${loc} (missing ${f.missing.kind} in ${f.missing.scope})`
|
|
271
|
+
: f.entity
|
|
272
|
+
? `${loc} (${f.entity})`
|
|
273
|
+
: loc;
|
|
256
274
|
const title = catalog[f.checkId]?.title ?? f.checkId;
|
|
257
275
|
lines.push(` [${f.checkId}] ${f.severity} ${where} — ${title}`);
|
|
258
276
|
}
|
|
@@ -280,11 +298,21 @@ function renderSarif(findings: AuditFinding[], catalog: Record<string, RuleMeta>
|
|
|
280
298
|
shortDescription: { text: m?.title ?? id },
|
|
281
299
|
...(m?.remediation ? { help: { text: m.remediation } } : {}),
|
|
282
300
|
helpUri: m?.authority?.[0]?.url,
|
|
283
|
-
|
|
301
|
+
// `priorArt` credits the upstream tools whose rules check the same thing
|
|
302
|
+
// (audit/prior-art.ts) — tool key, upstream rule id, doc URL, relation.
|
|
303
|
+
...(m?.category || m?.lineage?.length
|
|
304
|
+
? {
|
|
305
|
+
properties: {
|
|
306
|
+
...(m?.category ? { category: m.category, dimension: m.category } : {}),
|
|
307
|
+
...(m?.lineage?.length ? { priorArt: m.lineage } : {}),
|
|
308
|
+
},
|
|
309
|
+
}
|
|
310
|
+
: {}),
|
|
284
311
|
};
|
|
285
312
|
});
|
|
286
313
|
const results = findings.map((f) => {
|
|
287
314
|
const tier = catalog[f.checkId]?.tier;
|
|
315
|
+
const properties = { ...(tier ? { tier } : {}), ...(f.missing ? { missing: f.missing } : {}) };
|
|
288
316
|
return {
|
|
289
317
|
ruleId: f.checkId,
|
|
290
318
|
level: sarifLevel(f.severity),
|
|
@@ -297,7 +325,12 @@ function renderSarif(findings: AuditFinding[], catalog: Record<string, RuleMeta>
|
|
|
297
325
|
},
|
|
298
326
|
},
|
|
299
327
|
],
|
|
300
|
-
|
|
328
|
+
// `f.missing` (chant #2113) is Snyk policy-engine's "missing-resource"
|
|
329
|
+
// archetype: nothing at `f.file` actually declares the absent thing, so
|
|
330
|
+
// there is no line to point SARIF's region at either. `missing` rides
|
|
331
|
+
// in the property bag (SARIF's core schema has no dedicated slot for an
|
|
332
|
+
// absence finding) rather than pretending a region exists.
|
|
333
|
+
...(Object.keys(properties).length > 0 ? { properties } : {}),
|
|
301
334
|
};
|
|
302
335
|
});
|
|
303
336
|
return JSON.stringify(
|
|
@@ -375,7 +408,11 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
375
408
|
// shape alone can't disambiguate them.
|
|
376
409
|
candidates = collectCandidates(options.path);
|
|
377
410
|
}
|
|
378
|
-
|
|
411
|
+
// A local target hands its directory to the classifier, so an input whose
|
|
412
|
+
// path names a directory carries the directory itself (#2217): terraform's
|
|
413
|
+
// parse-to-graph follows a root module's local `module` calls from there. A
|
|
414
|
+
// URL target has no local tree, and never fetches `.tf` in the first place.
|
|
415
|
+
const inputs = classifyFiles(candidates, plugins, isUrl ? {} : { baseDir: options.path });
|
|
379
416
|
const unclaimed = unclaimedFiles(candidates, inputs, plugins);
|
|
380
417
|
const scanned = inputs.map((i) => i.path);
|
|
381
418
|
|
|
@@ -395,29 +432,37 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
395
432
|
// its own path detector plus a parsed-content marker check (a `.conf` in a
|
|
396
433
|
// shared directory name like conf.d/ only counts once it parses as nginx).
|
|
397
434
|
const nginxFindings = auditNginxConfigs(candidates);
|
|
435
|
+
// TF023 (#2110) — committed Terraform state. Lexicon-independent for the
|
|
436
|
+
// same reason the three above are: it reads the discovered paths, not a
|
|
437
|
+
// parsed root module, so it fires with or without the terraform lexicon.
|
|
438
|
+
const terraformStateFindings = auditTerraformState(candidates);
|
|
398
439
|
|
|
399
440
|
if (plugins.length === 0) {
|
|
400
441
|
const output = format === "json" ? renderNoLexiconsJson(options.path, unclaimed) : renderNoLexicons(options.path, unclaimed);
|
|
401
|
-
return { success: true, status: "no-lexicons", output, findings: [...secretsFindings, ...wranglerFindings, ...nginxFindings], scanned: [], unclaimed, exitCode: NO_LEXICONS_EXIT_CODE, stream: format === "json" ? "stdout" : "stderr" };
|
|
442
|
+
return { success: true, status: "no-lexicons", output, findings: [...secretsFindings, ...wranglerFindings, ...nginxFindings, ...terraformStateFindings], scanned: [], unclaimed, exitCode: NO_LEXICONS_EXIT_CODE, stream: format === "json" ? "stdout" : "stderr" };
|
|
402
443
|
}
|
|
403
444
|
|
|
404
445
|
const missingLexiconNote = missingLexiconHint(unclaimed);
|
|
405
446
|
|
|
406
|
-
if (inputs.length === 0 && secretsFindings.length === 0 && wranglerFindings.length === 0 && nginxFindings.length === 0) {
|
|
447
|
+
if (inputs.length === 0 && secretsFindings.length === 0 && wranglerFindings.length === 0 && nginxFindings.length === 0 && terraformStateFindings.length === 0) {
|
|
407
448
|
const output = `No auditable files found under ${options.path}.${missingLexiconNote ? ` ${missingLexiconNote}` : ""}`;
|
|
408
449
|
return { success: true, status: "ok", output, findings: [], scanned: [], unclaimed, exitCode: 0 };
|
|
409
450
|
}
|
|
410
451
|
|
|
411
452
|
let findings: AuditFinding[] = [];
|
|
453
|
+
// chant #2111: mutated in place by `auditFiles` so its inline (`# chant-
|
|
454
|
+
// ignore*`) suppressed count reaches the summary without changing
|
|
455
|
+
// `auditFiles`'s `AuditFinding[]` return type; see `SuppressionStats`'s doc.
|
|
456
|
+
const suppressionStats: SuppressionStats = { count: 0 };
|
|
412
457
|
if (inputs.length > 0) {
|
|
413
458
|
try {
|
|
414
|
-
findings = await auditFiles(inputs, { checksProvider: options.checksProvider });
|
|
459
|
+
findings = await auditFiles(inputs, { checksProvider: options.checksProvider, suppressionStats });
|
|
415
460
|
} catch (err) {
|
|
416
461
|
const msg = err instanceof Error ? err.message : String(err);
|
|
417
462
|
return { success: false, output: "", findings: [], scanned, exitCode: 1, error: msg };
|
|
418
463
|
}
|
|
419
464
|
}
|
|
420
|
-
findings = [...findings, ...secretsFindings, ...wranglerFindings, ...nginxFindings];
|
|
465
|
+
findings = [...findings, ...secretsFindings, ...wranglerFindings, ...nginxFindings, ...terraformStateFindings];
|
|
421
466
|
// Resolve the audit catalog once, aggregating the audited lexicons' own
|
|
422
467
|
// metadata over core's static catalog (#687). The lexicons are already loaded
|
|
423
468
|
// by `auditFiles` above, so this is cheap. Core's static catalog (always
|
|
@@ -428,6 +473,11 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
428
473
|
if (tier === "merge-worthy") findings = findings.filter((f) => isMergeWorthy(f, catalog));
|
|
429
474
|
const notes = coverageNotes(inputs);
|
|
430
475
|
if (missingLexiconNote) notes.push(missingLexiconNote);
|
|
476
|
+
if (suppressionStats.count > 0) {
|
|
477
|
+
notes.push(
|
|
478
|
+
`${suppressionStats.count} finding${suppressionStats.count === 1 ? "" : "s"} suppressed via inline chant-ignore comments.`,
|
|
479
|
+
);
|
|
480
|
+
}
|
|
431
481
|
|
|
432
482
|
// Diff-bearing renderers (markdown, html) need action SHAs / image digests
|
|
433
483
|
// resolved up front (sync maps so rendering stays synchronous).
|
|
@@ -495,10 +545,10 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
495
545
|
} catch (err) {
|
|
496
546
|
return { success: false, output, findings, scanned, exitCode: 1, error: `Failed to write ${options.output}: ${err instanceof Error ? err.message : String(err)}` };
|
|
497
547
|
}
|
|
498
|
-
return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode, wroteTo: options.output };
|
|
548
|
+
return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode, wroteTo: options.output, suppressedCount: suppressionStats.count };
|
|
499
549
|
}
|
|
500
550
|
|
|
501
|
-
return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode };
|
|
551
|
+
return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode, suppressedCount: suppressionStats.count };
|
|
502
552
|
}
|
|
503
553
|
|
|
504
554
|
/** Print an audit result to stdout. */
|