@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
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
2
|
import { readdirSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
|
-
import { RULE_CATALOG, RULE_CATEGORY, auditRule, resolveAuditCatalog } from "./catalog";
|
|
4
|
+
import { PRIOR_ART, RULE_CATALOG, RULE_CATEGORY, auditRule, resolveAuditCatalog, canonicalRuleId } from "./catalog";
|
|
5
5
|
import { loadPlugins } from "../cli/plugins";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Every lexicon in the repo, enumerated rather than listed (#1346).
|
|
9
9
|
*
|
|
10
10
|
* This was nine hardcoded names. It missed fountain, which contributes a
|
|
11
|
-
* catalog, and could not notice fly
|
|
11
|
+
* catalog, and could not notice fly, which did not — so their six
|
|
12
12
|
* post-synth checks reached `chant audit` with no title, tier, fix kind, or
|
|
13
13
|
* category, and the guard meant to catch exactly that was blind to them. A
|
|
14
14
|
* thirteenth lexicon would have been invisible to it on arrival.
|
|
@@ -67,6 +67,11 @@ describe("RULE_CATALOG (aggregated: core static + lexicon-contributed, #687)", (
|
|
|
67
67
|
.filter((id) => !shipped.has(id))
|
|
68
68
|
// Core's own cross-cutting ids are not any one lexicon's to ship.
|
|
69
69
|
.filter((id) => !(id in RULE_CATALOG))
|
|
70
|
+
// A deprecated rule is deliberately kept in the catalog after its check
|
|
71
|
+
// stops shipping (chant #2113). That's the whole point of `deprecated`
|
|
72
|
+
// over deleting the entry, so it's not "stale" the way an orphaned
|
|
73
|
+
// entry with no explanation is.
|
|
74
|
+
.filter((id) => !catalog[id]?.deprecated)
|
|
70
75
|
.sort();
|
|
71
76
|
expect(stale).toEqual([]);
|
|
72
77
|
});
|
|
@@ -123,6 +128,33 @@ describe("RULE_CATALOG (aggregated: core static + lexicon-contributed, #687)", (
|
|
|
123
128
|
}
|
|
124
129
|
});
|
|
125
130
|
|
|
131
|
+
test("lineage entries key into PRIOR_ART, link over https, and use a known relation", async () => {
|
|
132
|
+
const relations = new Set(["equivalent", "overlaps", "extends"]);
|
|
133
|
+
for (const [id, m] of Object.entries(await aggregate())) {
|
|
134
|
+
for (const l of m.lineage ?? []) {
|
|
135
|
+
expect(l.tool in PRIOR_ART, `${id} credits unregistered tool "${l.tool}"`).toBe(true);
|
|
136
|
+
expect(l.url.startsWith("https://"), `${id} lineage url ${l.url}`).toBe(true);
|
|
137
|
+
expect(relations.has(l.relation), `${id} lineage relation ${l.relation}`).toBe(true);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("lineage is credit, not authority: it never forces a category or tier", () => {
|
|
143
|
+
const m = auditRule("ZZZ001", "report-only", "guidance", "t", "r", {
|
|
144
|
+
lineage: [{ tool: Object.keys(PRIOR_ART)[0] as never, url: "https://example.invalid/x", relation: "overlaps" }],
|
|
145
|
+
});
|
|
146
|
+
expect(m.category).toBe("best-practice");
|
|
147
|
+
expect(m.tier).toBe("report-only");
|
|
148
|
+
expect(m.lineage?.length).toBe(1);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("every registered prior-art tool is credited by at least one rule", async () => {
|
|
152
|
+
const used = new Set<string>();
|
|
153
|
+
for (const m of Object.values(await aggregate())) for (const l of m.lineage ?? []) used.add(l.tool);
|
|
154
|
+
const idle = Object.keys(PRIOR_ART).filter((k) => !used.has(k)).sort();
|
|
155
|
+
expect(idle, "PRIOR_ART entries no rule credits").toEqual([]);
|
|
156
|
+
});
|
|
157
|
+
|
|
126
158
|
test("flagship security rules carry an authority citation", async () => {
|
|
127
159
|
const catalog = await aggregate();
|
|
128
160
|
const flagship = ["GHA017", "GHA021", "GHA029", "GHA033", "GHA034", "GHA036", "GHA037", "WGL016", "WGL029"];
|
|
@@ -134,13 +166,34 @@ describe("RULE_CATALOG (aggregated: core static + lexicon-contributed, #687)", (
|
|
|
134
166
|
}
|
|
135
167
|
});
|
|
136
168
|
|
|
169
|
+
test("a deprecated id must still resolve (chant #2113): the inverse of the every-check-has-metadata test above", async () => {
|
|
170
|
+
// A retired check ships no runtime PostSynthCheck any more, so it is
|
|
171
|
+
// absent from `realCheckIds()`/`shippedRuleIds()` — that's what makes it
|
|
172
|
+
// "deprecated" rather than merely renamed. The catalog entry, and its
|
|
173
|
+
// resolution, must survive that removal.
|
|
174
|
+
const catalog = {
|
|
175
|
+
...RULE_CATALOG,
|
|
176
|
+
ZZZ900: auditRule("ZZZ900", "report-only", "guidance", "Retired check", "No longer applicable.", {
|
|
177
|
+
category: "best-practice",
|
|
178
|
+
}),
|
|
179
|
+
};
|
|
180
|
+
catalog.ZZZ900.deprecated = "superseded by ZZZ901";
|
|
181
|
+
expect(catalog.ZZZ900).toBeDefined();
|
|
182
|
+
expect(catalog.ZZZ900.deprecated).toBe("superseded by ZZZ901");
|
|
183
|
+
expect(canonicalRuleId("ZZZ900", catalog)).toBe("ZZZ900");
|
|
184
|
+
});
|
|
185
|
+
|
|
137
186
|
test("deterministic fixes are limited to the safe mechanical set", async () => {
|
|
138
187
|
const deterministic = Object.values(await aggregate())
|
|
139
188
|
.filter((m) => m.fixKind === "deterministic")
|
|
140
189
|
.map((m) => m.id)
|
|
141
190
|
.sort();
|
|
191
|
+
// TF016 and TF019 (#2110) are the first report-only rules in the set: both
|
|
192
|
+
// unquote or delete exactly the flagged line, which is as mechanical as
|
|
193
|
+
// pinning an action, and `report-model.ts` renders them as quick wins for
|
|
194
|
+
// that reason. Every entry here needs a case in `proof.ts`'s `proveFix`.
|
|
142
195
|
expect(deterministic).toEqual(
|
|
143
|
-
["GHA017", "GHA021", "GHA029", "GHA030", "GHA033", "WGL031"].sort(),
|
|
196
|
+
["GHA017", "GHA021", "GHA029", "GHA030", "GHA033", "TF016", "TF019", "WGL031"].sort(),
|
|
144
197
|
);
|
|
145
198
|
});
|
|
146
199
|
});
|
|
@@ -164,6 +217,29 @@ describe("auditRule (lexicon-facing catalog constructor, #687)", () => {
|
|
|
164
217
|
});
|
|
165
218
|
});
|
|
166
219
|
|
|
220
|
+
describe("canonicalRuleId (chant #2113, alias resolution)", () => {
|
|
221
|
+
const catalog: Record<string, ReturnType<typeof auditRule>> = {
|
|
222
|
+
NEW001: { ...auditRule("NEW001", "report-only", "guidance", "T", "r"), aliases: ["OLD001", "LEGACY_OLD"] },
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
test("a canonical id passes through unchanged", () => {
|
|
226
|
+
expect(canonicalRuleId("NEW001", catalog)).toBe("NEW001");
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("an alias resolves to the canonical id", () => {
|
|
230
|
+
expect(canonicalRuleId("OLD001", catalog)).toBe("NEW001");
|
|
231
|
+
expect(canonicalRuleId("LEGACY_OLD", catalog)).toBe("NEW001");
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("an id that is neither a key nor an alias passes through unchanged", () => {
|
|
235
|
+
expect(canonicalRuleId("NOBODY_KNOWS_THIS", catalog)).toBe("NOBODY_KNOWS_THIS");
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("defaults to the static core RULE_CATALOG when no catalog is given", () => {
|
|
239
|
+
expect(canonicalRuleId("SEC001")).toBe("SEC001");
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
167
243
|
describe("resolveAuditCatalog (#687 aggregation seam)", () => {
|
|
168
244
|
test("with no lexicons, returns a copy of the static core catalog", async () => {
|
|
169
245
|
const resolved = await resolveAuditCatalog([]);
|
package/src/audit/catalog.ts
CHANGED
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
* test asserts the aggregate stays in sync with the lexicons' post-synth checks.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
import type { Lineage } from "./prior-art";
|
|
21
|
+
import { applyLineage as attachLineage } from "./prior-art";
|
|
22
|
+
import { coreAuditLineage } from "./lineage";
|
|
23
|
+
export { PRIOR_ART, applyLineage, lineageLabel, type Lineage, type LineageRelation, type PriorArtEntry, type PriorArtTool } from "./prior-art";
|
|
24
|
+
|
|
20
25
|
export type Tier = "merge-worthy" | "report-only";
|
|
21
26
|
|
|
22
27
|
/** deterministic = safe auto-fix/diff; guidance = report text only (needs judgment). */
|
|
@@ -48,14 +53,42 @@ export interface RuleMeta {
|
|
|
48
53
|
title: string;
|
|
49
54
|
/** External backing so a finding isn't just chant's opinion. */
|
|
50
55
|
authority?: Authority[];
|
|
56
|
+
/**
|
|
57
|
+
* Prior art: the open-source tools whose rules check the same condition
|
|
58
|
+
* (./prior-art.ts). Credit, not authority — never affects tier or category.
|
|
59
|
+
*/
|
|
60
|
+
lineage?: Lineage[];
|
|
51
61
|
/** One-line fix guidance (always present). */
|
|
52
62
|
remediation: string;
|
|
53
63
|
/**
|
|
54
64
|
* False if the check reads the chant model (`ctx.entities`) rather than the
|
|
55
65
|
* emitted YAML (`ctx.outputs`) — such a check won't fire on audited YAML.
|
|
56
|
-
*
|
|
66
|
+
* Most post-synth checks are output-based, but cpln, fly, fountain, k3s
|
|
67
|
+
* and render all ship model-based checks with this false.
|
|
57
68
|
*/
|
|
58
69
|
yamlBased: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Retired ids this rule used to ship under (chant #2113). A rule id is
|
|
72
|
+
* public the day it ships. trivy's checks carry `id`, `long_id` and
|
|
73
|
+
* `aliases` for exactly this reason, so a rename or a scheme migration
|
|
74
|
+
* doesn't strand every existing reference to the old id. An alias resolves
|
|
75
|
+
* to this entry wherever a rule id is looked up by a human or a config.
|
|
76
|
+
* `canonicalRuleId()` (below) is the shared resolver; a `lint.rules` key
|
|
77
|
+
* and #2111's HCL suppression both go through it (or will, since #2111
|
|
78
|
+
* lands separately and is not touched by this change).
|
|
79
|
+
*/
|
|
80
|
+
aliases?: string[];
|
|
81
|
+
/**
|
|
82
|
+
* Set when this rule is retired: `true`, or a string explaining what
|
|
83
|
+
* replaced it / why it no longer fires. A deprecated rule stays in the
|
|
84
|
+
* catalog rather than being deleted. trivy's `deprecated: true` field lets
|
|
85
|
+
* a ruleset retire a check without breaking every existing suppression or
|
|
86
|
+
* dashboard that still names it, so its id keeps resolving (via `aliases`
|
|
87
|
+
* on whichever rule superseded it, when there is one) and the rules page
|
|
88
|
+
* still lists it, greyed out, instead of the id going dark with no
|
|
89
|
+
* explanation.
|
|
90
|
+
*/
|
|
91
|
+
deprecated?: boolean | string;
|
|
59
92
|
}
|
|
60
93
|
|
|
61
94
|
// ── Authority references ─────────────────────────────────────────────
|
|
@@ -147,6 +180,10 @@ export const CWE_HARDCODED_CREDS: Authority = {
|
|
|
147
180
|
name: "CWE-798 — Use of Hard-coded Credentials",
|
|
148
181
|
url: "https://cwe.mitre.org/data/definitions/798.html",
|
|
149
182
|
};
|
|
183
|
+
export const HASHICORP_STYLE_GITIGNORE: Authority = {
|
|
184
|
+
name: "HashiCorp Terraform style guide — .gitignore",
|
|
185
|
+
url: "https://developer.hashicorp.com/terraform/language/style#gitignore",
|
|
186
|
+
};
|
|
150
187
|
export const CWE_CLEARTEXT: Authority = {
|
|
151
188
|
name: "CWE-319 — Cleartext Transmission of Sensitive Information",
|
|
152
189
|
url: "https://cwe.mitre.org/data/definitions/319.html",
|
|
@@ -159,13 +196,14 @@ function meta(
|
|
|
159
196
|
title: string,
|
|
160
197
|
remediation: string,
|
|
161
198
|
authority?: Authority[],
|
|
199
|
+
lineage?: Lineage[],
|
|
162
200
|
): RuleMeta {
|
|
163
201
|
// An authority citation is the strongest security signal, so it wins. Otherwise
|
|
164
202
|
// the curated RULE_CATEGORY map (defined below, before RULE_CATALOG runs)
|
|
165
203
|
// decides correctness vs best-practice vs security-without-authority; the drift
|
|
166
204
|
// test guarantees every id is mapped, so the fallback is only a type belt.
|
|
167
205
|
const category: Category = authority && authority.length > 0 ? "security" : RULE_CATEGORY[id] ?? "best-practice";
|
|
168
|
-
return { id, tier, fixKind, category, title, remediation, authority, yamlBased: true };
|
|
206
|
+
return { id, tier, fixKind, category, title, remediation, authority, ...(lineage?.length ? { lineage } : {}), yamlBased: true };
|
|
169
207
|
}
|
|
170
208
|
|
|
171
209
|
const M = "merge-worthy" as const;
|
|
@@ -221,6 +259,10 @@ export const RULE_CATEGORY: Record<string, Category> = {
|
|
|
221
259
|
NGX005: "security",
|
|
222
260
|
NGX006: "best-practice",
|
|
223
261
|
NGX007: "best-practice",
|
|
262
|
+
// TF023 — Terraform state committed to the repository. Core-owned for the
|
|
263
|
+
// same reason SEC/WRG/NGX are: it reads the discovered file list, not a
|
|
264
|
+
// parsed root module. See ./terraform-state.ts.
|
|
265
|
+
TF023: "security",
|
|
224
266
|
// AGT — agent configuration (`chant audit --agents`). Core-owned like COR/EXT:
|
|
225
267
|
// these run against the machine's own agent config, not against any one
|
|
226
268
|
// lexicon's emitted output, so no lexicon ships them.
|
|
@@ -282,6 +324,21 @@ export const RULE_CATALOG: Record<string, RuleMeta> = {
|
|
|
282
324
|
NGX006: meta("NGX006", R, G, "Server version disclosure", "Add `server_tokens off;` in the http block so nginx stops advertising its exact version in the Server header and error pages."),
|
|
283
325
|
NGX007: meta("NGX007", R, G, "Access logging disabled at server scope", "Re-enable access_log at http/server scope (silencing a single noisy location is fine) so requests are recorded for incident investigation."),
|
|
284
326
|
|
|
327
|
+
// TF023 (#2110) — Terraform state, or the `.terraform/` working directory,
|
|
328
|
+
// committed to the repository. The one TF rule core owns: every other TF id
|
|
329
|
+
// is a post-synth check over a parsed root module (the terraform lexicon's
|
|
330
|
+
// `auditCatalog()`), while this one reads the discovered file list and never
|
|
331
|
+
// runs during `chant build`. Same shape as SEC/WRG/NGX above, and the same
|
|
332
|
+
// reason it lives here: `terraform-state.ts` needs no lexicon installed.
|
|
333
|
+
TF023: meta(
|
|
334
|
+
"TF023",
|
|
335
|
+
M,
|
|
336
|
+
G,
|
|
337
|
+
"Terraform state committed to the repository",
|
|
338
|
+
"Delete the state file (or `.terraform/`) from version control, add it to .gitignore, move the state to a remote backend, and rotate every credential the file held.",
|
|
339
|
+
[HASHICORP_STYLE_GITIGNORE],
|
|
340
|
+
),
|
|
341
|
+
|
|
285
342
|
// ── Agent configuration (`chant audit --agents`) ──────────────────
|
|
286
343
|
AGT001: agentMeta(
|
|
287
344
|
"AGT001",
|
|
@@ -337,6 +394,9 @@ export const RULE_CATALOG: Record<string, RuleMeta> = {
|
|
|
337
394
|
),
|
|
338
395
|
};
|
|
339
396
|
|
|
397
|
+
// Prior art credits for the core static rules (SEC/WRG/NGX/AGT/EXT) — ./lineage.ts.
|
|
398
|
+
attachLineage(RULE_CATALOG, coreAuditLineage);
|
|
399
|
+
|
|
340
400
|
/**
|
|
341
401
|
* Build one catalog entry — the lexicon-facing constructor for
|
|
342
402
|
* `LexiconPlugin.auditCatalog()` (#687), so a lexicon can declare its own rules'
|
|
@@ -351,10 +411,20 @@ export function auditRule(
|
|
|
351
411
|
fixKind: FixKind,
|
|
352
412
|
title: string,
|
|
353
413
|
remediation: string,
|
|
354
|
-
opts?: { authority?: Authority[]; category?: Category },
|
|
414
|
+
opts?: { authority?: Authority[]; category?: Category; lineage?: Lineage[] },
|
|
355
415
|
): RuleMeta {
|
|
356
416
|
const category: Category = opts?.authority && opts.authority.length > 0 ? "security" : (opts?.category ?? "best-practice");
|
|
357
|
-
return {
|
|
417
|
+
return {
|
|
418
|
+
id,
|
|
419
|
+
tier,
|
|
420
|
+
fixKind,
|
|
421
|
+
category,
|
|
422
|
+
title,
|
|
423
|
+
remediation,
|
|
424
|
+
authority: opts?.authority,
|
|
425
|
+
...(opts?.lineage?.length ? { lineage: opts.lineage } : {}),
|
|
426
|
+
yamlBased: true,
|
|
427
|
+
};
|
|
358
428
|
}
|
|
359
429
|
|
|
360
430
|
/**
|
|
@@ -374,15 +444,18 @@ export async function resolveAuditCatalog(lexicons: string[]): Promise<Record<st
|
|
|
374
444
|
lexicons.includes("forgejo") && !lexicons.includes("github") ? [...lexicons, "github"] : lexicons;
|
|
375
445
|
if (needed.length === 0) return catalog;
|
|
376
446
|
// Lazy import so importing this module doesn't pull in the plugin/config graph
|
|
377
|
-
// (matches ./core.ts's `load` — see #408).
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
447
|
+
// (matches ./core.ts's `load` — see #408). Loaded one at a time (rather than
|
|
448
|
+
// the bulk `loadPlugins`, which aborts on the first failure) so one
|
|
449
|
+
// not-yet-installed lexicon (e.g. terraform, #2085) doesn't drop every other
|
|
450
|
+
// lexicon's contributed catalog back to the static core map.
|
|
451
|
+
const { loadPlugin } = await import("../cli/plugins");
|
|
452
|
+
for (const name of needed) {
|
|
453
|
+
let plugin: { auditCatalog?(): Record<string, RuleMeta> };
|
|
454
|
+
try {
|
|
455
|
+
plugin = await loadPlugin(name);
|
|
456
|
+
} catch {
|
|
457
|
+
continue; // a lexicon package isn't installed, its catalog just doesn't contribute.
|
|
458
|
+
}
|
|
386
459
|
const contributed = plugin?.auditCatalog?.();
|
|
387
460
|
if (contributed) Object.assign(catalog, contributed);
|
|
388
461
|
}
|
|
@@ -394,6 +467,31 @@ export function ruleMeta(id: string): RuleMeta | undefined {
|
|
|
394
467
|
return RULE_CATALOG[id];
|
|
395
468
|
}
|
|
396
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Resolve a rule id to its canonical form against `catalog` (chant #2113). An
|
|
472
|
+
* id that is itself a key of `catalog` passes through unchanged (this covers
|
|
473
|
+
* the common case without scanning every entry's `aliases`); otherwise every
|
|
474
|
+
* entry's `aliases` is searched for a match, and the id it belongs to is
|
|
475
|
+
* returned. An id neither a key nor anyone's alias passes through unchanged
|
|
476
|
+
* too, since this is a best-effort resolver, not a validator, so an unknown
|
|
477
|
+
* id is a caller's problem, not this function's.
|
|
478
|
+
*
|
|
479
|
+
* Exported so a `lint.rules`/preset key that names an alias resolves to the
|
|
480
|
+
* rule that actually runs (see `resolveConfiguredSeverity`,
|
|
481
|
+
* `../lint/config.ts`), and so #2111's HCL suppression code can key an
|
|
482
|
+
* inline `# chant-ignore <id>` off the same resolution once it lands — that
|
|
483
|
+
* suppression mechanism does not exist yet in this worktree (#2111 is a
|
|
484
|
+
* separate, parallel issue), so this function is exported now for it to call
|
|
485
|
+
* later rather than being wired into it here.
|
|
486
|
+
*/
|
|
487
|
+
export function canonicalRuleId(id: string, catalog: Record<string, RuleMeta> = RULE_CATALOG): string {
|
|
488
|
+
if (id in catalog) return id;
|
|
489
|
+
for (const [canonicalId, meta] of Object.entries(catalog)) {
|
|
490
|
+
if (meta.aliases?.includes(id)) return canonicalId;
|
|
491
|
+
}
|
|
492
|
+
return id;
|
|
493
|
+
}
|
|
494
|
+
|
|
397
495
|
/** Docs path for the audit rules reference (one anchor per rule id). */
|
|
398
496
|
export const RULES_DOC_PATH = "/chant/lint-rules/audit-rules/";
|
|
399
497
|
|
package/src/audit/core.test.ts
CHANGED
|
@@ -208,6 +208,32 @@ describe("auditFiles", () => {
|
|
|
208
208
|
expect(findings.map((f) => `${f.file}:${f.entity}`).sort()).toEqual(["a.yaml:one", "b.yaml:two"]);
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
+
test("parse-to-graph with an async entities parser: auditFiles awaits it into ctx.entities (#2082)", async () => {
|
|
212
|
+
const entityCheck: PostSynthCheck = {
|
|
213
|
+
id: "ENT003",
|
|
214
|
+
description: "reads ctx.entities, not ctx.outputs",
|
|
215
|
+
check: (ctx) =>
|
|
216
|
+
[...ctx.entities.keys()].map((name) => ({ checkId: "ENT003", severity: "warning" as const, message: `saw ${name}`, entity: name })),
|
|
217
|
+
};
|
|
218
|
+
const findings = await auditFiles(
|
|
219
|
+
[
|
|
220
|
+
{ path: "a.tf", content: "one", lexicon: "terraform" },
|
|
221
|
+
{ path: "b.tf", content: "two", lexicon: "terraform" },
|
|
222
|
+
],
|
|
223
|
+
{
|
|
224
|
+
checksProvider: async () => [entityCheck],
|
|
225
|
+
// The lexicon's parser is inherently async (a lazy-loaded wasm HCL
|
|
226
|
+
// parser, per #1567's terraform follow-up). The returned Promise
|
|
227
|
+
// must be awaited before the check sees ctx.entities.
|
|
228
|
+
entitiesProvider: async () => async (content) => {
|
|
229
|
+
await Promise.resolve();
|
|
230
|
+
return new Map([[content, { entityType: "T::X" } as never]]);
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
);
|
|
234
|
+
expect(findings.map((f) => `${f.file}:${f.entity}`).sort()).toEqual(["a.tf:one", "b.tf:two"]);
|
|
235
|
+
});
|
|
236
|
+
|
|
211
237
|
test("parse-to-graph merge: the same entity name in two files keeps both — a duplicate check sees the collision cross-file", async () => {
|
|
212
238
|
const dupCheck: PostSynthCheck = {
|
|
213
239
|
id: "DUP001",
|
package/src/audit/core.ts
CHANGED
|
@@ -22,9 +22,10 @@
|
|
|
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
|
-
import type { LexiconPlugin } from "../lexicon";
|
|
28
|
+
import type { AuditEntitiesInput, LexiconPlugin } from "../lexicon";
|
|
28
29
|
import type { Declarable } from "../declarable";
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -60,6 +61,16 @@ export interface AuditInput {
|
|
|
60
61
|
* — checks that read `output.files` (helm, docker) see the whole bundle.
|
|
61
62
|
*/
|
|
62
63
|
files?: Record<string, string>;
|
|
64
|
+
/**
|
|
65
|
+
* Absolute directory this input's files were read from, set by discovery on
|
|
66
|
+
* a local walk for the inputs whose `path` names a directory (#2217). It
|
|
67
|
+
* reaches a lexicon's `auditEntities` as `AuditEntitiesInput.dir`, which is
|
|
68
|
+
* what lets terraform follow a root module's local `module` calls. Undefined
|
|
69
|
+
* for a remote tree fetch and for a single-file input.
|
|
70
|
+
*/
|
|
71
|
+
dir?: string;
|
|
72
|
+
/** Absolute path of the audited root, when discovery walked one (#2217). The boundary `dir` may read within. */
|
|
73
|
+
baseDir?: string;
|
|
63
74
|
}
|
|
64
75
|
|
|
65
76
|
/** A finding produced by a post-synth check against an audited file. */
|
|
@@ -82,6 +93,8 @@ export interface AuditFinding {
|
|
|
82
93
|
* itself.
|
|
83
94
|
*/
|
|
84
95
|
fingerprint?: string;
|
|
96
|
+
/** Carried verbatim from the producing `PostSynthDiagnostic.missing` (chant #2113) — see its doc comment. */
|
|
97
|
+
missing?: { kind: string; scope: string };
|
|
85
98
|
}
|
|
86
99
|
|
|
87
100
|
/**
|
|
@@ -94,8 +107,18 @@ export type ChecksProvider = (lexicon: AuditLexicon) => Promise<PostSynthCheck[]
|
|
|
94
107
|
* Parse one classified file's content into the lexicon's entity graph — the
|
|
95
108
|
* parse-to-graph half of the audit (#1567). The returned map is what
|
|
96
109
|
* `ctx.entities` holds during a build, so entity-reading checks run unchanged.
|
|
110
|
+
* May return a `Promise` for a lexicon whose parser is inherently async (e.g.
|
|
111
|
+
* terraform's HCL parser); `auditLexicon`'s `entitiesFor` awaits it.
|
|
112
|
+
*
|
|
113
|
+
* The second argument carries the input's discovery facts (its path, and the
|
|
114
|
+
* directory it was read from on a local walk), so a lexicon whose unit of
|
|
115
|
+
* parsing is a directory can name its scope after that directory and read the
|
|
116
|
+
* directories it references (#2217).
|
|
97
117
|
*/
|
|
98
|
-
export type EntitiesParser = (
|
|
118
|
+
export type EntitiesParser = (
|
|
119
|
+
content: string,
|
|
120
|
+
input?: AuditEntitiesInput,
|
|
121
|
+
) => Map<string, Declarable> | Promise<Map<string, Declarable>>;
|
|
99
122
|
|
|
100
123
|
/**
|
|
101
124
|
* Resolve the entities parser for a lexicon (its plugin's `auditEntities`,
|
|
@@ -179,13 +202,25 @@ async function defaultEntitiesProvider(lexicon: AuditLexicon): Promise<EntitiesP
|
|
|
179
202
|
return parser;
|
|
180
203
|
}
|
|
181
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Mutable out-param for the inline (`# chant-ignore*`) suppression count
|
|
207
|
+
* (chant #2111): `auditFiles`'s return type stays `AuditFinding[]` for every
|
|
208
|
+
* existing caller, so a caller that wants the count (`chant audit`'s CLI, for
|
|
209
|
+
* its summary line) passes this in and reads `.count` back afterward, rather
|
|
210
|
+
* than `auditFiles` growing a `{findings, suppressedCount}` shape that would
|
|
211
|
+
* touch every existing call site.
|
|
212
|
+
*/
|
|
213
|
+
export interface SuppressionStats {
|
|
214
|
+
count: number;
|
|
215
|
+
}
|
|
216
|
+
|
|
182
217
|
/**
|
|
183
218
|
* Audit a set of CI files and return all findings. Pure with respect to the
|
|
184
219
|
* filesystem and network — callers supply file contents.
|
|
185
220
|
*/
|
|
186
221
|
export async function auditFiles(
|
|
187
222
|
inputs: AuditInput[],
|
|
188
|
-
opts: { checksProvider?: ChecksProvider; entitiesProvider?: EntitiesProvider } = {},
|
|
223
|
+
opts: { checksProvider?: ChecksProvider; entitiesProvider?: EntitiesProvider; suppressionStats?: SuppressionStats } = {},
|
|
189
224
|
): Promise<AuditFinding[]> {
|
|
190
225
|
const provider = opts.checksProvider ?? defaultChecksProvider;
|
|
191
226
|
const entitiesProvider = opts.entitiesProvider ?? defaultEntitiesProvider;
|
|
@@ -203,7 +238,7 @@ export async function auditFiles(
|
|
|
203
238
|
const checks = await provider(lexicon);
|
|
204
239
|
if (checks.length === 0) continue;
|
|
205
240
|
const parseEntities = await entitiesProvider(lexicon);
|
|
206
|
-
findings.push(...auditLexicon(lexicon, files, checks, parseEntities));
|
|
241
|
+
findings.push(...(await auditLexicon(lexicon, files, checks, parseEntities, opts.suppressionStats)));
|
|
207
242
|
}
|
|
208
243
|
|
|
209
244
|
return findings;
|
|
@@ -224,14 +259,20 @@ function toOutput(file: AuditInput): string | SerializerResult {
|
|
|
224
259
|
return { primary: file.content, files: { [basename(file.path)]: file.content } };
|
|
225
260
|
}
|
|
226
261
|
|
|
262
|
+
interface CheckRunResult {
|
|
263
|
+
diagnostics: PostSynthDiagnostic[];
|
|
264
|
+
suppressed: PostSynthDiagnostic[];
|
|
265
|
+
meta: SuppressionMetaFinding[];
|
|
266
|
+
}
|
|
267
|
+
|
|
227
268
|
function runChecks(
|
|
228
269
|
checks: PostSynthCheck[],
|
|
229
270
|
outputs: Map<string, string | SerializerResult>,
|
|
230
271
|
entities: Map<string, Declarable> = new Map(),
|
|
231
|
-
):
|
|
272
|
+
): CheckRunResult {
|
|
232
273
|
const buildResult: PostSynthContext["buildResult"] = { outputs, entities, warnings: [], errors: [], sourceFileCount: outputs.size };
|
|
233
274
|
const ctx: PostSynthContext = { outputs, entities: buildResult.entities, buildResult };
|
|
234
|
-
const diags = [];
|
|
275
|
+
const diags: PostSynthDiagnostic[] = [];
|
|
235
276
|
for (const check of checks) {
|
|
236
277
|
try {
|
|
237
278
|
diags.push(...check.check(ctx));
|
|
@@ -239,7 +280,11 @@ function runChecks(
|
|
|
239
280
|
// A check that throws on unusual external YAML must not abort the audit.
|
|
240
281
|
}
|
|
241
282
|
}
|
|
242
|
-
|
|
283
|
+
// chant #2111: `rules` (for `ignorable: false`) is omitted, since `chant audit`
|
|
284
|
+
// has no project `lint.rules` to read here (see `applyInlineSuppressions`'s
|
|
285
|
+
// doc comment for why that's a deliberate scope line, not an oversight), so
|
|
286
|
+
// every rule id reads as ignorable, same as an unconfigured one everywhere.
|
|
287
|
+
return applyInlineSuppressions(diags, entities);
|
|
243
288
|
}
|
|
244
289
|
|
|
245
290
|
function diagKey(d: { checkId: string; entity?: string; message: string }): string {
|
|
@@ -281,40 +326,61 @@ function mergeEntities(maps: Array<Map<string, Declarable>>): Map<string, Declar
|
|
|
281
326
|
* the entity graph parsed from its files — per file for the per-file pass, all
|
|
282
327
|
* files merged for the all-files pass — so entity-reading checks fire and
|
|
283
328
|
* cross-file facts (an Agent's Environment declared elsewhere, a name declared
|
|
284
|
-
* twice across the apply directory) resolve in the all-files pass.
|
|
329
|
+
* twice across the apply directory) resolve in the all-files pass. The parser
|
|
330
|
+
* may be async (a lexicon's own choice, e.g. a wasm-backed HCL parser); this
|
|
331
|
+
* function awaits each file's entities before either pass runs.
|
|
285
332
|
*/
|
|
286
|
-
function auditLexicon(
|
|
287
|
-
|
|
333
|
+
async function auditLexicon(
|
|
334
|
+
lexicon: AuditLexicon,
|
|
335
|
+
files: AuditInput[],
|
|
336
|
+
checks: PostSynthCheck[],
|
|
337
|
+
parseEntities?: EntitiesParser,
|
|
338
|
+
suppressionStats?: SuppressionStats,
|
|
339
|
+
): Promise<AuditFinding[]> {
|
|
340
|
+
const entitiesFor = async (file: AuditInput): Promise<Map<string, Declarable>> => {
|
|
288
341
|
if (!parseEntities) return new Map();
|
|
289
342
|
try {
|
|
290
|
-
return parseEntities(file.content
|
|
343
|
+
return await parseEntities(file.content, {
|
|
344
|
+
path: file.path,
|
|
345
|
+
...(file.dir !== undefined ? { dir: file.dir } : {}),
|
|
346
|
+
...(file.baseDir !== undefined ? { baseDir: file.baseDir } : {}),
|
|
347
|
+
});
|
|
291
348
|
} catch {
|
|
292
349
|
// The audit contract is "runs against any repo" — unparseable content
|
|
293
350
|
// contributes no entities, never a crash.
|
|
294
351
|
return new Map();
|
|
295
352
|
}
|
|
296
353
|
};
|
|
297
|
-
const perEntities = new Map(files.map((f) => [f.path, entitiesFor(f)]));
|
|
354
|
+
const perEntities = new Map(await Promise.all(files.map(async (f) => [f.path, await entitiesFor(f)] as const)));
|
|
298
355
|
|
|
299
356
|
const perFindings: AuditFinding[] = [];
|
|
300
357
|
const perKeys = new Set<string>();
|
|
358
|
+
// chant #2111: meta findings (an expired/misplaced/denied suppression
|
|
359
|
+
// directive) are collected from the per-file pass only: the all-files pass
|
|
360
|
+
// below sees the same entities merged, so it would report the identical
|
|
361
|
+
// directive again. Counted the same way, once, here too.
|
|
362
|
+
const metaFindings: AuditFinding[] = [];
|
|
301
363
|
for (const file of files) {
|
|
302
|
-
const diags = runChecks(checks, new Map([[file.path, toOutput(file)]]), perEntities.get(file.path));
|
|
364
|
+
const { diagnostics: diags, suppressed, meta } = runChecks(checks, new Map([[file.path, toOutput(file)]]), perEntities.get(file.path));
|
|
365
|
+
if (suppressionStats) suppressionStats.count += suppressed.length;
|
|
366
|
+
for (const m of meta) {
|
|
367
|
+
metaFindings.push({ checkId: m.checkId, severity: m.severity, message: m.message, file: m.file, lexicon, line: m.line });
|
|
368
|
+
}
|
|
303
369
|
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 });
|
|
370
|
+
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
371
|
perKeys.add(diagKey(d));
|
|
306
372
|
}
|
|
307
373
|
}
|
|
308
374
|
|
|
309
375
|
const allOutputs = new Map<string, string | SerializerResult>(files.map((f) => [f.path, toOutput(f)]));
|
|
310
|
-
const allDiags = runChecks(checks, allOutputs, mergeEntities([...perEntities.values()]));
|
|
376
|
+
const { diagnostics: allDiags } = runChecks(checks, allOutputs, mergeEntities([...perEntities.values()]));
|
|
311
377
|
const allKeys = new Set(allDiags.map(diagKey));
|
|
312
378
|
|
|
313
379
|
const out: AuditFinding[] = perFindings.filter((f) => allKeys.has(diagKey(f)));
|
|
314
380
|
for (const d of allDiags) {
|
|
315
381
|
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 });
|
|
382
|
+
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
383
|
}
|
|
318
384
|
}
|
|
319
|
-
return out;
|
|
385
|
+
return [...out, ...metaFindings];
|
|
320
386
|
}
|
|
@@ -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)", () => {
|