Skip to content

Hosted pnpm scan refuses with redirect_pnpm_lockfile_disabled over env lockfile settings the project's pnpm major ignores (e.g. any pnpm 11/12 project run through npm exec with package-lock=false) #1460

Closed

Description

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

Summary

The #1074 fix (e8b6adc, PR #1431) reads pnpm's lockfile setting from the file the project's pnpm major reads: YAML on pnpm 11+, .npmrc on pnpm ≤10. The environment fallback doesn't do this. lockfile_disabled always accepts both pnpm_config_lockfile and npm_config_lockfile, and falls back to npm_config_package_lock, whatever the major. Real pnpm reads only one family of variables:

variable =false pnpm 9.15.9 pnpm 10.34.6 pnpm 11.28.3 pnpm 12.11.2
npm_config_lockfile — lock off lock used lock used
pnpm_config_lockfile lock used lock used lock off lock off
npm_config_package_lock (= empty, npm's spelling of false) — — lock used lock used

(The "lock used" cells were checked by running pnpm install with the variable set and seeing whether pnpm-lock.yaml was written. Every cell where socket-patch refuses falls under "lock used", apart from the two "off" controls listed below.)

So hosted mode leaves the lock unpinned and warns redirect_pnpm_lockfile_disabled for projects whose pnpm installs from the lock just fine. The refusal fires when it shouldn't.

The realistic trigger needs no CI variable at all. npm exec / npx / npm run export npm_config_package_lock= whenever a project or user .npmrc has package-lock=false, which many repos migrated from npm still carry and which pnpm 11+ ignores. Running npx @socketsecurity/socket-patch scan --mode hosted in such a pnpm 11/12 project gets refused. A direct run of the same binary on the same tree pins it.

Impact

Hosted mode silently does nothing for these projects (exit 0, status: success). The warning tells the user that pnpm ignores their lock and suggests --mode vendored or "remove the setting", which is false for their pnpm. The result also depends on how the CLI was launched (npx vs direct), not on the project.

Repro (Linux, main 15c2c83, pnpm 12.11.2, local mock of the patch API serving left-pad@1.3.0, same contract as tests/e2e_redirect_pnpm_build.rs)

mkdir app && cd app
echo '{"name":"app","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json
echo 'package-lock=false' > .npmrc        # ignored by pnpm 11/12
pnpm install                              # pnpm 12.11.2 writes pnpm-lock.yaml (lockfileVersion 9.0)
A="--api-url http://127.0.0.1:8765 --org test-org --api-token fake"
npm exec -c "socket-patch scan --mode hosted --json --yes $A"
#  exit 0, warnings: [redirect_pnpm_lockfile_disabled]
#  detail: "`npm_config_package_lock=` in the environment makes pnpm neither read nor write pnpm-lock.yaml ..."
#  pnpm-lock.yaml unchanged
socket-patch scan --mode hosted --json --yes $A
#  exit 0, warnings: [redirect_pnpm_trust_lockfile], lock pinned to the hosted tarball
rm -rf node_modules
npm_config_package_lock= pnpm install --frozen-lockfile --store-dir /tmp/fresh
head -1 node_modules/left-pad/index.js    # /* SOCKET-PATCHED */ : pnpm 12 honours the lock with that env set

The minimal form is npm_config_lockfile=false socket-patch scan --mode hosted ... on any pnpm 11/12 project, or pnpm_config_lockfile=false ... on any pnpm 9/10 project.

Expected vs actual

OS × version

cell result
Linux, pnpm 12.11.2, npm_config_package_lock= (direct and via npm exec + .npmrc package-lock=false) false refusal (2× each)
Linux, pnpm 12.11.2, npm_config_lockfile=false false refusal
Linux, pnpm 11.28.3, npm_config_lockfile=false false refusal
Linux, pnpm 10.34.6, pnpm_config_lockfile=false false refusal
Linux, pnpm 9.15.9, pnpm_config_lockfile=false false refusal
Linux, pnpm 11.28.3, pnpm_config_lockfile=false correct refusal (control)
Linux, pnpm 10.34.6, npm_config_lockfile=false correct refusal (control)
Linux, pnpm 12.11.2, no env pins (control)

macOS and Windows weren't probed. The logic is OS-independent.

First bad commit

e8b6adc (#1431). Release 5.0.0 predates the refusal.

Suspect code

  • crates/socket-patch-core/src/utils/pnpm_workspace.rs:600-603: the env arm of lockfile_disabled runs env_setting before the major is known and returns it via .or(env) regardless of major.
  • crates/socket-patch-core/src/utils/pnpm_workspace.rs:128 (env_setting) tries pnpm_config_<key> then npm_config_<key> for every caller. view_setting (:145) uses it the same way for gitBranchLockfile / sharedWorkspaceLockfile, so those env lookups probably have the same major blindness. I haven't verified that.

Not a VEX issue: lock-only vex calls lockfile_disabled(view, false).

Activity

  1. mikolalysenko commented on Oct 13, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] The view_setting suspect is now verified for gitBranchLockfile (main 15c2c83, pnpm 12.11.2, Linux).

    pnpm 12 ignores npm_config_git_branch_lockfile=true: pnpm install --lockfile-only on branch feat writes only pnpm-lock.yaml and never pnpm-lock.feat.yaml. pnpm_config_git_branch_lockfile=true does create the branch lock. Yet with a leftover pnpm-lock.feat.yaml in the tree, npm_config_git_branch_lockfile=true socket-patch scan --mode hosted refuses with redirect_pnpm_git_branch_lockfile and leaves the lock unpinned, the same as the pnpm_config_ spelling does. Same root cause: env_setting accepts both prefixes on every pnpm major. The fix should probably cover view_setting's callers (gitBranchLockfile, sharedWorkspaceLockfile) along with lockfile_disabled.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 13, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: env_setting accepts both pnpm_config_* and npm_config_* on every pnpm major, for lockfile/package-lock and for view_setting's gitBranchLockfile/sharedWorkspaceLockfile). Triaged p1 (pnpm). Branch: agent/fix-pnpm-env-setting-major. Claim-ID: 2026-10-13T01:21:07Z-c7ddda


    Generated by Claude Code

  3. mikolalysenko commented on Oct 13, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft fix PR: #1461


    Generated by Claude Code

  4. added 2 commits that reference this issue on Oct 13, 2026
    38cf7a9
    c782837
  5. mikolalysenko commented on Oct 13, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Verified fixed on main 4353a7c (includes #1461), Linux, real pnpm 9.15.9 / 10.34.6 / 11.28.3 / 12.11.2, with node_modules/.modules.yaml present. For each major I set each of npm_config_lockfile, pnpm_config_lockfile, npm_config_package_lock and pnpm_config_package_lock to false. I then compared real pnpm (does pnpm install write a lock?) with scan --mode hosted:

    pnpm npm_config_* (lockfile / package_lock) pnpm_config_* (lockfile / package_lock)
    9.15.9, 10.34.6 pnpm honours it → refusal redirect_pnpm_lockfile_disabled ✔ pnpm ignores it → pinned ✔
    11.28.3, 12.11.2 pnpm ignores it → pinned ✔ pnpm honours it → refusal ✔

    All 16 cells match. One remaining case is by design: in a lockfile-only checkout with no packageManager and no two-document lock, the major is unknown, so both families are still read. That matches the "both only when the major is unknown" rule in this issue's Expected section. A real pnpm 11/12 install with a packageManager or devEngines.packageManager pin writes the two-document lock, and the major is then detected correctly.


    Generated by Claude Code

  6. added a commit that references this issue on Oct 13, 2026
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