@intentius/chant 0.56.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/catalog.d.ts +51 -1
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/core.d.ts +20 -1
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +14 -8
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/lineage.d.ts +10 -0
- package/dist/audit/lineage.d.ts.map +1 -0
- package/dist/audit/prior-art.d.ts +385 -0
- package/dist/audit/prior-art.d.ts.map +1 -0
- package/dist/audit/proof.d.ts +16 -0
- package/dist/audit/proof.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +8 -1
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/report.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts +3 -0
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/audit/secrets.d.ts +15 -0
- package/dist/audit/secrets.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +46 -0
- package/dist/audit/terraform-state.d.ts.map +1 -0
- package/dist/build.d.ts +17 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/claimed-fields.d.ts +161 -0
- package/dist/claimed-fields.d.ts.map +1 -0
- package/dist/cli/build-params-cli.d.ts +2 -2
- package/dist/cli/command-group.d.ts +14 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +17 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +2 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +82 -16
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +49 -42
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts +28 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts +16 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/resources/context.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +26 -9
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
- package/dist/components/auto-release.d.ts +10 -20
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/capability.d.ts +4 -4
- package/dist/components/cli-support.d.ts +20 -21
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +3 -3
- package/dist/components/component.d.ts.map +1 -1
- package/dist/components/driver-output.d.ts.map +1 -1
- package/dist/components/driver.d.ts +66 -36
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/manifest-persistence.d.ts +1 -9
- package/dist/components/manifest-persistence.d.ts.map +1 -1
- package/dist/components/run-progress.d.ts +6 -6
- package/dist/components/run-progress.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts +9 -15
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/components/verbs/verify.d.ts +2 -2
- package/dist/deep-observation.d.ts +17 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/discovery/files.d.ts +4 -4
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +33 -0
- package/dist/discovery/param-deps.d.ts.map +1 -1
- package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
- package/dist/discovery/sandbox/config-wire.d.ts +2 -2
- package/dist/fold/foldable-helpers.d.ts +5 -1
- package/dist/fold/foldable-helpers.d.ts.map +1 -1
- package/dist/fold-provenance.d.ts +236 -0
- package/dist/fold-provenance.d.ts.map +1 -0
- package/dist/graph-ops.d.ts +1 -1
- package/dist/held-elsewhere.d.ts +85 -0
- package/dist/held-elsewhere.d.ts.map +1 -0
- package/dist/import/ir-utils.d.ts +1 -1
- package/dist/import/ir-utils.d.ts.map +1 -1
- package/dist/import/parser.d.ts +33 -0
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/kubectl-context.d.ts +3 -3
- package/dist/lexicon-config.d.ts +4 -4
- package/dist/lexicon-plugin-helpers.d.ts +1 -1
- package/dist/lexicon.d.ts +104 -14
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/change-set.d.ts +13 -0
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/deep-diff.d.ts +133 -6
- package/dist/lifecycle/deep-diff.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +71 -3
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +2 -2
- package/dist/lifecycle/run-ledger.d.ts +53 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -0
- package/dist/lifecycle/symptoms.d.ts +3 -3
- package/dist/lifecycle/symptoms.d.ts.map +1 -1
- package/dist/lint/config.d.ts +81 -2
- package/dist/lint/config.d.ts.map +1 -1
- package/dist/lint/post-synth.d.ts +55 -2
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
- package/dist/lint/rules/comp/index.d.ts +1 -1
- package/dist/lint/rules/comp/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.d.ts +16 -0
- package/dist/lint/rules/op/index.d.ts.map +1 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
- package/dist/lint/rules/op/support.d.ts +12 -0
- package/dist/lint/rules/op/support.d.ts.map +1 -0
- package/dist/lint/suppressions.d.ts +108 -0
- package/dist/lint/suppressions.d.ts.map +1 -0
- package/dist/live-endpoint.d.ts +1 -1
- package/dist/op/activities/activity-contracts.d.ts +54 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -0
- package/dist/op/activities/apply.d.ts +262 -0
- package/dist/op/activities/apply.d.ts.map +1 -0
- package/dist/op/activities/build.d.ts +13 -0
- package/dist/op/activities/build.d.ts.map +1 -0
- package/dist/op/activities/converge.d.ts +110 -0
- package/dist/op/activities/converge.d.ts.map +1 -0
- package/dist/op/activities/env-teardown.d.ts +76 -0
- package/dist/op/activities/env-teardown.d.ts.map +1 -0
- package/dist/op/activities/guard-validate.d.ts +64 -0
- package/dist/op/activities/guard-validate.d.ts.map +1 -0
- package/dist/op/activities/http-check.d.ts +34 -0
- package/dist/op/activities/http-check.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +31 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
- package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
- package/dist/op/activities/lifecycle.d.ts +42 -0
- package/dist/op/activities/lifecycle.d.ts.map +1 -0
- package/dist/op/activities/pipeline-audit.d.ts +84 -0
- package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
- package/dist/op/activities/policy.d.ts +17 -0
- package/dist/op/activities/policy.d.ts.map +1 -0
- package/dist/op/activities/reconcile.d.ts +89 -0
- package/dist/op/activities/reconcile.d.ts.map +1 -0
- package/dist/op/activities/shell.d.ts +13 -0
- package/dist/op/activities/shell.d.ts.map +1 -0
- package/dist/op/activities/teardown.d.ts +10 -0
- package/dist/op/activities/teardown.d.ts.map +1 -0
- package/dist/op/activities/util.d.ts +7 -0
- package/dist/op/activities/util.d.ts.map +1 -0
- package/dist/op/activities/wait.d.ts +28 -0
- package/dist/op/activities/wait.d.ts.map +1 -0
- package/dist/op/activities/workflow-audit.d.ts +92 -0
- package/dist/op/activities/workflow-audit.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts +76 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -0
- package/dist/op/activity-contract.d.ts +25 -18
- package/dist/op/activity-contract.d.ts.map +1 -1
- package/dist/op/activity-failure.d.ts +28 -0
- package/dist/op/activity-failure.d.ts.map +1 -0
- package/dist/op/activity-profiles.d.ts +118 -0
- package/dist/op/activity-profiles.d.ts.map +1 -0
- package/dist/op/activity-registry.d.ts +22 -38
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-runtime.d.ts +3 -14
- package/dist/op/activity-runtime.d.ts.map +1 -1
- package/dist/op/builders.d.ts +84 -29
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/composites/apply-op.d.ts +113 -0
- package/dist/op/composites/apply-op.d.ts.map +1 -0
- package/dist/op/composites/converge-op.d.ts +120 -0
- package/dist/op/composites/converge-op.d.ts.map +1 -0
- package/dist/op/composites/index.d.ts +26 -0
- package/dist/op/composites/index.d.ts.map +1 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
- package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
- package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
- package/dist/op/composites/reconcile-op.d.ts +57 -0
- package/dist/op/composites/reconcile-op.d.ts.map +1 -0
- package/dist/op/composites/watch-op.d.ts +60 -0
- package/dist/op/composites/watch-op.d.ts.map +1 -0
- package/dist/op/composites/workflow-audit-op.d.ts +57 -0
- package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
- package/dist/op/converge-rule.d.ts +12 -10
- package/dist/op/converge-rule.d.ts.map +1 -1
- package/dist/op/cron.d.ts +51 -0
- package/dist/op/cron.d.ts.map +1 -0
- package/dist/op/discover.d.ts +9 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/duration.d.ts +11 -0
- package/dist/op/duration.d.ts.map +1 -0
- package/dist/op/emulator-lifecycle.d.ts.map +1 -1
- package/dist/op/gate.d.ts +90 -0
- package/dist/op/gate.d.ts.map +1 -0
- package/dist/op/generate-pipeline.d.ts +9 -0
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/index.d.ts +23 -7
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +114 -28
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-output.d.ts +11 -1
- package/dist/op/local-output.d.ts.map +1 -1
- package/dist/op/op-ir.d.ts +184 -0
- package/dist/op/op-ir.d.ts.map +1 -0
- package/dist/op/op-verb-class.d.ts +10 -2
- package/dist/op/op-verb-class.d.ts.map +1 -1
- package/dist/op/operator.d.ts +30 -5
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/receipt-store.d.ts +1 -1
- package/dist/op/receipt-store.d.ts.map +1 -1
- package/dist/op/resource.d.ts +15 -1
- package/dist/op/resource.d.ts.map +1 -1
- package/dist/op/runtime.d.ts +200 -0
- package/dist/op/runtime.d.ts.map +1 -0
- package/dist/op/runtimes/local.d.ts +29 -0
- package/dist/op/runtimes/local.d.ts.map +1 -0
- package/dist/op/step-output-ref.d.ts +15 -13
- package/dist/op/step-output-ref.d.ts.map +1 -1
- package/dist/op/types.d.ts +79 -27
- package/dist/op/types.d.ts.map +1 -1
- package/dist/ownership.d.ts +1 -1
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -1
- package/dist/serializer-walker.d.ts.map +1 -1
- package/dist/stack-output.d.ts +5 -0
- package/dist/stack-output.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/audit/catalog.test.ts +79 -3
- package/src/audit/catalog.ts +111 -13
- package/src/audit/core.test.ts +26 -0
- package/src/audit/core.ts +61 -17
- package/src/audit/discover.test.ts +39 -0
- package/src/audit/discover.ts +91 -15
- package/src/audit/lineage.ts +100 -0
- package/src/audit/prior-art.ts +153 -0
- package/src/audit/proof.test.ts +67 -0
- package/src/audit/proof.ts +70 -0
- package/src/audit/report-html.ts +9 -4
- package/src/audit/report-model.ts +16 -2
- package/src/audit/report.test.ts +33 -0
- package/src/audit/report.ts +5 -3
- package/src/audit/rules-doc.test.ts +34 -2
- package/src/audit/rules-doc.ts +82 -9
- package/src/audit/secrets.ts +15 -3
- package/src/audit/terraform-state.test.ts +144 -0
- package/src/audit/terraform-state.ts +131 -0
- package/src/build.ts +31 -1
- package/src/claimed-fields.test.ts +145 -0
- package/src/claimed-fields.ts +208 -0
- package/src/cli/build-params-cli.ts +2 -2
- package/src/cli/command-group.ts +14 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
- package/src/cli/commands/audit.test.ts +189 -11
- package/src/cli/commands/audit.ts +73 -27
- package/src/cli/commands/build.test.ts +292 -9
- package/src/cli/commands/build.ts +156 -24
- package/src/cli/commands/check-lexicon-docs.ts +1 -1
- package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
- package/src/cli/commands/check-lexicon-examples.ts +42 -10
- package/src/cli/commands/import.ts +6 -0
- package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
- package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
- package/src/cli/commands/init-lexicon.test.ts +97 -2
- package/src/cli/commands/init-lexicon.ts +4 -2
- package/src/cli/commands/init.test.ts +35 -1
- package/src/cli/commands/init.ts +14 -3
- package/src/cli/commands/lint.test.ts +48 -0
- package/src/cli/commands/lint.ts +123 -0
- package/src/cli/commands/onboard.test.ts +75 -0
- package/src/cli/commands/onboard.ts +54 -23
- package/src/cli/handlers/build.test.ts +81 -1
- package/src/cli/handlers/build.ts +15 -1
- package/src/cli/handlers/graph.test.ts +3 -3
- package/src/cli/handlers/lifecycle.test.ts +195 -12
- package/src/cli/handlers/lifecycle.ts +123 -6
- package/src/cli/handlers/misc.ts +1 -1
- package/src/cli/handlers/operator.test.ts +183 -11
- package/src/cli/handlers/operator.ts +260 -55
- package/src/cli/handlers/run.test.ts +431 -1287
- package/src/cli/handlers/run.ts +365 -915
- package/src/cli/main.test.ts +23 -15
- package/src/cli/main.ts +88 -33
- package/src/cli/mcp/op-tools.ts +147 -88
- package/src/cli/mcp/resource-handlers.ts +10 -37
- package/src/cli/mcp/resources/context.ts +11 -12
- package/src/cli/mcp/server.test.ts +25 -27
- package/src/cli/mcp/server.ts +2 -2
- package/src/cli/registry.ts +26 -9
- package/src/codegen/docs.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.ts +1 -1
- package/src/components/README.md +3 -2
- package/src/components/auto-release.test.ts +4 -20
- package/src/components/auto-release.ts +17 -42
- package/src/components/capability.ts +4 -4
- package/src/components/cli-support.test.ts +10 -5
- package/src/components/cli-support.ts +52 -44
- package/src/components/component.schema.json +2 -2
- package/src/components/component.ts +3 -3
- package/src/components/driver-output.ts +14 -2
- package/src/components/driver.test.ts +92 -22
- package/src/components/driver.ts +193 -83
- package/src/components/index.ts +1 -6
- package/src/components/manifest-persistence.test.ts +2 -20
- package/src/components/manifest-persistence.ts +4 -24
- package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
- package/src/components/run-progress.ts +6 -6
- package/src/components/verbs/run-agent.ts +11 -17
- package/src/components/verbs/verify.ts +2 -2
- package/src/deep-observation.test.ts +75 -0
- package/src/deep-observation.ts +48 -0
- package/src/discovery/collect.test.ts +3 -3
- package/src/discovery/files.ts +4 -4
- package/src/discovery/fold-import.ts +105 -2
- package/src/discovery/param-deps.test.ts +112 -1
- package/src/discovery/param-deps.ts +193 -0
- package/src/discovery/sandbox/bundle.ts +15 -1
- package/src/discovery/sandbox/config-wire.test.ts +2 -2
- package/src/discovery/sandbox/config-wire.ts +2 -2
- package/src/fold/fold.test.ts +56 -0
- package/src/fold/foldable-helpers.ts +81 -1
- package/src/fold-provenance-exclusion.test.ts +186 -0
- package/src/fold-provenance.test.ts +495 -0
- package/src/fold-provenance.ts +441 -0
- package/src/graph-ops.test.ts +2 -2
- package/src/graph-ops.ts +1 -1
- package/src/held-elsewhere.test.ts +102 -0
- package/src/held-elsewhere.ts +114 -0
- package/src/import/ir-utils.ts +11 -1
- package/src/import/parser.ts +35 -0
- package/src/index.ts +3 -0
- package/src/kubectl-context.ts +3 -3
- package/src/lexicon-config.test.ts +5 -5
- package/src/lexicon-config.ts +4 -4
- package/src/lexicon-plugin-helpers.ts +1 -1
- package/src/lexicon.ts +120 -14
- package/src/lifecycle/change-set.test.ts +49 -0
- package/src/lifecycle/change-set.ts +67 -0
- package/src/lifecycle/converge-ledger.ts +1 -1
- package/src/lifecycle/deep-diff.test.ts +267 -14
- package/src/lifecycle/deep-diff.ts +217 -14
- package/src/lifecycle/deep-observe.test.ts +3 -1
- package/src/lifecycle/deep-observe.ts +16 -6
- package/src/lifecycle/gate-ledger.test.ts +74 -2
- package/src/lifecycle/gate-ledger.ts +143 -36
- package/src/lifecycle/index.ts +1 -0
- package/src/lifecycle/release-ledger.ts +2 -2
- package/src/lifecycle/run-ledger.test.ts +268 -0
- package/src/lifecycle/run-ledger.ts +201 -0
- package/src/lifecycle/symptoms.ts +3 -3
- package/src/lint/config.test.ts +82 -1
- package/src/lint/config.ts +112 -3
- package/src/lint/policy.test.ts +1 -1
- package/src/lint/post-synth.test.ts +31 -0
- package/src/lint/post-synth.ts +54 -1
- package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
- package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
- package/src/lint/rules/comp/comp.test.ts +1 -1
- package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +74 -0
- package/src/lint/rules/comp/index.ts +3 -3
- package/src/lint/rules/op/index.ts +23 -0
- package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
- package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
- package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
- package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
- package/src/lint/rules/op/support.ts +11 -0
- package/src/lint/suppressions.test.ts +163 -0
- package/src/lint/suppressions.ts +204 -0
- package/src/live-endpoint.ts +1 -1
- package/src/op/activities/activity-contracts.ts +66 -0
- package/src/op/activities/apply.test.ts +567 -0
- package/src/op/activities/apply.ts +662 -0
- package/src/op/activities/build.ts +26 -0
- package/src/op/activities/converge.test.ts +330 -0
- package/src/op/activities/converge.ts +449 -0
- package/src/op/activities/env-teardown.test.ts +187 -0
- package/src/op/activities/env-teardown.ts +171 -0
- package/src/op/activities/guard-validate.test.ts +139 -0
- package/src/op/activities/guard-validate.ts +223 -0
- package/src/op/activities/http-check.test.ts +48 -0
- package/src/op/activities/http-check.ts +55 -0
- package/src/op/activities/index.ts +119 -0
- package/src/op/activities/lexicon-upgrade.test.ts +492 -0
- package/src/op/activities/lexicon-upgrade.ts +673 -0
- package/src/op/activities/lifecycle.ts +85 -0
- package/src/op/activities/pipeline-audit.test.ts +67 -0
- package/src/op/activities/pipeline-audit.ts +201 -0
- package/src/op/activities/policy.test.ts +19 -0
- package/src/op/activities/policy.ts +37 -0
- package/src/op/activities/reconcile.test.ts +81 -0
- package/src/op/activities/reconcile.ts +187 -0
- package/src/op/activities/shell.ts +26 -0
- package/src/op/activities/teardown.ts +22 -0
- package/src/op/activities/util.ts +6 -0
- package/src/op/activities/wait.ts +69 -0
- package/src/op/activities/workflow-audit.test.ts +90 -0
- package/src/op/activities/workflow-audit.ts +203 -0
- package/src/op/activity-contract-registry.test.ts +98 -0
- package/src/op/activity-contract-registry.ts +114 -0
- package/src/op/activity-contract.test.ts +1 -1
- package/src/op/activity-contract.ts +37 -33
- package/src/op/activity-failure.ts +34 -0
- package/src/op/activity-profiles.test.ts +56 -0
- package/src/op/activity-profiles.ts +116 -0
- package/src/op/activity-registry.test.ts +37 -28
- package/src/op/activity-registry.ts +27 -56
- package/src/op/activity-runtime.ts +3 -47
- package/src/op/builders-exports.test.ts +0 -49
- package/src/op/builders-typed.test.ts +164 -0
- package/src/op/builders.ts +123 -39
- package/src/op/composites/apply-op.ts +208 -0
- package/src/op/composites/composites.test.ts +366 -0
- package/src/op/composites/converge-op.test.ts +168 -0
- package/src/op/composites/converge-op.ts +214 -0
- package/src/op/composites/index.ts +26 -0
- package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
- package/src/op/composites/lexicon-upgrade-op.ts +134 -0
- package/src/op/composites/pipeline-audit-op.test.ts +25 -0
- package/src/op/composites/pipeline-audit-op.ts +78 -0
- package/src/op/composites/reconcile-op.test.ts +51 -0
- package/src/op/composites/reconcile-op.ts +107 -0
- package/src/op/composites/watch-op.ts +108 -0
- package/src/op/composites/workflow-audit-op.test.ts +25 -0
- package/src/op/composites/workflow-audit-op.ts +88 -0
- package/src/op/converge-rule.ts +12 -10
- package/src/op/cron.test.ts +128 -0
- package/src/op/cron.ts +149 -0
- package/src/op/discover-root.test.ts +105 -1
- package/src/op/discover.test.ts +6 -2
- package/src/op/discover.ts +84 -16
- package/src/op/duration.ts +21 -0
- package/src/op/effect-step.test.ts +26 -11
- package/src/op/emulator-lifecycle.ts +1 -2
- package/src/op/gate.ts +155 -0
- package/src/op/generate-pipeline.test.ts +65 -3
- package/src/op/generate-pipeline.ts +19 -2
- package/src/op/index.ts +39 -6
- package/src/op/local-executor.test.ts +163 -34
- package/src/op/local-executor.ts +370 -102
- package/src/op/local-output.test.ts +95 -11
- package/src/op/local-output.ts +33 -4
- package/src/op/op-ir.test.ts +281 -0
- package/src/op/op-ir.ts +417 -0
- package/src/op/op-verb-class.ts +19 -5
- package/src/op/op.test.ts +26 -5
- package/src/op/operator.test.ts +175 -6
- package/src/op/operator.ts +63 -16
- package/src/op/receipt-store.ts +1 -1
- package/src/op/resource.ts +20 -2
- package/src/op/runtime.ts +203 -0
- package/src/op/runtimes/local.test.ts +181 -0
- package/src/op/runtimes/local.ts +212 -0
- package/src/op/step-output-ref.ts +15 -13
- package/src/op/types.ts +84 -24
- package/src/ownership.ts +1 -1
- package/src/provenance.test.ts +6 -0
- package/src/provenance.ts +21 -1
- package/src/serializer-walker.test.ts +42 -0
- package/src/serializer-walker.ts +21 -2
- package/src/stack-output.ts +6 -1
- 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
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prior art for chant's audit rules.
|
|
3
|
+
*
|
|
4
|
+
* Most of what `chant audit` checks has been checked before by a dedicated
|
|
5
|
+
* open-source tool: zizmor and poutine for GitHub Actions, hadolint for
|
|
6
|
+
* Dockerfiles, cfn-lint and cfn_nag for CloudFormation, kube-linter and
|
|
7
|
+
* kube-score for Kubernetes manifests, gitleaks for secrets, and so on. chant's
|
|
8
|
+
* rules were written independently against the emitted output, but the ideas
|
|
9
|
+
* are shared, and the people who wrote them down first deserve the credit.
|
|
10
|
+
*
|
|
11
|
+
* A rule declares its lineage as `Lineage[]` on its `RuleMeta` entry: which
|
|
12
|
+
* tool, which upstream rule, where it is documented, and how the two relate.
|
|
13
|
+
* `PRIOR_ART` is the registry those entries key into, so a tool is named,
|
|
14
|
+
* linked and licensed once. The audit rules reference renders both, the
|
|
15
|
+
* markdown and HTML reports credit the tool beside the finding, and the SARIF
|
|
16
|
+
* and JSON outputs carry the entries verbatim for consumers.
|
|
17
|
+
*
|
|
18
|
+
* Lineage is credit, not authority. `authority` (./catalog.ts) is a standard
|
|
19
|
+
* or vendor document that says a finding matters; lineage is a tool that
|
|
20
|
+
* checks the same condition. The two are independent: a rule may have either,
|
|
21
|
+
* both, or neither, and lineage never changes a rule's category or tier.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** How a chant rule relates to the upstream check it credits. */
|
|
25
|
+
export type LineageRelation =
|
|
26
|
+
/** Same condition, same scope. */
|
|
27
|
+
| "equivalent"
|
|
28
|
+
/** The core condition is shared; one side is narrower or broader, or a different dialect of the same control. */
|
|
29
|
+
| "overlaps"
|
|
30
|
+
/** chant checks a strict superset of the upstream check. */
|
|
31
|
+
| "extends";
|
|
32
|
+
|
|
33
|
+
/** One credit: an upstream tool's rule that checks what this chant rule checks. */
|
|
34
|
+
export interface Lineage {
|
|
35
|
+
/** A key of {@link PRIOR_ART}. */
|
|
36
|
+
tool: PriorArtTool;
|
|
37
|
+
/** The upstream rule id or check name, in the tool's own vocabulary (e.g. `template-injection`, `DL3007`, `CKV_AWS_18`). Omitted when the tool has no per-rule ids. */
|
|
38
|
+
rule?: string;
|
|
39
|
+
/** Where the upstream rule is documented. */
|
|
40
|
+
url: string;
|
|
41
|
+
relation: LineageRelation;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** A tool (or, for a few families, a vendor validator) that chant's rules credit. */
|
|
45
|
+
export interface PriorArtEntry {
|
|
46
|
+
name: string;
|
|
47
|
+
url: string;
|
|
48
|
+
/** SPDX identifier where confirmed, else "unverified" or "n/a" for documentation. */
|
|
49
|
+
license: string;
|
|
50
|
+
/** What kind of thing this is, so the rendered credit is honest about it. */
|
|
51
|
+
kind: "scanner" | "vendor-validator" | "specification";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The registry of prior art. Keys are the slugs `Lineage.tool` uses. Populated
|
|
56
|
+
* from the per-family research recorded in the PR that introduced lineage; the
|
|
57
|
+
* prior-art sweep (scripts/prior-art-sweep.ts) reads the same table.
|
|
58
|
+
*/
|
|
59
|
+
export const PRIOR_ART = {
|
|
60
|
+
"checkov": { name: "Checkov", url: "https://www.checkov.io/", license: "Apache-2.0", kind: "scanner" },
|
|
61
|
+
"kics": { name: "KICS", url: "https://docs.kics.io/", license: "Apache-2.0", kind: "scanner" },
|
|
62
|
+
"zizmor": { name: "zizmor", url: "https://docs.zizmor.sh/", license: "MIT", kind: "scanner" },
|
|
63
|
+
"cfn-nag": { name: "cfn_nag", url: "https://github.com/stelligent/cfn_nag", license: "MIT", kind: "scanner" },
|
|
64
|
+
"gitlab-docs": { name: "GitLab CI/CD YAML reference", url: "https://docs.gitlab.com/ci/yaml/", license: "CC-BY-SA-4.0", kind: "specification" },
|
|
65
|
+
"guard-rules-registry": { name: "AWS Guard Rules Registry", url: "https://github.com/aws-cloudformation/aws-guard-rules-registry", license: "Apache-2.0", kind: "scanner" },
|
|
66
|
+
"polaris": { name: "Fairwinds Polaris", url: "https://polaris.docs.fairwinds.com/", license: "Apache-2.0", kind: "scanner" },
|
|
67
|
+
"kube-linter": { name: "KubeLinter", url: "https://github.com/stackrox/kube-linter", license: "Apache-2.0", kind: "scanner" },
|
|
68
|
+
"kube-score": { name: "kube-score", url: "https://github.com/zegl/kube-score", license: "MIT", kind: "scanner" },
|
|
69
|
+
"psrule-azure": { name: "PSRule for Azure", url: "https://azure.github.io/PSRule.Rules.Azure/", license: "MIT", kind: "scanner" },
|
|
70
|
+
"gcp-policy-library": { name: "Google Cloud Policy Library", url: "https://github.com/GoogleCloudPlatform/policy-library", license: "Apache-2.0", kind: "scanner" },
|
|
71
|
+
"poutine": { name: "poutine", url: "https://boostsecurityio.github.io/poutine/", license: "Apache-2.0", kind: "scanner" },
|
|
72
|
+
"scorecard": { name: "OpenSSF Scorecard", url: "https://github.com/ossf/scorecard", license: "Apache-2.0", kind: "scanner" },
|
|
73
|
+
"cfn-lint": { name: "cfn-lint", url: "https://github.com/aws-cloudformation/cfn-lint", license: "MIT-0", kind: "scanner" },
|
|
74
|
+
"controlplane-docs": { name: "Control Plane reference documentation", url: "https://docs.controlplane.com", license: "n/a", kind: "specification" },
|
|
75
|
+
"gitleaks": { name: "gitleaks", url: "https://github.com/gitleaks/gitleaks", license: "MIT", kind: "scanner" },
|
|
76
|
+
"octoscan": { name: "octoscan", url: "https://github.com/synacktiv/octoscan", license: "GPL-3.0", kind: "scanner" },
|
|
77
|
+
"detect-secrets": { name: "detect-secrets", url: "https://github.com/Yelp/detect-secrets", license: "Apache-2.0", kind: "scanner" },
|
|
78
|
+
"actionlint": { name: "actionlint", url: "https://github.com/rhysd/actionlint", license: "MIT", kind: "scanner" },
|
|
79
|
+
"trufflehog": { name: "TruffleHog", url: "https://github.com/trufflesecurity/trufflehog", license: "AGPL-3.0", kind: "scanner" },
|
|
80
|
+
"gixy-ng": { name: "gixy-ng (maintained Gixy fork)", url: "https://github.com/dvershinin/gixy", license: "MPL-2.0", kind: "scanner" },
|
|
81
|
+
"kubesec": { name: "kubesec", url: "https://kubesec.io/", license: "Apache-2.0", kind: "scanner" },
|
|
82
|
+
"datree": { name: "Datree", url: "https://hub.datree.io/built-in-rules", license: "Apache-2.0", kind: "scanner" },
|
|
83
|
+
"helm-lint": { name: "helm lint", url: "https://helm.sh/docs/helm/helm_lint/", license: "Apache-2.0", kind: "vendor-validator" },
|
|
84
|
+
"cedar-validator": { name: "Cedar policy validator", url: "https://docs.cedarpolicy.com/policies/validation.html", license: "Apache-2.0", kind: "vendor-validator" },
|
|
85
|
+
"flux-docs": { name: "Flux Kustomization API reference", url: "https://fluxcd.io/flux/components/kustomize/kustomizations/", license: "n/a", kind: "specification" },
|
|
86
|
+
"hadolint": { name: "hadolint", url: "https://github.com/hadolint/hadolint", license: "GPL-3.0", kind: "scanner" },
|
|
87
|
+
"agent-audit": { name: "agent-audit", url: "https://github.com/piiiico/agent-audit", license: "MIT", kind: "scanner" },
|
|
88
|
+
"arm-ttk": { name: "ARM Template Toolkit (arm-ttk)", url: "https://github.com/Azure/arm-ttk", license: "MIT", kind: "scanner" },
|
|
89
|
+
"bicep-linter": { name: "Bicep linter", url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/linter", license: "MIT", kind: "scanner" },
|
|
90
|
+
"cedar-cli": { name: "Cedar CLI", url: "https://github.com/cedar-policy/cedar/tree/main/cedar-policy-cli", license: "Apache-2.0", kind: "vendor-validator" },
|
|
91
|
+
"dockle": { name: "Dockle", url: "https://github.com/goodwithtech/dockle", license: "Apache-2.0", kind: "scanner" },
|
|
92
|
+
"mcp-audit": { name: "mcp-audit (APIsec)", url: "https://github.com/apisec-inc/mcp-audit", license: "MIT", kind: "scanner" },
|
|
93
|
+
"render-docs": { name: "Render Blueprint YAML reference", url: "https://render.com/docs/blueprint-spec", license: "n/a", kind: "specification" },
|
|
94
|
+
"agent-scan": { name: "Snyk Agent Scan (formerly mcp-scan)", url: "https://github.com/invariantlabs-ai/mcp-scan", license: "Apache-2.0", kind: "scanner" },
|
|
95
|
+
"cedar-docs": { name: "Cedar policy language reference", url: "https://docs.cedarpolicy.com/policies/json-format.html", license: "n/a", kind: "specification" },
|
|
96
|
+
"cedar-policy-crate": { name: "cedar-policy crate (PolicySet loader)", url: "https://docs.rs/cedar-policy/latest/cedar_policy/", license: "Apache-2.0", kind: "vendor-validator" },
|
|
97
|
+
"chart-testing": { name: "chart-testing (ct)", url: "https://github.com/helm/chart-testing", license: "Apache-2.0", kind: "vendor-validator" },
|
|
98
|
+
"fly-docs": { name: "Fly Machines API reference", url: "https://fly.io/docs/machines/api/machines-resource/", license: "n/a", kind: "specification" },
|
|
99
|
+
"gixy": { name: "Gixy", url: "https://github.com/yandex/gixy", license: "MPL-2.0", kind: "scanner" },
|
|
100
|
+
"kube-bench": { name: "kube-bench", url: "https://github.com/aquasecurity/kube-bench", license: "Apache-2.0", kind: "scanner" },
|
|
101
|
+
"pluto": { name: "Pluto", url: "https://github.com/FairwindsOps/pluto", license: "Apache-2.0", kind: "scanner" },
|
|
102
|
+
"choudoufu": { name: "choudoufu", url: "https://github.com/INTENTIUS/choudoufu", license: "MPL-2.0", kind: "scanner" },
|
|
103
|
+
// Terraform lint family (#2107). Registry entries recorded in
|
|
104
|
+
// lexicons/terraform/src/lint/audit-lineage.ts's header comment; added here
|
|
105
|
+
// the first time a shipped rule cites them (#2109).
|
|
106
|
+
"tflint-ruleset-terraform": { name: "tflint-ruleset-terraform", url: "https://github.com/terraform-linters/tflint-ruleset-terraform", license: "MPL-2.0", kind: "scanner" },
|
|
107
|
+
"terraform-sentinel-policies": { name: "HashiCorp reference Sentinel policies", url: "https://github.com/hashicorp/terraform-sentinel-policies", license: "MPL-2.0", kind: "specification" },
|
|
108
|
+
"hashicorp-style-guide": { name: "HashiCorp Terraform style guide", url: "https://developer.hashicorp.com/terraform/language/style", license: "n/a", kind: "specification" },
|
|
109
|
+
"aws-terraform-prescriptive-guidance": { name: "AWS prescriptive guidance for Terraform", url: "https://docs.aws.amazon.com/prescriptive-guidance/latest/terraform-aws-provider-best-practices/", license: "n/a", kind: "specification" },
|
|
110
|
+
// A multi-page series; a credit cites the specific page and fragment, not
|
|
111
|
+
// this landing URL. First cited by TF014 and TF015 (#2112).
|
|
112
|
+
"gcp-terraform-best-practices": { name: "Google Cloud Terraform best-practices series", url: "https://docs.cloud.google.com/docs/terraform/best-practices/general-style-structure", license: "n/a", kind: "specification" },
|
|
113
|
+
"tflint-ruleset-redeploy": { name: "tflint-ruleset-redeploy", url: "https://github.com/RedeployAB/tflint-ruleset-redeploy", license: "0BSD", kind: "scanner" },
|
|
114
|
+
"tflint-ruleset-avm": { name: "tflint-ruleset-avm (Azure Verified Modules)", url: "https://github.com/Azure/tflint-ruleset-avm", license: "MIT", kind: "scanner" },
|
|
115
|
+
// tfsec is frozen rather than archived, and its docs site 404s above v0.61.x,
|
|
116
|
+
// so its credits cite pinned repository paths (a tag or `master`), never the
|
|
117
|
+
// docs site. The three `general/secrets` checks chant credits live only here:
|
|
118
|
+
// trivy dropped them when it absorbed tfsec.
|
|
119
|
+
"tfsec": { name: "tfsec", url: "https://github.com/aquasecurity/tfsec", license: "MIT", kind: "scanner" },
|
|
120
|
+
// Semgrep's engine is LGPL-2.1 but its rules are not: they ship under the
|
|
121
|
+
// proprietary Semgrep Rules License v1.0, which forbids redistribution. So
|
|
122
|
+
// this entry carries no SPDX id, and no semgrep rule text is ever copied into
|
|
123
|
+
// this repository — a credit by name and link is the whole of it (#2107).
|
|
124
|
+
"semgrep": { name: "Semgrep Registry rules", url: "https://github.com/semgrep/semgrep-rules", license: "Semgrep Rules License v1.0 (proprietary, not an SPDX id)", kind: "scanner" },
|
|
125
|
+
} as const satisfies Record<string, PriorArtEntry>;
|
|
126
|
+
|
|
127
|
+
export type PriorArtTool = keyof typeof PRIOR_ART;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Attach lineage to a catalog in place. A lexicon keeps its lineage in a
|
|
131
|
+
* sibling `audit-lineage.ts` (one `Record<ruleId, Lineage[]>`) and calls this
|
|
132
|
+
* once at the bottom of its `audit-catalog.ts`, so the prose metadata and the
|
|
133
|
+
* credits stay next to each other but the credits can be regenerated by the
|
|
134
|
+
* prior-art sweep without touching the rule text. Throws at module load if the
|
|
135
|
+
* lineage names a rule the catalog does not have: a stale id is a bug, not a
|
|
136
|
+
* silent no-op.
|
|
137
|
+
*/
|
|
138
|
+
export function applyLineage<T extends { id: string; lineage?: Lineage[] }>(
|
|
139
|
+
catalog: Record<string, T>,
|
|
140
|
+
lineage: Record<string, Lineage[]>,
|
|
141
|
+
): void {
|
|
142
|
+
for (const [id, entries] of Object.entries(lineage)) {
|
|
143
|
+
const rule = catalog[id];
|
|
144
|
+
if (!rule) throw new Error(`audit lineage names ${id}, which this catalog does not define`);
|
|
145
|
+
if (entries.length > 0) rule.lineage = entries;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Render one lineage entry as "tool rule (relation)" for prose contexts. */
|
|
150
|
+
export function lineageLabel(l: Lineage): string {
|
|
151
|
+
const tool = (PRIOR_ART as Record<string, PriorArtEntry>)[l.tool]?.name ?? l.tool;
|
|
152
|
+
return l.rule ? `${tool} ${l.rule}` : tool;
|
|
153
|
+
}
|
package/src/audit/proof.test.ts
CHANGED
|
@@ -124,6 +124,73 @@ describe("proveFix — guidance findings are not auto-fixed", () => {
|
|
|
124
124
|
});
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
+
/** A Terraform root with both of #2110's deterministic findings in it. */
|
|
128
|
+
const TF_ROOT = `variable "log_level" {
|
|
129
|
+
type = string
|
|
130
|
+
sensitive = false
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
resource "aws_s3_bucket" "assets" {
|
|
134
|
+
bucket = "\${var.assets_bucket}"
|
|
135
|
+
tags = {
|
|
136
|
+
name = "\${var.project}-assets"
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
lifecycle {
|
|
140
|
+
prevent_destroy = false
|
|
141
|
+
create_before_destroy = var.replace_first
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
`;
|
|
145
|
+
|
|
146
|
+
describe("proveFix — TF019, delete a meta-argument set to its default false", () => {
|
|
147
|
+
test("drops both redundant lines and leaves the expression alone", () => {
|
|
148
|
+
const res = proveFix("TF019", TF_ROOT);
|
|
149
|
+
expect(res.applied).toBe(true);
|
|
150
|
+
expect(res.patched).not.toContain("sensitive = false");
|
|
151
|
+
expect(res.patched).not.toContain("prevent_destroy = false");
|
|
152
|
+
expect(res.patched).toContain("create_before_destroy = var.replace_first");
|
|
153
|
+
expect(res.diff).toContain("- sensitive = false");
|
|
154
|
+
expect(res.diff).toContain("- prevent_destroy = false");
|
|
155
|
+
// Deletions only: nothing is added.
|
|
156
|
+
expect((res.diff ?? "").split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++"))).toEqual([]);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("a root with no redundant default is a no-op, not a patch", () => {
|
|
160
|
+
const res = proveFix("TF019", 'resource "aws_s3_bucket" "assets" {\n bucket = var.assets_bucket\n}\n');
|
|
161
|
+
expect(res.applied).toBe(false);
|
|
162
|
+
expect(res.reason).toBe("noop");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("a line with a trailing comment is left alone: deleting it would delete the comment", () => {
|
|
166
|
+
const res = proveFix("TF019", " sensitive = false # deliberate, see #123\n");
|
|
167
|
+
expect(res.applied).toBe(false);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe("proveFix — TF016, unwrap an interpolation-only value", () => {
|
|
172
|
+
test("unquotes the single interpolation and leaves a real template alone", () => {
|
|
173
|
+
const res = proveFix("TF016", TF_ROOT);
|
|
174
|
+
expect(res.applied).toBe(true);
|
|
175
|
+
expect(res.patched).toContain("bucket = var.assets_bucket");
|
|
176
|
+
expect(res.patched).toContain('name = "${var.project}-assets"');
|
|
177
|
+
expect(res.diff).toContain("+ bucket = var.assets_bucket");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("the two fixes combine into one patch, as a quick-win diff does", () => {
|
|
181
|
+
const first = proveFix("TF016", TF_ROOT);
|
|
182
|
+
const second = proveFix("TF019", first.patched ?? TF_ROOT);
|
|
183
|
+
expect(second.patched).toContain("bucket = var.assets_bucket");
|
|
184
|
+
expect(second.patched).not.toContain("prevent_destroy");
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("a root written the modern way is a no-op", () => {
|
|
188
|
+
const res = proveFix("TF016", 'output "arn" {\n value = aws_s3_bucket.assets.arn\n}\n');
|
|
189
|
+
expect(res.applied).toBe(false);
|
|
190
|
+
expect(res.reason).toBe("noop");
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
127
194
|
describe("unifiedDiff", () => {
|
|
128
195
|
test("identical input produces an empty diff", () => {
|
|
129
196
|
expect(unifiedDiff(WF, WF)).toBe("");
|
package/src/audit/proof.ts
CHANGED
|
@@ -147,6 +147,70 @@ function narrowWriteAll(content: string): { patched: string; changed: boolean }
|
|
|
147
147
|
return { patched: content.replace(re, "permissions:\n contents: read"), changed: true };
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/**
|
|
151
|
+
* TF019 — drop a meta-argument explicitly set to its own default `false`
|
|
152
|
+
* (`sensitive`, `ephemeral`, `prevent_destroy`, `create_before_destroy`). The
|
|
153
|
+
* fix is the deletion of the whole line, which is what tflint-ruleset-redeploy's
|
|
154
|
+
* own autofix for `terraform_redundant_default` does. Only a literal `false` is
|
|
155
|
+
* touched: `prevent_destroy = var.protect` is left alone, and so is a line with
|
|
156
|
+
* a trailing comment, where deleting the line would delete prose too.
|
|
157
|
+
*/
|
|
158
|
+
const REDUNDANT_DEFAULT_RE = /^[ \t]*(?:sensitive|ephemeral|prevent_destroy|create_before_destroy)[ \t]*=[ \t]*false[ \t]*$/;
|
|
159
|
+
|
|
160
|
+
function dropRedundantDefaults(content: string): { patched: string; changed: boolean } {
|
|
161
|
+
const kept = content.split("\n").filter((line) => !REDUNDANT_DEFAULT_RE.test(line));
|
|
162
|
+
const patched = kept.join("\n");
|
|
163
|
+
return { patched, changed: patched !== content };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* TF016 — unwrap an attribute whose whole value is one interpolation
|
|
168
|
+
* (`x = "${var.y}"` becomes `x = var.y`), the deprecated pre-0.12 style
|
|
169
|
+
* tflint's `terraform_deprecated_interpolation` reports. Anchored on the
|
|
170
|
+
* source text rather than the parsed body for the reason TF016's own module
|
|
171
|
+
* gives: `@cdktf/hcl2json` renders a bare reference and a quoted interpolation
|
|
172
|
+
* identically, so the quotes only exist here.
|
|
173
|
+
*/
|
|
174
|
+
const INTERPOLATION_ONLY_RE = /^([ \t]*)([A-Za-z_][A-Za-z0-9_-]*)([ \t]*=[ \t]*)"\$\{([^"]+)\}"([ \t]*(?:#.*)?)$/;
|
|
175
|
+
|
|
176
|
+
/** One `name = "${expr}"` line, split into the parts the fix reassembles. */
|
|
177
|
+
export interface InterpolationOnlyLine {
|
|
178
|
+
/** The attribute's name. */
|
|
179
|
+
name: string;
|
|
180
|
+
/** The expression inside the interpolation, without the `${` and `}`. */
|
|
181
|
+
expr: string;
|
|
182
|
+
/** The same line with the quotes and the `${}` taken off. */
|
|
183
|
+
unwrapped: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Parse a line whose whole value is one interpolation. Exported because a rule
|
|
188
|
+
* and its fix have to agree on what counts: the terraform lexicon's TF016
|
|
189
|
+
* check calls this over a block's source text, and `unwrapInterpolations`
|
|
190
|
+
* below rewrites exactly the lines it accepts.
|
|
191
|
+
*/
|
|
192
|
+
export function interpolationOnlyLine(line: string): InterpolationOnlyLine | undefined {
|
|
193
|
+
const m = INTERPOLATION_ONLY_RE.exec(line);
|
|
194
|
+
if (!m) return undefined;
|
|
195
|
+
const expr = m[4];
|
|
196
|
+
// A nested `${` means the template is not one interpolation (it embeds
|
|
197
|
+
// another), and a `}` before the end means the interpolation closed early,
|
|
198
|
+
// so the quotes are carrying literal text as well.
|
|
199
|
+
if (expr.includes("${") || expr.includes("}")) return undefined;
|
|
200
|
+
return { name: m[2], expr, unwrapped: `${m[1]}${m[2]}${m[3]}${expr}${m[5]}` };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function unwrapInterpolations(content: string): { patched: string; changed: boolean } {
|
|
204
|
+
let changed = false;
|
|
205
|
+
const lines = content.split("\n").map((line) => {
|
|
206
|
+
const parsed = interpolationOnlyLine(line);
|
|
207
|
+
if (!parsed) return line;
|
|
208
|
+
changed = true;
|
|
209
|
+
return parsed.unwrapped;
|
|
210
|
+
});
|
|
211
|
+
return { patched: lines.join("\n"), changed };
|
|
212
|
+
}
|
|
213
|
+
|
|
150
214
|
/**
|
|
151
215
|
* Produce a deterministic fix + diff for a finding, if one is mechanical.
|
|
152
216
|
* Returns `applied: false` with guidance for non-deterministic findings.
|
|
@@ -181,6 +245,12 @@ export function proveFix(checkId: string, content: string, opts: ProveOptions =
|
|
|
181
245
|
case "GHA033":
|
|
182
246
|
result = narrowWriteAll(content);
|
|
183
247
|
break;
|
|
248
|
+
case "TF016":
|
|
249
|
+
result = unwrapInterpolations(content);
|
|
250
|
+
break;
|
|
251
|
+
case "TF019":
|
|
252
|
+
result = dropRedundantDefaults(content);
|
|
253
|
+
break;
|
|
184
254
|
default:
|
|
185
255
|
return notApplied(checkId, "needs-input", cat?.remediation || "No deterministic fix implemented for this rule yet.");
|
|
186
256
|
}
|
package/src/audit/report-html.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { AuditFinding } from "./core";
|
|
12
|
-
import { ruleDocUrl, type RuleMeta } from "./catalog";
|
|
12
|
+
import { ruleDocUrl, type RuleMeta, lineageLabel } from "./catalog";
|
|
13
13
|
import { buildReportModel, buildReportJson, type AuditSnapshot, type BuildModelOptions, type EnrichedFinding, type GuidanceCluster, type QuickWinFile, type ReportCounts } from "./report-model";
|
|
14
14
|
|
|
15
15
|
export type { AuditSnapshot } from "./report-model";
|
|
@@ -48,9 +48,14 @@ function ruleLink(id: string): string {
|
|
|
48
48
|
|
|
49
49
|
/** External backing for a merge-worthy rule (OSSF Scorecard, CIS, vendor docs). */
|
|
50
50
|
function authorityLinks(meta: RuleMeta): string {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
const parts: string[] = [];
|
|
52
|
+
if (meta.authority?.length) {
|
|
53
|
+
parts.push(`per ${meta.authority.map((a) => `<a href="${esc(a.url)}">${esc(a.name)}</a>`).join(", ")}`);
|
|
54
|
+
}
|
|
55
|
+
if (meta.lineage?.length) {
|
|
56
|
+
parts.push(`prior art: ${meta.lineage.map((l) => `<a href="${esc(l.url)}">${esc(lineageLabel(l))}</a>`).join(", ")}`);
|
|
57
|
+
}
|
|
58
|
+
return parts.length ? ` <span class="muted">${parts.join(" · ")}</span>` : "";
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
function renderDiff(diff: string): string {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { AuditFinding } from "./core";
|
|
9
9
|
import type { UnclaimedFile } from "./discover";
|
|
10
|
-
import { RULE_CATALOG, ruleDocUrl, type Authority, type Category, type FixKind, type RuleMeta, type Tier } from "./catalog";
|
|
10
|
+
import { RULE_CATALOG, ruleDocUrl, type Authority, type Category, type FixKind, type Lineage, type RuleMeta, type Tier } from "./catalog";
|
|
11
11
|
import { proveFix, unifiedDiff, type ProveOptions } from "./proof";
|
|
12
12
|
import type { Severity } from "../lint/rule";
|
|
13
13
|
|
|
@@ -98,6 +98,8 @@ export interface SerializedFinding {
|
|
|
98
98
|
line?: number;
|
|
99
99
|
/** Redaction-safe fingerprint of a flagged value, when applicable (see `secrets.ts`). */
|
|
100
100
|
fingerprint?: string;
|
|
101
|
+
/** Set when the finding is an absence rather than a defect — see `PostSynthDiagnostic.missing` (chant #2113). */
|
|
102
|
+
missing?: { kind: string; scope: string };
|
|
101
103
|
lexicon: string;
|
|
102
104
|
tier: Tier;
|
|
103
105
|
fixKind: FixKind;
|
|
@@ -106,6 +108,8 @@ export interface SerializedFinding {
|
|
|
106
108
|
title: string;
|
|
107
109
|
remediation: string;
|
|
108
110
|
authority: Authority[];
|
|
111
|
+
/** Prior art: upstream tools whose rules check the same condition (credit, not authority). */
|
|
112
|
+
lineage: Lineage[];
|
|
109
113
|
/** Link to this rule's entry in the audit rules reference. */
|
|
110
114
|
docUrl: string;
|
|
111
115
|
}
|
|
@@ -246,7 +250,13 @@ export function buildReportModel(findings: AuditFinding[], opts: BuildModelOptio
|
|
|
246
250
|
const contents = new Map((opts.files ?? []).map((f) => [f.path, f.content]));
|
|
247
251
|
|
|
248
252
|
const mergeWorthy = enriched.filter((f) => f.meta.tier === "merge-worthy");
|
|
249
|
-
|
|
253
|
+
// A deterministic fix is a quick win whatever the finding's tier. Every
|
|
254
|
+
// deterministic rule was merge-worthy until terraform's TF016 and TF019
|
|
255
|
+
// (#2110): both are hygiene, and both delete or unquote exactly the flagged
|
|
256
|
+
// line, which is the same safe mechanical patch a merge-worthy quick win
|
|
257
|
+
// offers. Filing them under "needs a judgement call" or in the hygiene table
|
|
258
|
+
// would hide a ready-to-apply diff.
|
|
259
|
+
const quickWinFindings = enriched.filter((f) => f.meta.fixKind === "deterministic");
|
|
250
260
|
const needsReviewFindings = mergeWorthy.filter((f) => f.meta.fixKind === "guidance");
|
|
251
261
|
|
|
252
262
|
// De-noise: drop a report-only finding only when a specific merge-worthy
|
|
@@ -254,6 +264,8 @@ export function buildReportModel(findings: AuditFinding[], opts: BuildModelOptio
|
|
|
254
264
|
const mwOnEntity = new Set(mergeWorthy.filter((f) => f.entity).map((f) => `${f.file}:${f.entity}:${f.checkId}`));
|
|
255
265
|
const reportOnly = enriched.filter((f) => {
|
|
256
266
|
if (f.meta.tier !== "report-only") return false;
|
|
267
|
+
// Counted and rendered as a quick win instead, so the three buckets stay disjoint.
|
|
268
|
+
if (f.meta.fixKind === "deterministic") return false;
|
|
257
269
|
const supers = SUPERSEDED_BY[f.checkId];
|
|
258
270
|
if (supers && f.entity && supers.some((id) => mwOnEntity.has(`${f.file}:${f.entity}:${id}`))) return false;
|
|
259
271
|
return true;
|
|
@@ -304,6 +316,7 @@ export function buildReportJson(
|
|
|
304
316
|
entity: f.entity,
|
|
305
317
|
line: f.line,
|
|
306
318
|
fingerprint: f.fingerprint,
|
|
319
|
+
missing: f.missing,
|
|
307
320
|
lexicon: f.lexicon,
|
|
308
321
|
tier: f.meta.tier,
|
|
309
322
|
fixKind: f.meta.fixKind,
|
|
@@ -311,6 +324,7 @@ export function buildReportJson(
|
|
|
311
324
|
title: f.meta.title,
|
|
312
325
|
remediation: f.meta.remediation,
|
|
313
326
|
authority: f.meta.authority ?? [],
|
|
327
|
+
lineage: f.meta.lineage ?? [],
|
|
314
328
|
docUrl: ruleDocUrl(f.checkId),
|
|
315
329
|
})),
|
|
316
330
|
unclaimed: opts.unclaimed && opts.unclaimed.length > 0 ? opts.unclaimed : undefined,
|
package/src/audit/report.test.ts
CHANGED
|
@@ -149,6 +149,39 @@ describe("efficiency dimension (#444)", () => {
|
|
|
149
149
|
});
|
|
150
150
|
});
|
|
151
151
|
|
|
152
|
+
describe("a report-only rule with a deterministic fix (#2110)", () => {
|
|
153
|
+
const TF_ROOT = `variable "log_level" {
|
|
154
|
+
type = string
|
|
155
|
+
sensitive = false
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
const findings: AuditFinding[] = [
|
|
159
|
+
{
|
|
160
|
+
checkId: "TF019",
|
|
161
|
+
severity: "info",
|
|
162
|
+
message: "sensitive = false is already the default.",
|
|
163
|
+
file: "infra/prod",
|
|
164
|
+
lexicon: "terraform",
|
|
165
|
+
entity: "infra/prod/var.log_level.sensitive",
|
|
166
|
+
},
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
test("renders in quick wins with its diff, not in the hygiene table", async () => {
|
|
170
|
+
const catalog = await resolveAuditCatalog(["terraform"]);
|
|
171
|
+
const out = renderMarkdown(findings, { catalog, files: [{ path: "infra/prod", content: TF_ROOT }] });
|
|
172
|
+
expect(out).toContain("## Quick wins (deterministic)");
|
|
173
|
+
expect(out).toContain("- sensitive = false");
|
|
174
|
+
expect(out).toContain("1 finding — 1 quick-win, 0 needs-review, 0 report-only");
|
|
175
|
+
expect(out).not.toContain("Report-only (hygiene)");
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("the credit rides beside the fix", async () => {
|
|
179
|
+
const catalog = await resolveAuditCatalog(["terraform"]);
|
|
180
|
+
const out = renderMarkdown(findings, { catalog, files: [{ path: "infra/prod", content: TF_ROOT }] });
|
|
181
|
+
expect(out).toContain("prior art: [tflint-ruleset-redeploy terraform_redundant_default]");
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
152
185
|
describe("catalog threading (#687)", () => {
|
|
153
186
|
test("buildReportModel uses a passed-in catalog over core's static one", async () => {
|
|
154
187
|
const { buildReportModel } = await import("./report-model");
|
package/src/audit/report.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import type { AuditFinding } from "./core";
|
|
14
|
-
import { ruleDocUrl, type RuleMeta } from "./catalog";
|
|
14
|
+
import { lineageLabel, ruleDocUrl, type RuleMeta } from "./catalog";
|
|
15
15
|
import type { ProveOptions } from "./proof";
|
|
16
16
|
import { buildReportModel, type EnrichedFinding, type GuidanceCluster, type QuickWinFile } from "./report-model";
|
|
17
17
|
|
|
@@ -22,8 +22,10 @@ function ruleLink(id: string): string {
|
|
|
22
22
|
|
|
23
23
|
/** External backing for a merge-worthy rule (OSSF Scorecard, CIS, vendor docs). */
|
|
24
24
|
function authorityLinks(meta: RuleMeta): string {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const authority = meta.authority?.length ? ` — per ${meta.authority.map((a) => `[${a.name}](${a.url})`).join(", ")}` : "";
|
|
26
|
+
// Prior art rides beside the authority: the tool that checked this first.
|
|
27
|
+
const lineage = meta.lineage?.length ? ` · prior art: ${meta.lineage.map((l) => `[${lineageLabel(l)}](${l.url})`).join(", ")}` : "";
|
|
28
|
+
return authority + lineage;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export interface RenderOptions {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
2
|
import { readFileSync } from "fs";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
|
-
import { renderRulesReference } from "./rules-doc";
|
|
5
|
-
import { resolveAuditCatalog, ruleDocUrl } from "./catalog";
|
|
4
|
+
import { renderRulesReference, ruleBlock } from "./rules-doc";
|
|
5
|
+
import { resolveAuditCatalog, ruleDocUrl, auditRule } from "./catalog";
|
|
6
6
|
import { AUDIT_LEXICONS } from "./discover";
|
|
7
7
|
|
|
8
8
|
const PAGE = fileURLToPath(new URL("../../../../docs/src/content/docs/lint-rules/audit-rules.mdx", import.meta.url));
|
|
@@ -23,3 +23,35 @@ describe("audit rules reference", () => {
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
});
|
|
26
|
+
|
|
27
|
+
describe("ruleBlock, aliases and deprecated (chant #2113)", () => {
|
|
28
|
+
test("renders aliases as a list", () => {
|
|
29
|
+
const m = { ...auditRule("NEW001", "report-only", "guidance", "T", "r"), aliases: ["OLD001", "LEGACY_OLD"] };
|
|
30
|
+
const block = ruleBlock(m);
|
|
31
|
+
expect(block).toContain("Also known as: `OLD001`, `LEGACY_OLD`.");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("greys a deprecated rule (tags line, not the heading; the heading is the anchor `ruleDocUrl` targets)", () => {
|
|
35
|
+
const m = { ...auditRule("OLD002", "report-only", "guidance", "T", "r") };
|
|
36
|
+
m.deprecated = "superseded by NEW002";
|
|
37
|
+
const block = ruleBlock(m);
|
|
38
|
+
expect(block).toContain("### OLD002"); // heading unchanged: `ruleDocUrl("OLD002")` must still resolve
|
|
39
|
+
expect(block).toContain("~~deprecated~~");
|
|
40
|
+
expect(block).toContain(":::note[Deprecated]");
|
|
41
|
+
expect(block).toContain("superseded by NEW002");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("a deprecated rule with no string reason gets a generic note", () => {
|
|
45
|
+
const m = { ...auditRule("OLD003", "report-only", "guidance", "T", "r") };
|
|
46
|
+
m.deprecated = true;
|
|
47
|
+
expect(ruleBlock(m)).toContain("This rule no longer runs.");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("a rule with neither aliases nor deprecated renders unchanged from before #2113", () => {
|
|
51
|
+
const m = auditRule("PLAIN001", "report-only", "guidance", "T", "r");
|
|
52
|
+
const block = ruleBlock(m);
|
|
53
|
+
expect(block).not.toContain("Also known as");
|
|
54
|
+
expect(block).not.toContain(":::note[Deprecated]");
|
|
55
|
+
expect(block).not.toContain("~~deprecated~~");
|
|
56
|
+
});
|
|
57
|
+
});
|
package/src/audit/rules-doc.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* (lowercased) — the anchor `ruleDocUrl()` links to.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { resolveAuditCatalog, type RuleMeta } from "./catalog";
|
|
10
|
+
import { PRIOR_ART, resolveAuditCatalog, type Lineage, type PriorArtEntry, type RuleMeta } from "./catalog";
|
|
11
11
|
import { AUDIT_LEXICONS } from "./discover";
|
|
12
12
|
|
|
13
13
|
const GROUPS: Array<{ heading: string; prefixes: string[]; blurb: string }> = [
|
|
@@ -21,6 +21,7 @@ const GROUPS: Array<{ heading: string; prefixes: string[]; blurb: string }> = [
|
|
|
21
21
|
{ heading: "GCP Config Connector (WGC)", prefixes: ["WGC"], blurb: "Run against Config Connector (cnrm.cloud.google.com) manifests." },
|
|
22
22
|
{ heading: "Helm (WHM)", prefixes: ["WHM"], blurb: "Run against Helm charts (Chart.yaml + templates)." },
|
|
23
23
|
{ heading: "fountain (FTN)", prefixes: ["FTN"], blurb: "Run against fountain manifests (`apiVersion: fountain.dev/v1`) — standalone `fountain apply` YAML is parsed back into the entity graph, so the same rules fire on `chant build` and `chant audit`." },
|
|
24
|
+
{ heading: "Terraform (TF)", prefixes: ["TF"], blurb: "Run against Terraform root modules (a directory of `.tf` files) parsed into the entity graph, so the same rules fire on `chant build` over `terraform.roots` and on `chant audit` (#2083)." },
|
|
24
25
|
{ heading: "Secrets & credentials (SEC)", prefixes: ["SEC"], blurb: "Lexicon-independent — scans the raw text of every scanned file for likely credentials, regardless of which audit lexicons are installed. Matched values are always redacted; see [suppressing false positives](/chant/cli/audit/#suppressing-a-secrets-finding)." },
|
|
25
26
|
{ heading: "Wrangler config (WRG)", prefixes: ["WRG"], blurb: "Lexicon-independent, audit-only (#446) — scans `wrangler.toml`, Cloudflare Workers' native deploy config, which the engine cannot otherwise parse (it is not YAML/JSON). No authoring surface: chant does not write Wrangler config, it only reads it for these checks." },
|
|
26
27
|
{ heading: "nginx config (NGX)", prefixes: ["NGX"], blurb: "Lexicon-independent, audit-only (#1979) — parses nginx's native directive/block config (`nginx.conf` and `.conf` files under nginx-ish directories, confirmed by content). No authoring surface: chant does not write nginx config, it only reads it for these checks." },
|
|
@@ -41,12 +42,84 @@ function mdxEscape(text: string): string {
|
|
|
41
42
|
return text.replace(/\{/g, "\\{").replace(/</g, "\\<");
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
function
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
function lineageLink(l: Lineage): string {
|
|
46
|
+
const tool = (PRIOR_ART as Record<string, PriorArtEntry>)[l.tool]?.name ?? l.tool;
|
|
47
|
+
const label = l.rule ? `${tool} \`${l.rule}\`` : tool;
|
|
48
|
+
return `[${mdxEscape(label)}](${l.url}) (${l.relation})`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A rule's backing, as a two-column table: the authority that says the finding
|
|
53
|
+
* matters, and the prior art that checked it first. A table rather than two
|
|
54
|
+
* "Label: ..." lines so that 379 rules do not read as 379 repeated sentences.
|
|
55
|
+
*/
|
|
56
|
+
function backingTable(m: RuleMeta): string {
|
|
57
|
+
const rows: string[] = [];
|
|
58
|
+
if (m.authority?.length) rows.push(`| Authority | ${m.authority.map((a) => `[${a.name}](${a.url})`).join(" · ")} |`);
|
|
59
|
+
if (m.lineage?.length) rows.push(`| Prior art | ${m.lineage.map(lineageLink).join(" · ")} |`);
|
|
60
|
+
return rows.length ? `\n\n| | |\n|---|---|\n${rows.join("\n")}` : "";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A rule kept the day it retires (chant #2113) rather than dropped: still
|
|
65
|
+
* gets a heading (so old links and `lint.rules`/suppression entries that
|
|
66
|
+
* name it still land somewhere), but greyed via a `:::note` aside instead of
|
|
67
|
+
* reading like a live rule, and with the `deprecated` string, when one was
|
|
68
|
+
* given, saying what replaced it.
|
|
69
|
+
*/
|
|
70
|
+
function deprecatedNote(deprecated: boolean | string): string {
|
|
71
|
+
const reason = typeof deprecated === "string" ? mdxEscape(deprecated) : "This rule no longer runs.";
|
|
72
|
+
return `\n\n:::note[Deprecated]\n${reason} The id is kept so a reference to it still resolves.\n:::`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function aliasesLine(aliases: string[]): string {
|
|
76
|
+
return `\n\nAlso known as: ${aliases.map((a) => `\`${mdxEscape(a)}\``).join(", ")}.`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Exported for `rules-doc.test.ts` to exercise the alias/deprecated rendering directly against a synthetic `RuleMeta`, without needing a real catalog entry to carry either (chant #2113). */
|
|
80
|
+
export function ruleBlock(m: RuleMeta): string {
|
|
81
|
+
// The heading text is what Starlight slugifies into `#<id>`, the anchor
|
|
82
|
+
// `ruleDocUrl()` links to (`./catalog.ts`), so it stays exactly `### <ID>`
|
|
83
|
+
// even for a deprecated rule. The "greyed out" treatment goes below the
|
|
84
|
+
// heading instead, so an old link/alias reference still lands on the id.
|
|
85
|
+
const tags = m.deprecated ? `${m.tier} · ${m.fixKind} · ~~deprecated~~` : `${m.tier} · ${m.fixKind}`;
|
|
86
|
+
return (
|
|
87
|
+
`### ${m.id}\n\n**${mdxEscape(m.title)}** — ${tags}\n\n${mdxEscape(m.remediation)}` +
|
|
88
|
+
`${backingTable(m)}` +
|
|
89
|
+
`${m.aliases?.length ? aliasesLine(m.aliases) : ""}` +
|
|
90
|
+
`${m.deprecated ? deprecatedNote(m.deprecated) : ""}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The closing section: every tool the rules credit, with its licence and how
|
|
96
|
+
* many rules point at it. Rendered from the same registry the entries key into,
|
|
97
|
+
* so a tool cannot be credited on a rule without appearing here.
|
|
98
|
+
*/
|
|
99
|
+
function priorArtSection(catalog: Record<string, RuleMeta>): string {
|
|
100
|
+
const counts = new Map<string, number>();
|
|
101
|
+
for (const m of Object.values(catalog)) {
|
|
102
|
+
for (const l of m.lineage ?? []) counts.set(l.tool, (counts.get(l.tool) ?? 0) + 1);
|
|
103
|
+
}
|
|
104
|
+
const rows = (Object.entries(PRIOR_ART) as Array<[string, PriorArtEntry]>)
|
|
105
|
+
.filter(([key]) => counts.has(key))
|
|
106
|
+
.sort(([a], [b]) => (counts.get(b) ?? 0) - (counts.get(a) ?? 0) || a.localeCompare(b))
|
|
107
|
+
.map(([key, t]) => `| [${t.name}](${t.url}) | ${t.kind} | ${t.license} | ${counts.get(key)} |`);
|
|
108
|
+
if (rows.length === 0) return "";
|
|
109
|
+
return `## Prior art
|
|
110
|
+
|
|
111
|
+
chant's rules were written against the output chant emits, but much of what they check was checked first by a dedicated tool. Each rule above that has a counterpart names it under **Prior art**, with the upstream rule id and how the two relate.
|
|
112
|
+
|
|
113
|
+
- \`equivalent\` is the same condition at the same scope.
|
|
114
|
+
- \`overlaps\` shares the core condition, with one side narrower or broader, or expressed for a different dialect of the same control.
|
|
115
|
+
- \`extends\` means chant checks a strict superset of the upstream rule.
|
|
116
|
+
|
|
117
|
+
This is credit rather than authority, and it never changes a rule's tier or category. A credit counts once per upstream rule cited, so a chant rule that names two zizmor audits adds two.
|
|
118
|
+
|
|
119
|
+
| Tool | Kind | Licence | Credits |
|
|
120
|
+
|---|---|---|---|
|
|
121
|
+
${rows.join("\n")}
|
|
122
|
+
`;
|
|
50
123
|
}
|
|
51
124
|
|
|
52
125
|
/**
|
|
@@ -73,8 +146,8 @@ diataxis: reference
|
|
|
73
146
|
|
|
74
147
|
This is the reference for every rule [\`chant audit\`](/chant/cli/audit/) can report. Each finding in a report links to its rule here.
|
|
75
148
|
|
|
76
|
-
Each rule is tagged with its **tier** — \`merge-worthy\` (a security or correctness issue worth a PR) or \`report-only\` (hygiene) — and its **fix kind** — \`deterministic\` (a safe mechanical fix the report can apply as a diff) or \`guidance\` (needs a judgement call).
|
|
149
|
+
Each rule is tagged with its **tier** — \`merge-worthy\` (a security or correctness issue worth a PR) or \`report-only\` (hygiene) — and its **fix kind** — \`deterministic\` (a safe mechanical fix the report can apply as a diff) or \`guidance\` (needs a judgement call). Where an open-source tool checks the same thing, the rule says so under **Prior art**; the [Prior art](#prior-art) section at the end lists every tool the rules on this page credit.
|
|
77
150
|
|
|
78
|
-
${sections.join("\n\n")}
|
|
151
|
+
${[...sections, priorArtSection(catalog)].filter(Boolean).join("\n\n")}
|
|
79
152
|
`;
|
|
80
153
|
}
|