[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
scan --mode vendored over a hosted pnpm pin restores the pin to its upstream registry entry first, and only then runs the pnpm vendored backend. When the backend then refuses the package for a project-level or lock-shape reason that hosted mode handles fine, the package ends up in neither mode. The hosted pin is gone from pnpm-lock.yaml, nothing is vendored, and the next install gets the unpatched registry bytes.
Three ordinary shapes trigger it, and they're all documented vendored refusals:
- a pnpm catalog dependency (
"left-pad": "catalog:") → vendor_lock_entry_unsupported
- a CRLF
pnpm-lock.yaml (a core.autocrlf checkout; hosted mode handles CRLF) → vendor_lockfile_crlf_unsupported
- a user exact-pin override in
pnpm-workspace.yaml (overrides: { left-pad: 1.3.0 }) → vendor_override_conflict
The run does exit 1 / partial_failure, but the restore has already been written, so the project has lost its patch.
Impact
A project that is correctly hosted-patched and asks to switch to vendored loses its patch. Its committed lock now installs the vulnerable upstream release. Re-running hosted mode recovers, but nothing in the output says the package was un-hosted. The only hint is the vendor_takeover_reverted_redirect advisory next to the failure.
Repro (Linux, Node 22, main 4646693)
I used a local mock patch API that serves left-pad@1.3.0 with a marker prepended to index.js (hosted tarball, vendor grant, view, /registry mirror; SOCKET_PATCH_SERVER_URL / SOCKET_NPM_REGISTRY point at it).
API="--api-url http://127.0.0.1:8787 --org acme --api-token x"
mkdir p && cd p
echo '{"name":"c","version":"1.0.0","dependencies":{"left-pad":"catalog:"}}' > package.json
printf 'packages:\n - .\ncatalog:\n left-pad: 1.3.0\n' > pnpm-workspace.yaml
pnpm install
socket-patch scan --mode hosted --json --yes $API # success, redirected 1; lock pins the hosted tarball
socket-patch scan --mode vendored --dry-run --json --yes $API # exit 0, action would_vendor, no refusal predicted
socket-patch scan --mode vendored --json --yes $API # exit 1, partial_failure:
# vendor_takeover_reverted_redirect (hosted pin restored to registry)
# failed vendor_lock_entry_unsupported (catalog)
grep -c 127.0.0.1 pnpm-lock.yaml # 0: the hosted pin is gone
ls .socket/vendor 2>/dev/null # nothing vendored
rm -rf node_modules && pnpm install --frozen-lockfile # exit 0, unpatched upstream left-pad
For the CRLF variant, use a plain "left-pad": "1.3.0" dependency and run sed -i 's/$/\r/' pnpm-lock.yaml after the hosted scan. For the override variant, use a plain dependency plus overrides:\n left-pad: 1.3.0 in pnpm-workspace.yaml.
Expected vs actual
Matrix (scan exit / hosted pins left in the lock / fresh pnpm install --frozen-lockfile)
| pnpm |
catalog |
CRLF lock |
workspace exact-pin override |
control (plain dep) |
| 9.15.9 |
1 / 0 / unpatched |
1 / 0 / unpatched |
1 / 0 / unpatched |
not run |
| 10.34.5 |
1 / 0 / unpatched |
1 / 0 / unpatched |
1 / 0 / unpatched |
not run |
| 11.28.3 |
1 / 0 / unpatched |
1 / 0 / unpatched |
1 / 0 / unpatched |
not run |
| 12.8.1 |
1 / 0 / unpatched |
1 / 0 / unpatched |
1 / 0 / unpatched |
0 / 0 / patched (vendored) |
Every failing cell was reproduced at least twice (12.8.1 and 10.34.5 three times). I tested Linux only. The ordering is OS-independent, and the CRLF variant is the one Windows autocrlf users would hit.
Not a regression: release 4.0.0 behaves the same (catalog variant, pnpm 12.8.1: exit 1, vendor_lock_entry_unsupported after vendor_takeover_reverted_redirect, hosted pin gone).
Suspect code
[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
scan --mode vendoredover a hosted pnpm pin restores the pin to its upstream registry entry first, and only then runs the pnpm vendored backend. When the backend then refuses the package for a project-level or lock-shape reason that hosted mode handles fine, the package ends up in neither mode. The hosted pin is gone frompnpm-lock.yaml, nothing is vendored, and the next install gets the unpatched registry bytes.Three ordinary shapes trigger it, and they're all documented vendored refusals:
"left-pad": "catalog:") →vendor_lock_entry_unsupportedpnpm-lock.yaml(acore.autocrlfcheckout; hosted mode handles CRLF) →vendor_lockfile_crlf_unsupportedpnpm-workspace.yaml(overrides: { left-pad: 1.3.0 }) →vendor_override_conflictThe run does exit 1 /
partial_failure, but the restore has already been written, so the project has lost its patch.Impact
A project that is correctly hosted-patched and asks to switch to vendored loses its patch. Its committed lock now installs the vulnerable upstream release. Re-running hosted mode recovers, but nothing in the output says the package was un-hosted. The only hint is the
vendor_takeover_reverted_redirectadvisory next to the failure.Repro (Linux, Node 22, main
4646693)I used a local mock patch API that serves
left-pad@1.3.0with a marker prepended toindex.js(hosted tarball, vendor grant, view,/registrymirror;SOCKET_PATCH_SERVER_URL/SOCKET_NPM_REGISTRYpoint at it).For the CRLF variant, use a plain
"left-pad": "1.3.0"dependency and runsed -i 's/$/\r/' pnpm-lock.yamlafter the hosted scan. For the override variant, use a plain dependency plusoverrides:\n left-pad: 1.3.0inpnpm-workspace.yaml.Expected vs actual
failed <code>with the hosted wiring and active … lock byte-untouched (exit 1 /partial_failure): the package stays hosted-patched instead of being un-hosted and then refused". That's implemented for Bun (bun_preflight), yarn berry and npm package-lock (npm lockfileVersion 1: scan/get --mode vendored un-host a hosted patch and then refuse to vendor it, so the project silently goes back to unpatched (vendor eject rolls back correctly) #659).--dry-runshould preview the samefailed <code>.--dry-runpredictswould_vendor, exit 0.Matrix (scan exit / hosted pins left in the lock / fresh
pnpm install --frozen-lockfile)Every failing cell was reproduced at least twice (12.8.1 and 10.34.5 three times). I tested Linux only. The ordering is OS-independent, and the CRLF variant is the one Windows
autocrlfusers would hit.Not a regression: release 4.0.0 behaves the same (catalog variant, pnpm 12.8.1: exit 1,
vendor_lock_entry_unsupportedaftervendor_takeover_reverted_redirect, hosted pin gone).Suspect code
crates/socket-patch-cli/src/commands/vendor.rs:2517-2576: forpkg:npm/candidates the pre-restore gate runs onlyyarn_berry_vendor_preflight,npm_lock_vendor_preflightandyarn_berry_vendor_target_preflight, and then callsrestore_upstream(:2576). There is no pnpm equivalent for the pnpm backend's project and entry refusals: the CRLF lock / workspace check, the catalog and peer-suffixedvendor_lock_entry_unsupported, thevendor_override_conflictchecks incrates/socket-patch-core/src/vendor/pnpm_lock.rs:617-625(classify_pkg_override/check_lock_override/check_workspace_override), and the legacy-lockvendor_lockfile_version_unsupported.vendor_workspace_member) and Gem hosted → vendored takeover un-hosts a gem declared inside agroupblock and then refuses to vendor it (gemfile_declaration_not_editable), so the project silently goes back to unpatched #775 (gem).