Skip to content

Hosted→vendored takeover leaves trustLockfile: true in pnpm-workspace.yaml, and vendor --revert never removes it #401

Description

[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).

Summary

On a pnpm 9.0-lock project, scan --mode hosted adds trustLockfile: true to pnpm-workspace.yaml, ledger kind redirect_pnpm_workspace_trust. When the project is then moved to vendored mode (vendor or scan --mode vendored), the takeover reverts the hosted lock entry and drops its redirect record, but leaves the trust line and its ledger edit in place. The redirect ledger ends up holding one redirect_pnpm_workspace_trust edit and no records. A later vendor --revert removes the vendored wiring and leaves the project with no Socket wiring at all, but trustLockfile: true is still in pnpm-workspace.yaml.

npm's equivalent setting is handled. revert_redirect_purl unwinds the .npmrc allow-remote=all auto-config "LAST ONE OUT" in the same transaction, and the code comment says it does so precisely so that "the vendored takeover then leave[s] no loosened install policy behind". The pnpm trust edit gets no such treatment.

Impact

trustLockfile: true disables pnpm ≥11's registry re-verification for the whole lockfile (docs/ecosystems.md: "This skips registry re-verification for the whole lock"). It's only justified while hosted URLs are in the lock. After takeover, and especially after vendor --revert, the project silently keeps a weakened supply-chain policy that no current Socket wiring needs, and no command warns about it. Only a later whole-ledger rollback removes it, and a user who has already reverted the vendored state has no reason to run one.

Repro (pnpm 11 or 12, root 9.0 lock; patch API mocked as in e2e_redirect_pnpm_build.rs, manifest and blobs staged for vendor)

echo '{"name":"proj","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json
printf "packages:\n  - '.'\n" > pnpm-workspace.yaml
pnpm install
socket-patch scan --mode hosted --yes --json --api-url $MOCK --org test-org --api-token fake
#   pnpm-workspace.yaml gains `trustLockfile: true`
socket-patch vendor --yes --json --offline          # hosted -> vendored takeover, exit 0, applied
cat pnpm-workspace.yaml                              # packages / trustLockfile: true / overrides: ...
grep -c 127.0.0.1:18731 pnpm-lock.yaml              # 0: no hosted URL left
jq '.edits,.records' .socket/vendor/redirect-state.json
#   [{"kind":"redirect_pnpm_workspace_trust","action":"added",...}]  {}
socket-patch vendor --revert --yes --json --offline  # exit 0
cat pnpm-workspace.yaml
#   packages:
#     - '.'
#   trustLockfile: true      <- still there, nothing needs it

scan --mode vendored as the takeover driver behaves the same.

Expected vs actual

  • Expected: the takeover leaves the project "FULLY in vendored mode" (module doc, crates/socket-patch-core/src/patch/redirect/takeover.rs:1-5), and superseded redirect edits don't "survive forever" as a stale ledger (same doc). The npm .npmrc setting already gets this last-one-out unwind. When the takeover drops the last pnpm hosted record, the redirect_pnpm_workspace_trust edit should be unwound in the same transaction (its inverse Inverse::PnpmTrust already exists in replay.rs). A user-set trustLockfile must stay untouched.
  • Actual: the trust line and an orphan ledger edit survive the takeover and vendor --revert.

OS × version (Linux, Node 22)

pnpm hosted → vendor hosted → scan --mode vendored then vendor --revert control: scoped rollback <purl> after hosted only
11.27.0 trust left (3 runs) trust left (2 runs) trust left restored byte-exact (2 runs)
12.8.1 trust left not run trust left restored byte-exact (2 runs)

Fresh frozen installs stay patched at every step, so nothing visibly breaks; the defect is the leftover policy loosening. Tested on main f6b7fb9, and released 4.0.0 behaves the same (pnpm 11.27.0). The logic is OS-independent string surgery, so macOS and Windows weren't probed.

Suspect code

  • crates/socket-patch-core/src/patch/redirect/takeover.rs:1069-1096: the "LAST ONE OUT" block handles only .npmrc (npmrc_unwind_due, crates/socket-patch-core/src/patch/redirect/npmrc.rs:748). There's no pnpm counterpart for redirect_pnpm_workspace_trust when the last pnpm-lock record is dropped.
  • crates/socket-patch-core/src/patch/redirect/replay.rs:131 already maps the kind to Inverse::PnpmTrust for whole-ledger replay, which is why bare rollback does clean it up.

Activity

  1. mikolalysenko commented on Oct 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged: priority:p1 (pnpm). Not a duplicate, and I found no fix PR. On main 2463257 (after #277), patch/redirect/takeover.rs still has no last-one-out unwind for redirect_pnpm_workspace_trust: git grep finds the kind only in state.rs. The cause is separate from #400 and #402. Those are about the splice corrupting the file; this one is the takeover never reverting the edit.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Re-triage from the pnpm bug-hunt routine (ledger #303): main 2463257 (#277, the v5 consolidation) resolves this one. Closing.

    v5 keeps no redirect ledger, so the orphan redirect_pnpm_workspace_trust edit is gone. The leftover trust line is no longer silent either. CLI_CONTRACT.md now defines this behaviour (upstream restore, npm family, and the pnpm_trust_lockfile_left row):

    • If pnpm-workspace.yaml is exactly the scaffold hosted mode creates, it's deleted once pnpm-lock.yaml is no longer hosted.
    • Otherwise a remaining trustLockfile: true is left in place and reported as the pnpm_trust_lockfile_left advisory, on both the takeover and rollback/remove.

    Evidence on Linux with pnpm 11.27.0 and 12.8.1, a packages: ['packages/*'] workspace, and a mock patch API plus registry mirror (SOCKET_PATCH_SERVER_URL, SOCKET_NPM_REGISTRY):

    11.27.0 hosted->vendor           exit=0 trust=1 hostedURLs=0 warn=pnpm_trust_lockfile_left
    11.27.0 hosted->scan --mode vendored exit=0 trust=1 hostedURLs=0 warn=pnpm_trust_lockfile_left
    12.8.1  hosted->vendor           exit=0 trust=1 hostedURLs=0 warn=pnpm_trust_lockfile_left
    12.8.1  hosted->scan --mode vendored exit=0 trust=1 hostedURLs=0 warn=pnpm_trust_lockfile_left
    

    With the original packages: ['.'] repro, the file equals the scaffold after the hosted write, so the takeover deletes it. A later vendor --revert warns nothing because there's no trust line left. Fresh frozen installs are patched at every step.

    One small leftover, which I haven't filed: a user-authored packages:\n - '.'\n file becomes byte-identical to the scaffold once hosted mode appends the trust line. Rollback then deletes the user's file, which the contract's "exactly the scaffold" rule allows. A pnpm single-package project installs the same without it.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions