Skip to content

Hosted scan with pnpm gitBranchLockfile pins the stale pnpm-lock.yaml and reports success, while pnpm installs unpatched bytes from pnpm-lock.<branch>.yaml #556

Description

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

Summary

pnpm's gitBranchLockfile setting (git-branch-lockfile=true in .npmrc on pnpm ≤10, gitBranchLockfile: true in pnpm-workspace.yaml) makes pnpm read and write pnpm-lock.<branch>.yaml instead of pnpm-lock.yaml. Hosted mode only looks at files named exactly pnpm-lock.yaml / shrinkwrap.yaml. That causes two failures:

  1. Both locks present (the usual case: pnpm-lock.yaml committed on main, and a branch lock written once the feature branch changes deps): scan --mode hosted pins the stale pnpm-lock.yaml and reports status: success, redirected: 1. pnpm installs from pnpm-lock.feature.yaml, which still points at the registry, so a fresh pnpm install --frozen-lockfile silently installs the unpatched upstream bytes. vex honestly declines (not_applied), so it's the only signal.
  2. Only a branch lock present: the scan exits 0 with redirected: 0 and the warning redirect_pnpm_no_lockfile, whose remedy ("run pnpm install to generate one") cannot work, because pnpm will just rewrite the branch lock.

Impact

On a feature branch with this setting, the user is told the patch is pinned (and the pin lands in a file they will commit), but CI and fresh checkouts on that branch install the vulnerable package. When the branch merges, pnpm's mergeGitBranchLockfiles flow can carry the unpinned branch entry over the pinned main one.

Repro (Linux, pnpm 12.8.1; local mock of the patch API serving a patched is-number@7.0.0 tarball)

mkdir app && cd app && git init -q -b main
echo '{"name":"app","version":"1.0.0","dependencies":{"is-number":"7.0.0"}}' > package.json
printf "packages:\n  - '.'\n" > pnpm-workspace.yaml; echo node_modules > .gitignore
pnpm install && git add -A && git commit -qm init && git checkout -qb feature
printf "gitBranchLockfile: true\n" >> pnpm-workspace.yaml     # pnpm 10: echo git-branch-lockfile=true > .npmrc
pnpm add is-odd@3.0.1            # writes pnpm-lock.feature.yaml; pnpm-lock.yaml stays as on main
socket-patch scan --mode hosted --json --yes --api-url $MOCK --org test-org --api-token fake
#  -> status success, redirected 1, rewrittenFiles [pnpm-lock.yaml, pnpm-workspace.yaml]
grep -c $MOCK pnpm-lock.yaml pnpm-lock.feature.yaml   # 1 / 0
# fresh checkout, empty store:
rm -rf node_modules && pnpm install --frozen-lockfile --store-dir $(mktemp -d)
head -1 node_modules/is-number/index.js    # "/*!" (upstream), not the patch marker
socket-patch vex --output v.json           # "omitting pkg:npm/is-number@7.0.0 ... (not_applied)"

Only-branch-lock variant: git init -b feature, enable the setting before the first pnpm install. Hosted scan then gives redirected 0 plus redirect_pnpm_no_lockfile.

Expected vs actual

  • Expected: CLI_CONTRACT and docs/ecosystems.md say hosted mode pins the lock the package manager actually installs from, or refuses loudly when it can't. So hosted mode should either rewrite pnpm-lock.<current-branch>.yaml (and leave the stale lock alone, or pin both), or refuse with a dedicated warning code that names the setting.
  • Actual: it pins a file pnpm ignores on this branch, and reports success. With only the branch lock, it gives a misleading "no lockfile" remedy.

Matrix (Linux; each cell run twice)

pnpm branch lock written both-locks case only-branch-lock case
9.15.9 no (this fixture's .npmrc setting produced no branch lock) n/a: pinned pnpm-lock.yaml is the one used, patched n/a
10.34.5 yes fail: success, fresh frozen install unpatched not run
11.28.3 yes fail not run
12.8.1 yes fail fail (misleading redirect_pnpm_no_lockfile)

macOS and Windows weren't tested (no probe branch this run); the logic is filename-based, so they're likely the same. Vendored mode with this setting wasn't tested.

Suspect code

  • crates/socket-patch-core/src/formats/pnpm/hosted.rs:180 – lock_keys matches only pnpm-lock.yaml / shrinkwrap.yaml.
  • crates/socket-patch-core/src/formats/registry.rs:72 – the hosted read set lists only pnpm-lock.yaml.
  • crates/socket-patch-core/src/patch/redirect/mod.rs:806 – redirect_pnpm_no_lockfile fires without considering pnpm-lock.*.yaml.

Tested on main 61cfb9b (CLI 4.0.0).

Activity

  1. mikolalysenko commented on Oct 2, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triage: priority:p1 (pnpm). Hosted lock discovery only considers pnpm-lock.yaml/shrinkwrap.yaml and never consults pnpm's gitBranchLockfile setting, so the pin lands in a lock pnpm ignores on this branch. Distinct root cause from other open pnpm issues; no open PR covers it.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 2, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] More from the pnpm bug-hunt routine (ledger #303): vendored mode has the same lock-selection gap. Tested on main 203e092, Linux, pnpm 10.34.5 / 11.28.3 / 12.8.1, and it reproduced 2/2 on each.

    Setup: main commits pnpm-lock.yaml. On feature, gitBranchLockfile: true is added to pnpm-workspace.yaml and an unpatched dep (ms@2.1.3) is added, so pnpm writes pnpm-lock.feature.yaml.

    Running scan --mode vendored --json --yes on feature:

    • exits 0 with status: success (applied + vendor_prebuilt_downloaded) and no warnings.
    • writes the file:.socket/vendor/npm/<uuid>/is-number-7.0.0.tgz override into package.json pnpm.overrides and pnpm-workspace.yaml overrides:, and rewires the stale pnpm-lock.yaml.
    • leaves pnpm-lock.feature.yaml, the lock pnpm actually uses on this branch, untouched (0 Socket references).

    Result on a fresh checkout of the branch:

    install 10.34.5 11.28.3 12.8.1
    pnpm install --frozen-lockfile fails: "The current "overrides" configuration doesn't match the value found in the lockfile" same same
    pnpm install (non-frozen) patched (pnpm re-resolves the override into the branch lock) — patched

    So a "successful" vendor breaks every frozen install (CI) on the branch. Vendored mode needs to select the active branch lock as well. Alternatively, while gitBranchLockfile is on, both modes could refuse with a diagnostic that names it.

    Separately, when the branch lock contains a patched dep that pnpm-lock.yaml lacks, vendored fails loudly ("pnpm-lock.yaml has no packages entry for left-pad@1.3.0", partial_failure). That message also points at the wrong lock.


    Generated by Claude Code

  3. mikolalysenko commented on Oct 7, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue as part of the pnpm open-issue sweep in draft PR #1007. Branch: agent/fix-pnpm-open-issues. Claim-ID: 2026-10-07T12:44:29Z-pnpm07

  4. mikolalysenko commented on Oct 7, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] PR #1007 fixes this issue. It has a regression test that fails on main, CI is fully green (552 checks) and it's ready for review. The issue will close when #1007 merges. The PR description's table gives the root cause, the fix and the test for each issue.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions