feat: support staged publishes in trust scale#12056
Conversation
|
💖 Thanks for opening this pull request! 💖 |
Review Summary by QodoSupport staged publishes in npm package trust scale
WalkthroughsDescription• Add staged publish support to trust scale ranking system • Staged publishes now rank highest (3) above trusted publishers (2) • Detect staged publishes via approver field in package manifest • Add validation to prevent trust downgrades from staged to lower levels Diagramflowchart LR
A["Package Manifest"] -->|Check approver field| B["Staged Publish?"]
B -->|Yes| C["Trust Rank: 3"]
B -->|No| D["Check trustedPublisher"]
D -->|Yes| E["Trust Rank: 2"]
D -->|No| F["Check provenance"]
F -->|Yes| G["Trust Rank: 1"]
F -->|No| H["No Trust Evidence"]
C --> I["Validate No Downgrade"]
E --> I
G --> I
File Changes1. resolving/npm-resolver/src/trustChecks.ts
|
There was a problem hiding this comment.
Pull request overview
Adds support for npm “staged publishing” in pnpm’s trust-checking logic by detecting a new _npmUser.approver signal in the packument and treating it as the highest trust evidence to avoid false “no-downgrade” alerts.
Changes:
- Extend registry packument typings to include
_npmUser.approver. - Introduce
stagedPublishas a newTrustEvidencelevel ranked abovetrustedPublisherandprovenance. - Add tests covering staged-publish evidence detection and downgrade behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| resolving/registry/types/src/index.ts | Adds _npmUser.approver typing to represent staged publish approval metadata. |
| resolving/npm-resolver/src/trustChecks.ts | Detects staged publish trust evidence and ranks it highest in downgrade checks. |
| resolving/npm-resolver/test/trustChecks.test.ts | Adds/updates tests for staged publish evidence and downgrade scenarios. |
Comments suppressed due to low confidence (1)
resolving/npm-resolver/test/trustChecks.test.ts:442
- This is the only semicolon in the file and is inconsistent with the surrounding no-semicolon style; it’s likely to fail formatting checks.
};
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR adds a new trust evidence value, ChangesStaged Publish Trust Evidence
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@resolving/npm-resolver/src/trustChecks.ts`:
- Around line 112-117: The current logic in trustChecks.ts early-returns
'trustedPublisher' when trustEvidence === 'trustedPublisher', which makes
evidence detection order-dependent and can miss a later 'stagedPublish'; update
the function that inspects trustEvidence to defer returning until all evidence
types are evaluated and choose the strongest evidence (treat 'stagedPublish' as
higher priority than 'trustedPublisher'), e.g. track the bestEvidence while
iterating and return it after checks so both 'stagedPublish' and
'trustedPublisher' are considered regardless of evaluation order.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 68a3141c-ef0a-4414-848e-50b9d5ace91e
📒 Files selected for processing (3)
resolving/npm-resolver/src/trustChecks.tsresolving/npm-resolver/test/trustChecks.test.tsresolving/registry/types/src/index.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Follow Standard Style with trailing commas, preferring functions over classes, and declaring functions after they are used (relying on hoisting)
Use a single options object instead of multiple parameters when a function needs more than two or three arguments
Follow Import Order: Standard libraries first, then external dependencies (alphabetically), then relative imports
Write self-documenting code where function names, parameters, and types explain what a function does without requiring prose comments
Do not write comments that restate what the code already says; refactor via renaming, splitting helpers, or restructuring instead
Do not repeat documentation at call sites that already exists in JSDoc on the callee; update JSDoc once for all call sites to benefit
Use JSDoc only for a function's contract (preconditions, postconditions, edge cases, why the function exists), not for re-narrating the body
Do not record past implementation shape, refactor history, or 'the previous code did X' framing in code; use git log and git blame instead
Write comments only when: the reason for code is non-obvious (hidden invariant, workaround for known bug, deliberate exception), or the right name doesn't fit (temporary technical constraint)
Files:
resolving/registry/types/src/index.tsresolving/npm-resolver/src/trustChecks.tsresolving/npm-resolver/test/trustChecks.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use util.types.isNativeError() instead of instanceof Error for error type checking in Jest tests
Files:
resolving/npm-resolver/test/trustChecks.test.ts
🧠 Learnings (2)
📚 Learning: 2026-05-14T09:04:00.133Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11622
File: resolving/npm-resolver/test/publishedBy.test.ts:350-354
Timestamp: 2026-05-14T09:04:00.133Z
Learning: In the pnpm/pnpm repository, ESLint is the authoritative style linter. Do not raise review findings for missing trailing commas in multiline function calls (e.g., `fs.writeFileSync(...)`) when this repo’s ESLint configuration does not report them and lint passes. Prefer deferring to the ESLint results for this specific trailing-comma rule rather than enforcing it manually in code review.
Applied to files:
resolving/registry/types/src/index.tsresolving/npm-resolver/src/trustChecks.tsresolving/npm-resolver/test/trustChecks.test.ts
📚 Learning: 2026-05-23T17:29:56.247Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11878
File: resolving/npm-resolver/src/createNpmResolutionVerifier.ts:381-418
Timestamp: 2026-05-23T17:29:56.247Z
Learning: When reviewing the npm resolver code, note that `PackageMetaCache` is intentionally keyed only by `name` and `name:full` (no registry component). As a result, code that shares this cache (e.g., `createNpmResolutionVerifier.ts` via the shared `validateSharedMeta` guard) can prevent cross-package contamination by matching names, but it cannot distinguish two different registries that serve packages with the same name within a single install. Don’t flag (or attempt to fix) this as a local issue in the verifier alone—correctly distinguishing registries would require a coordinated change to the resolver cache key shape. (The Pacquet/Rust cache is already registry-qualified, unlike the npm-resolver cache.)
Applied to files:
resolving/npm-resolver/src/trustChecks.ts
🔇 Additional comments (3)
resolving/registry/types/src/index.ts (1)
32-35: LGTM!resolving/npm-resolver/src/trustChecks.ts (1)
8-14: LGTM!Also applies to: 85-86, 125-127
resolving/npm-resolver/test/trustChecks.test.ts (1)
98-116: LGTM!Also applies to: 394-446
There was a problem hiding this comment.
🧹 Nitpick comments (2)
resolving/npm-resolver/test/trustChecks.test.ts (2)
425-425: 💤 Low valueInconsistent email value for clarity.
The email is
'[email protected]'while the name is'test-publisher'and this version usestrustedPublisher. For clarity and consistency with the trust evidence type, consider using'[email protected]'instead. This doesn't affect test correctness but improves readability.♻️ Suggested fix
- email: '[email protected]', + email: '[email protected]',🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@resolving/npm-resolver/test/trustChecks.test.ts` at line 425, Update the inconsistent email literal used in the test data: locate the object with name 'test-publisher' and trust evidence type 'trustedPublisher' (in the test in trustChecks.test.ts) and change the email field from '[email protected]' to '[email protected]' so the name, trust type and email are consistent for clarity.
98-116: ⚡ Quick winConsider adding a test case for when both
approverandtrustedPublisherexist.The current test validates
stagedPublishdetection when onlyapproveris present. To ensure the trust hierarchy is correctly implemented, consider adding a test that validates behavior when both_npmUser.approverand_npmUser.trustedPublisherexist in the same version (staged publish should take priority according to the trust model).📝 Suggested test case
test('returns stagedPublish when both approver and trustedPublisher exist', () => { const manifest: PackageInRegistry = { name: 'foo', version: '1.0.0', _npmUser: { name: 'test-approver', email: '[email protected]', approver: { name: 'test-approver', email: '[email protected]', }, trustedPublisher: { id: 'test-provider', oidcConfigId: 'oidc:test-config-123', }, }, dist: { shasum: 'abc123', tarball: 'https://registry.example.com/foo/-/foo-1.0.0.tgz', }, } expect(getTrustEvidence(manifest)).toBe('stagedPublish') })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@resolving/npm-resolver/test/trustChecks.test.ts` around lines 98 - 116, Add a test that verifies getTrustEvidence returns 'stagedPublish' when both _npmUser.approver and _npmUser.trustedPublisher are present on the manifest; create a manifest similar to the existing "returns stagedPublish when approver exists" test but include a _npmUser.trustedPublisher object (e.g., with id and oidcConfigId) and assert expect(getTrustEvidence(manifest)).toBe('stagedPublish') to confirm stagedPublish takes priority over trustedPublisher.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@resolving/npm-resolver/test/trustChecks.test.ts`:
- Line 425: Update the inconsistent email literal used in the test data: locate
the object with name 'test-publisher' and trust evidence type 'trustedPublisher'
(in the test in trustChecks.test.ts) and change the email field from
'[email protected]' to '[email protected]' so the name, trust type and
email are consistent for clarity.
- Around line 98-116: Add a test that verifies getTrustEvidence returns
'stagedPublish' when both _npmUser.approver and _npmUser.trustedPublisher are
present on the manifest; create a manifest similar to the existing "returns
stagedPublish when approver exists" test but include a _npmUser.trustedPublisher
object (e.g., with id and oidcConfigId) and assert
expect(getTrustEvidence(manifest)).toBe('stagedPublish') to confirm
stagedPublish takes priority over trustedPublisher.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: ddb8d2ef-bf13-46ec-8153-b46f990abe27
📒 Files selected for processing (2)
resolving/npm-resolver/test/trustChecks.test.tsresolving/registry/types/src/index.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Compile & Lint
- GitHub Check: Analyze (javascript)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Follow Standard Style with trailing commas, preferring functions over classes, and declaring functions after they are used (relying on hoisting)
Use a single options object instead of multiple parameters when a function needs more than two or three arguments
Follow Import Order: Standard libraries first, then external dependencies (alphabetically), then relative imports
Write self-documenting code where function names, parameters, and types explain what a function does without requiring prose comments
Do not write comments that restate what the code already says; refactor via renaming, splitting helpers, or restructuring instead
Do not repeat documentation at call sites that already exists in JSDoc on the callee; update JSDoc once for all call sites to benefit
Use JSDoc only for a function's contract (preconditions, postconditions, edge cases, why the function exists), not for re-narrating the body
Do not record past implementation shape, refactor history, or 'the previous code did X' framing in code; use git log and git blame instead
Write comments only when: the reason for code is non-obvious (hidden invariant, workaround for known bug, deliberate exception), or the right name doesn't fit (temporary technical constraint)
Files:
resolving/registry/types/src/index.tsresolving/npm-resolver/test/trustChecks.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use util.types.isNativeError() instead of instanceof Error for error type checking in Jest tests
Files:
resolving/npm-resolver/test/trustChecks.test.ts
🧠 Learnings (1)
📚 Learning: 2026-05-14T09:04:00.133Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11622
File: resolving/npm-resolver/test/publishedBy.test.ts:350-354
Timestamp: 2026-05-14T09:04:00.133Z
Learning: In the pnpm/pnpm repository, ESLint is the authoritative style linter. Do not raise review findings for missing trailing commas in multiline function calls (e.g., `fs.writeFileSync(...)`) when this repo’s ESLint configuration does not report them and lint passes. Prefer deferring to the ESLint results for this specific trailing-comma rule rather than enforcing it manually in code review.
Applied to files:
resolving/registry/types/src/index.tsresolving/npm-resolver/test/trustChecks.test.ts
🔇 Additional comments (3)
resolving/registry/types/src/index.ts (1)
32-35: LGTM!resolving/npm-resolver/test/trustChecks.test.ts (2)
107-108: LGTM!
426-429: LGTM!
There was a problem hiding this comment.
🧹 Nitpick comments (1)
resolving/npm-resolver/test/trustChecks.test.ts (1)
446-446: 💤 Low valueRemove trailing semicolon for consistency.
Line 446 has a semicolon after the closing brace, while all other test fixtures in this file omit it. Standard Style (per coding guidelines) typically omits semicolons.
✨ Proposed fix
- }; + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@resolving/npm-resolver/test/trustChecks.test.ts` at line 446, Remove the trailing semicolon after the closing brace of the test fixture/object in trustChecks.test.ts (the "};" at the end of the fixture) so it matches the rest of the file's style; edit the closing brace for that fixture (the object literal used in the test) to end with just "}" and run the project's formatter/linter to ensure consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@resolving/npm-resolver/test/trustChecks.test.ts`:
- Line 446: Remove the trailing semicolon after the closing brace of the test
fixture/object in trustChecks.test.ts (the "};" at the end of the fixture) so it
matches the rest of the file's style; edit the closing brace for that fixture
(the object literal used in the test) to end with just "}" and run the project's
formatter/linter to ensure consistency.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 41a94ecf-7af6-4c21-ae9a-bae311bbb8c8
📒 Files selected for processing (1)
resolving/npm-resolver/test/trustChecks.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Compile & Lint
- GitHub Check: Analyze (javascript)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Follow Standard Style with trailing commas, preferring functions over classes, and declaring functions after they are used (relying on hoisting)
Use a single options object instead of multiple parameters when a function needs more than two or three arguments
Follow Import Order: Standard libraries first, then external dependencies (alphabetically), then relative imports
Write self-documenting code where function names, parameters, and types explain what a function does without requiring prose comments
Do not write comments that restate what the code already says; refactor via renaming, splitting helpers, or restructuring instead
Do not repeat documentation at call sites that already exists in JSDoc on the callee; update JSDoc once for all call sites to benefit
Use JSDoc only for a function's contract (preconditions, postconditions, edge cases, why the function exists), not for re-narrating the body
Do not record past implementation shape, refactor history, or 'the previous code did X' framing in code; use git log and git blame instead
Write comments only when: the reason for code is non-obvious (hidden invariant, workaround for known bug, deliberate exception), or the right name doesn't fit (temporary technical constraint)
Files:
resolving/npm-resolver/test/trustChecks.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use util.types.isNativeError() instead of instanceof Error for error type checking in Jest tests
Files:
resolving/npm-resolver/test/trustChecks.test.ts
🧠 Learnings (1)
📚 Learning: 2026-05-14T09:04:00.133Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11622
File: resolving/npm-resolver/test/publishedBy.test.ts:350-354
Timestamp: 2026-05-14T09:04:00.133Z
Learning: In the pnpm/pnpm repository, ESLint is the authoritative style linter. Do not raise review findings for missing trailing commas in multiline function calls (e.g., `fs.writeFileSync(...)`) when this repo’s ESLint configuration does not report them and lint passes. Prefer deferring to the ESLint results for this specific trailing-comma rule rather than enforcing it manually in code review.
Applied to files:
resolving/npm-resolver/test/trustChecks.test.ts
🔇 Additional comments (2)
resolving/npm-resolver/test/trustChecks.test.ts (2)
98-116: LGTM!
394-445: LGTM!Also applies to: 447-450
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
resolving/npm-resolver/src/createNpmResolutionVerifier.ts:463
projectTrustManifest()is intended to avoid keeping maintainer PII in caches, but this currently copies the full_npmUser.approverobject (name/email) into the projected manifest. SincegetTrustEvidence()only needs a boolean signal, store a minimal placeholder instead of the real approver details (and keep the existingtrustedPublisherobject as-is).
let npmUser: PackageInRegistry['_npmUser'] = undefined;
if (approver) {
npmUser ||= {}
npmUser.approver = approver
}
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
resolving/npm-resolver/src/createNpmResolutionVerifier.ts (1)
448-473:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix
_npmUserprojection comment + semicolon inprojectTrustManifest
- The comment for
_npmUsersays it’s narrowed to onlytrustedPublisher, butprojectTrustManifestnow also preservesmanifest._npmUser?.approver; sincegetTrustEvidenceonly checks the presence of_npmUser?.approver(truthiness), avoid carrying the real approvername/- Remove the trailing semicolon on
let npmUser: PackageInRegistry['_npmUser'] = undefined;to match the file’s style.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@resolving/npm-resolver/src/createNpmResolutionVerifier.ts` around lines 448 - 473, Update the comment in projectTrustManifest to reflect that _npmUser is narrowed to only the presence (truthiness) of approver rather than preserving approver name/email, and ensure we avoid carrying PII by projecting approver to a boolean-like indicator used by getTrustEvidence; also remove the trailing semicolon from the declaration let npmUser: PackageInRegistry['_npmUser'] = undefined to match file style. Refer to projectTrustManifest, manifest._npmUser?.approver, manifest._npmUser?.trustedPublisher, npmUser, and getTrustEvidence when making the edits.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@resolving/npm-resolver/src/createNpmResolutionVerifier.ts`:
- Line 459: The declaration "let npmUser: PackageInRegistry['_npmUser'] =
undefined;" has a trailing semicolon that violates the project's
no-semicolon/Standard style; remove the trailing semicolon from the variable
declaration for npmUser (the symbol npmUser in createNpmResolutionVerifier /
createNpmResolutionVerifier.ts) so the line becomes semicolon-free and matches
the rest of the file's style.
---
Outside diff comments:
In `@resolving/npm-resolver/src/createNpmResolutionVerifier.ts`:
- Around line 448-473: Update the comment in projectTrustManifest to reflect
that _npmUser is narrowed to only the presence (truthiness) of approver rather
than preserving approver name/email, and ensure we avoid carrying PII by
projecting approver to a boolean-like indicator used by getTrustEvidence; also
remove the trailing semicolon from the declaration let npmUser:
PackageInRegistry['_npmUser'] = undefined to match file style. Refer to
projectTrustManifest, manifest._npmUser?.approver,
manifest._npmUser?.trustedPublisher, npmUser, and getTrustEvidence when making
the edits.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 71263d2b-15f6-4ab1-b5df-53ce770fb0f4
📒 Files selected for processing (2)
resolving/npm-resolver/src/createNpmResolutionVerifier.tsresolving/npm-resolver/test/trustChecks.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- resolving/npm-resolver/test/trustChecks.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Agent
- GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Follow Standard Style with trailing commas, preferring functions over classes, and declaring functions after they are used (relying on hoisting)
Use a single options object instead of multiple parameters when a function needs more than two or three arguments
Follow Import Order: Standard libraries first, then external dependencies (alphabetically), then relative imports
Write self-documenting code where function names, parameters, and types explain what a function does without requiring prose comments
Do not write comments that restate what the code already says; refactor via renaming, splitting helpers, or restructuring instead
Do not repeat documentation at call sites that already exists in JSDoc on the callee; update JSDoc once for all call sites to benefit
Use JSDoc only for a function's contract (preconditions, postconditions, edge cases, why the function exists), not for re-narrating the body
Do not record past implementation shape, refactor history, or 'the previous code did X' framing in code; use git log and git blame instead
Write comments only when: the reason for code is non-obvious (hidden invariant, workaround for known bug, deliberate exception), or the right name doesn't fit (temporary technical constraint)
Files:
resolving/npm-resolver/src/createNpmResolutionVerifier.ts
🧠 Learnings (2)
📚 Learning: 2026-05-14T09:04:00.133Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11622
File: resolving/npm-resolver/test/publishedBy.test.ts:350-354
Timestamp: 2026-05-14T09:04:00.133Z
Learning: In the pnpm/pnpm repository, ESLint is the authoritative style linter. Do not raise review findings for missing trailing commas in multiline function calls (e.g., `fs.writeFileSync(...)`) when this repo’s ESLint configuration does not report them and lint passes. Prefer deferring to the ESLint results for this specific trailing-comma rule rather than enforcing it manually in code review.
Applied to files:
resolving/npm-resolver/src/createNpmResolutionVerifier.ts
📚 Learning: 2026-05-23T17:29:56.247Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11878
File: resolving/npm-resolver/src/createNpmResolutionVerifier.ts:381-418
Timestamp: 2026-05-23T17:29:56.247Z
Learning: When reviewing the npm resolver code, note that `PackageMetaCache` is intentionally keyed only by `name` and `name:full` (no registry component). As a result, code that shares this cache (e.g., `createNpmResolutionVerifier.ts` via the shared `validateSharedMeta` guard) can prevent cross-package contamination by matching names, but it cannot distinguish two different registries that serve packages with the same name within a single install. Don’t flag (or attempt to fix) this as a local issue in the verifier alone—correctly distinguishing registries would require a coordinated change to the resolver cache key shape. (The Pacquet/Rust cache is already registry-qualified, unlike the npm-resolver cache.)
Applied to files:
resolving/npm-resolver/src/createNpmResolutionVerifier.ts
5e6e726 to
e5e7a7f
Compare
Fixes pnpm#11887. Staged publishes now have a signal in the packument: `approver`. If this is set, the package is more trustworthy than a "trusted publisher" package, since it requires 2FA publish approvals.
e5e7a7f to
438dfc9
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #12056 +/- ##
==========================================
+ Coverage 89.13% 89.15% +0.01%
==========================================
Files 235 235
Lines 31550 31590 +40
==========================================
+ Hits 28123 28163 +40
Misses 3427 3427 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Integrated-Benchmark Report (Linux)Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.10954536356,
"stddev": 0.19044046502623177,
"median": 2.05272632606,
"user": 2.70656596,
"system": 3.2311539199999997,
"min": 1.96734418256,
"max": 2.60686441756,
"times": [
2.01594154656,
2.21064343956,
2.0796801575600004,
2.0257724945600004,
2.1339010155600002,
1.99023131156,
1.9811617265600001,
2.60686441756,
2.0839133435600004,
1.96734418256
]
},
{
"command": "pacquet@main",
"mean": 2.0421995384600002,
"stddev": 0.05626318560408155,
"median": 2.03390129356,
"user": 2.69769896,
"system": 3.222110419999999,
"min": 1.9390348055600002,
"max": 2.1447623025600002,
"times": [
2.02169692456,
1.9390348055600002,
2.0815124445600004,
2.09572575556,
2.02513184856,
2.04267073856,
2.00237274856,
2.05038681656,
2.1447623025600002,
2.01870099956
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6615299885400001,
"stddev": 0.01381411776863741,
"median": 0.6586944976400001,
"user": 0.35609677999999995,
"system": 1.3269796,
"min": 0.64307839464,
"max": 0.6872799126400001,
"times": [
0.6872799126400001,
0.6516521166400001,
0.6550121886400001,
0.6675205516400001,
0.6546885946400001,
0.64307839464,
0.6780141376400001,
0.6623768066400001,
0.64805324364,
0.6676239386400001
]
},
{
"command": "pacquet@main",
"mean": 0.6938061735400002,
"stddev": 0.05593772534191566,
"median": 0.66508731714,
"user": 0.35963697999999994,
"system": 1.3510296,
"min": 0.6540645396400001,
"max": 0.8296136966400001,
"times": [
0.6664070646400001,
0.6555962396400001,
0.6632898996400001,
0.66376756964,
0.6895159326400001,
0.6540645396400001,
0.6603602516400001,
0.7458359116400001,
0.7096106296400001,
0.8296136966400001
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.33626900024,
"stddev": 0.045101944171487565,
"median": 2.34129289074,
"user": 3.83508846,
"system": 3.04623866,
"min": 2.2337683807400004,
"max": 2.40226195274,
"times": [
2.3259062847400003,
2.29946182874,
2.40226195274,
2.3383262477400004,
2.33927018474,
2.3705188727400004,
2.2337683807400004,
2.34984948474,
2.34331559674,
2.3600111687400003
]
},
{
"command": "pacquet@main",
"mean": 2.36730146104,
"stddev": 0.012876470963525618,
"median": 2.36936424574,
"user": 3.8649967599999995,
"system": 3.0645063599999998,
"min": 2.3391888637400005,
"max": 2.3808122697400003,
"times": [
2.3808122697400003,
2.3798633017400004,
2.36955436674,
2.3391888637400005,
2.36917412474,
2.37212177974,
2.35642662474,
2.36740053274,
2.3793357307400003,
2.35913701574
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.5592468810999998,
"stddev": 0.016605008221568417,
"median": 1.5638224313,
"user": 1.7267913,
"system": 1.87712482,
"min": 1.5276951133,
"max": 1.5795533963000001,
"times": [
1.5596264013,
1.5330878943,
1.5276951133,
1.5644152383,
1.5717327203,
1.5795533963000001,
1.5633159233,
1.5568725883,
1.5718405963,
1.5643289393
]
},
{
"command": "pacquet@main",
"mean": 1.5686490019,
"stddev": 0.05860017824360079,
"median": 1.5512141332999998,
"user": 1.7355369999999997,
"system": 1.87350722,
"min": 1.5303166532999999,
"max": 1.7254497173,
"times": [
1.5575920402999999,
1.5448362263,
1.5303166532999999,
1.7254497173,
1.5314184222999998,
1.5588678793,
1.5356862163,
1.5651512663,
1.5404778263,
1.5966937713
]
}
]
} |
|
| Branch | pr/12056 |
| Testbed | pacquet |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 2,336.27 ms(-1.64%)Baseline: 2,375.33 ms | 2,850.40 ms (81.96%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,559.25 ms(+4.64%)Baseline: 1,490.05 ms | 1,788.06 ms (87.20%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 2,109.55 ms(-0.25%)Baseline: 2,114.83 ms | 2,537.80 ms (83.13%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 661.53 ms(-3.89%)Baseline: 688.31 ms | 825.97 ms (80.09%) |
|
Congrats on merging your first pull request! 🎉🎉🎉 |
Fixes #11887. Staged publishes now have a signal in the packument: `approver`. If this is set, the package is more trustworthy than a "trusted publisher" package, since it requires 2FA publish approvals. ## Changes **pnpm (TypeScript)** - `getTrustEvidence` recognizes `_npmUser.approver` and classifies it as a new `stagedPublish` trust evidence, ranked above `trustedPublisher` and `provenance`. - Trust-downgrade detection treats `stagedPublish` as the strongest rank, and the resolution verifier's PII-minimizing metadata projection retains the approver *signal* (without keeping the approver's name/email). **pacquet (Rust port)** - Ported the same staged-publish support: an `Approver` registry type, a `StagedPublish` trust evidence (rank 3 — above `TrustedPublisher`/`Provenance`), detection, pretty-printing, and the PII-stripping trust-meta projection. - Wired `trustPolicy='no-downgrade'` enforcement into the **resolver-time** path, not just the lockfile verifier. Previously pacquet only re-checked entries already in `pnpm-lock.yaml`; fresh resolutions weren't gated. The npm resolver now runs `fail_if_trust_downgraded` on each freshly picked version (full metadata is already forced under this policy), mirroring pnpm's resolver-time `failIfTrustDowngraded` call. - Ported the matching `trustChecks` tests for full parity with the TypeScript suite (staged-publish classification/downgrade, plus previously-unported `trustedPublisher → none`, no-evidence-anywhere, and exclude + missing-time cases). --------- Co-authored-by: Zoltan Kochan <[email protected]>
pnpm added a registry signal for staged publishes ([pnpm#12056](pnpm/pnpm#12056)): the packument's `_npmUser.approver` field is set when a package version was promoted out of staging via the 2FA-gated approve step. pnpm now ranks this as the highest trust evidence (above trustedPublisher and provenance) because it adds a human approval gate on top of the OIDC publisher identity. socket-lib's `getTrustStatus()` already reserved the `stagedPublish` boolean on `TrustStatus` and the trust ladder already gave it level 3 (highest) — only the detection was stubbed pending an actual registry signal. Wires the detection to read `_npmUser.approver` matching pnpm's shape. Existing trustedPublisher and provenance detection unchanged. Used by the cascading publish.mts to error on `--direct` when prior versions of the package were staged-published (downgrading erases the package's trust history). No consumer behavior change for packages that never used staging.
pnpm added a registry signal for staged publishes ([pnpm#12056](pnpm/pnpm#12056)): the packument's `_npmUser.approver` field is set when a package version was promoted out of staging via the 2FA-gated approve step. pnpm now ranks this as the highest trust evidence (above trustedPublisher and provenance) because it adds a human approval gate on top of the OIDC publisher identity. socket-lib's `getTrustStatus()` already reserved the `stagedPublish` boolean on `TrustStatus` and the trust ladder already gave it level 3 (highest) — only the detection was stubbed pending an actual registry signal. Wires the detection to read `_npmUser.approver` matching pnpm's shape. Existing trustedPublisher and provenance detection unchanged. Used by the cascading publish.mts to error on `--direct` when prior versions of the package were staged-published (downgrading erases the package's trust history). No consumer behavior change for packages that never used staging.
|
Is this severity too low to be backported to 10.x? |
Fixes #11887.
Staged publishes now have a signal in the packument:
approver.If this is set, the package is more trustworthy than a "trusted publisher" package, since it requires 2FA publish approvals.
Changes
pnpm (TypeScript)
getTrustEvidencerecognizes_npmUser.approverand classifies it as a newstagedPublishtrust evidence, ranked abovetrustedPublisherandprovenance.stagedPublishas the strongest rank, and the resolution verifier's PII-minimizing metadata projection retains the approver signal (without keeping the approver's name/email).pacquet (Rust port)
Approverregistry type, aStagedPublishtrust evidence (rank 3 — aboveTrustedPublisher/Provenance), detection, pretty-printing, and the PII-stripping trust-meta projection.trustPolicy='no-downgrade'enforcement into the resolver-time path, not just the lockfile verifier. Previously pacquet only re-checked entries already inpnpm-lock.yaml; fresh resolutions weren't gated. The npm resolver now runsfail_if_trust_downgradedon each freshly picked version (full metadata is already forced under this policy), mirroring pnpm's resolver-timefailIfTrustDowngradedcall.trustCheckstests for full parity with the TypeScript suite (staged-publish classification/downgrade, plus previously-unportedtrustedPublisher → none, no-evidence-anywhere, and exclude + missing-time cases).Summary by CodeRabbit
New Features
Tests
Description updated by an agent (Claude Code, claude-opus-4-8) to cover the pacquet port and resolver-time enforcement added to this PR.