[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
On pnpm 7 and 8 locks (lockfile 5.4 / 6.0), vendored mode rewrites the root dependency's specifier to the machine-absolute file:<project root>/.socket/vendor/...tgz spelling that pnpm itself records. It splices that value into pnpm-lock.yaml as a plain YAML scalar with no quoting. When the project path contains a YAML indicator sequence, the lock no longer says what socket-patch meant:
# (for example ~/src/My Project #2/): YAML reads everything after # as a comment. pnpm sees the specifier file:/…/My Project and refuses with ERR_PNPM_OUTDATED_LOCKFILE.
: (for example colon: x): the line is no longer valid YAML. pnpm fails with ERR_PNPM_BROKEN_LOCKFILE … bad indentation of a mapping entry.
pnpm writes the same value single-quoted when it generates the lock itself. In the same directory, pnpm install from the vendored package.json produces specifier: 'file:/…/hash #x/.socket/vendor/…/left-pad-1.3.0.tgz'.
Impact
scan --mode vendored / vendor report success, and the checkout is then uninstallable with --frozen-lockfile (the CI default) at the same path, not only in a moved checkout. That contradicts the vendor_pnpm_legacy_absolute_specifier caveat, which promises that the lock works at the recorded path.
vendor --check reports vendor_check_ok ("committed artifact and wiring verified") on the broken lock.
vex attests not_affected for a project that can't be installed from its lock.
vendor --revert restores the original correctly. Only the forward write is wrong.
Repro (Linux, pnpm 8.15.9; 7.33.7 is the same)
D="/tmp/w/hash #x"; mkdir -p "$D" && cd "$D"
printf '{"name":"proj","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}\n' > package.json
pnpm install
socket-patch scan --mode vendored --json --yes # status: success (any left-pad@1.3.0 patch; a mock API was used)
grep specifier pnpm-lock.yaml
# specifier: file:/tmp/w/hash #x/.socket/vendor/npm/<uuid>/left-pad-1.3.0.tgz <- unquoted
rm -rf node_modules
pnpm install --frozen-lockfile --offline
# ERR_PNPM_OUTDATED_LOCKFILE ... specifiers in the lockfile ({"left-pad":"file:/tmp/w/hash"}) don't match specs in package.json
# (the lock value is truncated at " #")
socket-patch vendor --check --json # status: success, vendor_check_ok
socket-patch vex --output v.json # not_affected
On a 5.4 lock (pnpm 7.33.7), the line written is specifiers: → left-pad: file:/…/hash #x/.socket/vendor/…tgz, which fails the same way.
Expected vs actual
- Expected: the vendored lock round-trips through pnpm's own YAML reader to the exact specifier pnpm would record. docs/ecosystems.md and the
vendor_pnpm_legacy_absolute_specifier warning say that a frozen install works in a checkout at the recorded path. CLI_CONTRACT: a reported success must leave a project that installs patched.
- Actual: the value is written unquoted, so YAML truncates or rejects it.
vendor --check and vex don't notice.
Matrix (Linux, main 045d7ec; each cell = vendored scan, then a fresh --frozen-lockfile --offline install)
| project dir name |
pnpm 7.33.7 (5.4) |
pnpm 8.15.9 (6.0) |
plain dir |
pass |
pass |
ünïcode |
pass |
pass |
a'quote, [br], x#y |
pass |
pass |
hash #x |
fail ERR_PNPM_OUTDATED_LOCKFILE |
fail ERR_PNPM_OUTDATED_LOCKFILE |
colon: x |
fail ERR_PNPM_BROKEN_LOCKFILE |
fail ERR_PNPM_BROKEN_LOCKFILE |
Reproduced twice on each failing cell. pnpm ≥ 9 vendored locks use only relative specifiers, so they aren't affected. Windows paths (C:/Users/x/My Project #2) would hit the same splice, but I haven't tested that on a runner.
First bad release: release 4.0.0 (npm) writes the same unquoted line and fails identically. 3.3.0 has no vendored mode. So it isn't a regression.
Suspect code
crates/socket-patch-core/src/vendor/pnpm_lock_legacy.rs:593 (v5.4 specifiers: line): format!(" {}: {}", yaml_key_like(key, repr), ctx.abs_spec)
crates/socket-patch-core/src/vendor/pnpm_lock_legacy.rs:654 (v6.0 nested specifier:): format!(" specifier: {}", ctx.abs_spec)
abs_spec is built at pnpm_lock_legacy.rs:358 from the canonical root without YAML escaping. The in-sync comparisons at :578 / :630 would also need to compare the decoded value. vendor --check evidently validates the same raw string, so it agrees with the broken write.
Probe runs: none (Linux reproduction only).
[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
On pnpm 7 and 8 locks (lockfile 5.4 / 6.0), vendored mode rewrites the root dependency's specifier to the machine-absolute
file:<project root>/.socket/vendor/...tgzspelling that pnpm itself records. It splices that value intopnpm-lock.yamlas a plain YAML scalar with no quoting. When the project path contains a YAML indicator sequence, the lock no longer says what socket-patch meant:#(for example~/src/My Project #2/): YAML reads everything after#as a comment. pnpm sees the specifierfile:/…/My Projectand refuses withERR_PNPM_OUTDATED_LOCKFILE.:(for examplecolon: x): the line is no longer valid YAML. pnpm fails withERR_PNPM_BROKEN_LOCKFILE … bad indentation of a mapping entry.pnpm writes the same value single-quoted when it generates the lock itself. In the same directory,
pnpm installfrom the vendoredpackage.jsonproducesspecifier: 'file:/…/hash #x/.socket/vendor/…/left-pad-1.3.0.tgz'.Impact
scan --mode vendored/vendorreportsuccess, and the checkout is then uninstallable with--frozen-lockfile(the CI default) at the same path, not only in a moved checkout. That contradicts thevendor_pnpm_legacy_absolute_specifiercaveat, which promises that the lock works at the recorded path.vendor --checkreportsvendor_check_ok("committed artifact and wiring verified") on the broken lock.vexattestsnot_affectedfor a project that can't be installed from its lock.vendor --revertrestores the original correctly. Only the forward write is wrong.Repro (Linux, pnpm 8.15.9; 7.33.7 is the same)
On a 5.4 lock (pnpm 7.33.7), the line written is
specifiers:→left-pad: file:/…/hash #x/.socket/vendor/…tgz, which fails the same way.Expected vs actual
vendor_pnpm_legacy_absolute_specifierwarning say that a frozen install works in a checkout at the recorded path. CLI_CONTRACT: a reported success must leave a project that installs patched.vendor --checkandvexdon't notice.Matrix (Linux, main
045d7ec; each cell = vendored scan, then a fresh--frozen-lockfile --offlineinstall)plain dirünïcodea'quote,[br],x#yhash #xERR_PNPM_OUTDATED_LOCKFILEERR_PNPM_OUTDATED_LOCKFILEcolon: xERR_PNPM_BROKEN_LOCKFILEERR_PNPM_BROKEN_LOCKFILEReproduced twice on each failing cell. pnpm ≥ 9 vendored locks use only relative specifiers, so they aren't affected. Windows paths (
C:/Users/x/My Project #2) would hit the same splice, but I haven't tested that on a runner.First bad release: release 4.0.0 (npm) writes the same unquoted line and fails identically. 3.3.0 has no vendored mode. So it isn't a regression.
Suspect code
crates/socket-patch-core/src/vendor/pnpm_lock_legacy.rs:593(v5.4specifiers:line):format!(" {}: {}", yaml_key_like(key, repr), ctx.abs_spec)crates/socket-patch-core/src/vendor/pnpm_lock_legacy.rs:654(v6.0 nestedspecifier:):format!(" specifier: {}", ctx.abs_spec)abs_specis built atpnpm_lock_legacy.rs:358from the canonical root without YAML escaping. The in-sync comparisons at:578/:630would also need to compare the decoded value.vendor --checkevidently validates the same raw string, so it agrees with the broken write.Probe runs: none (Linux reproduction only).