[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).
Summary
A yarn 4 workspace has member a that depends on left-pad@^1.3.0 from the registry. Member b depends on a copy of the same left-pad@1.3.0 under another dependency name: "lp2": "file:../../forks/left-pad-1.3.0.tgz", a file: directory, or the registry tarball URL https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz. Yarn locks that copy as lp2@file:… / lp2@https://…, with version: 1.3.0, and installs it as node_modules/lp2, whose package.json says left-pad@1.3.0.
scan --mode hosted and scan --mode vendored pin only the registry entry. Both exit 0 with status success and no warning about the lp2 copy.
- A lockfile-only
vex then attests pkg:npm/left-pad@1.3.0 as not_affected (inline_mitigations_already_exist) in both modes.
- After a real fresh
yarn install --immutable, node_modules/lp2/index.js is the unpatched upstream file. Hosted vex is honest at this point (not_applied, exit 1). Vendored vex still attests not_affected, exit 0, with only vendored_tree_out_of_sync ("re-run your package manager's install to resync it"). Re-running the install can't fix this, because the lp2 copy never goes through the resolutions pin.
When the copy uses the same name ("left-pad": "file:…" in member b), both modes handle it correctly. Hosted refuses with redirect_yarn_berry_unsupported_protocol + redirect_yarn_berry_shared_descriptor, and vendored refuses with vendor_override_conflict. The gap is only that every guard keys on the lock entry's ident (left-pad@…). For a non-registry protocol, yarn takes the ident from the dependency name (lp2), even though the installed package is left-pad@1.3.0.
This is the berry side of #935 (pnpm), #921 (yarn classic) and #497 (bun). Those issues cover a same-name copy, which berry already refuses. For berry, only the other-name copy gets through.
Impact
The VEX document (the CI / compliance artefact) says the product isn't affected, while the product ships an unpatched copy of exactly the patched name@version. An SCA scanner reading node_modules/lp2/package.json flags it as left-pad@1.3.0. Neither scan says anything about that copy.
Repro (yarn 4.18.1, main 9c43dfc)
# Mock patch API on :8790 serving pkg:npm/left-pad@1.3.0 (batch, by-package, view, patches/package with a
# tarball + yarn-berry-zip/yarnBerry10c0 artifact, and the hosted tarball), same contract as
# crates/socket-patch-cli/tests/e2e_redirect_yarn_berry_build.rs. Patched tgz = upstream with a marker line on index.js.
mkdir -p proj/packages/a proj/packages/b proj/forks && cd proj
npm pack left-pad@1.3.0 && mv left-pad-1.3.0.tgz forks/ # unmodified upstream
echo '{"name":"root","version":"1.0.0","private":true,"workspaces":["packages/*"]}' > package.json
echo '{"name":"a","version":"1.0.0","dependencies":{"left-pad":"^1.3.0"}}' > packages/a/package.json
echo '{"name":"b","version":"1.0.0","dependencies":{"lp2":"file:../../forks/left-pad-1.3.0.tgz"}}' > packages/b/package.json
printf 'nodeLinker: node-modules\n' > .yarnrc.yml
yarn install
socket-patch scan --mode hosted --json --yes --api-url http://127.0.0.1:8790 --org org --api-token x --patch-server-url http://127.0.0.1:8790
# exit 0, status success, redirect.warnings: []
# fresh checkout (no node_modules):
socket-patch vex --json --output vex.json --patch-server-url http://127.0.0.1:8790 --api-url http://127.0.0.1:8790 --org org --api-token x
# exit 0: pkg:npm/left-pad@1.3.0 not_affected (inline_mitigations_already_exist)
YARN_ENABLE_IMMUTABLE_INSTALLS=1 yarn install --immutable # cold global cache: exit 0
head -1 node_modules/left-pad/index.js # /* SOCKET-PATCHED */
head -1 node_modules/lp2/index.js # upstream header: UNPATCHED
node -p "require('./node_modules/lp2/package.json').name" # left-pad (version 1.3.0)
The copy's lock entry, which every reader skips:
"lp2@file:../../forks/left-pad-1.3.0.tgz::locator=b%40workspace%3Apackages%2Fb":
version: 1.3.0
resolution: "lp2@file:../../forks/left-pad-1.3.0.tgz#../../forks/left-pad-1.3.0.tgz::hash=5c8e4c&locator=b%40workspace%3Apackages%2Fb"
Vendored is the same with --mode vendored: it writes resolutions: {"left-pad": "file:./.socket/vendor/npm/<uuid>/left-pad-1.3.0.tgz"}, which yarn doesn't apply to the lp2 ident.
Expected vs actual
- Expected: CLI_CONTRACT.md ("Contested locks", line 380): when another entry of the same lock resolves the wired
name@version from a non-Socket source, "the package manager installs both entries, and that copy stays unpatched", so the reference is dropped with patched_ref_unattributable. The scans should name the unreached copy, as they already do for a same-name file: copy (redirect_yarn_berry_unsupported_protocol, vendor_override_conflict).
- Actual: neither scan warns. Lock-only
vex attests not_affected in both modes, and vendored vex still attests after a fresh --immutable install.
OS × version
Linux, Node 22, real yarn from @yarnpkg/cli-dist, nodeLinker: node-modules, cold global cache for each --immutable install, fresh copy of the tree. Every cell was run at least once; 4.18.1 file:-tgz/hosted was run twice.
| yarn |
lp2 spec |
Mode |
Scan warns? |
Lock-only vex |
--immutable |
node_modules/lp2 |
vex after install |
| 4.18.1 |
file: tgz |
hosted |
no |
not_affected |
ok |
unpatched |
declines (not_applied) |
| 4.18.1 |
file: tgz |
vendored |
no |
not_affected |
ok |
unpatched |
not_affected |
| 4.18.1 |
file: dir |
hosted |
no |
not_affected |
ok |
unpatched |
declines |
| 4.18.1 |
file: dir |
vendored |
no |
not_affected |
ok |
unpatched |
not_affected |
| 4.18.1 |
registry tarball URL |
hosted |
no |
not_affected |
ok |
unpatched |
declines |
| 4.18.1 |
registry tarball URL |
vendored |
no |
not_affected |
ok |
unpatched |
not_affected |
| 4.0.2 |
tgz / dir / URL |
hosted |
no |
not_affected |
ok |
unpatched |
declines |
| 4.0.2 |
tgz / dir / URL |
vendored |
no |
not_affected |
ok |
unpatched |
not_affected |
| 4.18.1 (control) |
"left-pad": "file:…" (same name) |
hosted / vendored |
refused loudly |
n/a |
n/a |
n/a |
n/a |
I didn't probe macOS or Windows. This run couldn't use probe branches, and the behaviour lives in lock parsing, which isn't OS-specific. Yarn 2/3 are refused by both modes anyway.
First bad version: none bisected. Release 4.0.0 has no manifest-less lockfile VEX, so this isn't a regression of a shipped feature.
Suspect code
crates/socket-patch-core/src/vex/discover/yarn.rs:403: a file: / http locator that isn't Socket's returns silently (no resolved_elsewhere, nothing that contests the ref). Even the URL variant names left-pad/-/left-pad-1.3.0.tgz in the locator, and the file: tarball and directory are in the checkout, so a lock-only reader can tell that lp2 is left-pad@1.3.0.
crates/socket-patch-core/src/patch/redirect/mod.rs:~3841 (redirect_yarn_berry_unsupported_protocol / shared_descriptor) and crates/socket-patch-core/src/vendor/yarn_berry_lock.rs:1297 (vendor_override_conflict) only look at entries whose ident is the patched name, so the other-name copy isn't seen.
- Vendored
vex treats the mismatch as vendored_tree_out_of_sync and attests from the committed artifact. Its remedy ("re-run your package manager's install") can't help with a copy the resolutions pin never reaches.
[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).
Summary
A yarn 4 workspace has member
athat depends onleft-pad@^1.3.0from the registry. Memberbdepends on a copy of the sameleft-pad@1.3.0under another dependency name:"lp2": "file:../../forks/left-pad-1.3.0.tgz", afile:directory, or the registry tarball URLhttps://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz. Yarn locks that copy aslp2@file:…/lp2@https://…, withversion: 1.3.0, and installs it asnode_modules/lp2, whosepackage.jsonsaysleft-pad@1.3.0.scan --mode hostedandscan --mode vendoredpin only the registry entry. Both exit 0 with statussuccessand no warning about thelp2copy.vexthen attestspkg:npm/left-pad@1.3.0asnot_affected(inline_mitigations_already_exist) in both modes.yarn install --immutable,node_modules/lp2/index.jsis the unpatched upstream file. Hostedvexis honest at this point (not_applied, exit 1). Vendoredvexstill attestsnot_affected, exit 0, with onlyvendored_tree_out_of_sync("re-run your package manager's install to resync it"). Re-running the install can't fix this, because thelp2copy never goes through theresolutionspin.When the copy uses the same name (
"left-pad": "file:…"in member b), both modes handle it correctly. Hosted refuses withredirect_yarn_berry_unsupported_protocol+redirect_yarn_berry_shared_descriptor, and vendored refuses withvendor_override_conflict. The gap is only that every guard keys on the lock entry's ident (left-pad@…). For a non-registry protocol, yarn takes the ident from the dependency name (lp2), even though the installed package is left-pad@1.3.0.This is the berry side of #935 (pnpm), #921 (yarn classic) and #497 (bun). Those issues cover a same-name copy, which berry already refuses. For berry, only the other-name copy gets through.
Impact
The VEX document (the CI / compliance artefact) says the product isn't affected, while the product ships an unpatched copy of exactly the patched
name@version. An SCA scanner readingnode_modules/lp2/package.jsonflags it asleft-pad@1.3.0. Neither scan says anything about that copy.Repro (yarn 4.18.1, main
9c43dfc)The copy's lock entry, which every reader skips:
Vendored is the same with
--mode vendored: it writesresolutions: {"left-pad": "file:./.socket/vendor/npm/<uuid>/left-pad-1.3.0.tgz"}, which yarn doesn't apply to thelp2ident.Expected vs actual
name@versionfrom a non-Socket source, "the package manager installs both entries, and that copy stays unpatched", so the reference is dropped withpatched_ref_unattributable. The scans should name the unreached copy, as they already do for a same-namefile:copy (redirect_yarn_berry_unsupported_protocol,vendor_override_conflict).vexattestsnot_affectedin both modes, and vendoredvexstill attests after a fresh--immutableinstall.OS × version
Linux, Node 22, real yarn from
@yarnpkg/cli-dist,nodeLinker: node-modules, cold global cache for each--immutableinstall, fresh copy of the tree. Every cell was run at least once; 4.18.1 file:-tgz/hosted was run twice.lp2specvex--immutablenode_modules/lp2vexafter installfile:tgznot_applied)file:tgzfile:dirfile:dir"left-pad": "file:…"(same name)I didn't probe macOS or Windows. This run couldn't use probe branches, and the behaviour lives in lock parsing, which isn't OS-specific. Yarn 2/3 are refused by both modes anyway.
First bad version: none bisected. Release 4.0.0 has no manifest-less lockfile VEX, so this isn't a regression of a shipped feature.
Suspect code
crates/socket-patch-core/src/vex/discover/yarn.rs:403: afile:/httplocator that isn't Socket's returns silently (noresolved_elsewhere, nothing that contests the ref). Even the URL variant namesleft-pad/-/left-pad-1.3.0.tgzin the locator, and thefile:tarball and directory are in the checkout, so a lock-only reader can tell thatlp2is left-pad@1.3.0.crates/socket-patch-core/src/patch/redirect/mod.rs:~3841(redirect_yarn_berry_unsupported_protocol/shared_descriptor) andcrates/socket-patch-core/src/vendor/yarn_berry_lock.rs:1297(vendor_override_conflict) only look at entries whose ident is the patched name, so the other-name copy isn't seen.vextreats the mismatch asvendored_tree_out_of_syncand attests from the committed artifact. Its remedy ("re-run your package manager's install") can't help with a copy theresolutionspin never reaches.