@intentius/chant 0.80.0 → 0.81.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/discover.d.ts +8 -16
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts +5 -0
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +13 -0
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +4 -5
- package/dist/audit/terraform-state.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +25 -9
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +8 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/import-agents.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/update.d.ts +6 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/vendor.d.ts +5 -0
- package/dist/cli/commands/vendor.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/init.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +7 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/promote.d.ts +7 -4
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/run-generate.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +4 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/vendor.d.ts +8 -3
- package/dist/cli/handlers/vendor.d.ts.map +1 -1
- package/dist/cli/main.d.ts +12 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts +12 -7
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +32 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts +10 -0
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +3 -1
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/discover.d.ts.map +1 -1
- package/dist/components/driver.d.ts +16 -2
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/gate-plan.d.ts +40 -0
- package/dist/components/gate-plan.d.ts.map +1 -0
- package/dist/components/promote.d.ts +60 -7
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/sandbox/driver.d.ts.map +1 -1
- package/dist/components/sandbox/run.d.ts.map +1 -1
- package/dist/components/verbs/build-archive.d.ts +22 -11
- package/dist/components/verbs/build-archive.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/config-static.d.ts +59 -0
- package/dist/config-static.d.ts.map +1 -0
- package/dist/config.d.ts +32 -10
- package/dist/config.d.ts.map +1 -1
- package/dist/content-digest.d.ts +17 -0
- package/dist/content-digest.d.ts.map +1 -0
- package/dist/discovery/files.d.ts +10 -5
- package/dist/discovery/files.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +20 -4
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/sandbox/driver.d.ts +20 -0
- package/dist/discovery/sandbox/driver.d.ts.map +1 -1
- package/dist/discovery/sandbox/fork.d.ts +20 -0
- package/dist/discovery/sandbox/fork.d.ts.map +1 -1
- package/dist/discovery/walk.d.ts +119 -0
- package/dist/discovery/walk.d.ts.map +1 -0
- package/dist/graph-ir.d.ts +4 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/join-key.d.ts +31 -0
- package/dist/join-key.d.ts.map +1 -0
- package/dist/lexicon-module.d.ts +51 -6
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lexicon.d.ts +58 -1
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/build-ledger-store.d.ts.map +1 -1
- package/dist/lifecycle/digest.d.ts +5 -1
- package/dist/lifecycle/digest.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +20 -5
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +64 -0
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/lease.d.ts +8 -1
- package/dist/lifecycle/lease.d.ts.map +1 -1
- package/dist/lifecycle/legacy-digest.d.ts +41 -8
- package/dist/lifecycle/legacy-digest.d.ts.map +1 -1
- package/dist/lifecycle/member-ledger.d.ts +67 -0
- package/dist/lifecycle/member-ledger.d.ts.map +1 -0
- package/dist/lifecycle/plan-digest.d.ts +4 -3
- package/dist/lifecycle/plan-digest.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +18 -2
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/status.d.ts +90 -5
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +32 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/propose-upgrade.d.ts +75 -0
- package/dist/op/activities/propose-upgrade.d.ts.map +1 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/gate-name.d.ts +7 -0
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-summary.d.ts +16 -0
- package/dist/op/gate-summary.d.ts.map +1 -1
- package/dist/op/gate.d.ts +16 -2
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/project-root.d.ts +26 -0
- package/dist/project-root.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.d.ts +37 -0
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts +11 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -0
- package/dist/workspace/checks/generated.d.ts +156 -0
- package/dist/workspace/checks/generated.d.ts.map +1 -0
- package/dist/workspace/checks/ledgers.d.ts +78 -0
- package/dist/workspace/checks/ledgers.d.ts.map +1 -0
- package/dist/workspace/checks/links.d.ts +25 -0
- package/dist/workspace/checks/links.d.ts.map +1 -0
- package/dist/workspace/checks/pipelines.d.ts +76 -0
- package/dist/workspace/checks/pipelines.d.ts.map +1 -0
- package/dist/workspace/checks.d.ts +161 -0
- package/dist/workspace/checks.d.ts.map +1 -0
- package/dist/workspace/compose-graph.d.ts +115 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -0
- package/dist/workspace/compose-reports.d.ts +68 -0
- package/dist/workspace/compose-reports.d.ts.map +1 -0
- package/dist/workspace/declaration.d.ts +220 -0
- package/dist/workspace/declaration.d.ts.map +1 -0
- package/dist/workspace/declaration.schema.json +434 -0
- package/dist/workspace/generated-files.d.ts +65 -0
- package/dist/workspace/generated-files.d.ts.map +1 -0
- package/dist/workspace/glob.d.ts +13 -0
- package/dist/workspace/glob.d.ts.map +1 -0
- package/dist/workspace/graph-cli.d.ts +75 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -0
- package/dist/workspace/init.d.ts +96 -0
- package/dist/workspace/init.d.ts.map +1 -0
- package/dist/workspace/jsonc.d.ts +38 -0
- package/dist/workspace/jsonc.d.ts.map +1 -0
- package/dist/workspace/kinds.d.ts +192 -0
- package/dist/workspace/kinds.d.ts.map +1 -0
- package/dist/workspace/lineage-check.d.ts +84 -0
- package/dist/workspace/lineage-check.d.ts.map +1 -0
- package/dist/workspace/lineage-cli.d.ts +31 -0
- package/dist/workspace/lineage-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-init.d.ts +101 -0
- package/dist/workspace/lineage-init.d.ts.map +1 -0
- package/dist/workspace/lineage-lock.d.ts +258 -0
- package/dist/workspace/lineage-lock.d.ts.map +1 -0
- package/dist/workspace/lineage-merge.d.ts +14 -0
- package/dist/workspace/lineage-merge.d.ts.map +1 -0
- package/dist/workspace/lineage-migrations.d.ts +178 -0
- package/dist/workspace/lineage-migrations.d.ts.map +1 -0
- package/dist/workspace/lineage-update.d.ts +59 -0
- package/dist/workspace/lineage-update.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts +40 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade.d.ts +146 -0
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -0
- package/dist/workspace/lineage-vendor.d.ts +63 -0
- package/dist/workspace/lineage-vendor.d.ts.map +1 -0
- package/dist/workspace/lineage-version.d.ts +32 -0
- package/dist/workspace/lineage-version.d.ts.map +1 -0
- package/dist/workspace/links.d.ts +150 -0
- package/dist/workspace/links.d.ts.map +1 -0
- package/dist/workspace/ls.d.ts +99 -0
- package/dist/workspace/ls.d.ts.map +1 -0
- package/dist/workspace/member-commands.d.ts +139 -0
- package/dist/workspace/member-commands.d.ts.map +1 -0
- package/dist/workspace/member-handles.d.ts +54 -0
- package/dist/workspace/member-handles.d.ts.map +1 -0
- package/dist/workspace/member-pipeline.d.ts +143 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -0
- package/dist/workspace/member-run.d.ts +67 -0
- package/dist/workspace/member-run.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +65 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -0
- package/dist/workspace/records-cli.d.ts +28 -4
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/root-refusal.d.ts +53 -0
- package/dist/workspace/root-refusal.d.ts.map +1 -0
- package/dist/workspace/status.d.ts +150 -0
- package/dist/workspace/status.d.ts.map +1 -0
- package/dist/workspace/template-manifest.d.ts +90 -0
- package/dist/workspace/template-manifest.d.ts.map +1 -0
- package/dist/workspace/tree.d.ts +36 -0
- package/dist/workspace/tree.d.ts.map +1 -0
- package/dist/workspace/trust/attestor.d.ts +67 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -0
- package/dist/workspace/trust/policy.d.ts +111 -0
- package/dist/workspace/trust/policy.d.ts.map +1 -0
- package/dist/workspace/trust/provenance.d.ts +67 -0
- package/dist/workspace/trust/provenance.d.ts.map +1 -0
- package/dist/workspace/trust/ssh-commit.d.ts +43 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -0
- package/dist/workspace/trust/test-repo.d.ts +42 -0
- package/dist/workspace/trust/test-repo.d.ts.map +1 -0
- package/dist/workspace/trust/verify-cli.d.ts +9 -0
- package/dist/workspace/trust/verify-cli.d.ts.map +1 -0
- package/dist/workspace/trust/verify.d.ts +66 -0
- package/dist/workspace/trust/verify.d.ts.map +1 -0
- package/dist/workspace/which-chant.d.ts +58 -0
- package/dist/workspace/which-chant.d.ts.map +1 -0
- package/dist/workspace/workspace-kinds.schema.json +133 -0
- package/package.json +2 -1
- package/src/audit/discover.ts +66 -78
- package/src/audit/report-html.ts +3 -1
- package/src/audit/report-model.ts +14 -1
- package/src/audit/terraform-state.test.ts +4 -9
- package/src/audit/terraform-state.ts +4 -5
- package/src/cli/commands/audit-truncation.test.ts +167 -0
- package/src/cli/commands/audit.ts +89 -60
- package/src/cli/commands/check-lexicon.ts +24 -0
- package/src/cli/commands/doctor.test.ts +30 -0
- package/src/cli/commands/doctor.ts +18 -0
- package/src/cli/commands/import-agents.ts +6 -2
- package/src/cli/commands/init.ts +22 -3
- package/src/cli/commands/lint.test.ts +17 -23
- package/src/cli/commands/lint.ts +24 -84
- package/src/cli/commands/update.ts +13 -4
- package/src/cli/commands/vendor.ts +11 -6
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +38 -0
- package/src/cli/handlers/build-member.test.ts +127 -0
- package/src/cli/handlers/build.ts +35 -1
- package/src/cli/handlers/components.test.ts +127 -0
- package/src/cli/handlers/components.ts +70 -8
- package/src/cli/handlers/dev.ts +5 -3
- package/src/cli/handlers/fan-out.ts +9 -2
- package/src/cli/handlers/init.ts +54 -1
- package/src/cli/handlers/operator.test.ts +68 -0
- package/src/cli/handlers/operator.ts +37 -9
- package/src/cli/handlers/promote.test.ts +118 -2
- package/src/cli/handlers/promote.ts +82 -16
- package/src/cli/handlers/run-generate-member.test.ts +83 -0
- package/src/cli/handlers/run-generate.test.ts +6 -8
- package/src/cli/handlers/run-generate.ts +36 -1
- package/src/cli/handlers/run.test.ts +47 -0
- package/src/cli/handlers/run.ts +28 -5
- package/src/cli/handlers/vendor.ts +147 -43
- package/src/cli/main.test.ts +105 -1
- package/src/cli/main.ts +230 -17
- package/src/cli/plugins.ts +17 -17
- package/src/cli/registry.ts +32 -0
- package/src/cli/static-config-read.test.ts +208 -0
- package/src/codegen/fetch.test.ts +63 -1
- package/src/codegen/fetch.ts +30 -1
- package/src/components/cli-support.ts +6 -1
- package/src/components/discover.test.ts +27 -57
- package/src/components/discover.ts +13 -68
- package/src/components/driver.test.ts +78 -1
- package/src/components/driver.ts +43 -7
- package/src/components/gate-plan.ts +53 -0
- package/src/components/promote.test.ts +276 -2
- package/src/components/promote.ts +119 -15
- package/src/components/sandbox/driver.ts +8 -8
- package/src/components/sandbox/run.ts +4 -1
- package/src/components/verbs/build-archive.ts +27 -21
- package/src/components/verbs/run-agent.ts +3 -5
- package/src/config-static.test.ts +136 -0
- package/src/config-static.ts +232 -0
- package/src/config.test.ts +2 -2
- package/src/config.ts +48 -15
- package/src/content-digest.test.ts +120 -0
- package/src/content-digest.ts +21 -0
- package/src/discovery/files.test.ts +10 -10
- package/src/discovery/files.ts +25 -81
- package/src/discovery/fold-import.ts +49 -5
- package/src/discovery/index.ts +1 -0
- package/src/discovery/sandbox/driver.ts +73 -35
- package/src/discovery/sandbox/fork-diagnostic.test.ts +136 -13
- package/src/discovery/sandbox/fork.ts +83 -43
- package/src/discovery/sandbox/path-lexicon-multifile.test.ts +285 -0
- package/src/discovery/walk.test.ts +263 -0
- package/src/discovery/walk.ts +334 -0
- package/src/effect-receipt.ts +2 -2
- package/src/graph-ir.ts +4 -1
- package/src/join-key.test.ts +25 -0
- package/src/join-key.ts +39 -0
- package/src/lexicon-module.ts +108 -10
- package/src/lexicon.ts +79 -1
- package/src/lifecycle/build-ledger-store.ts +11 -10
- package/src/lifecycle/digest.ts +9 -5
- package/src/lifecycle/gate-ledger.test.ts +20 -0
- package/src/lifecycle/gate-ledger.ts +28 -6
- package/src/lifecycle/git.test.ts +13 -1
- package/src/lifecycle/git.ts +221 -176
- package/src/lifecycle/lease.ts +25 -10
- package/src/lifecycle/legacy-digest.test.ts +115 -23
- package/src/lifecycle/legacy-digest.ts +60 -19
- package/src/lifecycle/member-ledger.test.ts +319 -0
- package/src/lifecycle/member-ledger.ts +145 -0
- package/src/lifecycle/plan-digest.ts +7 -7
- package/src/lifecycle/release-ledger.test.ts +20 -0
- package/src/lifecycle/release-ledger.ts +23 -7
- package/src/lifecycle/status.test.ts +151 -1
- package/src/lifecycle/status.ts +228 -7
- package/src/op/activities/activity-contracts.ts +37 -0
- package/src/op/activities/index.ts +10 -0
- package/src/op/activities/propose-upgrade.ts +206 -0
- package/src/op/discover.test.ts +14 -4
- package/src/op/discover.ts +19 -27
- package/src/op/gate-name.ts +8 -0
- package/src/op/gate-summary.test.ts +14 -0
- package/src/op/gate-summary.ts +25 -2
- package/src/op/gate.ts +36 -7
- package/src/op/generate-pipeline.test.ts +14 -5
- package/src/project-root.test.ts +66 -1
- package/src/project-root.ts +48 -0
- package/src/workspace/__fixtures__/contract-repo.ts +100 -0
- package/src/workspace/__fixtures__/two-member-workspace.ts +35 -0
- package/src/workspace/behold-kinds.test.ts +162 -0
- package/src/workspace/check-contract.test.ts +127 -0
- package/src/workspace/check.schema.json +347 -0
- package/src/workspace/checks/generated.test.ts +287 -0
- package/src/workspace/checks/generated.ts +475 -0
- package/src/workspace/checks/ledgers.test.ts +139 -0
- package/src/workspace/checks/ledgers.ts +246 -0
- package/src/workspace/checks/links.ts +183 -0
- package/src/workspace/checks/pipelines.test.ts +133 -0
- package/src/workspace/checks/pipelines.ts +225 -0
- package/src/workspace/checks-repo.test.ts +71 -0
- package/src/workspace/checks.test.ts +450 -0
- package/src/workspace/checks.ts +461 -0
- package/src/workspace/compose-graph.test.ts +146 -0
- package/src/workspace/compose-graph.ts +228 -0
- package/src/workspace/compose-reports.test.ts +109 -0
- package/src/workspace/compose-reports.ts +186 -0
- package/src/workspace/declaration.schema.json +434 -0
- package/src/workspace/declaration.test.ts +347 -0
- package/src/workspace/declaration.ts +623 -0
- package/src/workspace/generated-files.test.ts +114 -0
- package/src/workspace/generated-files.ts +94 -0
- package/src/workspace/glob.ts +49 -0
- package/src/workspace/graph-cli.ts +220 -0
- package/src/workspace/graph-contract.test.ts +204 -0
- package/src/workspace/graph.schema.json +662 -0
- package/src/workspace/init.test.ts +223 -0
- package/src/workspace/init.ts +585 -0
- package/src/workspace/jsonc.test.ts +65 -0
- package/src/workspace/jsonc.ts +231 -0
- package/src/workspace/kinds.test.ts +288 -0
- package/src/workspace/kinds.ts +506 -0
- package/src/workspace/lineage-check.ts +264 -0
- package/src/workspace/lineage-cli.ts +114 -0
- package/src/workspace/lineage-init.test.ts +231 -0
- package/src/workspace/lineage-init.ts +365 -0
- package/src/workspace/lineage-lock.test.ts +224 -0
- package/src/workspace/lineage-lock.ts +357 -0
- package/src/workspace/lineage-merge.ts +44 -0
- package/src/workspace/lineage-migrations.test.ts +178 -0
- package/src/workspace/lineage-migrations.ts +343 -0
- package/src/workspace/lineage-update.ts +160 -0
- package/src/workspace/lineage-upgrade-cli.ts +151 -0
- package/src/workspace/lineage-upgrade.test.ts +474 -0
- package/src/workspace/lineage-upgrade.ts +624 -0
- package/src/workspace/lineage-vendor.test.ts +126 -0
- package/src/workspace/lineage-vendor.ts +215 -0
- package/src/workspace/lineage-version.ts +127 -0
- package/src/workspace/links.test.ts +344 -0
- package/src/workspace/links.ts +331 -0
- package/src/workspace/ls-contract.test.ts +200 -0
- package/src/workspace/ls.schema.json +190 -0
- package/src/workspace/ls.ts +254 -0
- package/src/workspace/member-commands.e2e.test.ts +244 -0
- package/src/workspace/member-commands.test.ts +217 -0
- package/src/workspace/member-commands.ts +540 -0
- package/src/workspace/member-handles.test.ts +76 -0
- package/src/workspace/member-handles.ts +228 -0
- package/src/workspace/member-pipeline.test.ts +135 -0
- package/src/workspace/member-pipeline.ts +317 -0
- package/src/workspace/member-run.ts +146 -0
- package/src/workspace/read-contract.test.ts +129 -0
- package/src/workspace/reason-codes.test.ts +113 -0
- package/src/workspace/reason-codes.ts +81 -0
- package/src/workspace/records-cli.ts +86 -14
- package/src/workspace/records-contract.test.ts +5 -0
- package/src/workspace/records.schema.json +32 -2
- package/src/workspace/records.ts +3 -2
- package/src/workspace/root-refusal.ts +97 -0
- package/src/workspace/status-contract.test.ts +308 -0
- package/src/workspace/status.schema.json +210 -0
- package/src/workspace/status.ts +412 -0
- package/src/workspace/template-manifest.test.ts +79 -0
- package/src/workspace/template-manifest.ts +221 -0
- package/src/workspace/tree.ts +132 -0
- package/src/workspace/trust/attestor.ts +93 -0
- package/src/workspace/trust/policy.ts +306 -0
- package/src/workspace/trust/provenance.ts +181 -0
- package/src/workspace/trust/ssh-commit.ts +138 -0
- package/src/workspace/trust/test-repo.ts +138 -0
- package/src/workspace/trust/trust.test.ts +397 -0
- package/src/workspace/trust/verify-cli.ts +56 -0
- package/src/workspace/trust/verify.ts +194 -0
- package/src/workspace/which-chant.test.ts +117 -0
- package/src/workspace/which-chant.ts +151 -0
- package/src/workspace/workspace-kinds.schema.json +133 -0
- package/dist/components/unbound-gate-approval.d.ts +0 -24
- package/dist/components/unbound-gate-approval.d.ts.map +0 -1
- package/dist/discovery/convergence.d.ts +0 -97
- package/dist/discovery/convergence.d.ts.map +0 -1
- package/src/cli/commands/audit-walk-warnings.test.ts +0 -156
- package/src/components/unbound-gate-approval.test.ts +0 -82
- package/src/components/unbound-gate-approval.ts +0 -51
- package/src/discovery/convergence.test.ts +0 -231
- package/src/discovery/convergence.ts +0 -399
- package/src/op/discover-convergence.test.ts +0 -70
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant workspace verify [--base <rev>] [--head <rev>] [--require attested] [--json]`
|
|
3
|
+
* (#2547): check a change against the trust policy at its base. See ./verify.ts.
|
|
4
|
+
*
|
|
5
|
+
* Exit 0 when the change passes, 1 when it does not or cannot be checked.
|
|
6
|
+
*/
|
|
7
|
+
import type { CommandContext } from "../../cli/registry.js";
|
|
8
|
+
export declare function runWorkspaceVerify(ctx: CommandContext): Promise<number>;
|
|
9
|
+
//# sourceMappingURL=verify-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-cli.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/verify-cli.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAqB7E"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verify a change against the policy at its base (#2524 D5, Threat model).
|
|
3
|
+
*
|
|
4
|
+
* This is what CI runs on a protected branch: every commit in `base..head` is
|
|
5
|
+
* asked of the attestors with the policy read at base, so nothing the change
|
|
6
|
+
* does to its own copy of the policy counts. A change that edits the policy
|
|
7
|
+
* (the signers file or `.chant/trust.json`) is a protected write: every commit
|
|
8
|
+
* that touches those files must be attested by a signer the base policy
|
|
9
|
+
* trusts, and by an admin when the base policy grants the admin role.
|
|
10
|
+
*
|
|
11
|
+
* Attestation is opt-in (#2525 rule 6). Without a signers file at base the
|
|
12
|
+
* check passes and says so, unless `--require attested` asks for more.
|
|
13
|
+
*/
|
|
14
|
+
import { type CommitAttestor, type ProvenanceLevel } from "./attestor.js";
|
|
15
|
+
import { type BaseSource, type RecordProvenance } from "./provenance.js";
|
|
16
|
+
import { type ExcludedSigner } from "./policy.js";
|
|
17
|
+
export interface CommitVerdict extends RecordProvenance {
|
|
18
|
+
commit: string;
|
|
19
|
+
subject: string;
|
|
20
|
+
/** A merge that changes nothing of its own; its parents are checked instead. */
|
|
21
|
+
skipped?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ProtectedWrite {
|
|
24
|
+
commit: string;
|
|
25
|
+
paths: string[];
|
|
26
|
+
level: ProvenanceLevel;
|
|
27
|
+
principal?: string;
|
|
28
|
+
allowed: boolean;
|
|
29
|
+
reason: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ChangeReport {
|
|
32
|
+
base: string | null;
|
|
33
|
+
baseFrom: BaseSource | null;
|
|
34
|
+
head: string | null;
|
|
35
|
+
policy: {
|
|
36
|
+
active: boolean;
|
|
37
|
+
signersPath: string;
|
|
38
|
+
principals: string[];
|
|
39
|
+
excluded: ExcludedSigner[];
|
|
40
|
+
writers: string[];
|
|
41
|
+
problems: string[];
|
|
42
|
+
};
|
|
43
|
+
require: ProvenanceLevel | null;
|
|
44
|
+
commits: CommitVerdict[];
|
|
45
|
+
protectedPaths: string[];
|
|
46
|
+
/** Protected paths whose content differs between the merge base and head. */
|
|
47
|
+
protectedChanged: string[];
|
|
48
|
+
protectedWrites: ProtectedWrite[];
|
|
49
|
+
notes: string[];
|
|
50
|
+
failures: string[];
|
|
51
|
+
ok: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface VerifyOptions {
|
|
54
|
+
repo: string;
|
|
55
|
+
base?: string;
|
|
56
|
+
head?: string;
|
|
57
|
+
require?: ProvenanceLevel;
|
|
58
|
+
attestors: readonly CommitAttestor[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Commits in `range` that change any of `paths`, each with the paths it
|
|
62
|
+
* changes. Merges count only for what they change beyond every parent.
|
|
63
|
+
*/
|
|
64
|
+
export declare function commitsTouching(repo: string, range: string, paths: string[]): Map<string, string[]>;
|
|
65
|
+
export declare function verifyChange(opts: VerifyOptions): ChangeReport;
|
|
66
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/workspace/trust/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAA+C,KAAK,UAAU,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACnH,OAAO,EAAiC,KAAK,cAAc,EAAoB,MAAM,UAAU,CAAC;AAEhG,MAAM,WAAW,aAAc,SAAQ,gBAAgB;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,eAAe,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE;QACN,MAAM,EAAE,OAAO,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,QAAQ,EAAE,cAAc,EAAE,CAAC;QAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,6EAA6E;IAC7E,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;CACtC;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAUnG;AAQD,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CAqG9D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a workspace read starts, and which chant does it (#2536; #2524 D15,
|
|
3
|
+
* ws-016, ws-021).
|
|
4
|
+
*
|
|
5
|
+
* {@link locateWorkspace} finds the declaration from a directory, in the
|
|
6
|
+
* working tree or in one commit's git objects (`--at <rev>`), and gives the
|
|
7
|
+
* tree every read-contract command reads from.
|
|
8
|
+
*
|
|
9
|
+
* Which chant reads the declaration: the root's. The root names its chant by
|
|
10
|
+
* pinning `@intentius/chant` in the declaration's `pins`. When it pins one,
|
|
11
|
+
* that version reads the declaration, and any other chant refuses with
|
|
12
|
+
* `root-chant-required` (`parseDeclaration`'s `rootChant` option). The CLI
|
|
13
|
+
* first hands the whole command to the pinned chant when the root has it
|
|
14
|
+
* installed ({@link handToRootChant}), so a user sees the refusal only when
|
|
15
|
+
* it isn't. When the root pins no chant, the reader's own chant reads the
|
|
16
|
+
* declaration if it meets `minReader`, and refuses with `reader-too-old`
|
|
17
|
+
* otherwise. Members are always read by their own toolchains.
|
|
18
|
+
*/
|
|
19
|
+
import { type WorkspaceTree } from "./tree.js";
|
|
20
|
+
export interface LocatedWorkspace {
|
|
21
|
+
/** The files, rooted at the workspace root. */
|
|
22
|
+
tree: WorkspaceTree;
|
|
23
|
+
/** The workspace root relative to the git root, `"."` for the git root itself. Absolute outside git. */
|
|
24
|
+
root: string;
|
|
25
|
+
/** The workspace root on disk, where pinned packages and toolchains are installed. Also for `--at`. */
|
|
26
|
+
rootOnDisk: string;
|
|
27
|
+
/** The full commit id for `--at`, or null for the working tree. */
|
|
28
|
+
at: string | null;
|
|
29
|
+
/** The top of the git repository, when there is one. */
|
|
30
|
+
top: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Find the workspace whose declaration is nearest above `cwd`, in the working
|
|
34
|
+
* tree or at revision `at`. Throws a {@link WorkspaceReadError}:
|
|
35
|
+
* `declaration-missing`, or for `--at`, `not-a-git-repository` and
|
|
36
|
+
* `revision-unknown`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function locateWorkspace(cwd: string, at?: string): LocatedWorkspace;
|
|
39
|
+
/**
|
|
40
|
+
* The `bin/chant` of `@intentius/chant` at `version`, installed where the
|
|
41
|
+
* workspace root resolves packages. Undefined when it isn't installed there,
|
|
42
|
+
* or is installed at another version.
|
|
43
|
+
*/
|
|
44
|
+
export declare function rootChantBin(rootOnDisk: string, version: string): string | undefined;
|
|
45
|
+
/** Set in the environment of a command handed to the root's chant, so it is handed on once at most. */
|
|
46
|
+
export declare const HANDED_TO_ROOT_ENV = "CHANT_WORKSPACE_ROOT_CHANT";
|
|
47
|
+
/** The version the declaration at `located` pins for `@intentius/chant`, or undefined. Never throws. */
|
|
48
|
+
export declare function declaredChantPin(located: LocatedWorkspace): string | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Hand a read-contract command to the root's chant (ws-021). When the
|
|
51
|
+
* declaration above `cwd` (at `at`, for `--at`) pins a chant other than this
|
|
52
|
+
* one, and the root has that chant installed, run the same command line
|
|
53
|
+
* under it and return its exit code. Otherwise return undefined, and this
|
|
54
|
+
* chant runs the command: it reads the declaration itself, or refuses with
|
|
55
|
+
* `root-chant-required` when the pinned chant isn't installed.
|
|
56
|
+
*/
|
|
57
|
+
export declare function handToRootChant(cwd: string, at: string | undefined, argv?: string[]): Promise<number | undefined>;
|
|
58
|
+
//# sourceMappingURL=which-chant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"which-chant.d.ts","sourceRoot":"","sources":["../../src/workspace/which-chant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AASH,OAAO,EAA+C,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEzF,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,IAAI,EAAE,aAAa,CAAC;IACpB,wGAAwG;IACxG,IAAI,EAAE,MAAM,CAAC;IACb,uGAAuG;IACvG,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,wDAAwD;IACxD,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AAID;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAgC1E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAcpF;AAED,uGAAuG;AACvG,eAAO,MAAM,kBAAkB,+BAA+B,CAAC;AAE/D,wGAAwG;AACxG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAY9E;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAoB9I"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://intentius.io/chant/schemas/workspace/kinds/v1/workspace-kinds.schema.json",
|
|
4
|
+
"title": "workspace-kinds.json",
|
|
5
|
+
"description": "Member kinds a plugin supplies (#2524 D3, #2535, ws-031). A package publishes this file as data at its ./workspace-kinds subpath. chant reads it with the file system and never imports the package, so reading kinds never loads a lexicon.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"schema",
|
|
9
|
+
"kinds"
|
|
10
|
+
],
|
|
11
|
+
"properties": {
|
|
12
|
+
"$schema": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "An editor hint. chant ignores it."
|
|
15
|
+
},
|
|
16
|
+
"$comment": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "A note for people. chant ignores it."
|
|
19
|
+
},
|
|
20
|
+
"schema": {
|
|
21
|
+
"const": 1,
|
|
22
|
+
"description": "The kinds format version."
|
|
23
|
+
},
|
|
24
|
+
"kinds": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"minItems": 1,
|
|
27
|
+
"items": {
|
|
28
|
+
"$ref": "#/$defs/kind"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"patternProperties": {
|
|
33
|
+
"^x-": true
|
|
34
|
+
},
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"$defs": {
|
|
37
|
+
"kind": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"required": [
|
|
40
|
+
"name",
|
|
41
|
+
"description",
|
|
42
|
+
"precedence",
|
|
43
|
+
"probe"
|
|
44
|
+
],
|
|
45
|
+
"properties": {
|
|
46
|
+
"name": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"pattern": "^[a-z][a-z0-9-]{0,39}$",
|
|
49
|
+
"description": "The kind's name, as a member's kind field names it. chant, workspace, other and examples are built in and can't be supplied."
|
|
50
|
+
},
|
|
51
|
+
"description": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"minLength": 1,
|
|
54
|
+
"description": "One line saying what the member's directory holds, for listings and error messages."
|
|
55
|
+
},
|
|
56
|
+
"precedence": {
|
|
57
|
+
"type": "integer",
|
|
58
|
+
"minimum": 1,
|
|
59
|
+
"maximum": 999,
|
|
60
|
+
"description": "When the probes of several kinds claim one directory, the highest precedence decides. Two claiming kinds with the same highest precedence are a tie, and a tie fails. The built-in chant kind has 500 and workspace has 1000."
|
|
61
|
+
},
|
|
62
|
+
"probe": {
|
|
63
|
+
"$ref": "#/$defs/probe"
|
|
64
|
+
},
|
|
65
|
+
"outputs": {
|
|
66
|
+
"type": "array",
|
|
67
|
+
"uniqueItems": true,
|
|
68
|
+
"description": "The outputs every member of this kind exposes as link targets (#2524 D6, #2539). A member entry of the kind may list more in its own outputs field. Without this field the kind exposes only what each entry lists.",
|
|
69
|
+
"items": {
|
|
70
|
+
"type": "string",
|
|
71
|
+
"minLength": 1,
|
|
72
|
+
"maxLength": 255,
|
|
73
|
+
"pattern": "^[^\\u0000-\\u001f]+$"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"patternProperties": {
|
|
78
|
+
"^x-": true
|
|
79
|
+
},
|
|
80
|
+
"additionalProperties": false
|
|
81
|
+
},
|
|
82
|
+
"probe": {
|
|
83
|
+
"type": "object",
|
|
84
|
+
"description": "What the probe checks in a member's directory. It runs no code. It passes when any of its clauses does, and it needs at least one.",
|
|
85
|
+
"minProperties": 1,
|
|
86
|
+
"properties": {
|
|
87
|
+
"anyFile": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"minItems": 1,
|
|
90
|
+
"uniqueItems": true,
|
|
91
|
+
"description": "The directory passes when a file matching one of these names sits directly in it. A * in a name matches any run of characters, so *.tf matches every Terraform file.",
|
|
92
|
+
"items": {
|
|
93
|
+
"$ref": "#/$defs/fileName"
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"anyBlock": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"description": "The directory passes when a file directly in it, whose name matches one of in, has a line opening an unlabelled block named in blocks: leading space, the name, optional space, then {. No file is parsed.",
|
|
99
|
+
"required": [
|
|
100
|
+
"in",
|
|
101
|
+
"blocks"
|
|
102
|
+
],
|
|
103
|
+
"properties": {
|
|
104
|
+
"in": {
|
|
105
|
+
"type": "array",
|
|
106
|
+
"minItems": 1,
|
|
107
|
+
"uniqueItems": true,
|
|
108
|
+
"items": {
|
|
109
|
+
"$ref": "#/$defs/fileName"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"blocks": {
|
|
113
|
+
"type": "array",
|
|
114
|
+
"minItems": 1,
|
|
115
|
+
"uniqueItems": true,
|
|
116
|
+
"items": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_-]{0,63}$"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"additionalProperties": false
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"additionalProperties": false
|
|
126
|
+
},
|
|
127
|
+
"fileName": {
|
|
128
|
+
"type": "string",
|
|
129
|
+
"description": "A file name, with no directory part. A * matches any run of characters.",
|
|
130
|
+
"pattern": "^(?!\\.\\.?$)[^/\\\\]+$"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.0",
|
|
4
4
|
"description": "Declarative infrastructure-as-code toolkit — TypeScript on Node.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"types": "./dist/cli/index.d.ts",
|
|
50
50
|
"default": "./src/cli/index.ts"
|
|
51
51
|
},
|
|
52
|
+
"./workspace/*.schema.json": "./src/workspace/*.schema.json",
|
|
52
53
|
"./cli/*": {
|
|
53
54
|
"development": "./src/cli/*.ts",
|
|
54
55
|
"types": "./dist/cli/*.d.ts",
|
package/src/audit/discover.ts
CHANGED
|
@@ -34,12 +34,15 @@
|
|
|
34
34
|
* normalized to a JSON string here.
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
import {
|
|
37
|
+
import { readFileSync, statSync } from "fs";
|
|
38
38
|
import { basename, join, relative, resolve } from "path";
|
|
39
39
|
import { parseYAML } from "../yaml";
|
|
40
40
|
import type { LexiconPlugin } from "../lexicon";
|
|
41
41
|
import type { AuditInput, AuditLexicon } from "./core";
|
|
42
42
|
import { isNginxConfigPath } from "./nginx";
|
|
43
|
+
import { walkDiscovery } from "../discovery/walk";
|
|
44
|
+
import { findProjectConfig } from "../project-root";
|
|
45
|
+
import type { DiscoveryGlobs } from "../config";
|
|
43
46
|
import { gitignoreCoversTerraformState, isTerraformStatePath, nestedGitignoreCovering } from "./terraform-state";
|
|
44
47
|
|
|
45
48
|
/** Lexicons the auditor knows how to detect and run checks for. */
|
|
@@ -69,11 +72,8 @@ export async function loadAuditPlugins(names: readonly string[] = AUDIT_LEXICONS
|
|
|
69
72
|
return plugins;
|
|
70
73
|
}
|
|
71
74
|
|
|
72
|
-
const WALK_SKIP = new Set(["node_modules", ".git", "dist"]);
|
|
73
75
|
/** Terraform's local working directory: recorded by the walk, never entered (TF023). */
|
|
74
76
|
const TERRAFORM_WORK_DIR = ".terraform";
|
|
75
|
-
/** Dot-directories the walk descends into anyway (CI lives here). */
|
|
76
|
-
const WALK_DOT_DIRS = new Set([".github", ".forgejo"]);
|
|
77
77
|
/**
|
|
78
78
|
* How many files the local walk takes before it stops. Every file it reaches
|
|
79
79
|
* counts, candidate or not, so a tree with a large `docs/` or `assets/` can hit
|
|
@@ -83,53 +83,54 @@ export const DEFAULT_MAX_WALK_FILES = 1000;
|
|
|
83
83
|
/** Skip absurdly large files before parsing — mirrors the fetch layer's caps. */
|
|
84
84
|
const MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
85
85
|
|
|
86
|
-
/**
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
/**
|
|
87
|
+
* The project's `exclude`/`include` globs for the audit walk, read from a
|
|
88
|
+
* `chant.config.json` only. The audit reads repositories it has no reason to
|
|
89
|
+
* trust, so it never runs a `chant.config.ts` to find them.
|
|
90
|
+
*/
|
|
91
|
+
function auditGlobs(root: string): DiscoveryGlobs | undefined {
|
|
92
|
+
const { dir, configPath } = findProjectConfig(root);
|
|
93
|
+
if (!configPath?.endsWith(".json")) return undefined;
|
|
94
|
+
let raw: unknown;
|
|
95
|
+
try {
|
|
96
|
+
raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
97
|
+
} catch {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
const list = (v: unknown): string[] => (Array.isArray(v) ? v.filter((x): x is string => typeof x === "string" && x !== "") : []);
|
|
101
|
+
const exclude = list((raw as Record<string, unknown>)?.exclude);
|
|
102
|
+
const include = list((raw as Record<string, unknown>)?.include);
|
|
103
|
+
return exclude.length === 0 && include.length === 0 ? undefined : { root: dir, exclude, include };
|
|
90
104
|
}
|
|
91
105
|
|
|
92
106
|
/**
|
|
93
|
-
*
|
|
107
|
+
* Collect file paths under a root through the one discovery walk
|
|
108
|
+
* (`../discovery/walk.ts`, #2527), sorted per directory.
|
|
109
|
+
*
|
|
110
|
+
* The audit's own departures from the other walkers: `.github` and `.forgejo`
|
|
111
|
+
* are entered, since they hold the CI files it audits; `.terraform/` is
|
|
112
|
+
* recorded but never entered, since it is machine-generated, routinely
|
|
113
|
+
* hundreds of megabytes of provider binaries, and its presence is itself the
|
|
114
|
+
* finding (TF023); and Terraform state paths are left to TF023's own
|
|
115
|
+
* `.gitignore` reading rather than dropped for being git-ignored.
|
|
94
116
|
*
|
|
95
|
-
* Stops at `state.
|
|
117
|
+
* Stops at `state.max` paths. Once full it keeps looking only until it meets
|
|
96
118
|
* one more path it would have taken, which is what sets `truncated`: a tree of
|
|
97
|
-
* exactly `
|
|
98
|
-
* directories, was not truncated.
|
|
99
|
-
* `limit` either way.
|
|
119
|
+
* exactly `max` files, or one whose remaining entries are all skipped
|
|
120
|
+
* directories, was not truncated.
|
|
100
121
|
*/
|
|
101
|
-
function walkFiles(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
out.push(path);
|
|
115
|
-
return true;
|
|
116
|
-
};
|
|
117
|
-
for (const e of entries.sort((a, b) => (a.name < b.name ? -1 : 1))) {
|
|
118
|
-
if (state.truncated) return;
|
|
119
|
-
if (e.name.startsWith(".") && e.isDirectory() && !WALK_DOT_DIRS.has(e.name)) {
|
|
120
|
-
// `.terraform/` is never descended into: it is machine-generated,
|
|
121
|
-
// routinely hundreds of megabytes of provider binaries, and nothing in
|
|
122
|
-
// it is worth reading. Its presence is itself the finding (TF023), so
|
|
123
|
-
// the directory path alone is recorded and `collectCandidates` hands it
|
|
124
|
-
// on with no content.
|
|
125
|
-
if (e.name === TERRAFORM_WORK_DIR && !take(join(dir, e.name))) return;
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
if (WALK_SKIP.has(e.name)) continue;
|
|
129
|
-
const full = join(dir, e.name);
|
|
130
|
-
if (e.isDirectory()) walkFiles(full, out, state);
|
|
131
|
-
else if (!take(full)) return;
|
|
132
|
-
}
|
|
122
|
+
function walkFiles(root: string, state: { max: number; truncated: boolean }, excludeDirs?: readonly string[]): string[] {
|
|
123
|
+
return walkDiscovery({
|
|
124
|
+
walker: "audit",
|
|
125
|
+
root,
|
|
126
|
+
excludeDirs,
|
|
127
|
+
globs: auditGlobs(root),
|
|
128
|
+
sorted: true,
|
|
129
|
+
limit: state,
|
|
130
|
+
takeSkippedDir: (name) => name === TERRAFORM_WORK_DIR,
|
|
131
|
+
keepIgnored: (full) => isTerraformStatePath(relative(root, full)),
|
|
132
|
+
accept: () => true,
|
|
133
|
+
});
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
function readSafe(full: string): string | undefined {
|
|
@@ -625,18 +626,12 @@ export function discoverByDetection(root: string, plugins: DetectPlugin[]): Audi
|
|
|
625
626
|
export interface CandidateWalkOptions {
|
|
626
627
|
/** Files the walk takes before it stops, counting every file. Defaults to {@link DEFAULT_MAX_WALK_FILES}. */
|
|
627
628
|
maxFiles?: number;
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
*/
|
|
635
|
-
export interface NestedGitignoreChange {
|
|
636
|
-
/** The TF023 path, relative to the scan root. */
|
|
637
|
-
path: string;
|
|
638
|
-
/** The nested `.gitignore` that ignores it, relative to the scan root. */
|
|
639
|
-
gitignore: string;
|
|
629
|
+
/**
|
|
630
|
+
* Directories the walk leaves out, absolute: the workspace members and
|
|
631
|
+
* example-group matches below the root, from `workspaceMemberDirs`. The
|
|
632
|
+
* caller resolves them, since that is async and this walk is not.
|
|
633
|
+
*/
|
|
634
|
+
excludeDirs?: readonly string[];
|
|
640
635
|
}
|
|
641
636
|
|
|
642
637
|
/** What the local walk read, plus what it knows about its own limits. */
|
|
@@ -646,8 +641,6 @@ export interface CandidateWalk {
|
|
|
646
641
|
maxFiles: number;
|
|
647
642
|
/** True when the walk stopped at `maxFiles` with files left unread. */
|
|
648
643
|
truncated: boolean;
|
|
649
|
-
/** TF023 paths whose finding changes once nested `.gitignore` files are read. */
|
|
650
|
-
nestedGitignore: NestedGitignoreChange[];
|
|
651
644
|
}
|
|
652
645
|
|
|
653
646
|
/** The walk half of `discoverByDetection`: candidate files read into memory, paths relative to the root. */
|
|
@@ -656,39 +649,34 @@ export function collectCandidates(root: string): RepoFile[] {
|
|
|
656
649
|
}
|
|
657
650
|
|
|
658
651
|
/**
|
|
659
|
-
* `collectCandidates`
|
|
660
|
-
*
|
|
661
|
-
* Neither changes `files`; the CLI prints them on stderr (#2528).
|
|
652
|
+
* `collectCandidates` plus whether the walk stopped at its limit with files
|
|
653
|
+
* left unread, which the audit report states (#2528).
|
|
662
654
|
*/
|
|
663
655
|
export function walkCandidates(root: string, opts: CandidateWalkOptions = {}): CandidateWalk {
|
|
664
|
-
const
|
|
665
|
-
const
|
|
666
|
-
walkFiles(root, all, state);
|
|
656
|
+
const state = { max: opts.maxFiles ?? DEFAULT_MAX_WALK_FILES, truncated: false };
|
|
657
|
+
const all = walkFiles(root, state, opts.excludeDirs);
|
|
667
658
|
// TF023 reads paths, not content: a `.tfstate` can be tens of megabytes of
|
|
668
659
|
// machine-generated JSON, and the finding is that it is in the repository at
|
|
669
|
-
// all. The
|
|
670
|
-
// disk" here
|
|
660
|
+
// all. The `.gitignore` files are what separate "committed" from "merely on
|
|
661
|
+
// disk" here. A Terraform working tree almost always has an ignored
|
|
671
662
|
// `.terraform/` in it, and reporting that would make TF023 fire on every
|
|
672
|
-
// local audit.
|
|
673
|
-
//
|
|
663
|
+
// local audit. Every `.gitignore` from the scan root down to the file's own
|
|
664
|
+
// directory is read (#2528), so auditing from the root and from a
|
|
665
|
+
// subdirectory agree. A fetched repository has no such ambiguity (its file
|
|
666
|
+
// list is the tracked files) and is not filtered.
|
|
674
667
|
const gitignore = readSafe(join(root, ".gitignore")) ?? "";
|
|
675
|
-
// Only the root one decides, for now. The nested ones are read to say which
|
|
676
|
-
// findings the next release, which honours them, will stop reporting.
|
|
677
668
|
const nestedBodies = new Map<string, string | undefined>();
|
|
678
669
|
const nestedGitignoreAt = (dir: string): string | undefined => {
|
|
679
670
|
if (!nestedBodies.has(dir)) nestedBodies.set(dir, readSafe(join(root, dir, ".gitignore")));
|
|
680
671
|
return nestedBodies.get(dir);
|
|
681
672
|
};
|
|
682
673
|
const files: RepoFile[] = [];
|
|
683
|
-
const nestedGitignore: NestedGitignoreChange[] = [];
|
|
684
674
|
for (const full of all) {
|
|
685
675
|
const path = relative(root, full);
|
|
686
676
|
if (isTerraformStatePath(path)) {
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
if (dir !== undefined) nestedGitignore.push({ path, gitignore: `${dir}/.gitignore` });
|
|
691
|
-
}
|
|
677
|
+
const ignored =
|
|
678
|
+
gitignoreCoversTerraformState(gitignore, path) || nestedGitignoreCovering(path, nestedGitignoreAt) !== undefined;
|
|
679
|
+
if (!ignored) files.push({ path, content: "" });
|
|
692
680
|
continue;
|
|
693
681
|
}
|
|
694
682
|
// `.tf` is read locally so `classifyTerraform` can bundle real content.
|
|
@@ -702,5 +690,5 @@ export function walkCandidates(root: string, opts: CandidateWalkOptions = {}): C
|
|
|
702
690
|
const content = readSafe(full);
|
|
703
691
|
if (content !== undefined) files.push({ path, content });
|
|
704
692
|
}
|
|
705
|
-
return { files, maxFiles: state.
|
|
693
|
+
return { files, maxFiles: state.max, truncated: state.truncated };
|
|
706
694
|
}
|
package/src/audit/report-html.ts
CHANGED
|
@@ -31,6 +31,8 @@ export interface RenderHtmlOptions extends BuildModelOptions {
|
|
|
31
31
|
/** Full template override; `{{title}} {{accent}} {{logo}} {{meta}} {{body}} {{footer}}` slots. */
|
|
32
32
|
template?: string;
|
|
33
33
|
notes?: string[];
|
|
34
|
+
/** A truncated local walk (#2528), carried into the embedded JSON report. */
|
|
35
|
+
truncated?: { limit: number; flag: string };
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
const DEFAULT_ACCENT = "#7c3aed";
|
|
@@ -194,7 +196,7 @@ export function renderHtml(findings: AuditFinding[], opts: RenderHtmlOptions = {
|
|
|
194
196
|
|
|
195
197
|
// Machine-readable data embedded so the HTML report is also parseable.
|
|
196
198
|
// `<` escaped so the JSON can't break out of the <script> element.
|
|
197
|
-
const dataJson = JSON.stringify(buildReportJson(findings, { snapshot: opts.snapshot }), null, 0).replace(/</g, "\\u003c");
|
|
199
|
+
const dataJson = JSON.stringify(buildReportJson(findings, { snapshot: opts.snapshot, truncated: opts.truncated }), null, 0).replace(/</g, "\\u003c");
|
|
198
200
|
const dataScript = `<script type="application/json" id="chant-audit-report">${dataJson}</script>`;
|
|
199
201
|
|
|
200
202
|
let body: string;
|
|
@@ -129,6 +129,12 @@ export interface AuditReportJson {
|
|
|
129
129
|
findings: SerializedFinding[];
|
|
130
130
|
/** Candidate files that looked like they wanted a lexicon that is not installed. */
|
|
131
131
|
unclaimed?: UnclaimedFile[];
|
|
132
|
+
/**
|
|
133
|
+
* Present when the local walk stopped at its file limit with files left
|
|
134
|
+
* unread (#2528): the limit, and the flag that raises it. Absent for a whole
|
|
135
|
+
* scan and for a repository URL.
|
|
136
|
+
*/
|
|
137
|
+
truncated?: { limit: number; flag: string };
|
|
132
138
|
}
|
|
133
139
|
|
|
134
140
|
export function metaFor(id: string, catalog: Record<string, RuleMeta> = RULE_CATALOG): RuleMeta {
|
|
@@ -298,7 +304,13 @@ export function buildReportModel(findings: AuditFinding[], opts: BuildModelOptio
|
|
|
298
304
|
/** Build the versioned, machine-readable JSON report (stable contract). */
|
|
299
305
|
export function buildReportJson(
|
|
300
306
|
findings: AuditFinding[],
|
|
301
|
-
opts: {
|
|
307
|
+
opts: {
|
|
308
|
+
snapshot?: AuditSnapshot;
|
|
309
|
+
toolVersion?: string;
|
|
310
|
+
catalog?: Record<string, RuleMeta>;
|
|
311
|
+
unclaimed?: UnclaimedFile[];
|
|
312
|
+
truncated?: { limit: number; flag: string };
|
|
313
|
+
} = {},
|
|
302
314
|
): AuditReportJson {
|
|
303
315
|
const model = buildReportModel(findings, { catalog: opts.catalog });
|
|
304
316
|
const version = opts.toolVersion ?? opts.snapshot?.toolVersion ?? "0.0.0";
|
|
@@ -328,5 +340,6 @@ export function buildReportJson(
|
|
|
328
340
|
docUrl: ruleDocUrl(f.checkId),
|
|
329
341
|
})),
|
|
330
342
|
unclaimed: opts.unclaimed && opts.unclaimed.length > 0 ? opts.unclaimed : undefined,
|
|
343
|
+
truncated: opts.truncated,
|
|
331
344
|
};
|
|
332
345
|
}
|
|
@@ -196,22 +196,18 @@ describe("walkCandidates reports what the next release changes, and changes noth
|
|
|
196
196
|
rmSync(dir, { recursive: true, force: true });
|
|
197
197
|
});
|
|
198
198
|
|
|
199
|
-
test("a state file a nested .gitignore ignores is
|
|
199
|
+
test("a state file a nested .gitignore ignores is not a candidate (#2528)", () => {
|
|
200
200
|
const dir = tmpRepo();
|
|
201
201
|
mkdirSync(join(dir, "infra", ".terraform"), { recursive: true });
|
|
202
202
|
writeFileSync(join(dir, "infra", ".gitignore"), "*.tfstate\n.terraform/\n");
|
|
203
203
|
writeFileSync(join(dir, "infra", "terraform.tfstate"), "{}");
|
|
204
204
|
const walk = walkCandidates(dir);
|
|
205
|
-
expect(walk.files.map((f) => f.path)
|
|
206
|
-
expect(auditTerraformState(walk.files)).toHaveLength(
|
|
207
|
-
expect(walk.nestedGitignore).toEqual([
|
|
208
|
-
{ path: "infra/.terraform", gitignore: "infra/.gitignore" },
|
|
209
|
-
{ path: "infra/terraform.tfstate", gitignore: "infra/.gitignore" },
|
|
210
|
-
]);
|
|
205
|
+
expect(walk.files.map((f) => f.path)).toEqual([]);
|
|
206
|
+
expect(auditTerraformState(walk.files)).toHaveLength(0);
|
|
211
207
|
rmSync(dir, { recursive: true, force: true });
|
|
212
208
|
});
|
|
213
209
|
|
|
214
|
-
test("
|
|
210
|
+
test("only the .gitignore files between the path and the root apply, not a sibling's", () => {
|
|
215
211
|
const dir = tmpRepo();
|
|
216
212
|
mkdirSync(join(dir, "a"), { recursive: true });
|
|
217
213
|
mkdirSync(join(dir, "b"), { recursive: true });
|
|
@@ -223,7 +219,6 @@ describe("walkCandidates reports what the next release changes, and changes noth
|
|
|
223
219
|
writeFileSync(join(dir, "b", ".gitignore"), "*.tfstate\n");
|
|
224
220
|
const walk = walkCandidates(dir);
|
|
225
221
|
expect(walk.files.map((f) => f.path)).toEqual(["a/terraform.tfstate"]);
|
|
226
|
-
expect(walk.nestedGitignore).toEqual([]);
|
|
227
222
|
rmSync(dir, { recursive: true, force: true });
|
|
228
223
|
});
|
|
229
224
|
});
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
* walk that decides which paths reach here is `discover.ts`'s
|
|
23
23
|
* `collectCandidates`, which is also where a locally ignored path is dropped:
|
|
24
24
|
* see `isTerraformStatePath` there and `gitignoreCoversTerraformState` below.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* The root `.gitignore` and every nested one on the way to the path can drop
|
|
26
|
+
* it (`nestedGitignoreCovering`, #2528).
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
import type { AuditFinding } from "./core";
|
|
@@ -92,9 +92,8 @@ export function gitignoreCoversTerraformState(gitignore: string, path: string):
|
|
|
92
92
|
* the same narrow matcher as the root one. `readGitignore` takes a directory
|
|
93
93
|
* relative to the scan root and returns its `.gitignore` body, if any.
|
|
94
94
|
*
|
|
95
|
-
* The walk
|
|
96
|
-
*
|
|
97
|
-
* once every `.gitignore` is read (#2528).
|
|
95
|
+
* The local walk drops a TF023 path this returns a directory for, as it drops
|
|
96
|
+
* one the root `.gitignore` covers (#2528).
|
|
98
97
|
*/
|
|
99
98
|
export function nestedGitignoreCovering(
|
|
100
99
|
path: string,
|