@intentius/chant 0.124.52 → 0.124.54
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.map +1 -1
- package/dist/audit/discover.js +3 -1
- package/dist/behaviour.d.ts +1 -1
- package/dist/behaviour.d.ts.map +1 -1
- package/dist/capabilities/templates.d.ts +65 -0
- package/dist/capabilities/templates.d.ts.map +1 -0
- package/dist/capabilities/templates.js +193 -0
- package/dist/capabilities/templates.json +145 -0
- package/dist/change-set-graph.d.ts +35 -3
- package/dist/change-set-graph.d.ts.map +1 -1
- package/dist/change-set-graph.js +60 -9
- package/dist/change-set.d.ts +65 -7
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +23 -12
- package/dist/ci-identity.d.ts +7 -2
- package/dist/ci-identity.d.ts.map +1 -1
- package/dist/ci-identity.js +4 -1
- package/dist/cli/commands/import-index.d.ts +52 -0
- package/dist/cli/commands/import-index.d.ts.map +1 -0
- package/dist/cli/commands/import-index.js +226 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +4 -2
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/components.js +11 -2
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +16 -3
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +22 -4
- package/dist/cli/handlers/index-cloud.d.ts +14 -0
- package/dist/cli/handlers/index-cloud.d.ts.map +1 -0
- package/dist/cli/handlers/index-cloud.js +22 -0
- package/dist/cli/handlers/index-diff.d.ts +18 -0
- package/dist/cli/handlers/index-diff.d.ts.map +1 -0
- package/dist/cli/handlers/index-diff.js +142 -0
- package/dist/cli/handlers/lifecycle-drift.d.ts +25 -0
- package/dist/cli/handlers/lifecycle-drift.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-drift.js +64 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.js +188 -28
- package/dist/cli/handlers/misc.d.ts +9 -0
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/misc.js +77 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +15 -2
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +89 -15
- package/dist/cli/handlers/query.d.ts.map +1 -1
- package/dist/cli/handlers/query.js +28 -4
- package/dist/cli/handlers/scenario.d.ts.map +1 -1
- package/dist/cli/handlers/scenario.js +8 -7
- package/dist/cli/handlers/search.d.ts +34 -4
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/handlers/search.js +216 -36
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +55 -1
- package/dist/cli/mcp/lifecycle-tools.d.ts.map +1 -1
- package/dist/cli/mcp/lifecycle-tools.js +3 -2
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.js +4 -3
- package/dist/cli/mcp/workspace-tools.js +1 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +55 -2
- package/dist/cli/registry.d.ts +19 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cloud-lister/aws.d.ts +35 -0
- package/dist/cloud-lister/aws.d.ts.map +1 -0
- package/dist/cloud-lister/aws.js +203 -0
- package/dist/cloud-lister/azure.d.ts +34 -0
- package/dist/cloud-lister/azure.d.ts.map +1 -0
- package/dist/cloud-lister/azure.js +129 -0
- package/dist/cloud-lister/common.d.ts +40 -0
- package/dist/cloud-lister/common.d.ts.map +1 -0
- package/dist/cloud-lister/common.js +83 -0
- package/dist/cloud-lister/credentials.d.ts +50 -0
- package/dist/cloud-lister/credentials.d.ts.map +1 -0
- package/dist/cloud-lister/credentials.js +86 -0
- package/dist/cloud-lister/fakes.d.ts +27 -0
- package/dist/cloud-lister/fakes.d.ts.map +1 -0
- package/dist/cloud-lister/fakes.js +50 -0
- package/dist/cloud-lister/gcp.d.ts +42 -0
- package/dist/cloud-lister/gcp.d.ts.map +1 -0
- package/dist/cloud-lister/gcp.js +143 -0
- package/dist/cloud-lister/guard.d.ts +49 -0
- package/dist/cloud-lister/guard.d.ts.map +1 -0
- package/dist/cloud-lister/guard.js +103 -0
- package/dist/cloud-lister/ids.d.ts +23 -0
- package/dist/cloud-lister/ids.d.ts.map +1 -0
- package/dist/cloud-lister/ids.js +42 -0
- package/dist/cloud-lister/index.d.ts +67 -0
- package/dist/cloud-lister/index.d.ts.map +1 -0
- package/dist/cloud-lister/index.js +102 -0
- package/dist/cloud-lister/kubernetes.d.ts +64 -0
- package/dist/cloud-lister/kubernetes.d.ts.map +1 -0
- package/dist/cloud-lister/kubernetes.js +276 -0
- package/dist/cloud-lister/markers.d.ts +31 -0
- package/dist/cloud-lister/markers.d.ts.map +1 -0
- package/dist/cloud-lister/markers.js +73 -0
- package/dist/cloud-lister/mask.d.ts +31 -0
- package/dist/cloud-lister/mask.d.ts.map +1 -0
- package/dist/cloud-lister/mask.js +74 -0
- package/dist/cloud-lister/sigv4.d.ts +29 -0
- package/dist/cloud-lister/sigv4.d.ts.map +1 -0
- package/dist/cloud-lister/sigv4.js +54 -0
- package/dist/cloud-lister/transport.d.ts +55 -0
- package/dist/cloud-lister/transport.d.ts.map +1 -0
- package/dist/cloud-lister/transport.js +86 -0
- package/dist/cloud-lister/types.d.ts +121 -0
- package/dist/cloud-lister/types.d.ts.map +1 -0
- package/dist/cloud-lister/types.js +24 -0
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.js +6 -0
- package/dist/components/capability-plugin.d.ts +9 -0
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability.d.ts +215 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +21 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/cli-support.js +58 -14
- package/dist/components/declared-reads.d.ts +38 -0
- package/dist/components/declared-reads.d.ts.map +1 -0
- package/dist/components/declared-reads.js +131 -0
- package/dist/components/deploy-units.d.ts.map +1 -1
- package/dist/components/deploy-units.js +4 -0
- package/dist/components/driver.d.ts +34 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +66 -3
- package/dist/components/fan-out-run.d.ts +23 -1
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +82 -14
- package/dist/components/fan-out-support.d.ts +6 -0
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/fan-out-support.js +4 -1
- package/dist/components/fan-out.d.ts +28 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/groups.d.ts +229 -0
- package/dist/components/groups.d.ts.map +1 -0
- package/dist/components/groups.js +760 -0
- package/dist/components/index.d.ts +6 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +4 -0
- package/dist/components/plan-shape-conformance.d.ts +48 -0
- package/dist/components/plan-shape-conformance.d.ts.map +1 -0
- package/dist/components/plan-shape-conformance.js +122 -0
- package/dist/components/pr-run.d.ts +41 -4
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +209 -39
- package/dist/components/promote-rounds.d.ts +9 -0
- package/dist/components/promote-rounds.d.ts.map +1 -1
- package/dist/components/promote-rounds.js +23 -2
- package/dist/components/promote.d.ts +17 -0
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +36 -6
- package/dist/components/release-gate.d.ts +23 -1
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +97 -33
- package/dist/components/run-groups.d.ts +58 -0
- package/dist/components/run-groups.d.ts.map +1 -0
- package/dist/components/run-groups.js +78 -0
- package/dist/components/starter-plugin.d.ts +1 -0
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/starter-plugin.js +6 -1
- package/dist/components/verbs/index.d.ts +1 -0
- package/dist/components/verbs/index.d.ts.map +1 -1
- package/dist/components/verbs/index.js +1 -0
- package/dist/components/verbs/member-outputs.d.ts +187 -0
- package/dist/components/verbs/member-outputs.d.ts.map +1 -0
- package/dist/components/verbs/member-outputs.js +218 -0
- package/dist/config.d.ts +60 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +27 -0
- package/dist/deep-observation.d.ts +1 -13
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/deep-observation.js +5 -22
- package/dist/drift-record.d.ts +542 -0
- package/dist/drift-record.d.ts.map +1 -0
- package/dist/drift-record.js +886 -0
- package/dist/engine-facts.d.ts +144 -0
- package/dist/engine-facts.d.ts.map +1 -0
- package/dist/engine-facts.js +14 -0
- package/dist/graph-ir.d.ts +153 -2
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ir.js +224 -6
- package/dist/graph-refs.d.ts +145 -0
- package/dist/graph-refs.d.ts.map +1 -1
- package/dist/graph-refs.js +305 -0
- package/dist/graph-schema/fixtures/adopt.json +15 -0
- package/dist/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
- package/dist/graph-schema/fixtures/index.json +68 -0
- package/dist/graph-schema/fixtures/plunge.engine-graph.json +158 -0
- package/dist/graph-schema/fixtures.d.ts +23 -0
- package/dist/graph-schema/fixtures.d.ts.map +1 -0
- package/dist/graph-schema/fixtures.js +246 -0
- package/dist/graph-schema/generate.d.ts +15 -0
- package/dist/graph-schema/generate.d.ts.map +1 -0
- package/dist/graph-schema/generate.js +267 -0
- package/dist/graph-schema/graph.schema.json +1387 -0
- package/dist/health.schema.json +2 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/instance-nodes.d.ts +159 -0
- package/dist/instance-nodes.d.ts.map +1 -0
- package/dist/instance-nodes.js +231 -0
- package/dist/lexicon.d.ts +72 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/adoption-file.d.ts +56 -0
- package/dist/lifecycle/adoption-file.d.ts.map +1 -0
- package/dist/lifecycle/adoption-file.js +123 -0
- package/dist/lifecycle/cloud-index.d.ts +186 -0
- package/dist/lifecycle/cloud-index.d.ts.map +1 -0
- package/dist/lifecycle/cloud-index.js +618 -0
- package/dist/lifecycle/derive-edges.d.ts +59 -0
- package/dist/lifecycle/derive-edges.d.ts.map +1 -0
- package/dist/lifecycle/derive-edges.js +126 -0
- package/dist/lifecycle/git.d.ts +10 -9
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +7 -1
- package/dist/lifecycle/identity-match.d.ts +133 -0
- package/dist/lifecycle/identity-match.d.ts.map +1 -0
- package/dist/lifecycle/identity-match.js +147 -0
- package/dist/lifecycle/index-diff.d.ts +224 -0
- package/dist/lifecycle/index-diff.d.ts.map +1 -0
- package/dist/lifecycle/index-diff.js +327 -0
- package/dist/lifecycle/index-document.d.ts +130 -0
- package/dist/lifecycle/index-document.d.ts.map +1 -0
- package/dist/lifecycle/index-document.js +189 -0
- package/dist/lifecycle/index-drift.d.ts +121 -0
- package/dist/lifecycle/index-drift.d.ts.map +1 -0
- package/dist/lifecycle/index-drift.js +178 -0
- package/dist/lifecycle/index-import.d.ts +168 -0
- package/dist/lifecycle/index-import.d.ts.map +1 -0
- package/dist/lifecycle/index-import.js +515 -0
- package/dist/lifecycle/index-join.d.ts +129 -0
- package/dist/lifecycle/index-join.d.ts.map +1 -0
- package/dist/lifecycle/index-join.js +248 -0
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/observation-baseline.d.ts +61 -21
- package/dist/lifecycle/observation-baseline.d.ts.map +1 -1
- package/dist/lifecycle/observation-baseline.js +144 -19
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/observe.js +6 -1
- package/dist/lifecycle/query.d.ts +45 -4
- package/dist/lifecycle/query.d.ts.map +1 -1
- package/dist/lifecycle/query.js +89 -7
- package/dist/lifecycle/replay.d.ts +10 -3
- package/dist/lifecycle/replay.d.ts.map +1 -1
- package/dist/lifecycle/replay.js +10 -6
- package/dist/lifecycle/scenario.d.ts +4 -3
- package/dist/lifecycle/scenario.d.ts.map +1 -1
- package/dist/lifecycle/snapshot-store-fake.d.ts +9 -0
- package/dist/lifecycle/snapshot-store-fake.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store-fake.js +21 -0
- package/dist/lifecycle/snapshot-store-s3.d.ts +102 -0
- package/dist/lifecycle/snapshot-store-s3.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store-s3.js +254 -0
- package/dist/lifecycle/snapshot-store.d.ts +170 -0
- package/dist/lifecycle/snapshot-store.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store.js +832 -0
- package/dist/lifecycle/snapshot.d.ts +11 -0
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.js +18 -5
- package/dist/lifecycle/type-listing.d.ts +148 -0
- package/dist/lifecycle/type-listing.d.ts.map +1 -0
- package/dist/lifecycle/type-listing.js +166 -0
- package/dist/lint/component-checks.d.ts +15 -1
- package/dist/lint/component-checks.d.ts.map +1 -1
- package/dist/lint/component-checks.js +1 -0
- package/dist/lint/presets/relaxed.json +2 -1
- package/dist/lint/presets/strict.json +2 -1
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.js +13 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.js +8 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts +33 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.js +123 -0
- package/dist/lint/rules/comp/index.d.ts +2 -1
- package/dist/lint/rules/comp/index.d.ts.map +1 -1
- package/dist/lint/rules/comp/index.js +4 -1
- package/dist/observation.d.ts +19 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +18 -1
- package/dist/op/activities/activity-contracts.d.ts +1 -1
- package/dist/op/activities/activity-contracts.js +1 -1
- package/dist/op/activities/change-set.d.ts +6 -4
- package/dist/op/activities/change-set.d.ts.map +1 -1
- package/dist/op/activities/change-set.js +4 -2
- package/dist/op/activity-failure.d.ts +27 -0
- package/dist/op/activity-failure.d.ts.map +1 -1
- package/dist/op/activity-failure.js +31 -0
- package/dist/op/index.d.ts +2 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/index.js +1 -1
- package/dist/op/local-executor.d.ts +5 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +22 -8
- package/dist/pr-loop.d.ts +56 -1
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +51 -1
- package/dist/pulumi-urn.d.ts +101 -0
- package/dist/pulumi-urn.d.ts.map +1 -0
- package/dist/pulumi-urn.js +131 -0
- package/dist/release-floors.d.ts +23 -0
- package/dist/release-floors.d.ts.map +1 -0
- package/dist/release-floors.js +22 -0
- package/dist/sensitive-key.d.ts +23 -0
- package/dist/sensitive-key.d.ts.map +1 -0
- package/dist/sensitive-key.js +31 -0
- package/dist/wave-runtime.mjs +234 -234
- package/dist/workspace/adoption-report.d.ts +53 -0
- package/dist/workspace/adoption-report.d.ts.map +1 -0
- package/dist/workspace/adoption-report.js +76 -0
- package/dist/workspace/capabilities-contract.d.ts +7 -0
- package/dist/workspace/capabilities-contract.d.ts.map +1 -1
- package/dist/workspace/capabilities-contract.js +2 -1
- package/dist/workspace/capabilities.schema.json +98 -0
- package/dist/workspace/change-set.schema.json +42 -3
- package/dist/workspace/ci-cloud-identity.d.ts +13 -1
- package/dist/workspace/ci-cloud-identity.d.ts.map +1 -1
- package/dist/workspace/ci-cloud-identity.js +30 -1
- package/dist/workspace/compose-graph.d.ts +10 -1
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/compose-graph.js +6 -1
- package/dist/workspace/conformance/index.d.ts +1 -1
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/index.js +6 -1
- package/dist/workspace/drift-cli.d.ts +81 -0
- package/dist/workspace/drift-cli.d.ts.map +1 -0
- package/dist/workspace/drift-cli.js +317 -0
- package/dist/workspace/drift.schema.json +672 -0
- package/dist/workspace/graph-cli.d.ts +2 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/graph-cli.js +3 -0
- package/dist/workspace/graph.schema.json +72 -0
- package/dist/workspace/init.d.ts +5 -1
- package/dist/workspace/init.d.ts.map +1 -1
- package/dist/workspace/init.js +55 -9
- package/dist/workspace/ledger.schema.json +1 -1
- package/dist/workspace/nested-graph.d.ts.map +1 -1
- package/dist/workspace/nested-graph.js +2 -0
- package/dist/workspace/pr-report.schema.json +51 -2
- package/dist/workspace/promote-waves.d.ts +112 -0
- package/dist/workspace/promote-waves.d.ts.map +1 -0
- package/dist/workspace/promote-waves.js +290 -0
- package/dist/workspace/promote.d.ts +84 -2
- package/dist/workspace/promote.d.ts.map +1 -1
- package/dist/workspace/promote.js +347 -61
- package/dist/workspace/reason-codes.d.ts +12 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +14 -0
- package/dist/workspace/search-contract.d.ts +3 -3
- package/dist/workspace/search-contract.d.ts.map +1 -1
- package/dist/workspace/search-contract.js +2 -0
- package/dist/workspace/search.schema.json +103 -5
- package/package.json +14 -1
- package/src/__fixtures__/drift-record/README.md +58 -0
- package/src/__fixtures__/drift-record/choudoufu-cone.ndjson +4 -0
- package/src/__fixtures__/drift-record/choudoufu-resource.ndjson +3 -0
- package/src/__fixtures__/drift-record/component-status-ledger.json +65 -0
- package/src/__fixtures__/drift-record/component-status.json +91 -0
- package/src/__fixtures__/drift-record/deep-diff.json +259 -0
- package/src/__fixtures__/drift-record/lifecycle-diff-live.json +825 -0
- package/src/__fixtures__/drift-record/live-diff.json +125 -0
- package/src/audit/discover.e2e.test.ts +14 -0
- package/src/audit/discover.ts +3 -1
- package/src/behaviour.ts +1 -1
- package/src/capabilities/templates.json +145 -0
- package/src/capabilities/templates.test.ts +212 -0
- package/src/capabilities/templates.ts +204 -0
- package/src/change-set-bundle.test.ts +83 -0
- package/src/change-set-graph.ts +80 -9
- package/src/change-set.test.ts +24 -0
- package/src/change-set.ts +77 -14
- package/src/ci-identity-targets.test.ts +23 -0
- package/src/ci-identity.ts +10 -4
- package/src/cli/commands/import-index.test.ts +117 -0
- package/src/cli/commands/import-index.ts +265 -0
- package/src/cli/commands/lint.ts +4 -2
- package/src/cli/handlers/components.test.ts +9 -0
- package/src/cli/handlers/components.ts +12 -2
- package/src/cli/handlers/fan-out.ts +17 -3
- package/src/cli/handlers/graph.test.ts +42 -0
- package/src/cli/handlers/graph.ts +26 -4
- package/src/cli/handlers/index-cloud.ts +32 -0
- package/src/cli/handlers/index-diff.test.ts +68 -0
- package/src/cli/handlers/index-diff.ts +146 -0
- package/src/cli/handlers/ledger-root-member.test.ts +161 -0
- package/src/cli/handlers/lifecycle-drift.ts +68 -0
- package/src/cli/handlers/lifecycle.test.ts +33 -11
- package/src/cli/handlers/lifecycle.ts +205 -30
- package/src/cli/handlers/misc.ts +73 -0
- package/src/cli/handlers/pr-waves-blocked.test.ts +37 -2
- package/src/cli/handlers/pr.ts +14 -2
- package/src/cli/handlers/promote.test.ts +64 -0
- package/src/cli/handlers/promote.ts +81 -6
- package/src/cli/handlers/query.ts +27 -4
- package/src/cli/handlers/run-declared-reads-cli.test.ts +187 -0
- package/src/cli/handlers/run-groups-cli.test.ts +229 -0
- package/src/cli/handlers/scenario.test.ts +13 -0
- package/src/cli/handlers/scenario.ts +8 -7
- package/src/cli/handlers/search-index.test.ts +255 -0
- package/src/cli/handlers/search-json.test.ts +1 -1
- package/src/cli/handlers/search-ownership.test.ts +194 -0
- package/src/cli/handlers/search.ts +271 -38
- package/src/cli/main.test.ts +7 -0
- package/src/cli/main.ts +56 -1
- package/src/cli/mcp/lifecycle-tools.ts +3 -2
- package/src/cli/mcp/resource-handlers.ts +4 -3
- package/src/cli/mcp/workspace-tools.ts +1 -1
- package/src/cli/parse-args.ts +40 -2
- package/src/cli/registry.ts +19 -1
- package/src/cloud-lister/aws.test.ts +202 -0
- package/src/cloud-lister/aws.ts +226 -0
- package/src/cloud-lister/azure.test.ts +132 -0
- package/src/cloud-lister/azure.ts +142 -0
- package/src/cloud-lister/common.ts +115 -0
- package/src/cloud-lister/credentials.ts +110 -0
- package/src/cloud-lister/fakes.ts +66 -0
- package/src/cloud-lister/gcp.test.ts +121 -0
- package/src/cloud-lister/gcp.ts +159 -0
- package/src/cloud-lister/guard.test.ts +198 -0
- package/src/cloud-lister/guard.ts +111 -0
- package/src/cloud-lister/ids.ts +54 -0
- package/src/cloud-lister/index.ts +134 -0
- package/src/cloud-lister/kubernetes.test.ts +160 -0
- package/src/cloud-lister/kubernetes.ts +284 -0
- package/src/cloud-lister/markers.ts +74 -0
- package/src/cloud-lister/mask.ts +77 -0
- package/src/cloud-lister/shape.test.ts +81 -0
- package/src/cloud-lister/sigv4.ts +70 -0
- package/src/cloud-lister/transport.ts +118 -0
- package/src/cloud-lister/types.ts +135 -0
- package/src/components/capability-plugin-loader.ts +4 -0
- package/src/components/capability-plugin.ts +10 -0
- package/src/components/capability.ts +229 -0
- package/src/components/cli-support.ts +70 -14
- package/src/components/declared-reads.test.ts +103 -0
- package/src/components/declared-reads.ts +134 -0
- package/src/components/deploy-units.ts +4 -0
- package/src/components/driver.ts +89 -4
- package/src/components/fan-out-run.ts +95 -15
- package/src/components/fan-out-support.ts +10 -1
- package/src/components/fan-out.ts +30 -0
- package/src/components/groups.test.ts +320 -0
- package/src/components/groups.ts +880 -0
- package/src/components/index.ts +42 -0
- package/src/components/plan-shape-conformance.ts +130 -0
- package/src/components/pr-env-run-bundle.test.ts +2 -0
- package/src/components/pr-run.test.ts +516 -3
- package/src/components/pr-run.ts +231 -41
- package/src/components/promote-rounds.ts +25 -2
- package/src/components/promote.ts +55 -5
- package/src/components/registry.test.ts +1 -1
- package/src/components/release-gate.ts +115 -30
- package/src/components/release-groups.test.ts +362 -0
- package/src/components/run-groups.ts +113 -0
- package/src/components/starter-plugin.ts +6 -0
- package/src/components/verbs/index.ts +1 -0
- package/src/components/verbs/member-outputs.test.ts +205 -0
- package/src/components/verbs/member-outputs.ts +308 -0
- package/src/config.ts +44 -0
- package/src/deep-observation.ts +5 -23
- package/src/drift-record.test.ts +596 -0
- package/src/drift-record.ts +1206 -0
- package/src/engine-facts.guard.test.ts +111 -0
- package/src/engine-facts.test.ts +178 -0
- package/src/engine-facts.ts +154 -0
- package/src/gated-waves-bundle.test.ts +2 -0
- package/src/graph-ir-live.test.ts +92 -0
- package/src/graph-ir.ts +317 -7
- package/src/graph-refs.ts +416 -0
- package/src/graph-schema/fixtures/adopt.json +15 -0
- package/src/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
- package/src/graph-schema/fixtures/index.json +68 -0
- package/src/graph-schema/fixtures/plunge.engine-graph.json +158 -0
- package/src/graph-schema/fixtures.ts +267 -0
- package/src/graph-schema/generate.ts +263 -0
- package/src/graph-schema/graph-schema.test.ts +153 -0
- package/src/graph-schema/graph.schema.json +1387 -0
- package/src/health.schema.json +2 -1
- package/src/identity.ts +2 -0
- package/src/index.ts +2 -0
- package/src/instance-nodes.ts +288 -0
- package/src/lexicon.ts +76 -0
- package/src/lifecycle/__fixtures__/index-diff/20261026T120000Z-aaaaaaaaaaaa.json +149 -0
- package/src/lifecycle/__fixtures__/index-diff/20261026T130000Z-bbbbbbbbbbbb.json +127 -0
- package/src/lifecycle/adoption-file.test.ts +100 -0
- package/src/lifecycle/adoption-file.ts +154 -0
- package/src/lifecycle/cloud-index.test.ts +517 -0
- package/src/lifecycle/cloud-index.ts +736 -0
- package/src/lifecycle/derive-edges.test.ts +193 -0
- package/src/lifecycle/derive-edges.ts +182 -0
- package/src/lifecycle/git.ts +10 -4
- package/src/lifecycle/identity-match.test.ts +308 -0
- package/src/lifecycle/identity-match.ts +257 -0
- package/src/lifecycle/index-diff.test.ts +198 -0
- package/src/lifecycle/index-diff.ts +476 -0
- package/src/lifecycle/index-document.test.ts +64 -0
- package/src/lifecycle/index-document.ts +299 -0
- package/src/lifecycle/index-drift.test.ts +191 -0
- package/src/lifecycle/index-drift.ts +304 -0
- package/src/lifecycle/index-import.test.ts +255 -0
- package/src/lifecycle/index-import.ts +634 -0
- package/src/lifecycle/index-join.test.ts +250 -0
- package/src/lifecycle/index-join.ts +304 -0
- package/src/lifecycle/index-store-load-bearing.test.ts +187 -0
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/member-ledger.test.ts +25 -7
- package/src/lifecycle/observation-baseline.test.ts +72 -1
- package/src/lifecycle/observation-baseline.ts +190 -27
- package/src/lifecycle/observe.ts +6 -1
- package/src/lifecycle/query.test.ts +69 -1
- package/src/lifecycle/query.ts +103 -10
- package/src/lifecycle/replay.test.ts +9 -5
- package/src/lifecycle/replay.ts +9 -5
- package/src/lifecycle/scenario.ts +4 -3
- package/src/lifecycle/snapshot-store-fake.ts +29 -0
- package/src/lifecycle/snapshot-store-s3.ts +316 -0
- package/src/lifecycle/snapshot-store.test.ts +385 -0
- package/src/lifecycle/snapshot-store.ts +981 -0
- package/src/lifecycle/snapshot.test.ts +13 -0
- package/src/lifecycle/snapshot.ts +23 -6
- package/src/lifecycle/type-listing.test.ts +166 -0
- package/src/lifecycle/type-listing.ts +309 -0
- package/src/lint/component-checks.ts +17 -1
- package/src/lint/config.test.ts +3 -0
- package/src/lint/presets/relaxed.json +2 -1
- package/src/lint/presets/strict.json +2 -1
- package/src/lint/rules/__fixtures__/comp/comp008/fail/dns.component.ts +15 -0
- package/src/lint/rules/__fixtures__/comp/comp008/pass/dns.component.ts +10 -0
- package/src/lint/rules/comp/comp.test.ts +2 -1
- package/src/lint/rules/comp/comp008-applies-signal-only-root.ts +121 -0
- package/src/lint/rules/comp/comp008.test.ts +77 -0
- package/src/lint/rules/comp/index.ts +4 -1
- package/src/observation-edges.test.ts +33 -0
- package/src/observation.ts +36 -1
- package/src/op/activities/activity-contracts.ts +1 -1
- package/src/op/activities/change-set.ts +8 -5
- package/src/op/activity-failure.ts +36 -0
- package/src/op/index.ts +2 -1
- package/src/op/local-executor.test.ts +80 -0
- package/src/op/local-executor.ts +27 -9
- package/src/pr-loop-bundle.test.ts +5 -2
- package/src/pr-loop.test.ts +43 -0
- package/src/pr-loop.ts +111 -2
- package/src/pulumi-urn.ts +151 -0
- package/src/release-floors.ts +22 -0
- package/src/sensitive-key.ts +34 -0
- package/src/workspace/adoption-report.test.ts +61 -0
- package/src/workspace/adoption-report.ts +103 -0
- package/src/workspace/capabilities-contract.test.ts +4 -0
- package/src/workspace/capabilities-contract.ts +9 -1
- package/src/workspace/capabilities.schema.json +98 -0
- package/src/workspace/change-set.schema.json +42 -3
- package/src/workspace/ci-cloud-identity.ts +31 -1
- package/src/workspace/compose-graph.test.ts +12 -0
- package/src/workspace/compose-graph.ts +12 -1
- package/src/workspace/conformance/conformance.e2e.test.ts +2 -1
- package/src/workspace/conformance/index.ts +6 -1
- package/src/workspace/drift-cli.test.ts +65 -0
- package/src/workspace/drift-cli.ts +365 -0
- package/src/workspace/drift.schema.json +672 -0
- package/src/workspace/graph-cli.ts +5 -0
- package/src/workspace/graph.schema.json +72 -0
- package/src/workspace/init.test.ts +38 -1
- package/src/workspace/init.ts +50 -8
- package/src/workspace/intent-gaps.test.ts +18 -2
- package/src/workspace/ledger.schema.json +1 -1
- package/src/workspace/nested-graph.ts +1 -0
- package/src/workspace/pr-report.schema.json +51 -2
- package/src/workspace/promote-waves.test.ts +296 -0
- package/src/workspace/promote-waves.ts +340 -0
- package/src/workspace/promote.test.ts +158 -4
- package/src/workspace/promote.ts +398 -57
- package/src/workspace/reason-codes.test.ts +4 -1
- package/src/workspace/reason-codes.ts +14 -0
- package/src/workspace/search-contract.ts +2 -0
- package/src/workspace/search.schema.json +103 -5
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: what every substrate's lister shares: building one masked
|
|
3
|
+
* {@link ListedResource}, recording coverage and notes, and the coverage rule
|
|
4
|
+
* for a tag filter.
|
|
5
|
+
*/
|
|
6
|
+
import type { Transport, WireResponse } from "./transport.js";
|
|
7
|
+
import type { CloudId, CloudScope, CoverageEntry, IndexNote, ListedResource } from "./types.js";
|
|
8
|
+
export interface ListerContext {
|
|
9
|
+
scope: CloudScope;
|
|
10
|
+
transport: Transport;
|
|
11
|
+
now: () => Date;
|
|
12
|
+
resources: ListedResource[];
|
|
13
|
+
coverage: CoverageEntry[];
|
|
14
|
+
notes: IndexNote[];
|
|
15
|
+
}
|
|
16
|
+
export declare function buildResource(ctx: ListerContext, r: {
|
|
17
|
+
listedType: string;
|
|
18
|
+
location: string;
|
|
19
|
+
nativeId: string;
|
|
20
|
+
cloudIds: CloudId[];
|
|
21
|
+
tags: Record<string, string>;
|
|
22
|
+
properties: Record<string, unknown>;
|
|
23
|
+
observedAt: string;
|
|
24
|
+
labels: boolean;
|
|
25
|
+
}): ListedResource;
|
|
26
|
+
/**
|
|
27
|
+
* The verdict for a (type, location) the lister read in full. Under a tag
|
|
28
|
+
* filter it is `filtered`, because a filtered listing cannot tell an untagged
|
|
29
|
+
* object from a missing one (#1025 "Coverage"); the objects it did list are
|
|
30
|
+
* still observed.
|
|
31
|
+
*/
|
|
32
|
+
export declare function listedVerdict(scope: CloudScope): Pick<CoverageEntry, "verdict" | "detail">;
|
|
33
|
+
export declare function noteTagFilter(ctx: ListerContext, how: string): void;
|
|
34
|
+
export declare function sortResult(ctx: ListerContext): void;
|
|
35
|
+
/** A short, credential-free description of a failed response for a note or a coverage detail. */
|
|
36
|
+
export declare function describeFailure(res: WireResponse, code?: string, message?: string): string;
|
|
37
|
+
export declare const sleep: (ms: number) => Promise<void>;
|
|
38
|
+
/** Retries a throttled call (HTTP 429 or a substrate's throttling code) a few times with backoff. */
|
|
39
|
+
export declare function withThrottleRetry(send: () => Promise<WireResponse>, isThrottled: (res: WireResponse) => boolean, delays?: readonly number[]): Promise<WireResponse>;
|
|
40
|
+
//# sourceMappingURL=common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/cloud-lister/common.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE7F,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,wBAAgB,aAAa,CAC3B,GAAG,EAAE,aAAa,EAClB,CAAC,EAAE;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB,GACA,cAAc,CAehB;AAeD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,QAAQ,CAAC,CAK1F;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAMnE;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAKnD;AAED,iGAAiG;AACjG,wBAAgB,eAAe,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAK1F;AAED,eAAO,MAAM,KAAK,OAAQ,MAAM,kBAAgD,CAAC;AAEjF,qGAAqG;AACrG,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,EACjC,WAAW,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,EAC3C,MAAM,GAAE,SAAS,MAAM,EAAsB,GAC5C,OAAO,CAAC,YAAY,CAAC,CAQvB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: what every substrate's lister shares: building one masked
|
|
3
|
+
* {@link ListedResource}, recording coverage and notes, and the coverage rule
|
|
4
|
+
* for a tag filter.
|
|
5
|
+
*/
|
|
6
|
+
import { listedNodeId } from "./ids.js";
|
|
7
|
+
import { readMarkers } from "./markers.js";
|
|
8
|
+
import { maskProperties, maskTags } from "./mask.js";
|
|
9
|
+
export function buildResource(ctx, r) {
|
|
10
|
+
const tags = maskTags(r.tags);
|
|
11
|
+
const markers = readMarkers(r.tags, { labels: r.labels });
|
|
12
|
+
const cloudIds = dedupeCloudIds(r.cloudIds);
|
|
13
|
+
return {
|
|
14
|
+
id: listedNodeId({ substrate: ctx.scope.substrate, scopeId: ctx.scope.scopeId, location: r.location, listedType: r.listedType, nativeId: r.nativeId }),
|
|
15
|
+
listedType: r.listedType,
|
|
16
|
+
location: r.location,
|
|
17
|
+
nativeId: r.nativeId,
|
|
18
|
+
cloudIds,
|
|
19
|
+
tags,
|
|
20
|
+
properties: maskProperties(r.properties),
|
|
21
|
+
observedAt: r.observedAt,
|
|
22
|
+
facts: { ...(markers ? { markers } : {}), cloudIds, observedAt: r.observedAt },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function dedupeCloudIds(ids) {
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
const out = [];
|
|
28
|
+
for (const id of ids) {
|
|
29
|
+
if (!id.value)
|
|
30
|
+
continue;
|
|
31
|
+
const key = `${id.kind}\u0000${id.value}`;
|
|
32
|
+
if (seen.has(key))
|
|
33
|
+
continue;
|
|
34
|
+
seen.add(key);
|
|
35
|
+
out.push(id);
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The verdict for a (type, location) the lister read in full. Under a tag
|
|
41
|
+
* filter it is `filtered`, because a filtered listing cannot tell an untagged
|
|
42
|
+
* object from a missing one (#1025 "Coverage"); the objects it did list are
|
|
43
|
+
* still observed.
|
|
44
|
+
*/
|
|
45
|
+
export function listedVerdict(scope) {
|
|
46
|
+
if (scope.tagFilter && Object.keys(scope.tagFilter).length > 0) {
|
|
47
|
+
return { verdict: "filtered", detail: `tag filter ${JSON.stringify(scope.tagFilter)}` };
|
|
48
|
+
}
|
|
49
|
+
return { verdict: "listed" };
|
|
50
|
+
}
|
|
51
|
+
export function noteTagFilter(ctx, how) {
|
|
52
|
+
if (!ctx.scope.tagFilter || Object.keys(ctx.scope.tagFilter).length === 0)
|
|
53
|
+
return;
|
|
54
|
+
ctx.notes.push({
|
|
55
|
+
code: "tag-filter",
|
|
56
|
+
message: `the tag filter ${JSON.stringify(ctx.scope.tagFilter)} was applied ${how}; every coverage entry is "filtered", since a tag-filtered listing cannot say an untagged object is absent`,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
export function sortResult(ctx) {
|
|
60
|
+
ctx.resources.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
61
|
+
ctx.coverage.sort((a, b) => a.listedType !== b.listedType ? (a.listedType < b.listedType ? -1 : 1) : a.location < b.location ? -1 : a.location > b.location ? 1 : 0);
|
|
62
|
+
}
|
|
63
|
+
/** A short, credential-free description of a failed response for a note or a coverage detail. */
|
|
64
|
+
export function describeFailure(res, code, message) {
|
|
65
|
+
const parts = [`HTTP ${res.status}`];
|
|
66
|
+
if (code)
|
|
67
|
+
parts.push(code);
|
|
68
|
+
if (message)
|
|
69
|
+
parts.push(message.length > 300 ? `${message.slice(0, 300)}...` : message);
|
|
70
|
+
return parts.join(": ");
|
|
71
|
+
}
|
|
72
|
+
export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
73
|
+
/** Retries a throttled call (HTTP 429 or a substrate's throttling code) a few times with backoff. */
|
|
74
|
+
export async function withThrottleRetry(send, isThrottled, delays = [500, 2000, 5000]) {
|
|
75
|
+
let res = await send();
|
|
76
|
+
for (const d of delays) {
|
|
77
|
+
if (!isThrottled(res))
|
|
78
|
+
return res;
|
|
79
|
+
await sleep(d);
|
|
80
|
+
res = await send();
|
|
81
|
+
}
|
|
82
|
+
return res;
|
|
83
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: where the lister's credentials come from.
|
|
3
|
+
*
|
|
4
|
+
* The goal's hard rule: against an emulator the lister takes explicit
|
|
5
|
+
* endpoints and static credentials only, and never the AWS SDK's default
|
|
6
|
+
* provider chain, Azure's DefaultAzureCredential or google-auth's
|
|
7
|
+
* application-default credentials. So:
|
|
8
|
+
*
|
|
9
|
+
* - a target that names an `endpoint` must carry its credentials (a static
|
|
10
|
+
* AWS key pair, an Azure bearer token, a GCP token or an explicit `null`
|
|
11
|
+
* for an anonymous emulator). Without them the run is refused with
|
|
12
|
+
* {@link ListerConfigError}, and no chain is loaded;
|
|
13
|
+
* - only a target with no endpoint (a real cloud, which the maintainer runs)
|
|
14
|
+
* may fall back to a cloud SDK's chain. Those SDKs are optional peers,
|
|
15
|
+
* imported lazily by name, so chant's install and bundle do not grow for
|
|
16
|
+
* people who never index; their absence is a clear error naming the
|
|
17
|
+
* package.
|
|
18
|
+
*
|
|
19
|
+
* The lister itself reads no environment variable. `chant index` (chant#4532)
|
|
20
|
+
* turns its flags into these targets.
|
|
21
|
+
*/
|
|
22
|
+
export declare class ListerConfigError extends Error {
|
|
23
|
+
constructor(message: string);
|
|
24
|
+
}
|
|
25
|
+
export interface AwsStaticCredentials {
|
|
26
|
+
accessKeyId: string;
|
|
27
|
+
secretAccessKey: string;
|
|
28
|
+
sessionToken?: string;
|
|
29
|
+
}
|
|
30
|
+
/** The SDK chains a real-cloud run may use; replaced in tests so a test can prove none was touched. */
|
|
31
|
+
export interface CredentialChains {
|
|
32
|
+
aws(): Promise<AwsStaticCredentials>;
|
|
33
|
+
azure(): Promise<string>;
|
|
34
|
+
gcp(): Promise<string>;
|
|
35
|
+
}
|
|
36
|
+
export declare const sdkCredentialChains: CredentialChains;
|
|
37
|
+
export declare function resolveAwsCredentials(target: {
|
|
38
|
+
endpoint?: string;
|
|
39
|
+
credentials?: AwsStaticCredentials;
|
|
40
|
+
}, chains: CredentialChains): Promise<AwsStaticCredentials>;
|
|
41
|
+
export declare function resolveAzureToken(target: {
|
|
42
|
+
endpoint?: string;
|
|
43
|
+
token?: string;
|
|
44
|
+
}, chains: CredentialChains): Promise<string>;
|
|
45
|
+
/** `null` is an explicit anonymous call (floci-gcp with IAM enforcement off). */
|
|
46
|
+
export declare function resolveGcpToken(target: {
|
|
47
|
+
endpoint?: string;
|
|
48
|
+
token?: string | null;
|
|
49
|
+
}, chains: CredentialChains): Promise<string | null>;
|
|
50
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/cloud-lister/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,uGAAuG;AACvG,MAAM,WAAW,gBAAgB;IAC/B,GAAG,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACrC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACxB;AAcD,eAAO,MAAM,mBAAmB,EAAE,gBAqBjC,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,oBAAoB,CAAA;CAAE,EACjE,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,oBAAoB,CAAC,CAM/B;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAC7C,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,MAAM,CAAC,CAMjB;AAED,iFAAiF;AACjF,wBAAsB,eAAe,CACnC,MAAM,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACpD,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: where the lister's credentials come from.
|
|
3
|
+
*
|
|
4
|
+
* The goal's hard rule: against an emulator the lister takes explicit
|
|
5
|
+
* endpoints and static credentials only, and never the AWS SDK's default
|
|
6
|
+
* provider chain, Azure's DefaultAzureCredential or google-auth's
|
|
7
|
+
* application-default credentials. So:
|
|
8
|
+
*
|
|
9
|
+
* - a target that names an `endpoint` must carry its credentials (a static
|
|
10
|
+
* AWS key pair, an Azure bearer token, a GCP token or an explicit `null`
|
|
11
|
+
* for an anonymous emulator). Without them the run is refused with
|
|
12
|
+
* {@link ListerConfigError}, and no chain is loaded;
|
|
13
|
+
* - only a target with no endpoint (a real cloud, which the maintainer runs)
|
|
14
|
+
* may fall back to a cloud SDK's chain. Those SDKs are optional peers,
|
|
15
|
+
* imported lazily by name, so chant's install and bundle do not grow for
|
|
16
|
+
* people who never index; their absence is a clear error naming the
|
|
17
|
+
* package.
|
|
18
|
+
*
|
|
19
|
+
* The lister itself reads no environment variable. `chant index` (chant#4532)
|
|
20
|
+
* turns its flags into these targets.
|
|
21
|
+
*/
|
|
22
|
+
export class ListerConfigError extends Error {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "ListerConfigError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Imports a module by a name the bundler and the type checker do not follow. */
|
|
29
|
+
async function optionalPeer(name) {
|
|
30
|
+
const specifier = name;
|
|
31
|
+
try {
|
|
32
|
+
return (await import(specifier));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new ListerConfigError(`listing a real cloud without explicit credentials needs the optional package ${name}; install it beside chant, or pass credentials`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export const sdkCredentialChains = {
|
|
39
|
+
async aws() {
|
|
40
|
+
const mod = await optionalPeer("@aws-sdk/credential-providers");
|
|
41
|
+
const chain = mod.fromNodeProviderChain();
|
|
42
|
+
const c = await chain();
|
|
43
|
+
return { accessKeyId: c.accessKeyId, secretAccessKey: c.secretAccessKey, ...(c.sessionToken ? { sessionToken: c.sessionToken } : {}) };
|
|
44
|
+
},
|
|
45
|
+
async azure() {
|
|
46
|
+
const mod = await optionalPeer("@azure/identity");
|
|
47
|
+
const Ctor = mod.DefaultAzureCredential;
|
|
48
|
+
const t = await new Ctor().getToken("https://management.azure.com/.default");
|
|
49
|
+
if (!t)
|
|
50
|
+
throw new ListerConfigError("DefaultAzureCredential returned no token");
|
|
51
|
+
return t.token;
|
|
52
|
+
},
|
|
53
|
+
async gcp() {
|
|
54
|
+
const mod = await optionalPeer("google-auth-library");
|
|
55
|
+
const Ctor = mod.GoogleAuth;
|
|
56
|
+
const t = await new Ctor({ scopes: ["https://www.googleapis.com/auth/cloud-platform.read-only"] }).getAccessToken();
|
|
57
|
+
if (!t)
|
|
58
|
+
throw new ListerConfigError("google-auth returned no access token");
|
|
59
|
+
return t;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
export async function resolveAwsCredentials(target, chains) {
|
|
63
|
+
if (target.credentials)
|
|
64
|
+
return target.credentials;
|
|
65
|
+
if (target.endpoint !== undefined) {
|
|
66
|
+
throw new ListerConfigError(`aws endpoint ${target.endpoint} needs static credentials (accessKeyId and secretAccessKey); the default provider chain is never used with an endpoint`);
|
|
67
|
+
}
|
|
68
|
+
return chains.aws();
|
|
69
|
+
}
|
|
70
|
+
export async function resolveAzureToken(target, chains) {
|
|
71
|
+
if (target.token !== undefined)
|
|
72
|
+
return target.token;
|
|
73
|
+
if (target.endpoint !== undefined) {
|
|
74
|
+
throw new ListerConfigError(`azure endpoint ${target.endpoint} needs a static bearer token; DefaultAzureCredential is never used with an endpoint`);
|
|
75
|
+
}
|
|
76
|
+
return chains.azure();
|
|
77
|
+
}
|
|
78
|
+
/** `null` is an explicit anonymous call (floci-gcp with IAM enforcement off). */
|
|
79
|
+
export async function resolveGcpToken(target, chains) {
|
|
80
|
+
if (target.token !== undefined)
|
|
81
|
+
return target.token;
|
|
82
|
+
if (target.endpoint !== undefined) {
|
|
83
|
+
throw new ListerConfigError(`gcp endpoint ${target.endpoint} needs a static token, or token: null for an anonymous emulator; application-default credentials are never used with an endpoint`);
|
|
84
|
+
}
|
|
85
|
+
return chains.gcp();
|
|
86
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: test doubles for the lister. Nothing here opens a socket: a
|
|
3
|
+
* fake fetcher answers from a route function and throws on any call it was
|
|
4
|
+
* not given an answer for, and fake credential chains count their calls so a
|
|
5
|
+
* test can show none was touched.
|
|
6
|
+
*/
|
|
7
|
+
import type { WireCall } from "./guard.js";
|
|
8
|
+
import type { CredentialChains } from "./credentials.js";
|
|
9
|
+
import type { Fetcher, WireResponse } from "./transport.js";
|
|
10
|
+
export declare function json(status: number, body: unknown, headers?: Record<string, string>): WireResponse;
|
|
11
|
+
export interface FakeFetcher {
|
|
12
|
+
fetcher: Fetcher;
|
|
13
|
+
calls: WireCall[];
|
|
14
|
+
}
|
|
15
|
+
export declare function fakeFetcher(route: (call: WireCall) => WireResponse | undefined): FakeFetcher;
|
|
16
|
+
export interface CountingChains extends CredentialChains {
|
|
17
|
+
count: {
|
|
18
|
+
aws: number;
|
|
19
|
+
azure: number;
|
|
20
|
+
gcp: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Chains that fail if used; `count` says how often each was asked. */
|
|
24
|
+
export declare function refusingChains(): CountingChains;
|
|
25
|
+
/** A fixed clock that advances one second per reading, so timestamps are predictable. */
|
|
26
|
+
export declare function steppingClock(start?: string): () => Date;
|
|
27
|
+
//# sourceMappingURL=fakes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fakes.d.ts","sourceRoot":"","sources":["../../src/cloud-lister/fakes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzD,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,YAAY,CAEtG;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,YAAY,GAAG,SAAS,GAAG,WAAW,CAW5F;AAED,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CACpD;AAED,uEAAuE;AACvE,wBAAgB,cAAc,IAAI,cAAc,CAiB/C;AAED,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,KAAK,SAAyB,GAAG,MAAM,IAAI,CAOxE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: test doubles for the lister. Nothing here opens a socket: a
|
|
3
|
+
* fake fetcher answers from a route function and throws on any call it was
|
|
4
|
+
* not given an answer for, and fake credential chains count their calls so a
|
|
5
|
+
* test can show none was touched.
|
|
6
|
+
*/
|
|
7
|
+
export function json(status, body, headers = {}) {
|
|
8
|
+
return { status, headers: { "content-type": "application/json", ...headers }, body: JSON.stringify(body) };
|
|
9
|
+
}
|
|
10
|
+
export function fakeFetcher(route) {
|
|
11
|
+
const calls = [];
|
|
12
|
+
return {
|
|
13
|
+
calls,
|
|
14
|
+
fetcher: async (call) => {
|
|
15
|
+
calls.push(call);
|
|
16
|
+
const res = route(call);
|
|
17
|
+
if (!res)
|
|
18
|
+
throw new Error(`fake fetcher has no answer for ${call.method} ${call.url}`);
|
|
19
|
+
return res;
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Chains that fail if used; `count` says how often each was asked. */
|
|
24
|
+
export function refusingChains() {
|
|
25
|
+
const count = { aws: 0, azure: 0, gcp: 0 };
|
|
26
|
+
return {
|
|
27
|
+
count,
|
|
28
|
+
async aws() {
|
|
29
|
+
count.aws++;
|
|
30
|
+
throw new Error("the AWS default provider chain was asked for credentials");
|
|
31
|
+
},
|
|
32
|
+
async azure() {
|
|
33
|
+
count.azure++;
|
|
34
|
+
throw new Error("DefaultAzureCredential was asked for a token");
|
|
35
|
+
},
|
|
36
|
+
async gcp() {
|
|
37
|
+
count.gcp++;
|
|
38
|
+
throw new Error("application-default credentials were asked for a token");
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** A fixed clock that advances one second per reading, so timestamps are predictable. */
|
|
43
|
+
export function steppingClock(start = "2026-10-26T12:00:00Z") {
|
|
44
|
+
let t = Date.parse(start);
|
|
45
|
+
return () => {
|
|
46
|
+
const d = new Date(t);
|
|
47
|
+
t += 1000;
|
|
48
|
+
return d;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: GCP, through Cloud Asset Inventory's `searchAllResources` on
|
|
3
|
+
* one project, paged.
|
|
4
|
+
*
|
|
5
|
+
* Nothing is enabled for the person. The Cloud Asset API has to be on before
|
|
6
|
+
* a project can be searched, and turning it on changes the project. When the
|
|
7
|
+
* search answers SERVICE_DISABLED, the lister lists nothing for the project,
|
|
8
|
+
* marks every coverage entry `read-failed`, and puts the enable command in a
|
|
9
|
+
* note for the person to run. The guard (packages/core/src/cloud-lister/guard.ts) admits no
|
|
10
|
+
* Service Usage call at all, so the lister could not enable it if it tried.
|
|
11
|
+
*
|
|
12
|
+
* Types go in `assetTypes`, a tag filter in the query as `labels.KEY=VALUE`
|
|
13
|
+
* terms. Locations are filtered here, after the search, because floci-gcp's
|
|
14
|
+
* query subset has no location term.
|
|
15
|
+
*
|
|
16
|
+
* The pinned floci-gcp (ghcr.io/lex00/floci-gcp, source 641e6e8) serves this
|
|
17
|
+
* call under the path prefix `/cloudasset` (pass `http://localhost:4588/cloudasset`
|
|
18
|
+
* as the endpoint), for project scopes only, over the assets of four
|
|
19
|
+
* services (Compute, Cloud Storage, IAM, Pub/Sub), with SERVICE_DISABLED
|
|
20
|
+
* answered as GCP answers it when its Service Usage enforcement is on.
|
|
21
|
+
*/
|
|
22
|
+
import { type ListerContext } from "./common.js";
|
|
23
|
+
import { type CredentialChains } from "./credentials.js";
|
|
24
|
+
import { type TlsOptions, type WireResponse } from "./transport.js";
|
|
25
|
+
export interface GcpTarget {
|
|
26
|
+
/** An emulator's Cloud Asset base, e.g. `http://localhost:4588/cloudasset`. Absent means `https://cloudasset.googleapis.com`. */
|
|
27
|
+
endpoint?: string;
|
|
28
|
+
/** An OAuth access token; `null` sends none (an emulator with IAM enforcement off). Required with an endpoint. */
|
|
29
|
+
token?: string | null;
|
|
30
|
+
tls?: TlsOptions;
|
|
31
|
+
}
|
|
32
|
+
export declare function enableCommand(project: string): string;
|
|
33
|
+
export declare function searchUrl(target: GcpTarget, project: string, params: {
|
|
34
|
+
assetTypes?: readonly string[];
|
|
35
|
+
query?: string;
|
|
36
|
+
pageToken?: string;
|
|
37
|
+
}): string;
|
|
38
|
+
/** The `query` for a tag filter, or undefined. Exported for tests. */
|
|
39
|
+
export declare function labelQuery(filter: Readonly<Record<string, string>> | undefined): string | undefined;
|
|
40
|
+
export declare function isServiceDisabled(res: WireResponse): boolean;
|
|
41
|
+
export declare function listGcp(ctx: ListerContext, target: GcpTarget, chains: CredentialChains, retryDelays?: readonly number[]): Promise<void>;
|
|
42
|
+
//# sourceMappingURL=gcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp.d.ts","sourceRoot":"","sources":["../../src/cloud-lister/gcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAmF,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC/H,OAAO,EAAsC,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAY,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3E,MAAM,WAAW,SAAS;IACxB,iIAAiI;IACjI,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kHAAkH;IAClH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,GAAG,CAAC,EAAE,UAAU,CAAC;CAClB;AAOD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAQpJ;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CASnG;AAoBD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAI5D;AAED,wBAAsB,OAAO,CAC3B,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAC9B,OAAO,CAAC,IAAI,CAAC,CA+Df"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: GCP, through Cloud Asset Inventory's `searchAllResources` on
|
|
3
|
+
* one project, paged.
|
|
4
|
+
*
|
|
5
|
+
* Nothing is enabled for the person. The Cloud Asset API has to be on before
|
|
6
|
+
* a project can be searched, and turning it on changes the project. When the
|
|
7
|
+
* search answers SERVICE_DISABLED, the lister lists nothing for the project,
|
|
8
|
+
* marks every coverage entry `read-failed`, and puts the enable command in a
|
|
9
|
+
* note for the person to run. The guard (packages/core/src/cloud-lister/guard.ts) admits no
|
|
10
|
+
* Service Usage call at all, so the lister could not enable it if it tried.
|
|
11
|
+
*
|
|
12
|
+
* Types go in `assetTypes`, a tag filter in the query as `labels.KEY=VALUE`
|
|
13
|
+
* terms. Locations are filtered here, after the search, because floci-gcp's
|
|
14
|
+
* query subset has no location term.
|
|
15
|
+
*
|
|
16
|
+
* The pinned floci-gcp (ghcr.io/lex00/floci-gcp, source 641e6e8) serves this
|
|
17
|
+
* call under the path prefix `/cloudasset` (pass `http://localhost:4588/cloudasset`
|
|
18
|
+
* as the endpoint), for project scopes only, over the assets of four
|
|
19
|
+
* services (Compute, Cloud Storage, IAM, Pub/Sub), with SERVICE_DISABLED
|
|
20
|
+
* answered as GCP answers it when its Service Usage enforcement is on.
|
|
21
|
+
*/
|
|
22
|
+
import { buildResource, describeFailure, listedVerdict, noteTagFilter, withThrottleRetry } from "./common.js";
|
|
23
|
+
import { resolveGcpToken, ListerConfigError } from "./credentials.js";
|
|
24
|
+
import { jsonBody } from "./transport.js";
|
|
25
|
+
const PAGE_SIZE = 500;
|
|
26
|
+
const MAX_PAGES = 10000;
|
|
27
|
+
const PROJECT = /^[A-Za-z0-9._:-]+$/;
|
|
28
|
+
const LABEL_PART = /^[A-Za-z0-9_\-]+$/;
|
|
29
|
+
export function enableCommand(project) {
|
|
30
|
+
return `gcloud services enable cloudasset.googleapis.com --project=${project}`;
|
|
31
|
+
}
|
|
32
|
+
export function searchUrl(target, project, params) {
|
|
33
|
+
const base = (target.endpoint ?? "https://cloudasset.googleapis.com").replace(/\/+$/, "");
|
|
34
|
+
const url = new URL(`${base}/v1/projects/${encodeURIComponent(project)}:searchAllResources`);
|
|
35
|
+
for (const t of params.assetTypes ?? [])
|
|
36
|
+
url.searchParams.append("assetTypes", t);
|
|
37
|
+
if (params.query)
|
|
38
|
+
url.searchParams.set("query", params.query);
|
|
39
|
+
url.searchParams.set("pageSize", String(PAGE_SIZE));
|
|
40
|
+
if (params.pageToken)
|
|
41
|
+
url.searchParams.set("pageToken", params.pageToken);
|
|
42
|
+
return url.toString();
|
|
43
|
+
}
|
|
44
|
+
/** The `query` for a tag filter, or undefined. Exported for tests. */
|
|
45
|
+
export function labelQuery(filter) {
|
|
46
|
+
const entries = Object.entries(filter ?? {});
|
|
47
|
+
if (entries.length === 0)
|
|
48
|
+
return undefined;
|
|
49
|
+
return entries
|
|
50
|
+
.map(([k, v]) => {
|
|
51
|
+
if (!LABEL_PART.test(k) || !LABEL_PART.test(v))
|
|
52
|
+
throw new ListerConfigError(`gcp label filter ${k}=${v} is not a plain label key and value`);
|
|
53
|
+
return `labels.${k}=${v}`;
|
|
54
|
+
})
|
|
55
|
+
.join(" AND ");
|
|
56
|
+
}
|
|
57
|
+
function gcpError(res) {
|
|
58
|
+
const body = jsonBody(res);
|
|
59
|
+
const e = Array.isArray(body) ? body[0]?.error : body?.error;
|
|
60
|
+
const reasons = [...(e?.details ?? []), ...(e?.errors ?? [])]
|
|
61
|
+
.map((d) => d.reason ?? "")
|
|
62
|
+
.filter(Boolean);
|
|
63
|
+
return { status: e?.status ?? "", message: e?.message ?? "", reasons };
|
|
64
|
+
}
|
|
65
|
+
export function isServiceDisabled(res) {
|
|
66
|
+
if (res.status !== 403)
|
|
67
|
+
return false;
|
|
68
|
+
const err = gcpError(res);
|
|
69
|
+
return err.reasons.includes("SERVICE_DISABLED") || err.reasons.includes("accessNotConfigured") || /has not been used in project|is disabled/i.test(err.message);
|
|
70
|
+
}
|
|
71
|
+
export async function listGcp(ctx, target, chains, retryDelays) {
|
|
72
|
+
const { scope } = ctx;
|
|
73
|
+
if (!PROJECT.test(scope.scopeId))
|
|
74
|
+
throw new ListerConfigError(`gcp project ${JSON.stringify(scope.scopeId)} is not a project id`);
|
|
75
|
+
const token = await resolveGcpToken(target, chains);
|
|
76
|
+
const query = labelQuery(scope.tagFilter);
|
|
77
|
+
const assetTypes = scope.types === "*" ? undefined : scope.types;
|
|
78
|
+
const wantLocations = scope.locations === "*" ? undefined : new Set(scope.locations.map((l) => l.toLowerCase()));
|
|
79
|
+
const typesCovered = scope.types === "*" ? ["*"] : [...scope.types];
|
|
80
|
+
const locationsCovered = scope.locations === "*" ? ["*"] : [...scope.locations].map((l) => l.toLowerCase());
|
|
81
|
+
noteTagFilter(ctx, "as labels terms in the Cloud Asset query");
|
|
82
|
+
const found = [];
|
|
83
|
+
let pageToken;
|
|
84
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
85
|
+
const url = searchUrl(target, scope.scopeId, { assetTypes, query, pageToken });
|
|
86
|
+
const headers = { accept: "application/json" };
|
|
87
|
+
if (token !== null)
|
|
88
|
+
headers.authorization = `Bearer ${token}`;
|
|
89
|
+
const res = await withThrottleRetry(() => ctx.transport.send({ substrate: "gcp", method: "GET", url, headers }), (r) => r.status === 429, retryDelays);
|
|
90
|
+
const observedAt = ctx.now().toISOString();
|
|
91
|
+
if (res.status >= 400) {
|
|
92
|
+
const err = gcpError(res);
|
|
93
|
+
const detail = describeFailure(res, err.status, err.message);
|
|
94
|
+
if (isServiceDisabled(res)) {
|
|
95
|
+
const why = `Cloud Asset API is disabled on project ${scope.scopeId}`;
|
|
96
|
+
for (const t of typesCovered)
|
|
97
|
+
for (const l of locationsCovered)
|
|
98
|
+
ctx.coverage.push({ listedType: t, location: l, verdict: "read-failed", detail: why });
|
|
99
|
+
ctx.notes.push({
|
|
100
|
+
code: "api-disabled",
|
|
101
|
+
message: `${why}, so nothing was listed for it. chant never enables an API; enabling it changes the project. To list it, run the command yourself and index again.`,
|
|
102
|
+
command: enableCommand(scope.scopeId),
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const verdict = res.status === 401 || res.status === 403 ? "no-credentials" : "read-failed";
|
|
107
|
+
for (const t of typesCovered)
|
|
108
|
+
for (const l of locationsCovered)
|
|
109
|
+
ctx.coverage.push({ listedType: t, location: l, verdict, detail });
|
|
110
|
+
ctx.notes.push({ code: verdict === "no-credentials" ? "no-credentials" : "read-failed", message: `searchAllResources on project ${scope.scopeId} failed: ${detail}` });
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const parsed = jsonBody(res);
|
|
114
|
+
for (const r of parsed?.results ?? []) {
|
|
115
|
+
const name = typeof r.name === "string" ? r.name : undefined;
|
|
116
|
+
const assetType = typeof r.assetType === "string" ? r.assetType : undefined;
|
|
117
|
+
if (!name || !assetType)
|
|
118
|
+
continue;
|
|
119
|
+
const location = (typeof r.location === "string" && r.location ? r.location : "global").toLowerCase();
|
|
120
|
+
if (wantLocations && !wantLocations.has(location))
|
|
121
|
+
continue;
|
|
122
|
+
const tags = {};
|
|
123
|
+
if (r.labels && typeof r.labels === "object") {
|
|
124
|
+
for (const [k, v] of Object.entries(r.labels))
|
|
125
|
+
tags[k] = String(v ?? "");
|
|
126
|
+
}
|
|
127
|
+
const cloudIds = [{ kind: "provider-id", value: name }];
|
|
128
|
+
const attrs = r.additionalAttributes;
|
|
129
|
+
if (attrs && typeof attrs.selfLink === "string")
|
|
130
|
+
cloudIds.push({ kind: "self-link", value: attrs.selfLink });
|
|
131
|
+
const { labels: _labels, ...properties } = r;
|
|
132
|
+
found.push({ listedType: assetType, location, nativeId: name, cloudIds, tags, properties, observedAt, labels: true });
|
|
133
|
+
}
|
|
134
|
+
pageToken = parsed?.nextPageToken || undefined;
|
|
135
|
+
if (!pageToken)
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
for (const r of found)
|
|
139
|
+
ctx.resources.push(buildResource(ctx, r));
|
|
140
|
+
for (const t of typesCovered)
|
|
141
|
+
for (const l of locationsCovered)
|
|
142
|
+
ctx.coverage.push({ listedType: t, location: l, ...listedVerdict(scope) });
|
|
143
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* behold#1029: read-only by construction.
|
|
3
|
+
*
|
|
4
|
+
* Every request the lister makes is a {@link WireCall}, and every WireCall
|
|
5
|
+
* goes through {@link assertReadCall} before anything reaches the network
|
|
6
|
+
* (packages/core/src/cloud-lister/transport.ts is the only sender). The check classifies the
|
|
7
|
+
* call from what would go on the wire (method, path, `X-Amz-Target`), never
|
|
8
|
+
* from a label the caller attaches, and admits only the operations in
|
|
9
|
+
* {@link READ_OPERATIONS}:
|
|
10
|
+
*
|
|
11
|
+
* - aws: Cloud Control `ListResources` (a POST, but the JSON protocol's only
|
|
12
|
+
* verb; the target header names the operation);
|
|
13
|
+
* - azure: Resource Graph's query, `POST /providers/Microsoft.ResourceGraph/resources`
|
|
14
|
+
* (a read that takes its KQL in the body);
|
|
15
|
+
* - gcp: Cloud Asset `searchAllResources`, a GET. Nothing under
|
|
16
|
+
* `serviceusage` is admitted, so the lister cannot enable the Cloud Asset
|
|
17
|
+
* API on a project (#1029: "nothing is enabled for the person");
|
|
18
|
+
* - kubernetes: GET discovery (`/api`, `/apis`, `/api/v1`, `/apis/<g>/<v>`)
|
|
19
|
+
* and GET collection lists, never `watch`.
|
|
20
|
+
*
|
|
21
|
+
* packages/core/src/cloud-lister/guard.test.ts holds the allowlist to exactly this set and
|
|
22
|
+
* shows a mutating call of each substrate refused.
|
|
23
|
+
*/
|
|
24
|
+
import type { Substrate } from "./types.js";
|
|
25
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
|
|
26
|
+
/** One HTTP request as it would be sent. */
|
|
27
|
+
export interface WireCall {
|
|
28
|
+
substrate: Substrate;
|
|
29
|
+
method: HttpMethod;
|
|
30
|
+
url: string;
|
|
31
|
+
headers: Record<string, string>;
|
|
32
|
+
body?: string;
|
|
33
|
+
}
|
|
34
|
+
/** The only operations the lister may send, by substrate. */
|
|
35
|
+
export declare const READ_OPERATIONS: Readonly<Record<Substrate, readonly string[]>>;
|
|
36
|
+
export declare class MutatingCallRefused extends Error {
|
|
37
|
+
readonly call: Pick<WireCall, "substrate" | "method" | "url">;
|
|
38
|
+
readonly operation: string;
|
|
39
|
+
constructor(call: Pick<WireCall, "substrate" | "method" | "url">, operation: string);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The operation a call performs, read off the wire. Anything this cannot
|
|
43
|
+
* name precisely comes back as `<substrate>:unknown:<detail>`, which no
|
|
44
|
+
* allowlist entry matches.
|
|
45
|
+
*/
|
|
46
|
+
export declare function classifyCall(call: WireCall): string;
|
|
47
|
+
/** Throws {@link MutatingCallRefused} unless the call is one of {@link READ_OPERATIONS}. */
|
|
48
|
+
export declare function assertReadCall(call: WireCall): string;
|
|
49
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/cloud-lister/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAE1F,4CAA4C;AAC5C,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,6DAA6D;AAC7D,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC,CAK1E,CAAC;AAEF,qBAAa,mBAAoB,SAAQ,KAAK;IAE1C,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,QAAQ,GAAG,KAAK,CAAC;IAC7D,QAAQ,CAAC,SAAS,EAAE,MAAM;IAF5B,YACW,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,QAAQ,GAAG,KAAK,CAAC,EACpD,SAAS,EAAE,MAAM,EAI3B;CACF;AAQD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAqCnD;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAIrD"}
|