@mannyc1/ts-release 0.0.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +184 -92
- package/CHANGELOG.md +197 -0
- package/LICENSE +21 -0
- package/README.md +404 -327
- package/SPEC.md +257 -310
- package/apps/release-ts/README.md +39 -0
- package/apps/release-ts/src/cli/command.ts +164 -0
- package/apps/release-ts/src/cli/commands.ts +351 -0
- package/apps/release-ts/src/cli/main.ts +43 -0
- package/apps/release-ts/src/cli/node-main.ts +49 -0
- package/apps/ts-release-agents/LICENSE +21 -0
- package/apps/ts-release-agents/README.md +21 -0
- package/apps/ts-release-agents/evals/cases.json +11 -0
- package/apps/ts-release-agents/manifests/claude.json +5 -0
- package/apps/ts-release-agents/manifests/codex.json +5 -0
- package/apps/ts-release-agents/package.json +11 -0
- package/apps/ts-release-agents/skills/release/SKILL.md +48 -0
- package/apps/ts-release-agents/skills/release/references/configuration.md +18 -0
- package/apps/ts-release-agents/skills/release/references/recovery.md +10 -0
- package/apps/ts-release-agents/skills/release/references/staged-workflow.md +12 -0
- package/apps/ts-release-agents/skills/release/references/target-selection.md +13 -0
- package/apps/ts-release-agents/skills/release/references/verification.md +14 -0
- package/apps/ts-release-agents/src/build.ts +105 -0
- package/apps/ts-release-agents/src/check.ts +122 -0
- package/apps/ts-release-agents/src/install.ts +121 -0
- package/apps/ts-release-agents/test/contract.test.ts +56 -0
- package/apps/ts-release-agents/tsconfig.json +5 -0
- package/dist/api/api.d.ts +15 -0
- package/dist/api/api.d.ts.map +1 -0
- package/dist/api/api.js +244 -0
- package/dist/api/api.js.map +1 -0
- package/dist/api/errors.d.ts +39 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +32 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/input.d.ts +10 -0
- package/dist/api/input.d.ts.map +1 -0
- package/dist/api/input.js +73 -0
- package/dist/api/input.js.map +1 -0
- package/dist/api/runtime.d.ts +18 -0
- package/dist/api/runtime.d.ts.map +1 -0
- package/dist/api/runtime.js +7 -0
- package/dist/api/runtime.js.map +1 -0
- package/dist/api/types.d.ts +76 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +17 -0
- package/dist/api/types.js.map +1 -0
- package/dist/bin/ts-release.js +39346 -0
- package/dist/capabilities/bun-targets.d.ts +3 -0
- package/dist/capabilities/bun-targets.d.ts.map +1 -0
- package/dist/capabilities/bun-targets.js +3 -0
- package/dist/capabilities/bun-targets.js.map +1 -0
- package/dist/capabilities/field-ownership.d.ts +17 -0
- package/dist/capabilities/field-ownership.d.ts.map +1 -0
- package/dist/capabilities/field-ownership.js +40 -0
- package/dist/capabilities/field-ownership.js.map +1 -0
- package/dist/capabilities/module.d.ts +91 -0
- package/dist/capabilities/module.d.ts.map +1 -0
- package/dist/capabilities/module.js +2 -0
- package/dist/capabilities/module.js.map +1 -0
- package/dist/capabilities/registry.d.ts +829 -0
- package/dist/capabilities/registry.d.ts.map +1 -0
- package/dist/capabilities/registry.js +291 -0
- package/dist/capabilities/registry.js.map +1 -0
- package/dist/config/config.d.ts +6 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +42 -0
- package/dist/config/config.js.map +1 -0
- package/dist/correction/coordinator.d.ts +248 -0
- package/dist/correction/coordinator.d.ts.map +1 -0
- package/dist/correction/coordinator.js +274 -0
- package/dist/correction/coordinator.js.map +1 -0
- package/dist/correction/index.d.ts +3 -0
- package/dist/correction/index.d.ts.map +1 -0
- package/dist/correction/index.js +3 -0
- package/dist/correction/index.js.map +1 -0
- package/dist/correction/intent.d.ts +111 -0
- package/dist/correction/intent.d.ts.map +1 -0
- package/dist/correction/intent.js +146 -0
- package/dist/correction/intent.js.map +1 -0
- package/dist/drivers/archive.d.ts +9 -0
- package/dist/drivers/archive.d.ts.map +1 -0
- package/dist/drivers/archive.js +105 -0
- package/dist/drivers/archive.js.map +1 -0
- package/dist/drivers/contain.d.ts +6 -0
- package/dist/drivers/contain.d.ts.map +1 -0
- package/dist/drivers/contain.js +17 -0
- package/dist/drivers/contain.js.map +1 -0
- package/dist/drivers/environment.d.ts +4 -0
- package/dist/drivers/environment.d.ts.map +1 -0
- package/dist/drivers/environment.js +18 -0
- package/dist/drivers/environment.js.map +1 -0
- package/dist/drivers/errors.d.ts +9 -0
- package/dist/drivers/errors.d.ts.map +1 -0
- package/dist/drivers/errors.js +6 -0
- package/dist/drivers/errors.js.map +1 -0
- package/dist/drivers/glob.d.ts +2 -0
- package/dist/drivers/glob.d.ts.map +1 -0
- package/dist/drivers/glob.js +162 -0
- package/dist/drivers/glob.js.map +1 -0
- package/dist/drivers/process.d.ts +48 -0
- package/dist/drivers/process.d.ts.map +1 -0
- package/dist/drivers/process.js +257 -0
- package/dist/drivers/process.js.map +1 -0
- package/dist/drivers/python-wheel.d.ts +21 -0
- package/dist/drivers/python-wheel.d.ts.map +1 -0
- package/dist/drivers/python-wheel.js +109 -0
- package/dist/drivers/python-wheel.js.map +1 -0
- package/dist/drivers/redact.d.ts +4 -0
- package/dist/drivers/redact.d.ts.map +1 -0
- package/dist/drivers/redact.js +24 -0
- package/dist/drivers/redact.js.map +1 -0
- package/dist/drivers/seccomp-helper-source.d.ts +7 -0
- package/dist/drivers/seccomp-helper-source.d.ts.map +1 -0
- package/dist/drivers/seccomp-helper-source.js +121 -0
- package/dist/drivers/seccomp-helper-source.js.map +1 -0
- package/dist/drivers/utils.d.ts +4 -0
- package/dist/drivers/utils.d.ts.map +1 -0
- package/dist/drivers/utils.js +5 -0
- package/dist/drivers/utils.js.map +1 -0
- package/dist/drivers/workspace.d.ts +9 -0
- package/dist/drivers/workspace.d.ts.map +1 -0
- package/dist/drivers/workspace.js +53 -0
- package/dist/drivers/workspace.js.map +1 -0
- package/dist/extensions/provider-adapter.d.ts +48 -0
- package/dist/extensions/provider-adapter.d.ts.map +1 -0
- package/dist/extensions/provider-adapter.js +64 -0
- package/dist/extensions/provider-adapter.js.map +1 -0
- package/dist/host.d.ts +33 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +20 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +19 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/model/artifact-collection.d.ts +84 -0
- package/dist/model/artifact-collection.d.ts.map +1 -0
- package/dist/model/artifact-collection.js +185 -0
- package/dist/model/artifact-collection.js.map +1 -0
- package/dist/model/authority.d.ts +89 -0
- package/dist/model/authority.d.ts.map +1 -0
- package/dist/model/authority.js +80 -0
- package/dist/model/authority.js.map +1 -0
- package/dist/model/bun-targets.d.ts +34 -0
- package/dist/model/bun-targets.d.ts.map +1 -0
- package/dist/model/bun-targets.js +17 -0
- package/dist/model/bun-targets.js.map +1 -0
- package/dist/model/canonical.d.ts +8 -0
- package/dist/model/canonical.d.ts.map +1 -0
- package/dist/model/canonical.js +205 -0
- package/dist/model/canonical.js.map +1 -0
- package/dist/model/catalog.d.ts +76 -0
- package/dist/model/catalog.d.ts.map +1 -0
- package/dist/model/catalog.js +174 -0
- package/dist/model/catalog.js.map +1 -0
- package/dist/model/digest.d.ts +53 -0
- package/dist/model/digest.d.ts.map +1 -0
- package/dist/model/digest.js +127 -0
- package/dist/model/digest.js.map +1 -0
- package/dist/model/errors.d.ts +14 -0
- package/dist/model/errors.d.ts.map +1 -0
- package/dist/model/errors.js +8 -0
- package/dist/model/errors.js.map +1 -0
- package/dist/model/primitives.d.ts +19 -0
- package/dist/model/primitives.d.ts.map +1 -0
- package/dist/model/primitives.js +18 -0
- package/dist/model/primitives.js.map +1 -0
- package/dist/model/pypi.d.ts +12 -0
- package/dist/model/pypi.d.ts.map +1 -0
- package/dist/model/pypi.js +20 -0
- package/dist/model/pypi.js.map +1 -0
- package/dist/model/python-distribution.d.ts +33 -0
- package/dist/model/python-distribution.d.ts.map +1 -0
- package/dist/model/python-distribution.js +367 -0
- package/dist/model/python-distribution.js.map +1 -0
- package/dist/model/secret-patterns.d.ts +2 -0
- package/dist/model/secret-patterns.d.ts.map +1 -0
- package/dist/model/secret-patterns.js +12 -0
- package/dist/model/secret-patterns.js.map +1 -0
- package/dist/operation-journal/authority.d.ts +3 -0
- package/dist/operation-journal/authority.d.ts.map +1 -0
- package/dist/operation-journal/authority.js +147 -0
- package/dist/operation-journal/authority.js.map +1 -0
- package/dist/operation-journal/aws/deadline.d.ts +12 -0
- package/dist/operation-journal/aws/deadline.d.ts.map +1 -0
- package/dist/operation-journal/aws/deadline.js +38 -0
- package/dist/operation-journal/aws/deadline.js.map +1 -0
- package/dist/operation-journal/aws/oidc.d.ts +27 -0
- package/dist/operation-journal/aws/oidc.d.ts.map +1 -0
- package/dist/operation-journal/aws/oidc.js +361 -0
- package/dist/operation-journal/aws/oidc.js.map +1 -0
- package/dist/operation-journal/aws/policy.d.ts +42 -0
- package/dist/operation-journal/aws/policy.d.ts.map +1 -0
- package/dist/operation-journal/aws/policy.js +404 -0
- package/dist/operation-journal/aws/policy.js.map +1 -0
- package/dist/operation-journal/aws/s3-boundary.d.ts +17 -0
- package/dist/operation-journal/aws/s3-boundary.d.ts.map +1 -0
- package/dist/operation-journal/aws/s3-boundary.js +417 -0
- package/dist/operation-journal/aws/s3-boundary.js.map +1 -0
- package/dist/operation-journal/aws.d.ts +11 -0
- package/dist/operation-journal/aws.d.ts.map +1 -0
- package/dist/operation-journal/aws.js +50 -0
- package/dist/operation-journal/aws.js.map +1 -0
- package/dist/operation-journal/canonical.d.ts +52 -0
- package/dist/operation-journal/canonical.d.ts.map +1 -0
- package/dist/operation-journal/canonical.js +282 -0
- package/dist/operation-journal/canonical.js.map +1 -0
- package/dist/operation-journal/model.d.ts +251 -0
- package/dist/operation-journal/model.d.ts.map +1 -0
- package/dist/operation-journal/model.js +56 -0
- package/dist/operation-journal/model.js.map +1 -0
- package/dist/operation-journal/reducer.d.ts +28 -0
- package/dist/operation-journal/reducer.d.ts.map +1 -0
- package/dist/operation-journal/reducer.js +73 -0
- package/dist/operation-journal/reducer.js.map +1 -0
- package/dist/operation-journal/s3.d.ts +7 -0
- package/dist/operation-journal/s3.d.ts.map +1 -0
- package/dist/operation-journal/s3.js +667 -0
- package/dist/operation-journal/s3.js.map +1 -0
- package/dist/operation-journal.d.ts +14 -0
- package/dist/operation-journal.d.ts.map +1 -0
- package/dist/operation-journal.js +12 -0
- package/dist/operation-journal.js.map +1 -0
- package/dist/platform/bun.d.ts +7 -0
- package/dist/platform/bun.d.ts.map +1 -0
- package/dist/platform/bun.js +15 -0
- package/dist/platform/bun.js.map +1 -0
- package/dist/platform/credentials.d.ts +32 -0
- package/dist/platform/credentials.d.ts.map +1 -0
- package/dist/platform/credentials.js +834 -0
- package/dist/platform/credentials.js.map +1 -0
- package/dist/platform/host-support.d.ts +2 -0
- package/dist/platform/host-support.d.ts.map +1 -0
- package/dist/platform/host-support.js +4 -0
- package/dist/platform/host-support.js.map +1 -0
- package/dist/platform/node.d.ts +7 -0
- package/dist/platform/node.d.ts.map +1 -0
- package/dist/platform/node.js +15 -0
- package/dist/platform/node.js.map +1 -0
- package/dist/platform/release-runtime.d.ts +11 -0
- package/dist/platform/release-runtime.d.ts.map +1 -0
- package/dist/platform/release-runtime.js +34 -0
- package/dist/platform/release-runtime.js.map +1 -0
- package/dist/platform/services.d.ts +15 -0
- package/dist/platform/services.d.ts.map +1 -0
- package/dist/platform/services.js +30 -0
- package/dist/platform/services.js.map +1 -0
- package/dist/platform/source-observer.d.ts +5 -0
- package/dist/platform/source-observer.d.ts.map +1 -0
- package/dist/platform/source-observer.js +159 -0
- package/dist/platform/source-observer.js.map +1 -0
- package/dist/provider-sdk.d.ts +14 -0
- package/dist/provider-sdk.d.ts.map +1 -0
- package/dist/provider-sdk.js +10 -0
- package/dist/provider-sdk.js.map +1 -0
- package/dist/publication/adapter.d.ts +212 -0
- package/dist/publication/adapter.d.ts.map +1 -0
- package/dist/publication/adapter.js +83 -0
- package/dist/publication/adapter.js.map +1 -0
- package/dist/publication/authority.d.ts +137 -0
- package/dist/publication/authority.d.ts.map +1 -0
- package/dist/publication/authority.js +233 -0
- package/dist/publication/authority.js.map +1 -0
- package/dist/publication/catalog-git.d.ts +37 -0
- package/dist/publication/catalog-git.d.ts.map +1 -0
- package/dist/publication/catalog-git.js +451 -0
- package/dist/publication/catalog-git.js.map +1 -0
- package/dist/publication/claim.d.ts +39 -0
- package/dist/publication/claim.d.ts.map +1 -0
- package/dist/publication/claim.js +31 -0
- package/dist/publication/claim.js.map +1 -0
- package/dist/publication/coordinator.d.ts +246 -0
- package/dist/publication/coordinator.d.ts.map +1 -0
- package/dist/publication/coordinator.js +370 -0
- package/dist/publication/coordinator.js.map +1 -0
- package/dist/publication/github.d.ts +36 -0
- package/dist/publication/github.d.ts.map +1 -0
- package/dist/publication/github.js +695 -0
- package/dist/publication/github.js.map +1 -0
- package/dist/publication/http.d.ts +69 -0
- package/dist/publication/http.d.ts.map +1 -0
- package/dist/publication/http.js +29 -0
- package/dist/publication/http.js.map +1 -0
- package/dist/publication/index.d.ts +5 -0
- package/dist/publication/index.d.ts.map +1 -0
- package/dist/publication/index.js +5 -0
- package/dist/publication/index.js.map +1 -0
- package/dist/publication/npm.d.ts +40 -0
- package/dist/publication/npm.d.ts.map +1 -0
- package/dist/publication/npm.js +359 -0
- package/dist/publication/npm.js.map +1 -0
- package/dist/publication/profiles.d.ts +136 -0
- package/dist/publication/profiles.d.ts.map +1 -0
- package/dist/publication/profiles.js +90 -0
- package/dist/publication/profiles.js.map +1 -0
- package/dist/publication/publisher.d.ts +82 -0
- package/dist/publication/publisher.d.ts.map +1 -0
- package/dist/publication/publisher.js +53 -0
- package/dist/publication/publisher.js.map +1 -0
- package/dist/publication/pypi.d.ts +45 -0
- package/dist/publication/pypi.d.ts.map +1 -0
- package/dist/publication/pypi.js +369 -0
- package/dist/publication/pypi.js.map +1 -0
- package/dist/publication/recovery.d.ts +663 -0
- package/dist/publication/recovery.d.ts.map +1 -0
- package/dist/publication/recovery.js +252 -0
- package/dist/publication/recovery.js.map +1 -0
- package/dist/publication/report.d.ts +1061 -0
- package/dist/publication/report.d.ts.map +1 -0
- package/dist/publication/report.js +441 -0
- package/dist/publication/report.js.map +1 -0
- package/dist/recipes/config.d.ts +296 -0
- package/dist/recipes/config.d.ts.map +1 -0
- package/dist/recipes/config.js +325 -0
- package/dist/recipes/config.js.map +1 -0
- package/dist/release/capabilities.d.ts +12 -0
- package/dist/release/capabilities.d.ts.map +1 -0
- package/dist/release/capabilities.js +395 -0
- package/dist/release/capabilities.js.map +1 -0
- package/dist/release/compiler.d.ts +5 -0
- package/dist/release/compiler.d.ts.map +1 -0
- package/dist/release/compiler.js +19 -0
- package/dist/release/compiler.js.map +1 -0
- package/dist/release/config.d.ts +5 -0
- package/dist/release/config.d.ts.map +1 -0
- package/dist/release/config.js +10 -0
- package/dist/release/config.js.map +1 -0
- package/dist/release/context.d.ts +96 -0
- package/dist/release/context.d.ts.map +1 -0
- package/dist/release/context.js +168 -0
- package/dist/release/context.js.map +1 -0
- package/dist/release/graph.d.ts +271 -0
- package/dist/release/graph.d.ts.map +1 -0
- package/dist/release/graph.js +766 -0
- package/dist/release/graph.js.map +1 -0
- package/dist/release/inspect.d.ts +88 -0
- package/dist/release/inspect.d.ts.map +1 -0
- package/dist/release/inspect.js +75 -0
- package/dist/release/inspect.js.map +1 -0
- package/dist/release/prepare.d.ts +24 -0
- package/dist/release/prepare.d.ts.map +1 -0
- package/dist/release/prepare.js +1115 -0
- package/dist/release/prepare.js.map +1 -0
- package/dist/release/prepared-ref.d.ts +60 -0
- package/dist/release/prepared-ref.d.ts.map +1 -0
- package/dist/release/prepared-ref.js +131 -0
- package/dist/release/prepared-ref.js.map +1 -0
- package/dist/release/prepared-store.d.ts +90 -0
- package/dist/release/prepared-store.d.ts.map +1 -0
- package/dist/release/prepared-store.js +339 -0
- package/dist/release/prepared-store.js.map +1 -0
- package/dist/release/prepared.d.ts +172 -0
- package/dist/release/prepared.d.ts.map +1 -0
- package/dist/release/prepared.js +268 -0
- package/dist/release/prepared.js.map +1 -0
- package/dist/release/staging.d.ts +31 -0
- package/dist/release/staging.d.ts.map +1 -0
- package/dist/release/staging.js +218 -0
- package/dist/release/staging.js.map +1 -0
- package/dist/resolve/authored.d.ts +53 -0
- package/dist/resolve/authored.d.ts.map +1 -0
- package/dist/resolve/authored.js +55 -0
- package/dist/resolve/authored.js.map +1 -0
- package/dist/resolve/encode.d.ts +3 -0
- package/dist/resolve/encode.d.ts.map +1 -0
- package/dist/resolve/encode.js +21 -0
- package/dist/resolve/encode.js.map +1 -0
- package/dist/resolve/errors.d.ts +7 -0
- package/dist/resolve/errors.d.ts.map +1 -0
- package/dist/resolve/errors.js +17 -0
- package/dist/resolve/errors.js.map +1 -0
- package/dist/resolve/facts.d.ts +12 -0
- package/dist/resolve/facts.d.ts.map +1 -0
- package/dist/resolve/facts.js +18 -0
- package/dist/resolve/facts.js.map +1 -0
- package/dist/resolve/resolve.d.ts +10 -0
- package/dist/resolve/resolve.d.ts.map +1 -0
- package/dist/resolve/resolve.js +242 -0
- package/dist/resolve/resolve.js.map +1 -0
- package/dist/store.d.ts +11 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +9 -0
- package/dist/store.js.map +1 -0
- package/examples/README.md +20 -30
- package/examples/github-release/release.config.json +11 -18
- package/examples/homebrew-tap/README.md +11 -0
- package/examples/homebrew-tap/release.config.json +40 -28
- package/examples/multi-target/release.config.json +28 -53
- package/examples/npm-first-publish/release.config.json +23 -25
- package/examples/npm-only/release.config.json +22 -30
- package/examples/portable-cli/README.md +17 -0
- package/examples/portable-cli/artifacts/portable-example-0.1.0-darwin-arm64 +1 -0
- package/examples/portable-cli/artifacts/portable-example-0.1.0-darwin-x64 +1 -0
- package/examples/portable-cli/artifacts/portable-example-0.1.0-linux-arm64 +1 -0
- package/examples/portable-cli/artifacts/portable-example-0.1.0-linux-x64 +1 -0
- package/examples/portable-cli/artifacts/portable-example-0.1.0-windows-x64.exe +1 -0
- package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-macosx_10_15_x86_64.whl +1 -0
- package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-macosx_11_0_arm64.whl +1 -0
- package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-manylinux2014_aarch64.whl +1 -0
- package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-manylinux2014_x86_64.whl +1 -0
- package/examples/portable-cli/artifacts/portable_example-0.1.0-py3-none-win_amd64.whl +1 -0
- package/examples/portable-cli/package.json +9 -0
- package/examples/portable-cli/release.config.json +53 -0
- package/examples/portable-cli/src/cli.ts +3 -0
- package/examples/pypi-registry/README.md +8 -0
- package/examples/scoop-bucket/README.md +10 -0
- package/examples/scoop-bucket/release.config.json +40 -30
- package/package.json +73 -154
- package/src/api/api.ts +365 -0
- package/src/api/errors.ts +36 -0
- package/src/api/input.ts +92 -0
- package/src/api/runtime.ts +14 -0
- package/src/api/types.ts +103 -0
- package/src/capabilities/bun-targets.ts +2 -0
- package/src/capabilities/field-ownership.ts +60 -0
- package/src/capabilities/module.ts +158 -0
- package/src/capabilities/registry.ts +333 -0
- package/src/config/config.ts +48 -0
- package/src/correction/coordinator.ts +336 -0
- package/src/correction/index.ts +2 -0
- package/src/correction/intent.ts +172 -0
- package/src/drivers/archive.ts +111 -0
- package/src/drivers/contain.ts +20 -0
- package/src/drivers/environment.ts +28 -0
- package/src/drivers/errors.ts +6 -0
- package/src/drivers/glob.ts +137 -0
- package/src/drivers/process.ts +339 -0
- package/src/drivers/python-wheel.ts +145 -0
- package/src/drivers/redact.ts +31 -0
- package/src/drivers/seccomp-helper-source.ts +121 -0
- package/src/drivers/utils.ts +10 -0
- package/src/drivers/workspace.ts +46 -0
- package/src/extensions/provider-adapter.ts +99 -0
- package/src/host.ts +140 -0
- package/src/index.ts +64 -0
- package/src/model/artifact-collection.ts +237 -0
- package/src/model/authority.ts +116 -0
- package/src/model/bun-targets.ts +34 -0
- package/src/model/canonical.ts +205 -0
- package/src/model/catalog.ts +220 -0
- package/src/model/digest.ts +172 -0
- package/src/model/errors.ts +6 -0
- package/src/model/primitives.ts +37 -0
- package/src/model/pypi.ts +30 -0
- package/src/model/python-distribution.ts +360 -0
- package/src/model/secret-patterns.ts +11 -0
- package/src/operation-journal/authority.ts +162 -0
- package/src/operation-journal/aws/deadline.ts +41 -0
- package/src/operation-journal/aws/oidc.ts +439 -0
- package/src/operation-journal/aws/policy.ts +491 -0
- package/src/operation-journal/aws/s3-boundary.ts +562 -0
- package/src/operation-journal/aws.ts +60 -0
- package/src/operation-journal/canonical.ts +373 -0
- package/src/operation-journal/model.ts +301 -0
- package/src/operation-journal/reducer.ts +112 -0
- package/src/operation-journal/s3.ts +884 -0
- package/src/operation-journal.ts +51 -0
- package/src/platform/bun.ts +38 -0
- package/src/platform/credentials.ts +1151 -0
- package/src/platform/host-support.ts +4 -0
- package/src/platform/node.ts +38 -0
- package/src/platform/release-runtime.ts +47 -0
- package/src/platform/services.ts +57 -0
- package/src/platform/source-observer.ts +193 -0
- package/src/provider-sdk.ts +48 -0
- package/src/publication/adapter.ts +132 -0
- package/src/publication/authority.ts +397 -0
- package/src/publication/catalog-git.ts +603 -0
- package/src/publication/claim.ts +51 -0
- package/src/publication/coordinator.ts +578 -0
- package/src/publication/github.ts +1022 -0
- package/src/publication/http.ts +101 -0
- package/src/publication/index.ts +4 -0
- package/src/publication/npm.ts +525 -0
- package/src/publication/profiles.ts +94 -0
- package/src/publication/publisher.ts +130 -0
- package/src/publication/pypi.ts +463 -0
- package/src/publication/recovery.ts +349 -0
- package/src/publication/report.ts +628 -0
- package/src/recipes/config.ts +395 -0
- package/src/release/capabilities.ts +460 -0
- package/src/release/compiler.ts +28 -0
- package/src/release/config.ts +12 -0
- package/src/release/context.ts +223 -0
- package/src/release/graph.ts +866 -0
- package/src/release/inspect.ts +83 -0
- package/src/release/prepare.ts +1329 -0
- package/src/release/prepared-ref.ts +193 -0
- package/src/release/prepared-store.ts +378 -0
- package/src/release/prepared.ts +299 -0
- package/src/release/staging.ts +257 -0
- package/src/resolve/authored.ts +64 -0
- package/src/resolve/encode.ts +22 -0
- package/src/resolve/errors.ts +15 -0
- package/src/resolve/facts.ts +18 -0
- package/src/resolve/resolve.ts +328 -0
- package/src/store.ts +28 -0
- package/templates/README.md +55 -72
- package/templates/bun-cli-github/README.md +7 -9
- package/templates/bun-cli-github/release.config.json +35 -73
- package/templates/github-actions/release.yml +74 -0
- package/templates/github-actions/reviewed-release.yml +115 -0
- package/templates/multi-target-homebrew/README.md +10 -0
- package/templates/multi-target-homebrew/release.config.json +43 -60
- package/templates/multi-target-scoop/README.md +10 -0
- package/templates/multi-target-scoop/release.config.json +43 -62
- package/templates/npm-github/release.config.json +25 -38
- package/templates/npm-github/reviewed-release.config.json +37 -0
- package/templates/npm-only/README.md +10 -0
- package/templates/npm-only/release.config.json +15 -30
- package/templates/portable-cli/README.md +13 -0
- package/templates/portable-cli/release.config.json +53 -0
- package/dist/artifacts/adapter.d.ts +0 -39
- package/dist/artifacts/adapter.d.ts.map +0 -1
- package/dist/artifacts/adapter.js +0 -23
- package/dist/artifacts/adapter.js.map +0 -1
- package/dist/artifacts/registry.d.ts +0 -21
- package/dist/artifacts/registry.d.ts.map +0 -1
- package/dist/artifacts/registry.js +0 -21
- package/dist/artifacts/registry.js.map +0 -1
- package/dist/config/errors.d.ts +0 -24
- package/dist/config/errors.d.ts.map +0 -1
- package/dist/config/errors.js +0 -19
- package/dist/config/errors.js.map +0 -1
- package/dist/config/load.d.ts +0 -9
- package/dist/config/load.d.ts.map +0 -1
- package/dist/config/load.js +0 -29
- package/dist/config/load.js.map +0 -1
- package/dist/config/schema.d.ts +0 -12
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/config/schema.js +0 -19
- package/dist/config/schema.js.map +0 -1
- package/dist/domain/artifact.d.ts +0 -118
- package/dist/domain/artifact.d.ts.map +0 -1
- package/dist/domain/artifact.js +0 -197
- package/dist/domain/artifact.js.map +0 -1
- package/dist/domain/evidence.d.ts +0 -59
- package/dist/domain/evidence.d.ts.map +0 -1
- package/dist/domain/evidence.js +0 -51
- package/dist/domain/evidence.js.map +0 -1
- package/dist/domain/operation.d.ts +0 -143
- package/dist/domain/operation.d.ts.map +0 -1
- package/dist/domain/operation.js +0 -198
- package/dist/domain/operation.js.map +0 -1
- package/dist/domain/release.d.ts +0 -93
- package/dist/domain/release.d.ts.map +0 -1
- package/dist/domain/release.js +0 -82
- package/dist/domain/release.js.map +0 -1
- package/dist/domain/remote-state.d.ts +0 -58
- package/dist/domain/remote-state.d.ts.map +0 -1
- package/dist/domain/remote-state.js +0 -55
- package/dist/domain/remote-state.js.map +0 -1
- package/dist/domain/target.d.ts +0 -148
- package/dist/domain/target.d.ts.map +0 -1
- package/dist/domain/target.js +0 -151
- package/dist/domain/target.js.map +0 -1
- package/dist/host/host.d.ts +0 -32
- package/dist/host/host.d.ts.map +0 -1
- package/dist/host/host.js +0 -24
- package/dist/host/host.js.map +0 -1
- package/dist/host/http-live.d.ts +0 -6
- package/dist/host/http-live.d.ts.map +0 -1
- package/dist/host/http-live.js +0 -86
- package/dist/host/http-live.js.map +0 -1
- package/dist/host/http.d.ts +0 -43
- package/dist/host/http.d.ts.map +0 -1
- package/dist/host/http.js +0 -59
- package/dist/host/http.js.map +0 -1
- package/dist/host/platform.d.ts +0 -10
- package/dist/host/platform.d.ts.map +0 -1
- package/dist/host/platform.js +0 -114
- package/dist/host/platform.js.map +0 -1
- package/dist/host/test.d.ts +0 -23
- package/dist/host/test.d.ts.map +0 -1
- package/dist/host/test.js +0 -222
- package/dist/host/test.js.map +0 -1
- package/dist/internal/workspace-path.d.ts +0 -15
- package/dist/internal/workspace-path.d.ts.map +0 -1
- package/dist/internal/workspace-path.js +0 -36
- package/dist/internal/workspace-path.js.map +0 -1
- package/dist/planner/artifact-inventory.d.ts +0 -9
- package/dist/planner/artifact-inventory.d.ts.map +0 -1
- package/dist/planner/artifact-inventory.js +0 -86
- package/dist/planner/artifact-inventory.js.map +0 -1
- package/dist/planner/create-release-plan.d.ts +0 -5
- package/dist/planner/create-release-plan.d.ts.map +0 -1
- package/dist/planner/create-release-plan.js +0 -25
- package/dist/planner/create-release-plan.js.map +0 -1
- package/dist/planner/errors.d.ts +0 -67
- package/dist/planner/errors.d.ts.map +0 -1
- package/dist/planner/errors.js +0 -58
- package/dist/planner/errors.js.map +0 -1
- package/dist/planner/evidence-recorder.d.ts +0 -28
- package/dist/planner/evidence-recorder.d.ts.map +0 -1
- package/dist/planner/evidence-recorder.js +0 -362
- package/dist/planner/evidence-recorder.js.map +0 -1
- package/dist/planner/executor.d.ts +0 -58
- package/dist/planner/executor.d.ts.map +0 -1
- package/dist/planner/executor.js +0 -147
- package/dist/planner/executor.js.map +0 -1
- package/dist/planner/normalize-release.d.ts +0 -19
- package/dist/planner/normalize-release.d.ts.map +0 -1
- package/dist/planner/normalize-release.js +0 -342
- package/dist/planner/normalize-release.js.map +0 -1
- package/dist/planner/reconcile.d.ts +0 -17
- package/dist/planner/reconcile.d.ts.map +0 -1
- package/dist/planner/reconcile.js +0 -259
- package/dist/planner/reconcile.js.map +0 -1
- package/dist/planner/render-plan.d.ts +0 -15
- package/dist/planner/render-plan.d.ts.map +0 -1
- package/dist/planner/render-plan.js +0 -231
- package/dist/planner/render-plan.js.map +0 -1
- package/dist/targets/adapter-helpers.d.ts +0 -79
- package/dist/targets/adapter-helpers.d.ts.map +0 -1
- package/dist/targets/adapter-helpers.js +0 -194
- package/dist/targets/adapter-helpers.js.map +0 -1
- package/dist/targets/adapter.d.ts +0 -17
- package/dist/targets/adapter.d.ts.map +0 -1
- package/dist/targets/adapter.js +0 -2
- package/dist/targets/adapter.js.map +0 -1
- package/dist/targets/github-release.d.ts +0 -25
- package/dist/targets/github-release.d.ts.map +0 -1
- package/dist/targets/github-release.js +0 -82
- package/dist/targets/github-release.js.map +0 -1
- package/dist/targets/github.d.ts +0 -11
- package/dist/targets/github.d.ts.map +0 -1
- package/dist/targets/github.js +0 -117
- package/dist/targets/github.js.map +0 -1
- package/dist/targets/homebrew.d.ts +0 -11
- package/dist/targets/homebrew.d.ts.map +0 -1
- package/dist/targets/homebrew.js +0 -210
- package/dist/targets/homebrew.js.map +0 -1
- package/dist/targets/live.d.ts +0 -5
- package/dist/targets/live.d.ts.map +0 -1
- package/dist/targets/live.js +0 -50
- package/dist/targets/live.js.map +0 -1
- package/dist/targets/npm.d.ts +0 -10
- package/dist/targets/npm.d.ts.map +0 -1
- package/dist/targets/npm.js +0 -112
- package/dist/targets/npm.js.map +0 -1
- package/dist/targets/pypi.d.ts +0 -11
- package/dist/targets/pypi.d.ts.map +0 -1
- package/dist/targets/pypi.js +0 -141
- package/dist/targets/pypi.js.map +0 -1
- package/dist/targets/registry.d.ts +0 -25
- package/dist/targets/registry.d.ts.map +0 -1
- package/dist/targets/registry.js +0 -34
- package/dist/targets/registry.js.map +0 -1
- package/dist/targets/scoop.d.ts +0 -11
- package/dist/targets/scoop.d.ts.map +0 -1
- package/dist/targets/scoop.js +0 -88
- package/dist/targets/scoop.js.map +0 -1
- package/dist/types/effect-internal.d.ts +0 -5
- package/dist/types/effect-internal.d.ts.map +0 -1
- package/dist/types/effect-internal.js +0 -2
- package/dist/types/effect-internal.js.map +0 -1
- package/dist/workflows/config.d.ts +0 -202
- package/dist/workflows/config.d.ts.map +0 -1
- package/dist/workflows/config.js +0 -420
- package/dist/workflows/config.js.map +0 -1
- package/dist/workflows/diagnostics.d.ts +0 -62
- package/dist/workflows/diagnostics.d.ts.map +0 -1
- package/dist/workflows/diagnostics.js +0 -530
- package/dist/workflows/diagnostics.js.map +0 -1
- package/dist/workflows/distribution.d.ts +0 -25
- package/dist/workflows/distribution.d.ts.map +0 -1
- package/dist/workflows/distribution.js +0 -24
- package/dist/workflows/distribution.js.map +0 -1
- package/dist/workflows/evidence.d.ts +0 -17
- package/dist/workflows/evidence.d.ts.map +0 -1
- package/dist/workflows/evidence.js +0 -32
- package/dist/workflows/evidence.js.map +0 -1
- package/dist/workflows/index.d.ts +0 -8
- package/dist/workflows/index.d.ts.map +0 -1
- package/dist/workflows/index.js +0 -7
- package/dist/workflows/index.js.map +0 -1
- package/dist/workflows/init.d.ts +0 -84
- package/dist/workflows/init.d.ts.map +0 -1
- package/dist/workflows/init.js +0 -487
- package/dist/workflows/init.js.map +0 -1
- package/dist/workflows/live.d.ts +0 -9
- package/dist/workflows/live.d.ts.map +0 -1
- package/dist/workflows/live.js +0 -7
- package/dist/workflows/live.js.map +0 -1
- package/dist/workflows/options.d.ts +0 -30
- package/dist/workflows/options.d.ts.map +0 -1
- package/dist/workflows/options.js +0 -16
- package/dist/workflows/options.js.map +0 -1
- package/examples/non-strict-skips/artifacts/release-example-skips-0.1.0.tgz +0 -1
- package/examples/non-strict-skips/release.config.json +0 -31
- package/examples/pypi-registry/release.config.json +0 -31
- package/templates/github-actions/plan-and-approved-execute.yml +0 -58
- package/templates/github-actions/plan-only.yml +0 -33
- package/templates/github-actions/trusted-publishing.yml +0 -61
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { gunzipSync, inflateRawSync } from "node:zlib"
|
|
2
|
+
import * as Schema from "effect/Schema"
|
|
3
|
+
import { normalizePyPiProjectName, PyPiProjectName } from "./pypi.js"
|
|
4
|
+
import { NonEmptyName, Version } from "./primitives.js"
|
|
5
|
+
|
|
6
|
+
const metadataLimit = 4 * 1024 * 1024
|
|
7
|
+
const archiveLimit = 512 * 1024 * 1024
|
|
8
|
+
|
|
9
|
+
export class PythonDistributionError
|
|
10
|
+
extends Schema.TaggedErrorClass<PythonDistributionError>()("PythonDistributionError", {
|
|
11
|
+
filename: Schema.NonEmptyString,
|
|
12
|
+
reason: Schema.NonEmptyString
|
|
13
|
+
}) {}
|
|
14
|
+
|
|
15
|
+
export class WheelDistribution extends Schema.TaggedClass<WheelDistribution>()("wheel", {
|
|
16
|
+
project: PyPiProjectName,
|
|
17
|
+
version: Version,
|
|
18
|
+
metadataVersion: NonEmptyName,
|
|
19
|
+
pythonTag: NonEmptyName,
|
|
20
|
+
abiTag: NonEmptyName,
|
|
21
|
+
platformTag: NonEmptyName,
|
|
22
|
+
mediaType: Schema.Literal("application/zip")
|
|
23
|
+
}) {}
|
|
24
|
+
|
|
25
|
+
export class SourceDistribution extends Schema.TaggedClass<SourceDistribution>()("sdist", {
|
|
26
|
+
project: PyPiProjectName,
|
|
27
|
+
version: Version,
|
|
28
|
+
metadataVersion: NonEmptyName,
|
|
29
|
+
pythonTag: Schema.Literal("source"),
|
|
30
|
+
mediaType: Schema.Literal("application/gzip")
|
|
31
|
+
}) {}
|
|
32
|
+
|
|
33
|
+
export const PythonDistribution = Schema.Union([WheelDistribution, SourceDistribution])
|
|
34
|
+
export type PythonDistribution = typeof PythonDistribution.Type
|
|
35
|
+
|
|
36
|
+
const fail = (filename: string, reason: string): never => {
|
|
37
|
+
throw PythonDistributionError.make({ filename, reason })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const safePath = (filename: string, path: string): string => {
|
|
41
|
+
const checked = path.endsWith("/") ? path.slice(0, -1) : path
|
|
42
|
+
if (path.length === 0 || path.includes("\0") || path.includes("\\") || path.startsWith("/") ||
|
|
43
|
+
checked.length === 0 || /^[A-Za-z]:/u.test(path) || checked.split("/").some((part) => part === ".." || part === "")) {
|
|
44
|
+
fail(filename, "Distribution archive contains a malformed or escaping path.")
|
|
45
|
+
}
|
|
46
|
+
return path
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const utf8 = (filename: string, bytes: Uint8Array): string => {
|
|
50
|
+
try { return new TextDecoder("utf-8", { fatal: true }).decode(bytes) } catch {
|
|
51
|
+
return fail(filename, "Distribution metadata or archive path is not valid UTF-8.")
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type CoreMetadata = {
|
|
56
|
+
readonly name: string
|
|
57
|
+
readonly project: string
|
|
58
|
+
readonly version: string
|
|
59
|
+
readonly metadataVersion: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const coreMetadata = (filename: string, bytes: Uint8Array): CoreMetadata => {
|
|
63
|
+
if (bytes.length === 0 || bytes.length > metadataLimit) fail(filename, "Distribution core metadata has an invalid size.")
|
|
64
|
+
const lines = utf8(filename, bytes).replaceAll("\r\n", "\n").split("\n")
|
|
65
|
+
const values = new Map<string, Array<string>>()
|
|
66
|
+
let current: string | undefined
|
|
67
|
+
for (const line of lines) {
|
|
68
|
+
if (line === "") break
|
|
69
|
+
if (/^[ \t]/u.test(line)) {
|
|
70
|
+
if (current === undefined) fail(filename, "Distribution core metadata begins with a continuation line.")
|
|
71
|
+
const prior = values.get(current!)!
|
|
72
|
+
prior[prior.length - 1] = `${prior.at(-1)!}\n${line.slice(1)}`
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
const index = line.indexOf(":")
|
|
76
|
+
if (index <= 0) fail(filename, "Distribution core metadata contains a malformed header.")
|
|
77
|
+
current = line.slice(0, index).toLowerCase()
|
|
78
|
+
const value = line.slice(index + 1).trim()
|
|
79
|
+
values.set(current, [...(values.get(current) ?? []), value])
|
|
80
|
+
}
|
|
81
|
+
const one = (name: string): string => {
|
|
82
|
+
const found = values.get(name)
|
|
83
|
+
const value = found?.length === 1 ? found[0] : undefined
|
|
84
|
+
if (value === undefined || value.length === 0) fail(filename, `Distribution metadata requires exactly one ${name} field.`)
|
|
85
|
+
return value!
|
|
86
|
+
}
|
|
87
|
+
const name = one("name")
|
|
88
|
+
const version = one("version")
|
|
89
|
+
const metadataVersion = one("metadata-version")
|
|
90
|
+
if (!/^\d+\.\d+$/u.test(metadataVersion)) fail(filename, "Distribution Metadata-Version must use major.minor form.")
|
|
91
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9.!+_-]*$/u.test(version)) fail(filename, "Distribution Version is not a conservative Python package version.")
|
|
92
|
+
let project: string
|
|
93
|
+
try { project = normalizePyPiProjectName(name) } catch {
|
|
94
|
+
return fail(filename, "Distribution Name is not a valid PyPI project name.")
|
|
95
|
+
}
|
|
96
|
+
return { name, project, version, metadataVersion }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const u16 = (filename: string, view: DataView, offset: number): number => {
|
|
100
|
+
if (offset < 0 || offset + 2 > view.byteLength) fail(filename, "ZIP structure is truncated.")
|
|
101
|
+
return view.getUint16(offset, true)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const u32 = (filename: string, view: DataView, offset: number): number => {
|
|
105
|
+
if (offset < 0 || offset + 4 > view.byteLength) fail(filename, "ZIP structure is truncated.")
|
|
106
|
+
return view.getUint32(offset, true)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
type ZipEntry = {
|
|
110
|
+
readonly path: string
|
|
111
|
+
readonly flags: number
|
|
112
|
+
readonly method: number
|
|
113
|
+
readonly crc32: number
|
|
114
|
+
readonly compressedSize: number
|
|
115
|
+
readonly uncompressedSize: number
|
|
116
|
+
readonly localOffset: number
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const crcTable = Array.from({ length: 256 }, (_, value) => {
|
|
120
|
+
let crc = value
|
|
121
|
+
for (let bit = 0; bit < 8; bit += 1) crc = (crc & 1) === 1 ? 0xedb88320 ^ (crc >>> 1) : crc >>> 1
|
|
122
|
+
return crc >>> 0
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const crc32 = (data: Uint8Array): number => {
|
|
126
|
+
let crc = 0xffffffff
|
|
127
|
+
for (const byte of data) crc = (crc >>> 8) ^ crcTable[(crc ^ byte) & 0xff]!
|
|
128
|
+
return (crc ^ 0xffffffff) >>> 0
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const zipEntries = (filename: string, bytes: Uint8Array): ReadonlyArray<ZipEntry> => {
|
|
132
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
133
|
+
let eocd = -1
|
|
134
|
+
const minimum = Math.max(0, bytes.length - 65_557)
|
|
135
|
+
for (let offset = bytes.length - 22; offset >= minimum; offset -= 1) {
|
|
136
|
+
if (u32(filename, view, offset) === 0x06054b50) { eocd = offset; break }
|
|
137
|
+
}
|
|
138
|
+
if (eocd < 0 || u16(filename, view, eocd + 4) !== 0 || u16(filename, view, eocd + 6) !== 0) {
|
|
139
|
+
fail(filename, "Wheel ZIP must be a single-disk archive with an end record.")
|
|
140
|
+
}
|
|
141
|
+
const count = u16(filename, view, eocd + 10)
|
|
142
|
+
const size = u32(filename, view, eocd + 12)
|
|
143
|
+
const start = u32(filename, view, eocd + 16)
|
|
144
|
+
if (count === 0 || count === 0xffff || start === 0xffffffff || size === 0xffffffff || start + size > eocd) {
|
|
145
|
+
fail(filename, "Wheel ZIP central directory is invalid or requires unsupported ZIP64 fields.")
|
|
146
|
+
}
|
|
147
|
+
const entries: Array<ZipEntry> = []
|
|
148
|
+
const paths = new Set<string>()
|
|
149
|
+
let offset = start
|
|
150
|
+
for (let index = 0; index < count; index += 1) {
|
|
151
|
+
if (u32(filename, view, offset) !== 0x02014b50) fail(filename, "Wheel ZIP central directory entry is malformed.")
|
|
152
|
+
const flags = u16(filename, view, offset + 8)
|
|
153
|
+
const method = u16(filename, view, offset + 10)
|
|
154
|
+
const crc32 = u32(filename, view, offset + 16)
|
|
155
|
+
const compressedSize = u32(filename, view, offset + 20)
|
|
156
|
+
const uncompressedSize = u32(filename, view, offset + 24)
|
|
157
|
+
const nameLength = u16(filename, view, offset + 28)
|
|
158
|
+
const extraLength = u16(filename, view, offset + 30)
|
|
159
|
+
const commentLength = u16(filename, view, offset + 32)
|
|
160
|
+
const externalAttributes = u32(filename, view, offset + 38)
|
|
161
|
+
const localOffset = u32(filename, view, offset + 42)
|
|
162
|
+
const end = offset + 46 + nameLength + extraLength + commentLength
|
|
163
|
+
if (end > start + size || (flags & 1) !== 0 || ![0, 8].includes(method)) {
|
|
164
|
+
fail(filename, "Wheel ZIP uses an encrypted, unsupported, or truncated entry.")
|
|
165
|
+
}
|
|
166
|
+
const path = safePath(filename, utf8(filename, bytes.subarray(offset + 46, offset + 46 + nameLength)))
|
|
167
|
+
const mode = externalAttributes >>> 16
|
|
168
|
+
if ((mode & 0o170000) === 0o120000) fail(filename, "Wheel ZIP contains a symbolic link.")
|
|
169
|
+
if (paths.has(path)) fail(filename, "Wheel ZIP repeats an archive path.")
|
|
170
|
+
paths.add(path)
|
|
171
|
+
if ([compressedSize, uncompressedSize, localOffset].includes(0xffffffff)) {
|
|
172
|
+
fail(filename, "Wheel ZIP entry requires unsupported ZIP64 fields.")
|
|
173
|
+
}
|
|
174
|
+
entries.push({ path, flags, method, crc32, compressedSize, uncompressedSize, localOffset })
|
|
175
|
+
offset = end
|
|
176
|
+
}
|
|
177
|
+
if (offset !== start + size) fail(filename, "Wheel ZIP central directory has trailing or missing data.")
|
|
178
|
+
return entries
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const zipContents = (filename: string, archive: Uint8Array, entry: ZipEntry): Uint8Array => {
|
|
182
|
+
if (entry.uncompressedSize > metadataLimit) fail(filename, "Wheel metadata entry exceeds the supported bound.")
|
|
183
|
+
const view = new DataView(archive.buffer, archive.byteOffset, archive.byteLength)
|
|
184
|
+
if (u32(filename, view, entry.localOffset) !== 0x04034b50) fail(filename, "Wheel ZIP local entry is malformed.")
|
|
185
|
+
if (u16(filename, view, entry.localOffset + 6) !== entry.flags ||
|
|
186
|
+
u16(filename, view, entry.localOffset + 8) !== entry.method ||
|
|
187
|
+
u32(filename, view, entry.localOffset + 14) !== entry.crc32 ||
|
|
188
|
+
u32(filename, view, entry.localOffset + 18) !== entry.compressedSize ||
|
|
189
|
+
u32(filename, view, entry.localOffset + 22) !== entry.uncompressedSize) {
|
|
190
|
+
fail(filename, "Wheel ZIP local entry disagrees with its central directory.")
|
|
191
|
+
}
|
|
192
|
+
const nameLength = u16(filename, view, entry.localOffset + 26)
|
|
193
|
+
const extraLength = u16(filename, view, entry.localOffset + 28)
|
|
194
|
+
const start = entry.localOffset + 30 + nameLength + extraLength
|
|
195
|
+
const localPath = safePath(filename, utf8(filename, archive.subarray(entry.localOffset + 30, entry.localOffset + 30 + nameLength)))
|
|
196
|
+
if (localPath !== entry.path) fail(filename, "Wheel ZIP local path disagrees with its central directory.")
|
|
197
|
+
const end = start + entry.compressedSize
|
|
198
|
+
if (end > archive.length) fail(filename, "Wheel ZIP entry bytes are truncated.")
|
|
199
|
+
const compressed = archive.subarray(start, end)
|
|
200
|
+
let result: Uint8Array
|
|
201
|
+
try {
|
|
202
|
+
result = entry.method === 0 ? new Uint8Array(compressed) : new Uint8Array(inflateRawSync(compressed, { maxOutputLength: metadataLimit }))
|
|
203
|
+
} catch {
|
|
204
|
+
return fail(filename, "Wheel ZIP metadata could not be decompressed.")
|
|
205
|
+
}
|
|
206
|
+
if (result.length !== entry.uncompressedSize) fail(filename, "Wheel ZIP metadata size disagrees with its central directory.")
|
|
207
|
+
if (crc32(result) !== entry.crc32) fail(filename, "Wheel ZIP metadata CRC disagrees with its central directory.")
|
|
208
|
+
return result
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const wheel = (filename: string, bytes: Uint8Array, expectedProject: string, expectedVersion: string): WheelDistribution => {
|
|
212
|
+
const entries = zipEntries(filename, bytes)
|
|
213
|
+
const metadataEntries = entries.filter((entry) => /^[^/]+\.dist-info\/METADATA$/u.test(entry.path))
|
|
214
|
+
if (metadataEntries.length !== 1) fail(filename, "Wheel requires exactly one top-level dist-info/METADATA entry.")
|
|
215
|
+
const metadataEntry = metadataEntries[0]!
|
|
216
|
+
const distInfo = metadataEntry.path.slice(0, -"/METADATA".length)
|
|
217
|
+
const wheelEntry = entries.find((entry) => entry.path === `${distInfo}/WHEEL`)
|
|
218
|
+
if (wheelEntry === undefined) fail(filename, "Wheel requires WHEEL metadata beside core metadata.")
|
|
219
|
+
const metadata = coreMetadata(filename, zipContents(filename, bytes, metadataEntry))
|
|
220
|
+
if (metadata.project !== expectedProject || metadata.version !== expectedVersion) {
|
|
221
|
+
fail(filename, "Wheel embedded project/version disagrees with the resolved publication coordinate.")
|
|
222
|
+
}
|
|
223
|
+
const wheelProject = metadata.project.replaceAll("-", "_")
|
|
224
|
+
const wheelVersion = metadata.version.replaceAll("-", "_")
|
|
225
|
+
if (distInfo !== `${wheelProject}-${wheelVersion}.dist-info`) {
|
|
226
|
+
fail(filename, "Wheel dist-info directory disagrees with embedded project/version metadata.")
|
|
227
|
+
}
|
|
228
|
+
const escaped = (value: string): string => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")
|
|
229
|
+
const match = new RegExp(
|
|
230
|
+
`^${escaped(wheelProject)}-${escaped(wheelVersion)}(?:-[0-9][0-9A-Za-z_]*)?-([A-Za-z0-9_.]+)-([A-Za-z0-9_.]+)-([A-Za-z0-9_.]+)\\.whl$`,
|
|
231
|
+
"u"
|
|
232
|
+
).exec(filename)
|
|
233
|
+
if (match === null) fail(filename, "Wheel filename disagrees with embedded project/version or has malformed compatibility tags.")
|
|
234
|
+
const [pythonTag, abiTag, platformTag] = match!.slice(1) as [string, string, string]
|
|
235
|
+
const expanded = new Set(pythonTag.split(".").flatMap((python) =>
|
|
236
|
+
abiTag.split(".").flatMap((abi) => platformTag.split(".").map((platform) => `${python}-${abi}-${platform}`))))
|
|
237
|
+
const wheelTags = utf8(filename, zipContents(filename, bytes, wheelEntry!)).replaceAll("\r\n", "\n")
|
|
238
|
+
.split("\n").filter((line) => line.startsWith("Tag: ")).map((line) => line.slice(5).trim())
|
|
239
|
+
if (wheelTags.length === 0 || wheelTags.some((tag) => !expanded.has(tag))) {
|
|
240
|
+
fail(filename, "Wheel WHEEL tags do not agree with its filename compatibility tags.")
|
|
241
|
+
}
|
|
242
|
+
return WheelDistribution.make({
|
|
243
|
+
project: PyPiProjectName.make(metadata.project),
|
|
244
|
+
version: Version.make(metadata.version),
|
|
245
|
+
metadataVersion: NonEmptyName.make(metadata.metadataVersion),
|
|
246
|
+
pythonTag: NonEmptyName.make(pythonTag),
|
|
247
|
+
abiTag: NonEmptyName.make(abiTag),
|
|
248
|
+
platformTag: NonEmptyName.make(platformTag),
|
|
249
|
+
mediaType: "application/zip"
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const tarString = (bytes: Uint8Array): string => {
|
|
254
|
+
const nul = bytes.indexOf(0)
|
|
255
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(nul < 0 ? bytes : bytes.subarray(0, nul))
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const tarOctal = (filename: string, bytes: Uint8Array): number => {
|
|
259
|
+
const value = tarString(bytes).trim().replace(/^0+/u, "") || "0"
|
|
260
|
+
if (!/^[0-7]+$/u.test(value)) fail(filename, "Source distribution tar contains an invalid octal field.")
|
|
261
|
+
const parsed = Number.parseInt(value, 8)
|
|
262
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) fail(filename, "Source distribution tar size is out of range.")
|
|
263
|
+
return parsed
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const pax = (filename: string, bytes: Uint8Array): Readonly<Record<string, string>> => {
|
|
267
|
+
const text = utf8(filename, bytes)
|
|
268
|
+
const values: Record<string, string> = {}
|
|
269
|
+
let offset = 0
|
|
270
|
+
while (offset < text.length) {
|
|
271
|
+
const space = text.indexOf(" ", offset)
|
|
272
|
+
if (space < 0) fail(filename, "Source distribution PAX record is malformed.")
|
|
273
|
+
const length = Number(text.slice(offset, space))
|
|
274
|
+
if (!Number.isSafeInteger(length) || length <= space - offset + 1 || offset + length > text.length || text[offset + length - 1] !== "\n") {
|
|
275
|
+
fail(filename, "Source distribution PAX record has an invalid length.")
|
|
276
|
+
}
|
|
277
|
+
const record = text.slice(space + 1, offset + length - 1)
|
|
278
|
+
const equals = record.indexOf("=")
|
|
279
|
+
if (equals <= 0) fail(filename, "Source distribution PAX record is missing a key.")
|
|
280
|
+
values[record.slice(0, equals)] = record.slice(equals + 1)
|
|
281
|
+
offset += length
|
|
282
|
+
}
|
|
283
|
+
return values
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const sdist = (filename: string, bytes: Uint8Array, expectedProject: string, expectedVersion: string): SourceDistribution => {
|
|
287
|
+
let tar: Uint8Array
|
|
288
|
+
try { tar = new Uint8Array(gunzipSync(bytes, { maxOutputLength: archiveLimit })) } catch {
|
|
289
|
+
return fail(filename, "Source distribution is not a bounded valid gzip stream.")
|
|
290
|
+
}
|
|
291
|
+
let offset = 0
|
|
292
|
+
let pendingPax: Readonly<Record<string, string>> = {}
|
|
293
|
+
const metadata: Array<{ readonly path: string, readonly bytes: Uint8Array }> = []
|
|
294
|
+
while (offset + 512 <= tar.length) {
|
|
295
|
+
const header = tar.subarray(offset, offset + 512)
|
|
296
|
+
if (header.every((byte) => byte === 0)) {
|
|
297
|
+
if (!tar.subarray(offset).every((byte) => byte === 0)) fail(filename, "Source distribution tar has nonzero data after its end marker.")
|
|
298
|
+
break
|
|
299
|
+
}
|
|
300
|
+
let checksum = 0
|
|
301
|
+
for (let index = 0; index < 512; index += 1) checksum += index >= 148 && index < 156 ? 32 : header[index]!
|
|
302
|
+
if (checksum !== tarOctal(filename, header.subarray(148, 156))) fail(filename, "Source distribution tar header checksum is invalid.")
|
|
303
|
+
const size = tarOctal(filename, header.subarray(124, 136))
|
|
304
|
+
const dataStart = offset + 512
|
|
305
|
+
const dataEnd = dataStart + size
|
|
306
|
+
if (dataEnd > tar.length) fail(filename, "Source distribution tar entry is truncated.")
|
|
307
|
+
const type = String.fromCharCode(header[156]!)
|
|
308
|
+
const rawName = tarString(header.subarray(0, 100))
|
|
309
|
+
const prefix = tarString(header.subarray(345, 500))
|
|
310
|
+
const rawPath = prefix.length === 0 ? rawName : `${prefix}/${rawName}`
|
|
311
|
+
if (type === "x") {
|
|
312
|
+
pendingPax = pax(filename, tar.subarray(dataStart, dataEnd))
|
|
313
|
+
} else {
|
|
314
|
+
const path = safePath(filename, pendingPax.path ?? rawPath)
|
|
315
|
+
pendingPax = {}
|
|
316
|
+
if (type === "1" || type === "2") fail(filename, "Source distribution tar contains a hard or symbolic link.")
|
|
317
|
+
if (!["\0", "0", "5"].includes(type)) fail(filename, "Source distribution tar contains an unsupported entry type.")
|
|
318
|
+
if ((type === "\0" || type === "0") && /\/PKG-INFO$/u.test(path)) {
|
|
319
|
+
if (size > metadataLimit) fail(filename, "Source distribution PKG-INFO exceeds the supported bound.")
|
|
320
|
+
metadata.push({ path, bytes: tar.subarray(dataStart, dataEnd) })
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
offset = dataStart + Math.ceil(size / 512) * 512
|
|
324
|
+
}
|
|
325
|
+
if (metadata.length !== 1 || metadata[0]!.path.split("/").length !== 2) {
|
|
326
|
+
fail(filename, "Source distribution requires exactly one root-directory PKG-INFO entry.")
|
|
327
|
+
}
|
|
328
|
+
const parsed = coreMetadata(filename, metadata[0]!.bytes)
|
|
329
|
+
if (parsed.project !== expectedProject || parsed.version !== expectedVersion) {
|
|
330
|
+
fail(filename, "Source distribution embedded project/version disagrees with the resolved publication coordinate.")
|
|
331
|
+
}
|
|
332
|
+
const suffix = `-${parsed.version}.tar.gz`
|
|
333
|
+
if (!filename.endsWith(suffix) || normalizePyPiProjectName(filename.slice(0, -suffix.length)) !== parsed.project) {
|
|
334
|
+
fail(filename, "Source distribution filename disagrees with embedded project/version metadata.")
|
|
335
|
+
}
|
|
336
|
+
const root = metadata[0]!.path.split("/")[0]!
|
|
337
|
+
if (!root.endsWith(`-${parsed.version}`) || normalizePyPiProjectName(root.slice(0, -(`-${parsed.version}`).length)) !== parsed.project) {
|
|
338
|
+
fail(filename, "Source distribution root directory disagrees with embedded project/version metadata.")
|
|
339
|
+
}
|
|
340
|
+
return SourceDistribution.make({
|
|
341
|
+
project: PyPiProjectName.make(parsed.project),
|
|
342
|
+
version: Version.make(parsed.version),
|
|
343
|
+
metadataVersion: NonEmptyName.make(parsed.metadataVersion),
|
|
344
|
+
pythonTag: "source",
|
|
345
|
+
mediaType: "application/gzip"
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Strictly validate prebuilt distribution bytes against resolved identity. */
|
|
350
|
+
export const inspectPythonDistribution = (
|
|
351
|
+
filename: string,
|
|
352
|
+
bytes: Uint8Array,
|
|
353
|
+
expectedProject: string,
|
|
354
|
+
expectedVersion: string
|
|
355
|
+
): PythonDistribution => {
|
|
356
|
+
if (filename.includes("/") || filename.includes("\\") || filename.length === 0) fail(filename || "<empty>", "Distribution filename must be a basename.")
|
|
357
|
+
if (filename.endsWith(".whl")) return wheel(filename, bytes, expectedProject, expectedVersion)
|
|
358
|
+
if (filename.endsWith(".tar.gz")) return sdist(filename, bytes, expectedProject, expectedVersion)
|
|
359
|
+
return fail(filename, "Only .whl and .tar.gz source distributions are supported.")
|
|
360
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// The one home for token-shaped denylist patterns. Load-bearing for child
|
|
2
|
+
// output redaction and shipped-plugin checks.
|
|
3
|
+
export const secretPatterns: ReadonlyArray<RegExp> = [
|
|
4
|
+
/ghp_[A-Za-z0-9]{20,}/u,
|
|
5
|
+
/gho_[A-Za-z0-9]{20,}/u,
|
|
6
|
+
/github_pat_[A-Za-z0-9_]{20,}/u,
|
|
7
|
+
/xox[abps]-[A-Za-z0-9-]{10,}/u,
|
|
8
|
+
/AKIA[0-9A-Z]{16}/u,
|
|
9
|
+
/npm_[A-Za-z0-9]{30,}/u,
|
|
10
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY/u
|
|
11
|
+
]
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { encodeCanonicalJson } from "../model/canonical.js"
|
|
2
|
+
import {
|
|
3
|
+
JournalAuthorityMismatch,
|
|
4
|
+
type S3JournalAuthority
|
|
5
|
+
} from "./model.js"
|
|
6
|
+
|
|
7
|
+
const escapedPattern = (value: string): string => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")
|
|
8
|
+
|
|
9
|
+
const requireExactFields = (
|
|
10
|
+
value: object,
|
|
11
|
+
expected: ReadonlyArray<string>,
|
|
12
|
+
label: string
|
|
13
|
+
): void => {
|
|
14
|
+
const actual = Object.keys(value).sort()
|
|
15
|
+
const fields = [...expected].sort()
|
|
16
|
+
if (actual.length !== fields.length || actual.some((field, index) => field !== fields[index])) {
|
|
17
|
+
throw JournalAuthorityMismatch.make({ reason: `${label} fields are not the exact admitted set.` })
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const validateS3JournalAuthority = (authority: S3JournalAuthority): void => {
|
|
22
|
+
requireExactFields(authority, [
|
|
23
|
+
"accountId",
|
|
24
|
+
"bucketArn",
|
|
25
|
+
"bucketName",
|
|
26
|
+
"bucketOwnerEnforced",
|
|
27
|
+
"bucketPolicyDigest",
|
|
28
|
+
"conditionalWritesEnforced",
|
|
29
|
+
"deleteDenied",
|
|
30
|
+
"expectedBucketOwner",
|
|
31
|
+
"multipartDenied",
|
|
32
|
+
"objectLock",
|
|
33
|
+
"oidc",
|
|
34
|
+
"oidcTrust",
|
|
35
|
+
"oidcTrustPolicyDigest",
|
|
36
|
+
"prefix",
|
|
37
|
+
"publicAccessBlocked",
|
|
38
|
+
"region",
|
|
39
|
+
"retentionMode",
|
|
40
|
+
"retentionYears",
|
|
41
|
+
"roleArn",
|
|
42
|
+
"rolePolicyDigest",
|
|
43
|
+
"versioning"
|
|
44
|
+
], "Journal authority")
|
|
45
|
+
requireExactFields(authority.oidc, [
|
|
46
|
+
"audience",
|
|
47
|
+
"environment",
|
|
48
|
+
"eventName",
|
|
49
|
+
"issuer",
|
|
50
|
+
"jobWorkflowRef",
|
|
51
|
+
"jobWorkflowSha",
|
|
52
|
+
"ref",
|
|
53
|
+
"refType",
|
|
54
|
+
"repository",
|
|
55
|
+
"repositoryId",
|
|
56
|
+
"repositoryOwnerId",
|
|
57
|
+
"repositoryVisibility",
|
|
58
|
+
"runAttempt",
|
|
59
|
+
"runId",
|
|
60
|
+
"runnerEnvironment",
|
|
61
|
+
"sha",
|
|
62
|
+
"subject",
|
|
63
|
+
"workflow",
|
|
64
|
+
"workflowRef",
|
|
65
|
+
"workflowSha"
|
|
66
|
+
], "Journal OIDC claims")
|
|
67
|
+
requireExactFields(authority.oidcTrust, [
|
|
68
|
+
"audience",
|
|
69
|
+
"environment",
|
|
70
|
+
"jobWorkflowRef",
|
|
71
|
+
"ref",
|
|
72
|
+
"repository",
|
|
73
|
+
"repositoryId",
|
|
74
|
+
"repositoryOwnerId",
|
|
75
|
+
"subject",
|
|
76
|
+
"workflow"
|
|
77
|
+
], "Journal OIDC trust projection")
|
|
78
|
+
if (!/^[0-9]{12}$/u.test(authority.accountId) || authority.expectedBucketOwner !== authority.accountId) {
|
|
79
|
+
throw JournalAuthorityMismatch.make({ reason: "Journal AWS account and expected bucket owner are not the same exact account." })
|
|
80
|
+
}
|
|
81
|
+
if (!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/u.test(authority.bucketName) ||
|
|
82
|
+
authority.bucketArn !== `arn:aws:s3:::${authority.bucketName}`) {
|
|
83
|
+
throw JournalAuthorityMismatch.make({ reason: "Journal bucket identity is not canonical." })
|
|
84
|
+
}
|
|
85
|
+
if (!/^(?:af|ap|ca|eu|il|me|mx|sa|us)-[a-z]+-[0-9]$/u.test(authority.region)) {
|
|
86
|
+
throw JournalAuthorityMismatch.make({ reason: "Journal region is not canonical." })
|
|
87
|
+
}
|
|
88
|
+
if (authority.roleArn.length > 512 || !(new RegExp(
|
|
89
|
+
`^arn:aws:iam::${authority.accountId}:role\/(?:[A-Za-z0-9+=,.@_-]{1,128}\/)*[A-Za-z0-9+=,.@_-]{1,64}$`,
|
|
90
|
+
"u"
|
|
91
|
+
)).test(authority.roleArn)) {
|
|
92
|
+
throw JournalAuthorityMismatch.make({ reason: "Journal role does not belong to the exact bucket account." })
|
|
93
|
+
}
|
|
94
|
+
if (!/^sha256:[a-f0-9]{64}$/u.test(authority.bucketPolicyDigest) ||
|
|
95
|
+
!/^sha256:[a-f0-9]{64}$/u.test(authority.rolePolicyDigest) ||
|
|
96
|
+
!/^sha256:[a-f0-9]{64}$/u.test(authority.oidcTrustPolicyDigest)) {
|
|
97
|
+
throw JournalAuthorityMismatch.make({ reason: "Journal policy digests are not canonical SHA-256 values." })
|
|
98
|
+
}
|
|
99
|
+
if (authority.prefix !== "operation-journal/v1" || authority.versioning !== "Enabled" ||
|
|
100
|
+
authority.objectLock !== "Enabled" || authority.retentionMode !== "COMPLIANCE" ||
|
|
101
|
+
authority.retentionYears !== 10 || authority.bucketOwnerEnforced !== true ||
|
|
102
|
+
authority.publicAccessBlocked !== true || authority.deleteDenied !== true ||
|
|
103
|
+
authority.multipartDenied !== true || authority.conditionalWritesEnforced !== true) {
|
|
104
|
+
throw JournalAuthorityMismatch.make({ reason: "Journal bucket governance is not the one admitted ten-year COMPLIANCE policy." })
|
|
105
|
+
}
|
|
106
|
+
if (authority.oidc.issuer !== "https://token.actions.githubusercontent.com" ||
|
|
107
|
+
authority.oidc.audience !== "sts.amazonaws.com" ||
|
|
108
|
+
!/^[1-9][0-9]{0,19}$/u.test(authority.oidc.repositoryId) ||
|
|
109
|
+
!/^[1-9][0-9]{0,19}$/u.test(authority.oidc.repositoryOwnerId) ||
|
|
110
|
+
!/^[1-9][0-9]{0,19}$/u.test(authority.oidc.runId) ||
|
|
111
|
+
!/^[1-9][0-9]{0,19}$/u.test(authority.oidc.runAttempt) ||
|
|
112
|
+
authority.oidc.repository.length > 200 ||
|
|
113
|
+
!/^[A-Za-z0-9_.-]{1,100}\/[A-Za-z0-9_.-]{1,100}$/u.test(authority.oidc.repository) ||
|
|
114
|
+
authority.oidc.repositoryVisibility !== "public" ||
|
|
115
|
+
authority.oidc.eventName !== "workflow_dispatch" ||
|
|
116
|
+
authority.oidc.ref.length > 256 ||
|
|
117
|
+
!/^refs\/heads\/[A-Za-z0-9][A-Za-z0-9._/-]*$/u.test(authority.oidc.ref) ||
|
|
118
|
+
authority.oidc.refType !== "branch" ||
|
|
119
|
+
!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(authority.oidc.environment) ||
|
|
120
|
+
authority.oidc.runnerEnvironment !== "github-hosted" ||
|
|
121
|
+
!/^[ -~]{1,128}$/u.test(authority.oidc.workflow) ||
|
|
122
|
+
!/^[a-f0-9]{40}$/u.test(authority.oidc.sha) ||
|
|
123
|
+
authority.oidc.workflowSha !== authority.oidc.sha ||
|
|
124
|
+
authority.oidc.workflowRef.length > 512 || !(new RegExp(
|
|
125
|
+
`^${escapedPattern(authority.oidc.repository)}\\/.github\\/workflows\\/[A-Za-z0-9._-]+\\.ya?ml@${escapedPattern(authority.oidc.ref)}$`,
|
|
126
|
+
"u"
|
|
127
|
+
)).test(authority.oidc.workflowRef) ||
|
|
128
|
+
authority.oidc.jobWorkflowRef.length > 512 ||
|
|
129
|
+
!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/\.github\/workflows\/[A-Za-z0-9._-]+\.ya?ml@[a-f0-9]{40}$/u.test(authority.oidc.jobWorkflowRef) ||
|
|
130
|
+
!/^[a-f0-9]{40}$/u.test(authority.oidc.jobWorkflowSha) ||
|
|
131
|
+
!authority.oidc.jobWorkflowRef.endsWith(`@${authority.oidc.jobWorkflowSha}`)) {
|
|
132
|
+
throw JournalAuthorityMismatch.make({
|
|
133
|
+
reason: "Journal OIDC claims do not bind the exact caller and called workflow identities."
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
const [repositoryOwner, repositoryName] = authority.oidc.repository.split("/") as [string, string]
|
|
137
|
+
const admittedSubjects = [
|
|
138
|
+
`repo:${authority.oidc.repository}:environment:${authority.oidc.environment}`,
|
|
139
|
+
`repo:${repositoryOwner}@${authority.oidc.repositoryOwnerId}/${repositoryName}@${authority.oidc.repositoryId}:environment:${authority.oidc.environment}`
|
|
140
|
+
]
|
|
141
|
+
if (!admittedSubjects.includes(authority.oidc.subject)) {
|
|
142
|
+
throw JournalAuthorityMismatch.make({
|
|
143
|
+
reason: "Journal OIDC subject is neither the exact name-bound nor immutable-ID-bound environment subject."
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
const trustProjection = {
|
|
147
|
+
audience: authority.oidc.audience,
|
|
148
|
+
subject: authority.oidc.subject,
|
|
149
|
+
repository: authority.oidc.repository,
|
|
150
|
+
repositoryId: authority.oidc.repositoryId,
|
|
151
|
+
repositoryOwnerId: authority.oidc.repositoryOwnerId,
|
|
152
|
+
workflow: authority.oidc.workflow,
|
|
153
|
+
ref: authority.oidc.ref,
|
|
154
|
+
environment: authority.oidc.environment,
|
|
155
|
+
jobWorkflowRef: authority.oidc.jobWorkflowRef
|
|
156
|
+
}
|
|
157
|
+
if (encodeCanonicalJson(authority.oidcTrust) !== encodeCanonicalJson(trustProjection)) {
|
|
158
|
+
throw JournalAuthorityMismatch.make({
|
|
159
|
+
reason: "Journal AWS trust-policy conditions do not equal the re-observed trust-enforced OIDC claims."
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const operationJournalNetworkDeadlineMilliseconds = 10_000
|
|
2
|
+
|
|
3
|
+
export class OperationJournalNetworkDeadlineExceeded extends Error {
|
|
4
|
+
readonly name = "OperationJournalNetworkDeadlineExceeded"
|
|
5
|
+
|
|
6
|
+
constructor() {
|
|
7
|
+
super("The operational-journal network deadline elapsed.")
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Bound one complete network operation even when the underlying transport
|
|
13
|
+
* ignores AbortSignal. The signal still gives cooperative transports an
|
|
14
|
+
* immediate opportunity to close DNS, sockets, and response streams.
|
|
15
|
+
*/
|
|
16
|
+
export const withOperationJournalNetworkDeadline = async <Value>(
|
|
17
|
+
run: (signal: AbortSignal) => Promise<Value>,
|
|
18
|
+
milliseconds = operationJournalNetworkDeadlineMilliseconds
|
|
19
|
+
): Promise<Value> => {
|
|
20
|
+
if (!Number.isSafeInteger(milliseconds) || milliseconds < 1 ||
|
|
21
|
+
milliseconds > operationJournalNetworkDeadlineMilliseconds) {
|
|
22
|
+
throw new Error("The operational-journal network deadline is not canonical.")
|
|
23
|
+
}
|
|
24
|
+
const controller = new AbortController()
|
|
25
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
26
|
+
const deadline = new Promise<never>((_resolve, reject) => {
|
|
27
|
+
timer = setTimeout(() => {
|
|
28
|
+
const cause = new OperationJournalNetworkDeadlineExceeded()
|
|
29
|
+
controller.abort(cause)
|
|
30
|
+
reject(cause)
|
|
31
|
+
}, milliseconds)
|
|
32
|
+
})
|
|
33
|
+
try {
|
|
34
|
+
return await Promise.race([
|
|
35
|
+
Promise.resolve().then(() => run(controller.signal)),
|
|
36
|
+
deadline
|
|
37
|
+
])
|
|
38
|
+
} finally {
|
|
39
|
+
if (timer !== undefined) clearTimeout(timer)
|
|
40
|
+
}
|
|
41
|
+
}
|