From 44f88cc88dfce2565d98cb9d0b4b283ac6a4fc9a Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Tue, 3 Mar 2026 10:44:54 +1100 Subject: [PATCH 1/5] feat: include fetched attestation bundles in _attestations When verifyAttestations is enabled, pacote already fetches the full sigstore attestation bundles from the registry and uses them for verification. However, the fetched bundles are discarded after verification, and only the lightweight dist.attestations metadata (URL + predicate type) is saved to mani._attestations. This change expands _attestations to include the fetched bundles as a 'bundles' property, making the complete sigstore bundles (DSSE envelopes, verification material, tlog entries) available to downstream consumers like the npm CLI without requiring a re-fetch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- lib/registry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/registry.js b/lib/registry.js index 1bfee0dd..a446cc5a 100644 --- a/lib/registry.js +++ b/lib/registry.js @@ -341,7 +341,7 @@ class RegistryFetcher extends Fetcher { }) } } - mani._attestations = dist.attestations + mani._attestations = { ...dist.attestations, bundles: attestations } } else { mani._attestations = dist.attestations } From e2d0465895b38a6c2a69f8ef750c8b0363b43192 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Tue, 3 Mar 2026 11:20:53 +1100 Subject: [PATCH 2/5] test: assert attestation bundles on _attestations Add assertions to verify that the fetched attestation bundles are preserved on mani._attestations.bundles when verification is enabled, and absent when verification is disabled. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- test/registry.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/registry.js b/test/registry.js index c0969ef0..742b897d 100644 --- a/test/registry.js +++ b/test/registry.js @@ -394,6 +394,15 @@ t.test('verifyAttestations valid attestations', async t => { const mani = await f.manifest() t.ok(mani._attestations) + t.ok(mani._attestations.bundles, 'should include fetched attestation bundles') + t.equal(mani._attestations.bundles.length, 2, 'should have two attestation bundles') + t.equal(mani._attestations.bundles[0].predicateType, 'https://slsa.dev/provenance/v0.2') + t.equal( + mani._attestations.bundles[1].predicateType, + 'https://github.com/npm/attestation/tree/main/specs/publish/v0.1' + ) + t.ok(mani._attestations.url, 'should preserve original attestation url') + t.ok(mani._attestations.provenance, 'should preserve original provenance metadata') t.ok(mani._integrity) }) @@ -450,6 +459,8 @@ t.test('verifyAttestations with registry path does not duplicate path', async t const mani = await f.manifest() t.ok(mani._attestations) + t.ok(mani._attestations.bundles, 'should include fetched attestation bundles') + t.equal(mani._attestations.bundles.length, 2) t.ok(mani._integrity) }) @@ -554,6 +565,7 @@ t.test('disable verifyAttestations when package has attestations', async t => { const mani = await f.manifest() t.ok(mani._attestations) + t.notOk(mani._attestations.bundles, 'should not include bundles when verification is disabled') t.ok(mani._integrity) }) @@ -720,6 +732,7 @@ t.test('verifyAttestations no attestation with keyid', async t => { // Keyless attestations (no keyid) should not require registry keys const mani = await f.manifest() t.ok(mani._attestations) + t.ok(mani._attestations.bundles, 'should include bundles for keyless attestations') t.ok(mani._integrity) }) @@ -764,6 +777,7 @@ t.test('verifyAttestations keyless without registry keys', async t => { const mani = await f.manifest() t.ok(mani._attestations) + t.ok(mani._attestations.bundles, 'should include bundles for keyless attestations without registry keys') t.ok(mani._integrity) }) @@ -945,6 +959,7 @@ t.test('verifyAttestations rotated key', async t => { const mani = await f.manifest() t.ok(mani._attestations) + t.ok(mani._attestations.bundles, 'should include bundles with rotated key') t.ok(mani._integrity) }) From 2084e932e6c7cee04bd2de006e598d92c7b41a66 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 6 Mar 2026 11:25:29 +1100 Subject: [PATCH 3/5] refactor: store attestation bundles on separate _attestationBundles attribute Instead of merging fetched bundles into _attestations (which mirrors dist.attestations from the registry), store them on a separate _attestationBundles attribute. This avoids future collisions if the registry ever adds a 'bundles' field to dist.attestations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- lib/registry.js | 3 ++- test/registry.js | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/registry.js b/lib/registry.js index a446cc5a..f50b2795 100644 --- a/lib/registry.js +++ b/lib/registry.js @@ -341,7 +341,8 @@ class RegistryFetcher extends Fetcher { }) } } - mani._attestations = { ...dist.attestations, bundles: attestations } + mani._attestations = dist.attestations + mani._attestationBundles = attestations } else { mani._attestations = dist.attestations } diff --git a/test/registry.js b/test/registry.js index 742b897d..254facec 100644 --- a/test/registry.js +++ b/test/registry.js @@ -394,11 +394,11 @@ t.test('verifyAttestations valid attestations', async t => { const mani = await f.manifest() t.ok(mani._attestations) - t.ok(mani._attestations.bundles, 'should include fetched attestation bundles') - t.equal(mani._attestations.bundles.length, 2, 'should have two attestation bundles') - t.equal(mani._attestations.bundles[0].predicateType, 'https://slsa.dev/provenance/v0.2') + t.ok(mani._attestationBundles, 'should include fetched attestation bundles') + t.equal(mani._attestationBundles.length, 2, 'should have two attestation bundles') + t.equal(mani._attestationBundles[0].predicateType, 'https://slsa.dev/provenance/v0.2') t.equal( - mani._attestations.bundles[1].predicateType, + mani._attestationBundles[1].predicateType, 'https://github.com/npm/attestation/tree/main/specs/publish/v0.1' ) t.ok(mani._attestations.url, 'should preserve original attestation url') @@ -459,8 +459,8 @@ t.test('verifyAttestations with registry path does not duplicate path', async t const mani = await f.manifest() t.ok(mani._attestations) - t.ok(mani._attestations.bundles, 'should include fetched attestation bundles') - t.equal(mani._attestations.bundles.length, 2) + t.ok(mani._attestationBundles, 'should include fetched attestation bundles') + t.equal(mani._attestationBundles.length, 2) t.ok(mani._integrity) }) @@ -565,7 +565,7 @@ t.test('disable verifyAttestations when package has attestations', async t => { const mani = await f.manifest() t.ok(mani._attestations) - t.notOk(mani._attestations.bundles, 'should not include bundles when verification is disabled') + t.notOk(mani._attestationBundles, 'should not include bundles when verification is disabled') t.ok(mani._integrity) }) @@ -732,7 +732,7 @@ t.test('verifyAttestations no attestation with keyid', async t => { // Keyless attestations (no keyid) should not require registry keys const mani = await f.manifest() t.ok(mani._attestations) - t.ok(mani._attestations.bundles, 'should include bundles for keyless attestations') + t.ok(mani._attestationBundles, 'should include bundles for keyless attestations') t.ok(mani._integrity) }) @@ -777,7 +777,7 @@ t.test('verifyAttestations keyless without registry keys', async t => { const mani = await f.manifest() t.ok(mani._attestations) - t.ok(mani._attestations.bundles, 'should include bundles for keyless attestations without registry keys') + t.ok(mani._attestationBundles, 'should include bundles for keyless attestations without registry keys') t.ok(mani._integrity) }) @@ -959,7 +959,7 @@ t.test('verifyAttestations rotated key', async t => { const mani = await f.manifest() t.ok(mani._attestations) - t.ok(mani._attestations.bundles, 'should include bundles with rotated key') + t.ok(mani._attestationBundles, 'should include bundles with rotated key') t.ok(mani._integrity) }) From d798cecff1fae26ad755fee30ea5d4260a5f3630 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 6 Mar 2026 11:58:12 +1100 Subject: [PATCH 4/5] chore: update template-oss generated files Run template-oss-apply to regenerate managed files with release/v* branch patterns for CI, CodeQL, release workflows, settings, and dependabot configuration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/dependabot.yml | 34 +++++++++++++++++++++++++++ .github/settings.yml | 28 ++++++++++++++++++++++ .github/workflows/ci.yml | 1 + .github/workflows/codeql-analysis.yml | 2 ++ .github/workflows/release.yml | 1 + 5 files changed, 66 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7ecb4bdf..7e2ab742 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,3 +17,37 @@ updates: labels: - "Dependencies" open-pull-requests-limit: 10 + - package-ecosystem: npm + directory: / + schedule: + interval: daily + target-branch: "release/v19" + allow: + - dependency-type: direct + dependency-name: "@npmcli/template-oss" + versioning-strategy: increase-if-necessary + commit-message: + prefix: deps + prefix-development: chore + labels: + - "Dependencies" + - "Backport" + - "release/v19" + open-pull-requests-limit: 10 + - package-ecosystem: npm + directory: / + schedule: + interval: daily + target-branch: "release/v20" + allow: + - dependency-type: direct + dependency-name: "@npmcli/template-oss" + versioning-strategy: increase-if-necessary + commit-message: + prefix: deps + prefix-development: chore + labels: + - "Dependencies" + - "Backport" + - "release/v20" + open-pull-requests-limit: 10 diff --git a/.github/settings.yml b/.github/settings.yml index c7428712..5c378da0 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -25,3 +25,31 @@ branches: apps: [] users: [] teams: [ "cli-team" ] + - name: release/v19 + protection: + required_status_checks: null + enforce_admins: true + block_creations: true + required_pull_request_reviews: + required_approving_review_count: 1 + require_code_owner_reviews: true + require_last_push_approval: true + dismiss_stale_reviews: true + restrictions: + apps: [] + users: [] + teams: [ "cli-team" ] + - name: release/v20 + protection: + required_status_checks: null + enforce_admins: true + block_creations: true + required_pull_request_reviews: + required_approving_review_count: 1 + require_code_owner_reviews: true + require_last_push_approval: true + dismiss_stale_reviews: true + restrictions: + apps: [] + users: [] + teams: [ "cli-team" ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4bf745e..0c59a8b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ on: push: branches: - main + - release/v* schedule: # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1 - cron: "0 9 * * 1" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index af848e17..5304739f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -6,9 +6,11 @@ on: push: branches: - main + - release/v* pull_request: branches: - main + - release/v* schedule: # "At 10:00 UTC (03:00 PT) on Monday" https://crontab.guru/#0_10_*_*_1 - cron: "0 10 * * 1" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53ff3c24..217806da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,6 +6,7 @@ on: push: branches: - main + - release/v* permissions: contents: write From a46c9fba9a16de834146fbdefc80cc9943712463 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Fri, 6 Mar 2026 12:52:18 +1100 Subject: [PATCH 5/5] chore: revert unrelated template-oss changes These workflow/config changes are pre-existing drift unrelated to this PR and should be addressed separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/dependabot.yml | 34 --------------------------- .github/settings.yml | 28 ---------------------- .github/workflows/ci.yml | 1 - .github/workflows/codeql-analysis.yml | 2 -- .github/workflows/release.yml | 1 - 5 files changed, 66 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7e2ab742..7ecb4bdf 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,37 +17,3 @@ updates: labels: - "Dependencies" open-pull-requests-limit: 10 - - package-ecosystem: npm - directory: / - schedule: - interval: daily - target-branch: "release/v19" - allow: - - dependency-type: direct - dependency-name: "@npmcli/template-oss" - versioning-strategy: increase-if-necessary - commit-message: - prefix: deps - prefix-development: chore - labels: - - "Dependencies" - - "Backport" - - "release/v19" - open-pull-requests-limit: 10 - - package-ecosystem: npm - directory: / - schedule: - interval: daily - target-branch: "release/v20" - allow: - - dependency-type: direct - dependency-name: "@npmcli/template-oss" - versioning-strategy: increase-if-necessary - commit-message: - prefix: deps - prefix-development: chore - labels: - - "Dependencies" - - "Backport" - - "release/v20" - open-pull-requests-limit: 10 diff --git a/.github/settings.yml b/.github/settings.yml index 5c378da0..c7428712 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -25,31 +25,3 @@ branches: apps: [] users: [] teams: [ "cli-team" ] - - name: release/v19 - protection: - required_status_checks: null - enforce_admins: true - block_creations: true - required_pull_request_reviews: - required_approving_review_count: 1 - require_code_owner_reviews: true - require_last_push_approval: true - dismiss_stale_reviews: true - restrictions: - apps: [] - users: [] - teams: [ "cli-team" ] - - name: release/v20 - protection: - required_status_checks: null - enforce_admins: true - block_creations: true - required_pull_request_reviews: - required_approving_review_count: 1 - require_code_owner_reviews: true - require_last_push_approval: true - dismiss_stale_reviews: true - restrictions: - apps: [] - users: [] - teams: [ "cli-team" ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c59a8b9..f4bf745e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,6 @@ on: push: branches: - main - - release/v* schedule: # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1 - cron: "0 9 * * 1" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5304739f..af848e17 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -6,11 +6,9 @@ on: push: branches: - main - - release/v* pull_request: branches: - main - - release/v* schedule: # "At 10:00 UTC (03:00 PT) on Monday" https://crontab.guru/#0_10_*_*_1 - cron: "0 10 * * 1" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 217806da..53ff3c24 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,6 @@ on: push: branches: - main - - release/v* permissions: contents: write