Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: support staged publishes in trust scale#12056

Merged
zkochan merged 14 commits into
pnpm:mainfrom
43081j:trust-scale-staging
May 29, 2026
Merged

feat: support staged publishes in trust scale#12056
zkochan merged 14 commits into
pnpm:mainfrom
43081j:trust-scale-staging

Conversation

@43081j

@43081j 43081j commented May 29, 2026

Copy link
Copy Markdown
Contributor

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).

Summary by CodeRabbit

  • New Features

    • Introduced "staged publish" as a new, highest-ranked package trust evidence; downgrade detection now treats it specially
    • Registry metadata now recognizes an approver field and trust evidence labels render as "staged publish"
  • Tests

    • Added tests verifying staged publish classification and detecting high-risk trust downgrades involving staged publish

Review Change Stack


Description updated by an agent (Claude Code, claude-opus-4-8) to cover the pacquet port and resolver-time enforcement added to this PR.

@43081j 43081j requested a review from zkochan as a code owner May 29, 2026 08:07
Copilot AI review requested due to automatic review settings May 29, 2026 08:07
@welcome

welcome Bot commented May 29, 2026

Copy link
Copy Markdown

💖 Thanks for opening this pull request! 💖
Please be patient and we will get back to you as soon as we can.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Support staged publishes in npm package trust scale

✨ Enhancement

Grey Divider

Walkthroughs

Description
• 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
Diagram
flowchart 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

Loading

Grey Divider

File Changes

1. resolving/npm-resolver/src/trustChecks.ts ✨ Enhancement +9/-1

Implement staged publish trust detection and ranking

• Add stagedPublish to TrustEvidence type union
• Assign highest trust rank (3) to staged publishes in TRUST_RANK constant
• Detect staged publishes by checking manifest._npmUser?.approver field
• Return staged publish immediately when detected in detectStrongestTrustEvidenceBeforeDate
• Add pretty-print case for staged publish evidence

resolving/npm-resolver/src/trustChecks.ts


2. resolving/npm-resolver/test/trustChecks.test.ts 🧪 Tests +75/-0

Add staged publish trust evidence tests

• Add test case for getTrustEvidence returning stagedPublish when approver exists
• Add comprehensive test for trust downgrade detection from staged publish to trusted publisher
• Test validates that downgrading from staged publish (rank 3) to trusted publisher (rank 2) throws
 error

resolving/npm-resolver/test/trustChecks.test.ts


3. resolving/registry/types/src/index.ts ✨ Enhancement +4/-0

Add approver field to package registry types

• Extend _npmUser interface with optional approver field
• approver contains name and optional email properties
• Enables detection of staged publish packages in registry metadata

resolving/registry/types/src/index.ts


Grey Divider

Qodo Logo

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 stagedPublish as a new TrustEvidence level ranked above trustedPublisher and provenance.
  • 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.

Comment thread resolving/registry/types/src/index.ts
Comment thread resolving/npm-resolver/test/trustChecks.test.ts
Comment thread resolving/npm-resolver/test/trustChecks.test.ts
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds a new trust evidence value, stagedPublish, detected from manifest._npmUser?.approver, ranks it highest in trust ordering, updates pretty-printing to show “staged publish”, and adds tests validating detection and downgrade behavior.

Changes

Staged Publish Trust Evidence

Layer / File(s) Summary
Schema and trust evidence type definition
resolving/registry/types/src/index.ts, resolving/npm-resolver/src/trustChecks.ts, resolving/npm-resolver/src/createNpmResolutionVerifier.ts
PackageInRegistry gains optional approver?: { name: string; email?: string }. TrustEvidence adds 'stagedPublish' and TRUST_RANK places it above existing evidence types. _npmUser projection now conditionally includes both approver and trustedPublisher for verifier manifests.
Staged publish detection and formatting
resolving/npm-resolver/src/trustChecks.ts
getTrustEvidence checks manifest._npmUser?.approver and returns 'stagedPublish' before trustedPublisher/provenance checks. prettyPrintTrustEvidence formats stagedPublish as “staged publish”. Strongest-evidence detection short-circuits to return stagedPublish when ranked highest.
Test coverage for detection and downgrade validation
resolving/npm-resolver/test/trustChecks.test.ts
Adds tests asserting getTrustEvidence returns stagedPublish for manifests with _npmUser.approver and that downgrading from stagedPublish to trustedPublisher triggers a High-risk trust downgrade error.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • pnpm/pnpm#11911: Both PRs modify trustChecks.ts trust-evidence classification and ranking, affecting downgrade behavior and trustedPublisher handling.

Suggested reviewers

  • zkochan

Poem

🐰 I sniffed the manifest, ears all a-twitch,
Found an approver note tucked in a stitch.
"Staged publish" I cheered, gave it top place,
No more false downgrades, a tidy embrace.
Hop, hop — audits safer, peace in the trace.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat: support staged publishes in trust scale' accurately describes the main change—adding staged publish support to the trust evidence model with the new stagedPublish evidence type.
Linked Issues check ✅ Passed Changes implement all requirements from #11887: introducing stagedPublish as higher-rank evidence, detecting approver field in _npmUser, and preventing no-downgrade false positives with staged publishing.
Out of Scope Changes check ✅ Passed All changes are tightly scoped to staged publish support: new evidence type, manifest updates, trust checks, and related tests—nothing extraneous.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3cf2b86 and eca21ea.

