@intentius/chant-lexicon-terraform 0.124.52 → 0.124.55
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/README.md +14 -10
- package/dist/__fixtures__/pr-loop/estate.component.d.ts +7 -0
- package/dist/__fixtures__/pr-loop/estate.component.d.ts.map +1 -0
- package/dist/__fixtures__/pr-loop/estate.component.js +7 -0
- package/dist/__fixtures__/report/generate.d.ts +26 -0
- package/dist/__fixtures__/report/generate.d.ts.map +1 -0
- package/dist/__fixtures__/report/generate.js +58 -0
- package/dist/applied-state.d.ts +27 -0
- package/dist/applied-state.d.ts.map +1 -0
- package/dist/applied-state.js +44 -0
- package/dist/backend-config.d.ts +53 -0
- package/dist/backend-config.d.ts.map +1 -0
- package/dist/backend-config.js +136 -0
- package/dist/behaviour/kinds.d.ts.map +1 -1
- package/dist/behaviour/kinds.js +4 -0
- package/dist/behaviour/predict.d.ts.map +1 -1
- package/dist/behaviour/predict.js +6 -2
- package/dist/behaviour/reference-catalog.d.ts +16 -5
- package/dist/behaviour/reference-catalog.d.ts.map +1 -1
- package/dist/behaviour/reference-catalog.js +16 -5
- package/dist/behaviour/request.d.ts.map +1 -1
- package/dist/behaviour/request.js +3 -2
- package/dist/changed-roots.d.ts +6 -4
- package/dist/changed-roots.d.ts.map +1 -1
- package/dist/changed-roots.js +14 -18
- package/dist/ci-setup.d.ts +11 -0
- package/dist/ci-setup.d.ts.map +1 -0
- package/dist/ci-setup.js +41 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +4 -3
- package/dist/components/capability-plugin.d.ts +105 -9
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability-plugin.js +357 -36
- package/dist/components/compose-group.d.ts +145 -0
- package/dist/components/compose-group.d.ts.map +1 -0
- package/dist/components/compose-group.js +407 -0
- package/dist/components/placeholder-preview.d.ts +78 -0
- package/dist/components/placeholder-preview.d.ts.map +1 -0
- package/dist/components/placeholder-preview.js +254 -0
- package/dist/components/release-inputs.d.ts +29 -0
- package/dist/components/release-inputs.d.ts.map +1 -0
- package/dist/components/release-inputs.js +57 -0
- package/dist/components/trace-facts.d.ts +16 -0
- package/dist/components/trace-facts.d.ts.map +1 -0
- package/dist/components/trace-facts.js +19 -0
- package/dist/compose/apply-events.d.ts +46 -0
- package/dist/compose/apply-events.d.ts.map +1 -0
- package/dist/compose/apply-events.js +77 -0
- package/dist/compose/estate.d.ts +194 -0
- package/dist/compose/estate.d.ts.map +1 -0
- package/dist/compose/estate.js +363 -0
- package/dist/compose/hcl.d.ts +99 -0
- package/dist/compose/hcl.d.ts.map +1 -0
- package/dist/compose/hcl.js +302 -0
- package/dist/compose/module-address.d.ts +24 -0
- package/dist/compose/module-address.d.ts.map +1 -0
- package/dist/compose/module-address.js +43 -0
- package/dist/compose/root.d.ts +127 -0
- package/dist/compose/root.d.ts.map +1 -0
- package/dist/compose/root.js +586 -0
- package/dist/compose/run.d.ts +183 -0
- package/dist/compose/run.d.ts.map +1 -0
- package/dist/compose/run.js +447 -0
- package/dist/compose/slice.d.ts +102 -0
- package/dist/compose/slice.d.ts.map +1 -0
- package/dist/compose/slice.js +249 -0
- package/dist/compose/state-address.d.ts +28 -0
- package/dist/compose/state-address.d.ts.map +1 -0
- package/dist/compose/state-address.js +154 -0
- package/dist/compose/state-merge.d.ts +52 -0
- package/dist/compose/state-merge.d.ts.map +1 -0
- package/dist/compose/state-merge.js +135 -0
- package/dist/compose.d.ts +179 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +482 -0
- package/dist/composites/terraform-apply-op.d.ts.map +1 -1
- package/dist/composites/terraform-apply-op.js +7 -3
- package/dist/cone.d.ts +53 -0
- package/dist/cone.d.ts.map +1 -0
- package/dist/cone.js +119 -0
- package/dist/config.d.ts +59 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +43 -9
- package/dist/cost-hold.d.ts +27 -0
- package/dist/cost-hold.d.ts.map +1 -0
- package/dist/cost-hold.js +21 -0
- package/dist/describe-resources.d.ts +74 -12
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/describe-resources.js +374 -66
- package/dist/drift-read.d.ts +33 -0
- package/dist/drift-read.d.ts.map +1 -0
- package/dist/drift-read.js +243 -0
- package/dist/drift-record.d.ts +147 -0
- package/dist/drift-record.d.ts.map +1 -0
- package/dist/drift-record.js +383 -0
- package/dist/drift-rows.d.ts +61 -0
- package/dist/drift-rows.d.ts.map +1 -0
- package/dist/drift-rows.js +108 -0
- package/dist/exit-codes.d.ts +55 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +48 -0
- package/dist/hcl/edges.d.ts.map +1 -1
- package/dist/hcl/edges.js +24 -1
- package/dist/hcl/files.d.ts +61 -0
- package/dist/hcl/files.d.ts.map +1 -0
- package/dist/hcl/files.js +221 -0
- package/dist/hcl/instances.d.ts +82 -0
- package/dist/hcl/instances.d.ts.map +1 -0
- package/dist/hcl/instances.js +390 -0
- package/dist/hcl/parse.d.ts +81 -9
- package/dist/hcl/parse.d.ts.map +1 -1
- package/dist/hcl/parse.js +114 -16
- package/dist/hcl/roots.d.ts.map +1 -1
- package/dist/hcl/roots.js +5 -0
- package/dist/hcl/suppressions.d.ts.map +1 -1
- package/dist/hcl/suppressions.js +8 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -2
- package/dist/integrity.json +8 -5
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-catalog.js +27 -3
- package/dist/lint/audit-lineage.d.ts +14 -2
- package/dist/lint/audit-lineage.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/index.js +4 -0
- package/dist/lint/post-synth/record-store.d.ts +54 -0
- package/dist/lint/post-synth/record-store.d.ts.map +1 -0
- package/dist/lint/post-synth/record-store.js +104 -0
- package/dist/lint/post-synth/tf001.d.ts +2 -2
- package/dist/lint/post-synth/tf001.js +2 -2
- package/dist/lint/post-synth/tf024.d.ts +35 -11
- package/dist/lint/post-synth/tf024.d.ts.map +1 -1
- package/dist/lint/post-synth/tf024.js +92 -20
- package/dist/lint/post-synth/tf046.d.ts +64 -0
- package/dist/lint/post-synth/tf046.d.ts.map +1 -0
- package/dist/lint/post-synth/tf046.js +175 -0
- package/dist/lint/post-synth/tf047.d.ts +36 -0
- package/dist/lint/post-synth/tf047.d.ts.map +1 -0
- package/dist/lint/post-synth/tf047.js +94 -0
- package/dist/live-plan-reasons.d.ts +21 -0
- package/dist/live-plan-reasons.d.ts.map +1 -0
- package/dist/live-plan-reasons.js +47 -0
- package/dist/lock-rows.d.ts +36 -0
- package/dist/lock-rows.d.ts.map +1 -0
- package/dist/lock-rows.js +60 -0
- package/dist/lock-timeout.d.ts +22 -0
- package/dist/lock-timeout.d.ts.map +1 -0
- package/dist/lock-timeout.js +32 -0
- package/dist/lsp/option-keys.d.ts.map +1 -1
- package/dist/lsp/option-keys.js +2 -0
- package/dist/manifest.json +1 -1
- package/dist/module-catalog/index.d.ts +69 -0
- package/dist/module-catalog/index.d.ts.map +1 -0
- package/dist/module-catalog/index.js +110 -0
- package/dist/module-catalog/refresh-cli.d.ts +4 -0
- package/dist/module-catalog/refresh-cli.d.ts.map +1 -0
- package/dist/module-catalog/refresh-cli.js +64 -0
- package/dist/module-catalog/static-tier.d.ts +6 -0
- package/dist/module-catalog/static-tier.d.ts.map +1 -0
- package/dist/module-catalog/static-tier.js +1650 -0
- package/dist/module-catalog/types.d.ts +65 -0
- package/dist/module-catalog/types.d.ts.map +1 -0
- package/dist/module-catalog/types.js +33 -0
- package/dist/okf/index.md +3 -1
- package/dist/okf/rules/TF024.md +2 -2
- package/dist/okf/rules/TF046.md +11 -0
- package/dist/okf/rules/TF047.md +11 -0
- package/dist/op/activities/index.d.ts +6 -2
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/index.js +5 -1
- package/dist/op/activities/live-detect.d.ts +4 -3
- package/dist/op/activities/live-detect.d.ts.map +1 -1
- package/dist/op/activities/live-detect.js +57 -32
- package/dist/op/activities/pin-rollout.d.ts +2 -0
- package/dist/op/activities/pin-rollout.d.ts.map +1 -1
- package/dist/op/activities/pin-rollout.js +1 -0
- package/dist/op/activities/terraform.d.ts +182 -5
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activities/terraform.js +224 -24
- package/dist/op/activity-contracts.d.ts +27 -0
- package/dist/op/activity-contracts.d.ts.map +1 -1
- package/dist/op/activity-contracts.js +19 -1
- package/dist/op/builders.d.ts +7 -1
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +9 -0
- package/dist/pin/consumers.d.ts +35 -0
- package/dist/pin/consumers.d.ts.map +1 -0
- package/dist/pin/consumers.js +70 -0
- package/dist/pin/edit.d.ts.map +1 -1
- package/dist/pin/edit.js +35 -0
- package/dist/pin/rollout.d.ts +2 -0
- package/dist/pin/rollout.d.ts.map +1 -1
- package/dist/pin/rollout.js +14 -11
- package/dist/plan-shape.d.ts +30 -0
- package/dist/plan-shape.d.ts.map +1 -0
- package/dist/plan-shape.js +125 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +37 -18
- package/dist/registry/address.d.ts +63 -0
- package/dist/registry/address.d.ts.map +1 -0
- package/dist/registry/address.js +65 -0
- package/dist/registry/catalog-read.d.ts +14 -0
- package/dist/registry/catalog-read.d.ts.map +1 -0
- package/dist/registry/catalog-read.js +58 -0
- package/dist/registry/deprecated.d.ts +37 -0
- package/dist/registry/deprecated.d.ts.map +1 -0
- package/dist/registry/deprecated.js +43 -0
- package/dist/registry/docs.d.ts +96 -0
- package/dist/registry/docs.d.ts.map +1 -0
- package/dist/registry/docs.js +210 -0
- package/dist/registry/edge/edge.d.ts +46 -0
- package/dist/registry/edge/edge.d.ts.map +1 -0
- package/dist/registry/edge/edge.js +194 -0
- package/dist/registry/edge/index.d.ts +11 -0
- package/dist/registry/edge/index.d.ts.map +1 -0
- package/dist/registry/edge/index.js +10 -0
- package/dist/registry/edge/lambda-edge.d.ts +76 -0
- package/dist/registry/edge/lambda-edge.d.ts.map +1 -0
- package/dist/registry/edge/lambda-edge.js +95 -0
- package/dist/registry/edge/sigv4-web.d.ts +33 -0
- package/dist/registry/edge/sigv4-web.d.ts.map +1 -0
- package/dist/registry/edge/sigv4-web.js +56 -0
- package/dist/registry/edge/token-source.d.ts +73 -0
- package/dist/registry/edge/token-source.d.ts.map +1 -0
- package/dist/registry/edge/token-source.js +115 -0
- package/dist/registry/edge/worker.d.ts +45 -0
- package/dist/registry/edge/worker.d.ts.map +1 -0
- package/dist/registry/edge/worker.js +26 -0
- package/dist/registry/handler.d.ts +25 -0
- package/dist/registry/handler.d.ts.map +1 -0
- package/dist/registry/handler.js +75 -0
- package/dist/registry/headers-file.d.ts +21 -0
- package/dist/registry/headers-file.d.ts.map +1 -0
- package/dist/registry/headers-file.js +57 -0
- package/dist/registry/index.d.ts +31 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +30 -0
- package/dist/registry/mirror-paths.d.ts +9 -0
- package/dist/registry/mirror-paths.d.ts.map +1 -0
- package/dist/registry/mirror-paths.js +8 -0
- package/dist/registry/mirror.d.ts +130 -0
- package/dist/registry/mirror.d.ts.map +1 -0
- package/dist/registry/mirror.js +319 -0
- package/dist/registry/proxies.d.ts +73 -0
- package/dist/registry/proxies.d.ts.map +1 -0
- package/dist/registry/proxies.js +111 -0
- package/dist/registry/reader.d.ts +57 -0
- package/dist/registry/reader.d.ts.map +1 -0
- package/dist/registry/reader.js +198 -0
- package/dist/registry/semver.d.ts +16 -0
- package/dist/registry/semver.d.ts.map +1 -0
- package/dist/registry/semver.js +24 -0
- package/dist/registry/serve.d.ts +24 -0
- package/dist/registry/serve.d.ts.map +1 -0
- package/dist/registry/serve.js +64 -0
- package/dist/registry/source-forms.d.ts +79 -0
- package/dist/registry/source-forms.d.ts.map +1 -0
- package/dist/registry/source-forms.js +202 -0
- package/dist/registry/targets/artifactory.d.ts +28 -0
- package/dist/registry/targets/artifactory.d.ts.map +1 -0
- package/dist/registry/targets/artifactory.js +206 -0
- package/dist/registry/targets/fake-artifactory.d.ts +30 -0
- package/dist/registry/targets/fake-artifactory.d.ts.map +1 -0
- package/dist/registry/targets/fake-artifactory.js +142 -0
- package/dist/registry/targets/fake-gitlab.d.ts +39 -0
- package/dist/registry/targets/fake-gitlab.d.ts.map +1 -0
- package/dist/registry/targets/fake-gitlab.js +136 -0
- package/dist/registry/targets/fake-hcp.d.ts +50 -0
- package/dist/registry/targets/fake-hcp.d.ts.map +1 -0
- package/dist/registry/targets/fake-hcp.js +202 -0
- package/dist/registry/targets/fake-http.d.ts +31 -0
- package/dist/registry/targets/fake-http.d.ts.map +1 -0
- package/dist/registry/targets/fake-http.js +87 -0
- package/dist/registry/targets/fake-nexus.d.ts +27 -0
- package/dist/registry/targets/fake-nexus.d.ts.map +1 -0
- package/dist/registry/targets/fake-nexus.js +137 -0
- package/dist/registry/targets/gitlab.d.ts +53 -0
- package/dist/registry/targets/gitlab.d.ts.map +1 -0
- package/dist/registry/targets/gitlab.js +187 -0
- package/dist/registry/targets/hcp.d.ts +29 -0
- package/dist/registry/targets/hcp.d.ts.map +1 -0
- package/dist/registry/targets/hcp.js +310 -0
- package/dist/registry/targets/hosted.d.ts +62 -0
- package/dist/registry/targets/hosted.d.ts.map +1 -0
- package/dist/registry/targets/hosted.js +74 -0
- package/dist/registry/targets/index.d.ts +18 -0
- package/dist/registry/targets/index.d.ts.map +1 -0
- package/dist/registry/targets/index.js +17 -0
- package/dist/registry/targets/module-target.d.ts +68 -0
- package/dist/registry/targets/module-target.d.ts.map +1 -0
- package/dist/registry/targets/module-target.js +63 -0
- package/dist/registry/targets/nexus.d.ts +43 -0
- package/dist/registry/targets/nexus.d.ts.map +1 -0
- package/dist/registry/targets/nexus.js +250 -0
- package/dist/registry/targets/zip.d.ts +55 -0
- package/dist/registry/targets/zip.d.ts.map +1 -0
- package/dist/registry/targets/zip.js +278 -0
- package/dist/registry/writer.d.ts +101 -0
- package/dist/registry/writer.d.ts.map +1 -0
- package/dist/registry/writer.js +109 -0
- package/dist/report.d.ts +15 -6
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +28 -55
- package/dist/rules/record-store.ts +122 -0
- package/dist/rules/tf001.ts +2 -2
- package/dist/rules/tf024.ts +105 -22
- package/dist/rules/tf046.ts +204 -0
- package/dist/rules/tf047.ts +114 -0
- package/dist/skills/chant-terraform.md +1 -1
- package/dist/state/azure-blob.d.ts +9 -0
- package/dist/state/azure-blob.d.ts.map +1 -0
- package/dist/state/azure-blob.js +8 -0
- package/dist/state/backend.d.ts +158 -0
- package/dist/state/backend.d.ts.map +1 -0
- package/dist/state/backend.js +406 -0
- package/dist/state/exec.d.ts +39 -0
- package/dist/state/exec.d.ts.map +1 -0
- package/dist/state/exec.js +80 -0
- package/dist/state/gcs.d.ts +8 -0
- package/dist/state/gcs.d.ts.map +1 -0
- package/dist/state/gcs.js +7 -0
- package/dist/state/index.d.ts +15 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +14 -0
- package/dist/state/locks.d.ts +105 -0
- package/dist/state/locks.d.ts.map +1 -0
- package/dist/state/locks.js +130 -0
- package/dist/state/object-store.d.ts +8 -0
- package/dist/state/object-store.d.ts.map +1 -0
- package/dist/state/object-store.js +7 -0
- package/dist/state/root-states.d.ts +131 -0
- package/dist/state/root-states.d.ts.map +1 -0
- package/dist/state/root-states.js +241 -0
- package/dist/state/s3.d.ts +21 -0
- package/dist/state/s3.d.ts.map +1 -0
- package/dist/state/s3.js +19 -0
- package/dist/state/store.d.ts +51 -0
- package/dist/state/store.d.ts.map +1 -0
- package/dist/state/store.js +185 -0
- package/dist/state-ops.d.ts +255 -0
- package/dist/state-ops.d.ts.map +1 -0
- package/dist/state-ops.js +305 -0
- package/dist/state-reads.d.ts +57 -0
- package/dist/state-reads.d.ts.map +1 -0
- package/dist/state-reads.js +104 -0
- package/dist/terragrunt/affected.d.ts.map +1 -1
- package/dist/terragrunt/affected.js +2 -1
- package/dist/used-sources.d.ts +54 -0
- package/dist/used-sources.d.ts.map +1 -0
- package/dist/used-sources.js +157 -0
- package/package.json +24 -4
- package/src/__fixtures__/ACCEPTANCE.md +69 -7
- package/src/__fixtures__/compose/apply-failed-in-app.jsonl +12 -0
- package/src/__fixtures__/compose/state-app.json +66 -0
- package/src/__fixtures__/compose/state-edge.json +40 -0
- package/src/__fixtures__/compose/state-net.json +58 -0
- package/src/__fixtures__/compose/three-app.plan.json +184 -0
- package/src/__fixtures__/compose/three-composed.plan.json +509 -0
- package/src/__fixtures__/compose/three-edge.plan.json +144 -0
- package/src/__fixtures__/compose/three-net.plan.json +174 -0
- package/src/__fixtures__/compose/unknown-composed.plan.json +228 -0
- package/src/__fixtures__/compose/working-after-apply.json +145 -0
- package/src/__fixtures__/config-blocks/app/main.tf +61 -0
- package/src/__fixtures__/config-blocks/app/modules/network/main.tf +4 -0
- package/src/__fixtures__/config-blocks/app-json/main.tf.json +39 -0
- package/src/__fixtures__/config-files/shadow/main.tf +3 -0
- package/src/__fixtures__/config-files/shadow/main.tofu +3 -0
- package/src/__fixtures__/config-files/shadow/other.tf +3 -0
- package/src/__fixtures__/config-files/tf/main.tf +11 -0
- package/src/__fixtures__/config-files/tf-json/main.tf.json +18 -0
- package/src/__fixtures__/config-files/tofu/main.tofu +11 -0
- package/src/__fixtures__/config-files/tofu-json/main.tofu.json +18 -0
- package/src/__fixtures__/config-files/tofu-only/main.tofu +3 -0
- package/src/__fixtures__/drift-record/README.md +16 -0
- package/src/__fixtures__/drift-record/live-plan-sections.json +27 -0
- package/src/__fixtures__/drift-rows/live-root-plan.json +70 -0
- package/src/__fixtures__/drift-rows/refresh-plan.json +61 -0
- package/src/__fixtures__/instance-forms/app/main.tf +110 -0
- package/src/__fixtures__/instance-forms/app/modules/svc/main.tf +3 -0
- package/src/__fixtures__/instance-vars/app/main.tf +52 -0
- package/src/__fixtures__/instance-vars/app/modules/app/main.tf +12 -0
- package/src/__fixtures__/instance-vars/app/modules/edge/main.tf +3 -0
- package/src/__fixtures__/instance-vars/app/terraform.tfvars +1 -0
- package/src/__fixtures__/live-module-keyed/live-graph.json +259 -0
- package/src/__fixtures__/live-module-keyed/live-plan.json +87 -0
- package/src/__fixtures__/live-module-keyed/root/estate.chdf.hcl +1 -0
- package/src/__fixtures__/live-module-keyed/root/main.tf +21 -0
- package/src/__fixtures__/live-module-keyed/root/modules/app/main.tf +17 -0
- package/src/__fixtures__/live-module-keyed/root/modules/edge/main.tf +7 -0
- package/src/__fixtures__/live-module-keyed/root/versions.tf +30 -0
- package/src/__fixtures__/live-module-keyed/show-state.json +146 -0
- package/src/__fixtures__/pr-loop/README.md +50 -0
- package/src/__fixtures__/pr-loop/chant.config.json +13 -0
- package/src/__fixtures__/pr-loop/estate.component.ts +10 -0
- package/src/__fixtures__/pr-loop/roots/a/main.tf +11 -0
- package/src/__fixtures__/pr-loop/roots/app/main.tf +19 -0
- package/src/__fixtures__/pr-loop/roots/b/main.tf +7 -0
- package/src/__fixtures__/pr-loop/roots/dns/main.tf +3 -0
- package/src/__fixtures__/pr-loop/roots/net/main.tf +7 -0
- package/src/__fixtures__/report/applied.show.json +51 -0
- package/src/__fixtures__/report/generate.ts +63 -0
- package/src/__fixtures__/report/plan.show.json +200 -0
- package/src/__fixtures__/report/tf-apply/change-set.json +170 -0
- package/src/__fixtures__/report/tf-apply/report.json +338 -0
- package/src/__fixtures__/report/tf-plan/change-set.json +170 -0
- package/src/__fixtures__/report/tf-plan/report.json +320 -0
- package/src/__fixtures__/three-cloud-azure.graph.json +290 -0
- package/src/__fixtures__/used-sources/root/extra.tofu +1 -0
- package/src/__fixtures__/used-sources/root/main.tf +31 -0
- package/src/__fixtures__/used-sources/root/modules/net/main.tf +14 -0
- package/src/__fixtures__/used-sources/root/modules/net/versions.tf.json +7 -0
- package/src/__fixtures__/used-sources/unit/terragrunt.hcl +3 -0
- package/src/applied-state.ts +52 -0
- package/src/backend-config.test.ts +105 -0
- package/src/backend-config.ts +133 -0
- package/src/behaviour/kinds.ts +4 -0
- package/src/behaviour/predict.ts +5 -2
- package/src/behaviour/reference-catalog.plugin.test.ts +65 -0
- package/src/behaviour/reference-catalog.ts +16 -5
- package/src/behaviour/request.ts +3 -2
- package/src/change-set-instances.test.ts +98 -0
- package/src/changed-roots.ts +14 -16
- package/src/ci-setup.ts +42 -0
- package/src/commands.ts +4 -3
- package/src/components/capability-plugin.test.ts +198 -19
- package/src/components/capability-plugin.ts +383 -35
- package/src/components/change-set-graph.e2e.test.ts +11 -5
- package/src/components/compose-group.test.ts +486 -0
- package/src/components/compose-group.ts +489 -0
- package/src/components/demo-composed-roots.e2e.test.ts +440 -0
- package/src/components/member-outputs.test.ts +76 -0
- package/src/components/placeholder-preview.test.ts +147 -0
- package/src/components/placeholder-preview.ts +257 -0
- package/src/components/pr-loop-compose.tofu.e2e.test.ts +363 -0
- package/src/components/pr-loop.tofu.test.ts +113 -57
- package/src/components/release-inputs.ts +69 -0
- package/src/components/trace-facts.test.ts +21 -0
- package/src/components/trace-facts.ts +27 -0
- package/src/compose/apply-events.test.ts +62 -0
- package/src/compose/apply-events.ts +100 -0
- package/src/compose/estate.choudoufu.e2e.test.ts +112 -0
- package/src/compose/estate.test.ts +421 -0
- package/src/compose/estate.ts +443 -0
- package/src/compose/hcl.ts +311 -0
- package/src/compose/module-address.ts +49 -0
- package/src/compose/root.test.ts +416 -0
- package/src/compose/root.ts +592 -0
- package/src/compose/run.test.ts +440 -0
- package/src/compose/run.tofu.test.ts +176 -0
- package/src/compose/run.ts +527 -0
- package/src/compose/slice.test.ts +184 -0
- package/src/compose/slice.ts +290 -0
- package/src/compose/state-address.ts +157 -0
- package/src/compose/state-merge.test.ts +131 -0
- package/src/compose/state-merge.ts +148 -0
- package/src/compose.test.ts +248 -0
- package/src/compose.ts +627 -0
- package/src/composites/terraform-apply-op.test.ts +9 -0
- package/src/composites/terraform-apply-op.ts +7 -3
- package/src/cone.ts +138 -0
- package/src/config-file-listers.test.ts +265 -0
- package/src/config.ts +44 -9
- package/src/cost-hold.ts +29 -0
- package/src/describe-resources.engine-facts.test.ts +411 -0
- package/src/describe-resources.live-module-keyed.test.ts +290 -0
- package/src/describe-resources.ts +416 -70
- package/src/drift-read.ts +284 -0
- package/src/drift-record.test.ts +223 -0
- package/src/drift-record.ts +455 -0
- package/src/drift-rows.test.ts +135 -0
- package/src/drift-rows.ts +137 -0
- package/src/exit-codes.test.ts +23 -0
- package/src/exit-codes.ts +59 -0
- package/src/hcl/config-blocks.test.ts +174 -0
- package/src/hcl/edges.ts +21 -1
- package/src/hcl/files.ts +234 -0
- package/src/hcl/instance-forms.test.ts +94 -0
- package/src/hcl/instances.test.ts +137 -0
- package/src/hcl/instances.ts +422 -0
- package/src/hcl/parse.test.ts +139 -1
- package/src/hcl/parse.ts +149 -16
- package/src/hcl/roots.ts +6 -0
- package/src/hcl/suppressions.ts +8 -2
- package/src/index.ts +79 -0
- package/src/lint/audit-catalog.ts +29 -3
- package/src/lint/audit-lineage.ts +14 -2
- package/src/lint/post-synth/fixtures/TF024/live-with-record-store/main.tf +18 -0
- package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/estate.chdf.hcl +1 -0
- package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/main.tf +10 -0
- package/src/lint/post-synth/fixtures/TF046/azurerm-no-resource-group/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF046/backend-config-only/main.tf +14 -0
- package/src/lint/post-synth/fixtures/TF046/derived-s3/backend.tf +7 -0
- package/src/lint/post-synth/fixtures/TF046/derived-s3/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF046/explicit-local/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF046/explicit-s3-beside-local-backend/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF046/implied-local/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF046/sidecar-implied/estate.chdf.hcl +1 -0
- package/src/lint/post-synth/fixtures/TF046/sidecar-implied/main.tf +5 -0
- package/src/lint/post-synth/fixtures/TF047/clear/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/refused/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/unknown-module/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/unknown-version/main.tf +12 -0
- package/src/lint/post-synth/index.ts +4 -0
- package/src/lint/post-synth/record-store.ts +122 -0
- package/src/lint/post-synth/tf001.ts +2 -2
- package/src/lint/post-synth/tf024.test.ts +26 -5
- package/src/lint/post-synth/tf024.ts +105 -22
- package/src/lint/post-synth/tf046.test.ts +137 -0
- package/src/lint/post-synth/tf046.ts +204 -0
- package/src/lint/post-synth/tf047.test.ts +56 -0
- package/src/lint/post-synth/tf047.ts +114 -0
- package/src/live-plan-reasons.ts +54 -0
- package/src/lock-rows.test.ts +70 -0
- package/src/lock-rows.ts +82 -0
- package/src/lock-timeout.ts +34 -0
- package/src/lsp/completions.test.ts +1 -1
- package/src/lsp/option-keys.ts +2 -0
- package/src/module-catalog/index.test.ts +93 -0
- package/src/module-catalog/index.ts +159 -0
- package/src/module-catalog/refresh-cli.ts +68 -0
- package/src/module-catalog/static-tier.ts +1659 -0
- package/src/module-catalog/types.ts +88 -0
- package/src/op/activities/choudoufu-plan-set-op.test.ts +111 -0
- package/src/op/activities/index.ts +9 -0
- package/src/op/activities/live-detect.ts +55 -26
- package/src/op/activities/pin-rollout.ts +3 -0
- package/src/op/activities/registry.test.ts +2 -1
- package/src/op/activities/terraform.test.ts +55 -0
- package/src/op/activities/terraform.ts +312 -27
- package/src/op/activity-contracts.test.ts +1 -0
- package/src/op/activity-contracts.ts +24 -1
- package/src/op/builders.ts +14 -0
- package/src/op-wave-step.test.ts +109 -0
- package/src/pin/__fixtures__/consumers/live/dev/main.tf +4 -0
- package/src/pin/__fixtures__/consumers/live/prod/main.tf +21 -0
- package/src/pin/__fixtures__/consumers/units/vpc/terragrunt.hcl +7 -0
- package/src/pin/bundle.test.ts +2 -0
- package/src/pin/consumers.test.ts +57 -0
- package/src/pin/consumers.ts +95 -0
- package/src/pin/edit.ts +30 -0
- package/src/pin/rollout.ts +16 -11
- package/src/plan-shape.test.ts +148 -0
- package/src/plan-shape.ts +128 -0
- package/src/plugin.ts +39 -19
- package/src/registry/__fixtures__/docs/documented/.terraform-docs.yml +3 -0
- package/src/registry/__fixtures__/docs/documented/README.md +1 -0
- package/src/registry/__fixtures__/docs/documented/main.tf +8 -0
- package/src/registry/__fixtures__/docs/json/main.tf.json +14 -0
- package/src/registry/__fixtures__/docs/vpc/README.md +3 -0
- package/src/registry/__fixtures__/docs/vpc/examples/complete/main.tf +5 -0
- package/src/registry/__fixtures__/docs/vpc/examples/simple/README.md +1 -0
- package/src/registry/__fixtures__/docs/vpc/examples/simple/main.tf +4 -0
- package/src/registry/__fixtures__/docs/vpc/main.tf +14 -0
- package/src/registry/__fixtures__/docs/vpc/outputs.tf +9 -0
- package/src/registry/__fixtures__/docs/vpc/variables.tf +16 -0
- package/src/registry/__fixtures__/proxied-root/main.tf +12 -0
- package/src/registry/address.ts +100 -0
- package/src/registry/catalog-read.ts +56 -0
- package/src/registry/deprecated.test.ts +139 -0
- package/src/registry/deprecated.ts +76 -0
- package/src/registry/docs.test.ts +140 -0
- package/src/registry/docs.ts +299 -0
- package/src/registry/edge/bundle.test.ts +50 -0
- package/src/registry/edge/edge.test.ts +176 -0
- package/src/registry/edge/edge.ts +211 -0
- package/src/registry/edge/index.ts +10 -0
- package/src/registry/edge/lambda-edge.ts +127 -0
- package/src/registry/edge/sigv4-web.ts +79 -0
- package/src/registry/edge/token-source.test.ts +258 -0
- package/src/registry/edge/token-source.ts +169 -0
- package/src/registry/edge/worker.ts +61 -0
- package/src/registry/handler.ts +89 -0
- package/src/registry/headers-file.ts +61 -0
- package/src/registry/index.ts +31 -0
- package/src/registry/mirror-paths.ts +10 -0
- package/src/registry/mirror.test.ts +198 -0
- package/src/registry/mirror.ts +395 -0
- package/src/registry/proxies.test.ts +153 -0
- package/src/registry/proxies.ts +132 -0
- package/src/registry/reader.ts +225 -0
- package/src/registry/registry.test.ts +262 -0
- package/src/registry/semver.ts +34 -0
- package/src/registry/serve.ts +82 -0
- package/src/registry/source-forms.test.ts +87 -0
- package/src/registry/source-forms.ts +216 -0
- package/src/registry/targets/artifactory.test.ts +248 -0
- package/src/registry/targets/artifactory.ts +257 -0
- package/src/registry/targets/fake-artifactory.ts +158 -0
- package/src/registry/targets/fake-gitlab.ts +161 -0
- package/src/registry/targets/fake-hcp.ts +233 -0
- package/src/registry/targets/fake-http.ts +97 -0
- package/src/registry/targets/fake-nexus.ts +152 -0
- package/src/registry/targets/gitlab.test.ts +208 -0
- package/src/registry/targets/gitlab.ts +223 -0
- package/src/registry/targets/hcp.test.ts +230 -0
- package/src/registry/targets/hcp.ts +360 -0
- package/src/registry/targets/hosted.ts +117 -0
- package/src/registry/targets/index.ts +17 -0
- package/src/registry/targets/module-target.ts +131 -0
- package/src/registry/targets/nexus.test.ts +220 -0
- package/src/registry/targets/nexus.ts +314 -0
- package/src/registry/targets/zip.test.ts +65 -0
- package/src/registry/targets/zip.ts +287 -0
- package/src/registry/writer.ts +155 -0
- package/src/report.test.ts +82 -1
- package/src/report.ts +31 -54
- package/src/signal-only-root.test.ts +72 -0
- package/src/skills/chant-terraform.md +1 -1
- package/src/state/azure-blob.ts +8 -0
- package/src/state/backend-verdict.test.ts +187 -0
- package/src/state/backend.test.ts +127 -0
- package/src/state/backend.ts +488 -0
- package/src/state/exec.ts +111 -0
- package/src/state/gcs.ts +7 -0
- package/src/state/index.ts +15 -0
- package/src/state/locks.test.ts +84 -0
- package/src/state/locks.ts +201 -0
- package/src/state/object-store.ts +7 -0
- package/src/state/root-states-floci.e2e.test.ts +66 -0
- package/src/state/root-states.test.ts +462 -0
- package/src/state/root-states.ts +309 -0
- package/src/state/s3.ts +21 -0
- package/src/state/store.ts +225 -0
- package/src/state-ops.test.ts +182 -0
- package/src/state-ops.ts +462 -0
- package/src/state-reads.ts +116 -0
- package/src/terragrunt/affected.ts +2 -1
- package/src/used-sources.test.ts +75 -0
- package/src/used-sources.ts +204 -0
- package/src/workspace-kinds.test.ts +4 -17
- package/workspace-kinds.json +1 -24
- package/dist/workspace-member.d.ts +0 -30
- package/dist/workspace-member.d.ts.map +0 -1
- package/dist/workspace-member.js +0 -91
- package/schemas/pulumi.schema.json +0 -34
- package/src/testdata/pulumi-member/Pulumi.yaml +0 -2
- package/src/testdata/pulumi-member/stack-export.json +0 -53
- package/src/testdata/pulumi-unexported/Pulumi.yaml +0 -2
- package/src/workspace-member.test.ts +0 -89
- package/src/workspace-member.ts +0 -126
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The edge function in front of a bucket-hosted module registry (chant#4566),
|
|
3
|
+
* built on `registryHandler`. It does three things:
|
|
4
|
+
*
|
|
5
|
+
* - Adds `X-Terraform-Get` to every download answer, which is what Terraform
|
|
6
|
+
* installs from (terragucci#1287). Always on.
|
|
7
|
+
* - With `auth` set, checks the bearer token Terraform and OpenTofu send for
|
|
8
|
+
* the host (`TF_TOKEN_<host>` or credentials.tfrc.json) on discovery,
|
|
9
|
+
* `versions`, `download` and every other file, and answers 401 when it is
|
|
10
|
+
* missing or wrong. Off by default.
|
|
11
|
+
* - With `auth` set, a download whose location is a tarball behind this same
|
|
12
|
+
* host is answered with a short-lived signed URL, so the CLIs' tarball fetch
|
|
13
|
+
* (go-getter, which sends no token) needs no `.netrc`. With auth off the
|
|
14
|
+
* location is the tarball's plain URL, as stored.
|
|
15
|
+
* - With `auth` set, a provider mirror's `<version>.json` (chant#4564) is
|
|
16
|
+
* answered with each archive's URL signed the same way, so the CLIs'
|
|
17
|
+
* archive fetch passes too.
|
|
18
|
+
*
|
|
19
|
+
* Only web-standard APIs (Request, Response, URL, crypto.subtle,
|
|
20
|
+
* TextEncoder), so the same code runs in a Cloudflare Worker, Lambda@Edge on
|
|
21
|
+
* Node 20, Deno, or a test. Adapters for the first two are in ./worker and
|
|
22
|
+
* ./lambda-edge.
|
|
23
|
+
*/
|
|
24
|
+
import { DISCOVERY, keyFor } from "../address.js";
|
|
25
|
+
import { registryHandler } from "../handler.js";
|
|
26
|
+
import { isMirrorVersionPath } from "../mirror-paths.js";
|
|
27
|
+
import { downloadAnswer, downloadLocation, isDownloadPath } from "../writer.js";
|
|
28
|
+
/** The query parameters a signed tarball URL carries. */
|
|
29
|
+
export const SIGNED_EXPIRES = "expires";
|
|
30
|
+
export const SIGNED_SIGNATURE = "signature";
|
|
31
|
+
/** Default lifetime of a signed tarball URL, in seconds. */
|
|
32
|
+
export const DEFAULT_SIGNED_TTL = 300;
|
|
33
|
+
const encoder = new TextEncoder();
|
|
34
|
+
const base64url = (bytes) => {
|
|
35
|
+
let s = "";
|
|
36
|
+
for (const b of new Uint8Array(bytes))
|
|
37
|
+
s += String.fromCharCode(b);
|
|
38
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
39
|
+
};
|
|
40
|
+
const hmac = async (key, text) => {
|
|
41
|
+
const k = await crypto.subtle.importKey("raw", encoder.encode(key), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
42
|
+
return base64url(await crypto.subtle.sign("HMAC", k, encoder.encode(text)));
|
|
43
|
+
};
|
|
44
|
+
/** Compares two strings in time that depends only on their lengths. */
|
|
45
|
+
const sameText = (a, b) => {
|
|
46
|
+
const x = encoder.encode(a);
|
|
47
|
+
const y = encoder.encode(b);
|
|
48
|
+
let diff = x.length ^ y.length;
|
|
49
|
+
for (let i = 0; i < Math.max(x.length, y.length); i++)
|
|
50
|
+
diff |= (x[i] ?? 0) ^ (y[i] ?? 0);
|
|
51
|
+
return diff === 0;
|
|
52
|
+
};
|
|
53
|
+
const signingKeyOf = (auth) => auth.signingKey || auth.tokens.find((t) => !!t);
|
|
54
|
+
/** What a signature covers: the path and the expiry, so a URL can't be moved to another file or extended. */
|
|
55
|
+
const signedText = (pathname, expires) => `${pathname}\n${expires}`;
|
|
56
|
+
/** A location with `expires` and `signature` added, valid for `ttl` seconds from `nowMs`. */
|
|
57
|
+
export async function signLocation(location, key, nowMs, ttl = DEFAULT_SIGNED_TTL) {
|
|
58
|
+
const url = new URL(location);
|
|
59
|
+
const expires = Math.floor(nowMs / 1000) + ttl;
|
|
60
|
+
url.searchParams.delete(SIGNED_EXPIRES);
|
|
61
|
+
url.searchParams.delete(SIGNED_SIGNATURE);
|
|
62
|
+
url.searchParams.set(SIGNED_EXPIRES, String(expires));
|
|
63
|
+
url.searchParams.set(SIGNED_SIGNATURE, await hmac(key, signedText(url.pathname, expires)));
|
|
64
|
+
return url.toString();
|
|
65
|
+
}
|
|
66
|
+
/** Whether a URL carries a signature this key made for its path that has not expired. */
|
|
67
|
+
export async function verifySignedUrl(url, key, nowMs) {
|
|
68
|
+
const expires = Number(url.searchParams.get(SIGNED_EXPIRES));
|
|
69
|
+
const signature = url.searchParams.get(SIGNED_SIGNATURE);
|
|
70
|
+
if (!signature || !Number.isInteger(expires) || expires * 1000 < nowMs)
|
|
71
|
+
return false;
|
|
72
|
+
return sameText(signature, await hmac(key, signedText(url.pathname, expires)));
|
|
73
|
+
}
|
|
74
|
+
/** Whether a request carries one of the accepted bearer tokens. */
|
|
75
|
+
export function bearerAccepted(request, tokens) {
|
|
76
|
+
const m = /^Bearer\s+(.+)$/i.exec(request.headers.get("authorization") ?? "");
|
|
77
|
+
if (!m)
|
|
78
|
+
return false;
|
|
79
|
+
const given = m[1].trim();
|
|
80
|
+
// Check every token, so the time taken doesn't say which one matched.
|
|
81
|
+
let ok = false;
|
|
82
|
+
for (const t of tokens)
|
|
83
|
+
if (t && sameText(given, t))
|
|
84
|
+
ok = true;
|
|
85
|
+
return ok;
|
|
86
|
+
}
|
|
87
|
+
const unauthorized = () => new Response(`${JSON.stringify({ errors: ["a token is required: set TF_TOKEN_<host> or a credentials block for this host"] })}\n`, { status: 401, headers: { "content-type": "application/json", "www-authenticate": 'Bearer realm="terraform-registry"' } });
|
|
88
|
+
/** A tarball behind this host: the location's origin is the request's, and it names an archive. */
|
|
89
|
+
const isOwnTarball = (location, request) => location.origin === request.origin && /\.(tar\.gz|tgz|zip)$/.test(location.pathname);
|
|
90
|
+
/**
|
|
91
|
+
* The part of the function that decides before the bucket is read: a 401, a
|
|
92
|
+
* download answer, or undefined for "serve this file from the bucket as it is".
|
|
93
|
+
* Lambda@Edge uses this on its own and lets CloudFront fetch the rest.
|
|
94
|
+
*/
|
|
95
|
+
export function registryEdgeGate(options) {
|
|
96
|
+
const now = options.now ?? Date.now;
|
|
97
|
+
return async (request) => {
|
|
98
|
+
const url = new URL(request.url);
|
|
99
|
+
const method = request.method.toUpperCase();
|
|
100
|
+
const done = (res) => {
|
|
101
|
+
options.log?.(`${method} ${url.pathname} -> ${res.status}`);
|
|
102
|
+
return res;
|
|
103
|
+
};
|
|
104
|
+
const path = url.pathname.replace(/^\/+/, "");
|
|
105
|
+
const auth = options.auth;
|
|
106
|
+
if (auth) {
|
|
107
|
+
const protocolFile = path === DISCOVERY || isDownloadPath(path) || /(^|\/)versions$/.test(path);
|
|
108
|
+
let allowed = bearerAccepted(request, auth.tokens);
|
|
109
|
+
// A signed URL opens one file that is not a protocol answer: the tarball the download named.
|
|
110
|
+
const key = signingKeyOf(auth);
|
|
111
|
+
if (!allowed && !protocolFile && key)
|
|
112
|
+
allowed = await verifySignedUrl(url, key, now());
|
|
113
|
+
if (!allowed)
|
|
114
|
+
return done(unauthorized());
|
|
115
|
+
}
|
|
116
|
+
if (method !== "GET" && method !== "HEAD")
|
|
117
|
+
return undefined;
|
|
118
|
+
if (auth && isMirrorVersionPath(path))
|
|
119
|
+
return signedMirrorVersion(request, url, path, options, auth, now(), done);
|
|
120
|
+
if (!isDownloadPath(path))
|
|
121
|
+
return undefined;
|
|
122
|
+
let file;
|
|
123
|
+
try {
|
|
124
|
+
file = await options.files(keyFor(options.prefix, path));
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return done(new Response(null, { status: 502 }));
|
|
128
|
+
}
|
|
129
|
+
if (file.body === undefined)
|
|
130
|
+
return done(new Response(null, { status: 404 }));
|
|
131
|
+
let location = downloadLocation(typeof file.body === "string" ? file.body : new TextDecoder().decode(file.body));
|
|
132
|
+
if (!location)
|
|
133
|
+
return undefined;
|
|
134
|
+
const key = auth ? signingKeyOf(auth) : undefined;
|
|
135
|
+
if (auth && key) {
|
|
136
|
+
const resolved = new URL(location, url);
|
|
137
|
+
if (isOwnTarball(resolved, url))
|
|
138
|
+
location = await signLocation(resolved.toString(), key, now(), auth.ttl);
|
|
139
|
+
}
|
|
140
|
+
const answer = downloadAnswer(location);
|
|
141
|
+
return done(new Response(method === "HEAD" ? null : answer.body, { status: answer.status, headers: answer.headers }));
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** A mirror version file with each archive on this host signed; undefined (served as stored) when it is not the protocol's JSON. */
|
|
145
|
+
async function signedMirrorVersion(request, url, path, options, auth, nowMs, done) {
|
|
146
|
+
const key = signingKeyOf(auth);
|
|
147
|
+
if (!key)
|
|
148
|
+
return undefined;
|
|
149
|
+
let file;
|
|
150
|
+
try {
|
|
151
|
+
file = await options.files(keyFor(options.prefix, path));
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return done(new Response(null, { status: 502 }));
|
|
155
|
+
}
|
|
156
|
+
if (file.body === undefined)
|
|
157
|
+
return done(new Response(null, { status: 404 }));
|
|
158
|
+
let doc;
|
|
159
|
+
try {
|
|
160
|
+
doc = JSON.parse(typeof file.body === "string" ? file.body : new TextDecoder().decode(file.body));
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
for (const archive of Object.values(doc.archives ?? {})) {
|
|
166
|
+
if (!archive || typeof archive.url !== "string")
|
|
167
|
+
continue;
|
|
168
|
+
const resolved = new URL(archive.url, url);
|
|
169
|
+
if (resolved.origin === url.origin)
|
|
170
|
+
archive.url = await signLocation(resolved.toString(), key, nowMs, auth.ttl);
|
|
171
|
+
}
|
|
172
|
+
const body = `${JSON.stringify(doc)}\n`;
|
|
173
|
+
return done(new Response(request.method.toUpperCase() === "HEAD" ? null : body, { status: 200, headers: { "content-type": "application/json" } }));
|
|
174
|
+
}
|
|
175
|
+
/** The whole function: the gate, then every other file served from the bucket by `registryHandler`. */
|
|
176
|
+
export function registryEdge(options) {
|
|
177
|
+
const gate = registryEdgeGate(options);
|
|
178
|
+
const serve = registryHandler(options.files, { prefix: options.prefix, log: options.log });
|
|
179
|
+
return async (request) => (await gate(request)) ?? serve(request);
|
|
180
|
+
}
|
|
181
|
+
/** Registry files read from an origin over HTTP: a public bucket endpoint, a website endpoint, or a test server. */
|
|
182
|
+
export function originFiles(base, fetchFn = (u, i) => fetch(u, i)) {
|
|
183
|
+
const root = base.replace(/\/+$/, "");
|
|
184
|
+
return async (key) => {
|
|
185
|
+
const res = await fetchFn(`${root}/${key.split("/").map(encodeURIComponent).join("/")}`, { method: "GET" });
|
|
186
|
+
if (res.status === 404 || res.status === 403)
|
|
187
|
+
return {};
|
|
188
|
+
if (!res.ok)
|
|
189
|
+
throw new Error(`origin answered ${res.status}`);
|
|
190
|
+
return { body: new Uint8Array(await res.arrayBuffer()) };
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/** Accepted tokens from a secret's text: one per line or comma separated, blanks dropped. */
|
|
194
|
+
export const tokensFromSecret = (text) => (text ?? "").split(/[\n,]/).map((t) => t.trim()).filter((t) => !!t);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The edge function for bucket-hosted module registries (chant#4566): the
|
|
3
|
+
* download header always, and with `auth` a bearer token check and signed
|
|
4
|
+
* tarball URLs. Platform adapters for a Cloudflare Worker and Lambda@Edge.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./edge.js";
|
|
7
|
+
export * from "./worker.js";
|
|
8
|
+
export * from "./lambda-edge.js";
|
|
9
|
+
export * from "./token-source.js";
|
|
10
|
+
export * from "./sigv4-web.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/registry/edge/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The edge function for bucket-hosted module registries (chant#4566): the
|
|
3
|
+
* download header always, and with `auth` a bearer token check and signed
|
|
4
|
+
* tarball URLs. Platform adapters for a Cloudflare Worker and Lambda@Edge.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./edge.js";
|
|
7
|
+
export * from "./worker.js";
|
|
8
|
+
export * from "./lambda-edge.js";
|
|
9
|
+
export * from "./token-source.js";
|
|
10
|
+
export * from "./sigv4-web.js";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The registry edge function as a Lambda@Edge origin-request handler on
|
|
3
|
+
* CloudFront (chant#4566), Node 20 or later. Lambda@Edge has no environment
|
|
4
|
+
* variables, so the options are fixed when the function is built:
|
|
5
|
+
*
|
|
6
|
+
* export const handler = lambdaEdgeHandler({ files: originFiles("https://<bucket endpoint>"), auth: { tokens: [...] } });
|
|
7
|
+
*
|
|
8
|
+
* A request the gate refuses gets the 401 from the function. A download gets
|
|
9
|
+
* its answer, with `X-Terraform-Get` and, with auth on, a signed tarball URL.
|
|
10
|
+
* Every other request goes on to the bucket, without the query string when
|
|
11
|
+
* auth is on. CloudFront must pass
|
|
12
|
+
* the query string and the Authorization header to the function (an origin
|
|
13
|
+
* request policy) for the token check and signed URLs to work.
|
|
14
|
+
*
|
|
15
|
+
* A CloudFront Function cannot read the bucket's `download` body, so it
|
|
16
|
+
* cannot add the header; this needs Lambda@Edge.
|
|
17
|
+
*
|
|
18
|
+
* The accepted tokens come from `auth.source` (chant#4590): built into the
|
|
19
|
+
* zip as `auth.tokens` (`{ kind: "zip" }`, the default), or read from SSM
|
|
20
|
+
* Parameter Store or Secrets Manager at cold start and kept for a bounded
|
|
21
|
+
* time (./token-source.ts):
|
|
22
|
+
*
|
|
23
|
+
* lambdaEdgeHandler({ files, auth: { source: { kind: "ssm", name: "/registry/tokens", region: "us-east-1" } } });
|
|
24
|
+
*
|
|
25
|
+
* While a store read fails, every request gets a 503.
|
|
26
|
+
*/
|
|
27
|
+
import { type EdgeAuth, type RegistryEdgeOptions } from "./edge.js";
|
|
28
|
+
import { type EdgeTokenSource } from "./token-source.js";
|
|
29
|
+
/** The parts of a CloudFront request event this handler reads. */
|
|
30
|
+
export interface CloudFrontRequest {
|
|
31
|
+
uri: string;
|
|
32
|
+
querystring?: string;
|
|
33
|
+
method: string;
|
|
34
|
+
headers: Record<string, {
|
|
35
|
+
key?: string;
|
|
36
|
+
value: string;
|
|
37
|
+
}[]>;
|
|
38
|
+
}
|
|
39
|
+
export interface CloudFrontRequestEvent {
|
|
40
|
+
Records: {
|
|
41
|
+
cf: {
|
|
42
|
+
config?: {
|
|
43
|
+
distributionDomainName?: string;
|
|
44
|
+
};
|
|
45
|
+
request: CloudFrontRequest;
|
|
46
|
+
};
|
|
47
|
+
}[];
|
|
48
|
+
}
|
|
49
|
+
/** A response Lambda@Edge generates in place of the origin's. */
|
|
50
|
+
export interface CloudFrontResponse {
|
|
51
|
+
status: string;
|
|
52
|
+
statusDescription?: string;
|
|
53
|
+
headers: Record<string, {
|
|
54
|
+
key: string;
|
|
55
|
+
value: string;
|
|
56
|
+
}[]>;
|
|
57
|
+
body?: string;
|
|
58
|
+
}
|
|
59
|
+
/** The web Request a CloudFront request stands for. */
|
|
60
|
+
export declare function requestFromCloudFront(event: CloudFrontRequestEvent): Request;
|
|
61
|
+
/** A web Response as a Lambda@Edge generated response. */
|
|
62
|
+
export declare function responseToCloudFront(res: Response): Promise<CloudFrontResponse>;
|
|
63
|
+
/** Read auth on Lambda@Edge: `EdgeAuth`, with the tokens from `source`. */
|
|
64
|
+
export interface LambdaEdgeAuth extends Omit<EdgeAuth, "tokens"> {
|
|
65
|
+
/** The tokens built into the function, for `source` zip (the default). Ignored with a store source. */
|
|
66
|
+
tokens?: string[];
|
|
67
|
+
/** Where the tokens come from. Default `{ kind: "zip" }`. */
|
|
68
|
+
source?: EdgeTokenSource;
|
|
69
|
+
}
|
|
70
|
+
export interface LambdaEdgeOptions extends Omit<RegistryEdgeOptions, "auth"> {
|
|
71
|
+
/** Read auth. Absent (the default) means anyone who can reach the host reads. */
|
|
72
|
+
auth?: LambdaEdgeAuth;
|
|
73
|
+
}
|
|
74
|
+
/** An origin-request handler: a generated response, or the request passed on to the bucket. */
|
|
75
|
+
export declare function lambdaEdgeHandler(options: LambdaEdgeOptions): (event: CloudFrontRequestEvent) => Promise<CloudFrontRequest | CloudFrontResponse>;
|
|
76
|
+
//# sourceMappingURL=lambda-edge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lambda-edge.d.ts","sourceRoot":"","sources":["../../../src/registry/edge/lambda-edge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAoB,KAAK,QAAQ,EAAE,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AACnF,OAAO,EAAoB,KAAK,eAAe,EAAqB,MAAM,gBAAgB,CAAC;AAE3F,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE;QAAE,EAAE,EAAE;YAAE,MAAM,CAAC,EAAE;gBAAE,sBAAsB,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,OAAO,EAAE,iBAAiB,CAAA;SAAE,CAAA;KAAE,EAAE,CAAC;CACjG;AAED,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,uDAAuD;AACvD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAM5E;AAED,0DAA0D;AAC1D,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAOrF;AAED,2EAA2E;AAC3E,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC9D,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC;IAC1E,iFAAiF;IACjF,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB;AAUD,+FAA+F;AAC/F,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,CAAC,KAAK,EAAE,sBAAsB,KAAK,OAAO,CAAC,iBAAiB,GAAG,kBAAkB,CAAC,CAoChJ"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The registry edge function as a Lambda@Edge origin-request handler on
|
|
3
|
+
* CloudFront (chant#4566), Node 20 or later. Lambda@Edge has no environment
|
|
4
|
+
* variables, so the options are fixed when the function is built:
|
|
5
|
+
*
|
|
6
|
+
* export const handler = lambdaEdgeHandler({ files: originFiles("https://<bucket endpoint>"), auth: { tokens: [...] } });
|
|
7
|
+
*
|
|
8
|
+
* A request the gate refuses gets the 401 from the function. A download gets
|
|
9
|
+
* its answer, with `X-Terraform-Get` and, with auth on, a signed tarball URL.
|
|
10
|
+
* Every other request goes on to the bucket, without the query string when
|
|
11
|
+
* auth is on. CloudFront must pass
|
|
12
|
+
* the query string and the Authorization header to the function (an origin
|
|
13
|
+
* request policy) for the token check and signed URLs to work.
|
|
14
|
+
*
|
|
15
|
+
* A CloudFront Function cannot read the bucket's `download` body, so it
|
|
16
|
+
* cannot add the header; this needs Lambda@Edge.
|
|
17
|
+
*
|
|
18
|
+
* The accepted tokens come from `auth.source` (chant#4590): built into the
|
|
19
|
+
* zip as `auth.tokens` (`{ kind: "zip" }`, the default), or read from SSM
|
|
20
|
+
* Parameter Store or Secrets Manager at cold start and kept for a bounded
|
|
21
|
+
* time (./token-source.ts):
|
|
22
|
+
*
|
|
23
|
+
* lambdaEdgeHandler({ files, auth: { source: { kind: "ssm", name: "/registry/tokens", region: "us-east-1" } } });
|
|
24
|
+
*
|
|
25
|
+
* While a store read fails, every request gets a 503.
|
|
26
|
+
*/
|
|
27
|
+
import { registryEdgeGate } from "./edge.js";
|
|
28
|
+
import { storedTokenCache } from "./token-source.js";
|
|
29
|
+
/** The web Request a CloudFront request stands for. */
|
|
30
|
+
export function requestFromCloudFront(event) {
|
|
31
|
+
const { config, request } = event.Records[0].cf;
|
|
32
|
+
const host = request.headers.host?.[0]?.value ?? config?.distributionDomainName ?? "localhost";
|
|
33
|
+
const headers = new Headers();
|
|
34
|
+
for (const values of Object.values(request.headers))
|
|
35
|
+
for (const h of values)
|
|
36
|
+
if (h.key)
|
|
37
|
+
headers.append(h.key, h.value);
|
|
38
|
+
return new Request(`https://${host}${request.uri}${request.querystring ? `?${request.querystring}` : ""}`, { method: request.method, headers });
|
|
39
|
+
}
|
|
40
|
+
/** A web Response as a Lambda@Edge generated response. */
|
|
41
|
+
export async function responseToCloudFront(res) {
|
|
42
|
+
const headers = {};
|
|
43
|
+
res.headers.forEach((value, key) => {
|
|
44
|
+
headers[key.toLowerCase()] = [{ key, value }];
|
|
45
|
+
});
|
|
46
|
+
const body = res.body ? await res.text() : undefined;
|
|
47
|
+
return { status: String(res.status), headers, ...(body !== undefined ? { body } : {}) };
|
|
48
|
+
}
|
|
49
|
+
/** The answer while the token store cannot be read: the function fails closed, and CloudFront does not cache it. */
|
|
50
|
+
const storeUnavailable = () => ({
|
|
51
|
+
status: "503",
|
|
52
|
+
statusDescription: "Service Unavailable",
|
|
53
|
+
headers: { "content-type": [{ key: "Content-Type", value: "application/json" }], "cache-control": [{ key: "Cache-Control", value: "no-store" }] },
|
|
54
|
+
body: `${JSON.stringify({ errors: ["the registry cannot check tokens right now"] })}\n`,
|
|
55
|
+
});
|
|
56
|
+
/** An origin-request handler: a generated response, or the request passed on to the bucket. */
|
|
57
|
+
export function lambdaEdgeHandler(options) {
|
|
58
|
+
const { auth, ...rest } = options;
|
|
59
|
+
const source = auth?.source ?? { kind: "zip" };
|
|
60
|
+
const passOn = (event) => {
|
|
61
|
+
// The signature has been checked; the bucket gets the plain object key, so its own query auth never sees ours.
|
|
62
|
+
const request = event.Records[0].cf.request;
|
|
63
|
+
return auth ? { ...request, querystring: "" } : request;
|
|
64
|
+
};
|
|
65
|
+
const edgeAuth = (tokens, signingKey) => ({
|
|
66
|
+
tokens,
|
|
67
|
+
// A signing key kept in the store rotates with it, so it comes before one built into the zip.
|
|
68
|
+
...(signingKey || auth?.signingKey ? { signingKey: signingKey || auth?.signingKey } : {}),
|
|
69
|
+
...(auth?.ttl !== undefined ? { ttl: auth.ttl } : {}),
|
|
70
|
+
});
|
|
71
|
+
if (!auth || source.kind === "zip") {
|
|
72
|
+
const gate = registryEdgeGate({ ...rest, ...(auth ? { auth: edgeAuth(auth.tokens ?? []) } : {}) });
|
|
73
|
+
return async (event) => {
|
|
74
|
+
const answer = await gate(requestFromCloudFront(event));
|
|
75
|
+
return answer ? responseToCloudFront(answer) : passOn(event);
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const cache = storedTokenCache(source, rest.now);
|
|
79
|
+
// One gate per read, so a request between rotations is checked against one consistent token set.
|
|
80
|
+
let gateFor;
|
|
81
|
+
return async (event) => {
|
|
82
|
+
let stored;
|
|
83
|
+
try {
|
|
84
|
+
stored = await cache.get();
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
rest.log?.(`token store: ${e.message}`);
|
|
88
|
+
return storeUnavailable();
|
|
89
|
+
}
|
|
90
|
+
if (gateFor?.stored !== stored)
|
|
91
|
+
gateFor = { stored, gate: registryEdgeGate({ ...rest, auth: edgeAuth(stored.tokens, stored.signingKey) }) };
|
|
92
|
+
const answer = await gateFor.gate(requestFromCloudFront(event));
|
|
93
|
+
return answer ? responseToCloudFront(answer) : passOn(event);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Signature Version 4 with Web Crypto, for the registry edge function's
|
|
3
|
+
* token reads (chant#4590). chant's one AWS signer
|
|
4
|
+
* (packages/core/src/cloud-lister/sigv4.ts) hashes with node:crypto, which a
|
|
5
|
+
* browser-platform bundle of registry/edge must not reach (chant#4589), so
|
|
6
|
+
* this is the same canonical request built on crypto.subtle. It signs only
|
|
7
|
+
* what the edge sends: a JSON POST to an AWS query endpoint, with `host`,
|
|
8
|
+
* `x-amz-date`, `x-amz-target`, `content-type` and the session token.
|
|
9
|
+
*
|
|
10
|
+
* Its signatures match core's signer for the same input; the test checks
|
|
11
|
+
* the two against each other.
|
|
12
|
+
*/
|
|
13
|
+
/** The credentials a request is signed with: the function's execution role on Lambda@Edge. */
|
|
14
|
+
export interface EdgeAwsCredentials {
|
|
15
|
+
accessKeyId: string;
|
|
16
|
+
secretAccessKey: string;
|
|
17
|
+
sessionToken?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The headers to send with `body`, `authorization` included, in core's
|
|
21
|
+
* signer's shape: lower-case names, `host` and `x-amz-date` added.
|
|
22
|
+
*/
|
|
23
|
+
export declare function signV4Web(input: {
|
|
24
|
+
method: string;
|
|
25
|
+
url: string;
|
|
26
|
+
headers: Record<string, string>;
|
|
27
|
+
body: string;
|
|
28
|
+
region: string;
|
|
29
|
+
service: string;
|
|
30
|
+
credentials: EdgeAwsCredentials;
|
|
31
|
+
now: Date;
|
|
32
|
+
}): Promise<Record<string, string>>;
|
|
33
|
+
//# sourceMappingURL=sigv4-web.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sigv4-web.d.ts","sourceRoot":"","sources":["../../../src/registry/edge/sigv4-web.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,8FAA8F;AAC9F,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAuBD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,kBAAkB,CAAC;IAChC,GAAG,EAAE,IAAI,CAAC;CACX,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAwBlC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Signature Version 4 with Web Crypto, for the registry edge function's
|
|
3
|
+
* token reads (chant#4590). chant's one AWS signer
|
|
4
|
+
* (packages/core/src/cloud-lister/sigv4.ts) hashes with node:crypto, which a
|
|
5
|
+
* browser-platform bundle of registry/edge must not reach (chant#4589), so
|
|
6
|
+
* this is the same canonical request built on crypto.subtle. It signs only
|
|
7
|
+
* what the edge sends: a JSON POST to an AWS query endpoint, with `host`,
|
|
8
|
+
* `x-amz-date`, `x-amz-target`, `content-type` and the session token.
|
|
9
|
+
*
|
|
10
|
+
* Its signatures match core's signer for the same input; the test checks
|
|
11
|
+
* the two against each other.
|
|
12
|
+
*/
|
|
13
|
+
const encoder = new TextEncoder();
|
|
14
|
+
const hex = (bytes) => [...new Uint8Array(bytes)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
15
|
+
const sha256Hex = async (text) => hex(await crypto.subtle.digest("SHA-256", encoder.encode(text)));
|
|
16
|
+
async function hmac(key, text) {
|
|
17
|
+
const raw = typeof key === "string" ? encoder.encode(key) : key;
|
|
18
|
+
const k = await crypto.subtle.importKey("raw", raw, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
19
|
+
return crypto.subtle.sign("HMAC", k, encoder.encode(text));
|
|
20
|
+
}
|
|
21
|
+
const encodeRfc3986 = (value) => encodeURIComponent(value).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
22
|
+
function canonicalQuery(url) {
|
|
23
|
+
const pairs = [];
|
|
24
|
+
url.searchParams.forEach((v, k) => pairs.push([encodeRfc3986(k), encodeRfc3986(v)]));
|
|
25
|
+
pairs.sort((a, b) => (a[0] === b[0] ? (a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) : a[0] < b[0] ? -1 : 1));
|
|
26
|
+
return pairs.map(([k, v]) => `${k}=${v}`).join("&");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The headers to send with `body`, `authorization` included, in core's
|
|
30
|
+
* signer's shape: lower-case names, `host` and `x-amz-date` added.
|
|
31
|
+
*/
|
|
32
|
+
export async function signV4Web(input) {
|
|
33
|
+
const url = new URL(input.url);
|
|
34
|
+
const amzDate = input.now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}/, "");
|
|
35
|
+
const dateStamp = amzDate.slice(0, 8);
|
|
36
|
+
const payloadHash = await sha256Hex(input.body);
|
|
37
|
+
const headers = {};
|
|
38
|
+
for (const [k, v] of Object.entries(input.headers))
|
|
39
|
+
headers[k.toLowerCase()] = v.trim().replace(/\s+/g, " ");
|
|
40
|
+
headers.host = url.host;
|
|
41
|
+
headers["x-amz-date"] = amzDate;
|
|
42
|
+
if (input.credentials.sessionToken)
|
|
43
|
+
headers["x-amz-security-token"] = input.credentials.sessionToken;
|
|
44
|
+
const names = Object.keys(headers).sort();
|
|
45
|
+
const canonicalHeaders = names.map((n) => `${n}:${headers[n]}\n`).join("");
|
|
46
|
+
const signedHeaders = names.join(";");
|
|
47
|
+
const canonicalRequest = [input.method.toUpperCase(), url.pathname || "/", canonicalQuery(url), canonicalHeaders, signedHeaders, payloadHash].join("\n");
|
|
48
|
+
const scope = `${dateStamp}/${input.region}/${input.service}/aws4_request`;
|
|
49
|
+
const stringToSign = ["AWS4-HMAC-SHA256", amzDate, scope, await sha256Hex(canonicalRequest)].join("\n");
|
|
50
|
+
let key = await hmac(`AWS4${input.credentials.secretAccessKey}`, dateStamp);
|
|
51
|
+
for (const part of [input.region, input.service, "aws4_request"])
|
|
52
|
+
key = await hmac(key, part);
|
|
53
|
+
const signature = hex(await hmac(key, stringToSign));
|
|
54
|
+
headers.authorization = `AWS4-HMAC-SHA256 Credential=${input.credentials.accessKeyId}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
55
|
+
return headers;
|
|
56
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type EdgeAwsCredentials } from "./sigv4-web.js";
|
|
2
|
+
/** The fetch the reads call; a test passes a fake, so no AWS call is made. */
|
|
3
|
+
export type EdgeAwsFetch = (url: string, init: {
|
|
4
|
+
method: string;
|
|
5
|
+
headers: Record<string, string>;
|
|
6
|
+
body: string;
|
|
7
|
+
}) => Promise<{
|
|
8
|
+
ok: boolean;
|
|
9
|
+
status: number;
|
|
10
|
+
text(): Promise<string>;
|
|
11
|
+
}>;
|
|
12
|
+
/** What a store read shares. */
|
|
13
|
+
interface StoreSourceBase {
|
|
14
|
+
/**
|
|
15
|
+
* The region the parameter or secret is in. Default us-east-1, where a
|
|
16
|
+
* Lambda@Edge function is created; the replicas run in other regions, so
|
|
17
|
+
* the function's own AWS_REGION is never used.
|
|
18
|
+
*/
|
|
19
|
+
region?: string;
|
|
20
|
+
/** Seconds a read is kept before the next request reads again. Default 300, at most 3600. */
|
|
21
|
+
cacheSeconds?: number;
|
|
22
|
+
/** The credentials to sign with. Default: the execution role's, from the runtime's environment. */
|
|
23
|
+
credentials?: EdgeAwsCredentials | (() => Promise<EdgeAwsCredentials>);
|
|
24
|
+
/** The fetch to call. Default the global fetch. */
|
|
25
|
+
fetch?: EdgeAwsFetch;
|
|
26
|
+
}
|
|
27
|
+
/** An SSM parameter (String or SecureString) holding the tokens. */
|
|
28
|
+
export interface SsmTokenSource extends StoreSourceBase {
|
|
29
|
+
kind: "ssm";
|
|
30
|
+
/** The parameter's name, such as /registry/tokens. */
|
|
31
|
+
name: string;
|
|
32
|
+
}
|
|
33
|
+
/** A Secrets Manager secret whose SecretString holds the tokens. */
|
|
34
|
+
export interface SecretsManagerTokenSource extends StoreSourceBase {
|
|
35
|
+
kind: "secrets-manager";
|
|
36
|
+
/** The secret's name or ARN. */
|
|
37
|
+
secretId: string;
|
|
38
|
+
}
|
|
39
|
+
/** The tokens built into the function: `auth.tokens`. */
|
|
40
|
+
export interface ZipTokenSource {
|
|
41
|
+
kind: "zip";
|
|
42
|
+
}
|
|
43
|
+
export type EdgeTokenSource = ZipTokenSource | SsmTokenSource | SecretsManagerTokenSource;
|
|
44
|
+
/** The tokens a store holds, and the signing key when the store names one. */
|
|
45
|
+
export interface StoredTokens {
|
|
46
|
+
tokens: string[];
|
|
47
|
+
signingKey?: string;
|
|
48
|
+
}
|
|
49
|
+
export declare const DEFAULT_TOKEN_CACHE_SECONDS = 300;
|
|
50
|
+
export declare const MAX_TOKEN_CACHE_SECONDS = 3600;
|
|
51
|
+
/** After a failed read, requests are refused for this long before the next read, so a broken store is not read once per request. */
|
|
52
|
+
export declare const FAILED_READ_BACKOFF_MS = 5000;
|
|
53
|
+
/**
|
|
54
|
+
* A stored value as tokens. Either the worker's REGISTRY_TOKENS form (one per
|
|
55
|
+
* line or comma separated) or JSON: `{ "tokens": [...] | "a,b", "signingKey": "..." }`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function parseStoredTokens(text: string): StoredTokens;
|
|
58
|
+
/** One read of the store: the signed JSON call, and the value it answers. */
|
|
59
|
+
export declare function readStoredTokens(source: SsmTokenSource | SecretsManagerTokenSource, now?: Date): Promise<StoredTokens>;
|
|
60
|
+
/** Tokens read from a store and kept for a bounded time. `get` throws while the store cannot be read. */
|
|
61
|
+
export interface TokenCache {
|
|
62
|
+
get(): Promise<StoredTokens>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A cache over `readStoredTokens`. The first read starts at once (cold
|
|
66
|
+
* start). Requests that arrive during a read wait on that one read. A good
|
|
67
|
+
* read is kept for `cacheSeconds`; a failed one is kept for
|
|
68
|
+
* FAILED_READ_BACKOFF_MS, and every `get` in that time throws. The last good
|
|
69
|
+
* tokens are never served past their time, so the function fails closed.
|
|
70
|
+
*/
|
|
71
|
+
export declare function storedTokenCache(source: SsmTokenSource | SecretsManagerTokenSource, now?: () => number): TokenCache;
|
|
72
|
+
export {};
|
|
73
|
+
//# sourceMappingURL=token-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-source.d.ts","sourceRoot":"","sources":["../../../src/registry/edge/token-source.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAa,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjE,8EAA8E;AAC9E,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAAC;AAEvL,gCAAgC;AAChC,UAAU,eAAe;IACvB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mGAAmG;IACnG,WAAW,CAAC,EAAE,kBAAkB,GAAG,CAAC,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACvE,mDAAmD;IACnD,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,oEAAoE;AACpE,MAAM,WAAW,cAAe,SAAQ,eAAe;IACrD,IAAI,EAAE,KAAK,CAAC;IACZ,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,oEAAoE;AACpE,MAAM,WAAW,yBAA0B,SAAQ,eAAe;IAChE,IAAI,EAAE,iBAAiB,CAAC;IACxB,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,yDAAyD;AACzD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,KAAK,CAAC;CACb;AAED,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,cAAc,GAAG,yBAAyB,CAAC;AAE1F,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,oIAAoI;AACpI,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AAE5C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAa5D;AAaD,6EAA6E;AAC7E,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,yBAAyB,EAAE,GAAG,GAAE,IAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CA6BxI;AAED,yGAAyG;AACzG,MAAM,WAAW,UAAU;IACzB,GAAG,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,yBAAyB,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,UAAU,CAqB7H"}
|