[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
Vendored mode on a pnpm 9.0 lock is supposed to refuse a package that an npm: alias references (vendor_lock_entry_unsupported: "references … through an aliased importer version … that the pair surgery cannot rewrite — vendoring would leave a dangling reference pnpm rejects"). For an unscoped alias (lp: npm:left-pad@1.3.0) it does. For a scoped alias it doesn't. pnpm writes the importer's version quoted:
importers:
.:
dependencies:
sl:
specifier: npm:@isaacs/string-locale-compare@1.1.0
version: '@isaacs/string-locale-compare@1.1.0'
The guard compares the raw (still quoted) value against the unquoted registry key, so it never matches. The vendor surgery then renames the packages: / snapshots: entries to '@isaacs/string-locale-compare@file:.socket/vendor/…' and leaves the importer's version: '@isaacs/string-locale-compare@1.1.0' pointing at an entry that no longer exists. That is exactly the dangling reference the refusal exists to prevent.
Impact
scan --mode vendored exits 0 with status: success and vendor reports applied.
- Every
pnpm install --frozen-lockfile then fails with ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY ("Broken lockfile: no entry for '@isaacs/string-locale-compare@1.1.0' in pnpm-lock.yaml … probably caused by a badly resolved merge conflict").
- Manifest-less
vex attests not_affected for pkg:npm/%40isaacs/string-locale-compare@1.1.0, although nothing patched can be installed from this lock.
vendor --revert restores the lock byte for byte (pass).
Repro
The patch API is mocked locally (batch, by-package, view, package grant and the tarball route); the patch prepends a marker to index.js.
export PATH=/path/to/pnpm-12.8.1/bin:$PATH # or 9.15.9
mkdir proj && cd proj
cat > package.json <<'EOF'
{ "name": "cell", "version": "0.0.0", "private": true,
"dependencies": { "sl": "npm:@isaacs/string-locale-compare@1.1.0" } }
EOF
pnpm install
socket-patch scan --mode vendored --json --api-url $MOCK --org test-org --api-token tok
# -> exit 0, "status": "success", vendor event "applied"
grep -A2 '^ sl:' pnpm-lock.yaml # importer version still '@isaacs/string-locale-compare@1.1.0'
grep -n "string-locale-compare@1.1.0':" pnpm-lock.yaml # -> no packages/snapshots entry with that key any more
pnpm install --frozen-lockfile # -> ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY
socket-patch vex --output vex.json ... # -> not_affected
Control: {"lp": "npm:left-pad@1.3.0"} in the same project is refused correctly with vendor_lock_entry_unsupported ("an aliased importer version (left-pad@1.3.0)"), and the run reports partial_failure.
Expected vs actual
- Expected: the refusal fires for scoped aliases too (or the surgery rewrites the alias importer). The pnpm 9.0 path already refuses unscoped aliases, and the pnpm 6.0 (pnpm 8) path refuses the scoped one (
aliased root dependency (/@isaacs/string-locale-compare@1.1.0)). CLI_CONTRACT and docs/ecosystems.md describe lock shapes vendoring can't handle as loud refusals that write nothing, not as a success that breaks frozen installs.
- Actual: success, a broken lock, and a VEX attestation.
Matrix (Linux, Node 22, main 9c43dfc)
| pnpm (lock) |
scoped alias npm:@isaacs/string-locale-compare@1.1.0 |
unscoped alias npm:left-pad@1.3.0 (control) |
| 8.15.9 (6.0) |
pass (refused: aliased root dependency) |
pass (refused) |
| 9.15.9 (9.0) |
fail: success, frozen install broken, vex attests (2/2) |
pass (refused) |
| 12.8.1 (9.0) |
fail: same (2/2) |
pass (refused) |
pnpm 10 and 11 use the same 9.0 lock grammar and weren't run separately. macOS and Windows weren't probed (this is string matching, independent of OS).
First bad version
Not a regression: release 4.0.0 behaves the same on pnpm 12.8.1 (success, broken frozen install, not_affected).
Suspect code
crates/socket-patch-core/src/vendor/pnpm_lock.rs:1588 (index path) and :1643 (fallback scan):
if v == reg_key || v.starts_with(&key_peer_prefix) {
return refuse("an aliased importer version", v);
}
v is the raw YAML value ('@isaacs/string-locale-compare@1.1.0', quoted because it starts with @), and reg_key is unquoted. The same comparison on snapshot body references (rest == reg_key, around :1576 / :1617) probably misses a scoped alias inside a transitive dependency's snapshot in the same way. Unquoting (unquote_value) before comparing, as the catalog branch right below already does for the specifier, should cover all four sites. importer_ref_candidates may also need to look up by the unquoted value.
[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
Vendored mode on a pnpm 9.0 lock is supposed to refuse a package that an
npm:alias references (vendor_lock_entry_unsupported: "references … through an aliased importer version … that the pair surgery cannot rewrite — vendoring would leave a dangling reference pnpm rejects"). For an unscoped alias (lp: npm:left-pad@1.3.0) it does. For a scoped alias it doesn't. pnpm writes the importer's version quoted:The guard compares the raw (still quoted) value against the unquoted registry key, so it never matches. The vendor surgery then renames the
packages:/snapshots:entries to'@isaacs/string-locale-compare@file:.socket/vendor/…'and leaves the importer'sversion: '@isaacs/string-locale-compare@1.1.0'pointing at an entry that no longer exists. That is exactly the dangling reference the refusal exists to prevent.Impact
scan --mode vendoredexits 0 withstatus: successandvendorreportsapplied.pnpm install --frozen-lockfilethen fails withERR_PNPM_LOCKFILE_MISSING_DEPENDENCY("Broken lockfile: no entry for '@isaacs/string-locale-compare@1.1.0' in pnpm-lock.yaml … probably caused by a badly resolved merge conflict").vexattestsnot_affectedforpkg:npm/%40isaacs/string-locale-compare@1.1.0, although nothing patched can be installed from this lock.vendor --revertrestores the lock byte for byte (pass).Repro
The patch API is mocked locally (batch, by-package, view, package grant and the tarball route); the patch prepends a marker to
index.js.Control:
{"lp": "npm:left-pad@1.3.0"}in the same project is refused correctly withvendor_lock_entry_unsupported("an aliased importer version (left-pad@1.3.0)"), and the run reportspartial_failure.Expected vs actual
aliased root dependency (/@isaacs/string-locale-compare@1.1.0)). CLI_CONTRACT and docs/ecosystems.md describe lock shapes vendoring can't handle as loud refusals that write nothing, not as a success that breaks frozen installs.Matrix (Linux, Node 22, main
9c43dfc)npm:@isaacs/string-locale-compare@1.1.0npm:left-pad@1.3.0(control)aliased root dependency)pnpm 10 and 11 use the same 9.0 lock grammar and weren't run separately. macOS and Windows weren't probed (this is string matching, independent of OS).
First bad version
Not a regression: release 4.0.0 behaves the same on pnpm 12.8.1 (success, broken frozen install,
not_affected).Suspect code
crates/socket-patch-core/src/vendor/pnpm_lock.rs:1588(index path) and:1643(fallback scan):vis the raw YAML value ('@isaacs/string-locale-compare@1.1.0', quoted because it starts with@), andreg_keyis unquoted. The same comparison on snapshot body references (rest == reg_key, around:1576/:1617) probably misses a scoped alias inside a transitive dependency's snapshot in the same way. Unquoting (unquote_value) before comparing, as the catalog branch right below already does for the specifier, should cover all four sites.importer_ref_candidatesmay also need to look up by the unquoted value.