📒 Files selected for processing (3)
  • resolving/npm-resolver/src/trustChecks.ts
  • resolving/npm-resolver/test/trustChecks.test.ts
  • resolving/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.ts
  • resolving/npm-resolver/src/trustChecks.ts
  • 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 (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.ts
  • resolving/npm-resolver/src/trustChecks.ts
  • resolving/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

Comment thread resolving/npm-resolver/src/trustChecks.ts Outdated
Copilot AI review requested due to automatic review settings May 29, 2026 08:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
resolving/npm-resolver/test/trustChecks.test.ts (2)

425-425: 💤 Low value

Inconsistent email value for clarity.

The email is '[email protected]' while the name is 'test-publisher' and this version uses trustedPublisher. 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 win

Consider adding a test case for when both approver and trustedPublisher exist.

The current test validates stagedPublish detection when only approver is present. To ensure the trust hierarchy is correctly implemented, consider adding a test that validates behavior when both _npmUser.approver and _npmUser.trustedPublisher exist 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

📥 Commits

Reviewing files that changed from the base of the PR and between eca21ea and 1aa5d45.

📒 Files selected for processing (2)
  • resolving/npm-resolver/test/trustChecks.test.ts
  • resolving/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.ts
  • 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/registry/types/src/index.ts
  • resolving/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!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

Comment thread resolving/registry/types/src/index.ts
Comment thread resolving/npm-resolver/src/trustChecks.ts
Comment thread resolving/npm-resolver/test/trustChecks.test.ts
Comment thread resolving/npm-resolver/test/trustChecks.test.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
resolving/npm-resolver/test/trustChecks.test.ts (1)

446-446: 💤 Low value

Remove 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

📥 Commits

Reviewing files that changed from the base of the PR and between 8d5b6d7 and d756cd3.

📒 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

Copilot AI review requested due to automatic review settings May 29, 2026 08:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.approver object (name/email) into the projected manifest. Since getTrustEvidence() only needs a boolean signal, store a minimal placeholder instead of the real approver details (and keep the existing trustedPublisher object as-is).
  let npmUser: PackageInRegistry['_npmUser'] = undefined;
  if (approver) {
    npmUser ||= {}
    npmUser.approver = approver
  }

Comment thread resolving/npm-resolver/src/createNpmResolutionVerifier.ts
Comment thread resolving/registry/types/src/index.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Fix _npmUser projection comment + semicolon in projectTrustManifest

  • The comment for _npmUser says it’s narrowed to only trustedPublisher, but projectTrustManifest now also preserves manifest._npmUser?.approver; since getTrustEvidence only checks the presence of _npmUser?.approver (truthiness), avoid carrying the real approver name/email through the cache/projection and update the comment accordingly.
  • 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

📥 Commits

Reviewing files that changed from the base of the PR and between d756cd3 and cc2a974.

📒 Files selected for processing (2)
  • resolving/npm-resolver/src/createNpmResolutionVerifier.ts
  • resolving/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

Comment thread resolving/npm-resolver/src/createNpmResolutionVerifier.ts Outdated
Copilot AI review requested due to automatic review settings May 29, 2026 08:53
@43081j 43081j force-pushed the trust-scale-staging branch from 5e6e726 to e5e7a7f Compare May 29, 2026 08:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

43081j added 7 commits May 29, 2026 11:45
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.
@zkochan zkochan force-pushed the trust-scale-staging branch from e5e7a7f to 438dfc9 Compare May 29, 2026 09:50
Copilot AI review requested due to automatic review settings May 29, 2026 10:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codecov-commenter

codecov-commenter commented May 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.95918% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 89.15%. Comparing base (74dd8ba) to head (84d08a4).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...package-manager/src/install_with_fresh_lockfile.rs 92.30% 1 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Integrated-Benchmark Report (Linux)

Scenario: Isolated linker: fresh restore, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.110 ± 0.190 1.967 2.607 1.03 ± 0.10
pacquet@main 2.042 ± 0.056 1.939 2.145 1.00
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

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 661.5 ± 13.8 643.1 687.3 1.00
pacquet@main 693.8 ± 55.9 654.1 829.6 1.05 ± 0.09
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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.336 ± 0.045 2.234 2.402 1.00
pacquet@main 2.367 ± 0.013 2.339 2.381 1.01 ± 0.02
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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 1.559 ± 0.017 1.528 1.580 1.00
pacquet@main 1.569 ± 0.059 1.530 1.725 1.01 ± 0.04
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
      ]
    }
  ]
}

@github-actions

github-actions Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12056
Testbedpacquet
Click to view all benchmark results
BenchmarkLatencyBenchmark 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%)
🐰 View full continuous benchmarking report in Bencher

Copilot AI review requested due to automatic review settings May 29, 2026 10:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@zkochan zkochan merged commit 1e9ab29 into pnpm:main May 29, 2026
20 checks passed
@welcome

welcome Bot commented May 29, 2026

Copy link
Copy Markdown

Congrats on merging your first pull request! 🎉🎉🎉

@43081j 43081j deleted the trust-scale-staging branch May 29, 2026 11:14
zkochan added a commit that referenced this pull request May 29, 2026
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]>
jdalton added a commit to SocketDev/socket-lib that referenced this pull request May 31, 2026
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.
jdalton added a commit to SocketDev/socket-lib that referenced this pull request May 31, 2026
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.
@tats-u

tats-u commented Jun 7, 2026

Copy link
Copy Markdown

Is this severity too low to be backported to 10.x?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

no-downgrade false positive with staged publishing

5 participants