[agent] Found by the scheduled Yarn classic (1.x) bug-hunt routine (ledger #304).
Summary
After a hosted or vendored scan, an ordinary yarn command can add a second block for the same name@version to yarn.lock that resolves from the registry, next to the Socket-wired block. For example, in a workspace where the root and member b both depend on left-pad@^1.3.0, running yarn add -W left-pad --exact gives:
left-pad@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a…"
integrity sha512-XI5M…
left-pad@^1.3.0:
version "1.3.0"
resolved "<patch host>/…/left-pad-1.3.0.tgz#8196…" # or file:./.socket/vendor/npm/<uuid>/…
integrity sha512-AJZe…
yarn 1 dedupes installed packages by name@version. A fresh yarn install --frozen-lockfile accepts this lock unchanged and installs one copy, the root's: the unpatched registry tarball. Member b uses that hoisted copy, so no patched left-pad is on disk anywhere. socket-patch still attests the package:
- Lock-only
vex (hosted and vendored) returns not_affected, exit 0.
- Vendored post-install
vex also returns not_affected. Its warning says to "re-run your package manager's install to resync it", but reinstalling never installs the vendored copy. vendor --check reports "committed artifact and wiring verified" (exit 0).
- Hosted post-install
vex is correct: it omits the package (not_applied).
The cause is that the yarn extractor records a non-Socket block of the same name@version as resolved_elsewhere, but contest_across_locks only contests a ref from a different lock file (e.file != r.source_file). An unpatched sibling block in the same yarn.lock never blocks attestation. The git-copy path (git_copies, #363 / #710) already handles this same-lock case for git blocks.
Impact
A VEX document says the product isn't affected by the CVE while every install of that lock ships only the unpatched package. The trigger is a routine dependency edit made after the scan (pinning a dependency exactly, or a lock merge that brings in a separately keyed block), not a hand-edited lock. Re-running socket-patch scan heals the lock, but nothing tells the user to.
Expected vs actual
- CLI_CONTRACT.md (hosted): a dep counts only where the hosted URL "actually landed", and the VEX discovery rules (
vex/discover/mod.rs rule 10) say a ref another lock "CONTESTS by resolving the same package elsewhere" is dropped. docs/ecosystems.md ("yarn classic git dependencies") applies the same rule inside one yarn.lock for a git copy: "vex never attests the package from that lock while the git copy is there".
- Expected: a live registry block of the patched name@version in the same yarn.lock blocks attestation (with a diagnostic naming the block, as
git_copies does), or the pin is reported as shadowed.
- Actual:
not_affected, exit 0, no diagnostic.
Repro
This uses the local mock patch API from the ledger, serving a left-pad@1.3.0 patch.
BIN=target/release/socket-patch; Y=yarn # 1.7.0 – 1.22.22
API="--api-url http://127.0.0.1:8787 --org o --api-token x"
export SOCKET_PATCH_SERVER_URL=http://127.0.0.1:8787
mkdir -p w/b && cd w
echo '{"name":"root","version":"1.0.0","private":true,"workspaces":["b"],"dependencies":{"left-pad":"^1.3.0"}}' > package.json
echo '{"name":"b","version":"1.0.0","dependencies":{"left-pad":"^1.3.0"}}' > b/package.json
$Y install
$BIN scan --json --yes $API # hosted (or --mode vendored --vendor-source service)
$Y add -W left-pad --exact # yarn writes a separate registry block `left-pad@1.3.0`
rm -rf node_modules b/node_modules
$BIN vex --output v.json --product pkg:npm/root@1.0.0 $API # exit 0, "not_affected"
$Y install --frozen-lockfile # lock unchanged
grep -c SOCKET-PATCHED node_modules/left-pad/index.js # 0, and there's no b/node_modules/left-pad
For hosted, post-install vex then exits 1 with not_applied, which is correct. For vendored, post-install vex still attests.
OS × version
Linux, main 9c43dfc, Node 22. Each hosted cell was run twice.
| yarn |
hosted lock-only vex |
hosted post-install vex |
vendored lock-only vex |
vendored post-install vex |
fresh frozen install |
| 1.0.2 |
n/a (add -W doesn't exist) |
n/a |
n/a (yarn ≤1.6 vendored) |
n/a |
n/a |
| 1.7.0 |
attests (bug) |
omits (ok) |
attests (bug) |
attests (bug) |
unpatched only |
| 1.10.1 |
attests (bug) |
omits (ok) |
attests (bug) |
attests (bug) |
unpatched only |
| 1.22.22 |
attests (bug) |
omits (ok) |
attests (bug) |
attests (bug) |
unpatched only |
With a hand-ordered lock where the root's pattern hits the pinned block, yarn installs the patched copy instead. Which bytes land depends on which pattern yarn resolves first, so attestation from the lock isn't sound either way. macOS and Windows weren't probed; the code path is platform-independent lock parsing. v4.0.0 isn't comparable (its vex needs a manifest).
Suspect code
crates/socket-patch-core/src/vex/discover/mod.rs:606: && e.file != r.source_file excludes same-file evidence, so a registry block in the same yarn.lock never contests the Socket ref.
crates/socket-patch-core/src/vex/discover/yarn.rs:231: the comment says the non-Socket block is "evidence against another lock's wiring", and extract_classic only post-filters refs against git_copies (git blocks), not against live registry blocks of the same purl.
- Possibly related on the scan side: the hosted / vendored rewriters can't see this state until the next scan, so
vendor --check (vendored) also reports the wiring as verified.
Found by real-yarn runs, no probe branch (Linux only).
[agent] Found by the scheduled Yarn classic (1.x) bug-hunt routine (ledger #304).
Summary
After a hosted or vendored scan, an ordinary yarn command can add a second block for the same name@version to yarn.lock that resolves from the registry, next to the Socket-wired block. For example, in a workspace where the root and member
bboth depend onleft-pad@^1.3.0, runningyarn add -W left-pad --exactgives:yarn 1 dedupes installed packages by name@version. A fresh
yarn install --frozen-lockfileaccepts this lock unchanged and installs one copy, the root's: the unpatched registry tarball. Memberbuses that hoisted copy, so no patched left-pad is on disk anywhere. socket-patch still attests the package:vex(hosted and vendored) returnsnot_affected, exit 0.vexalso returnsnot_affected. Its warning says to "re-run your package manager's install to resync it", but reinstalling never installs the vendored copy.vendor --checkreports "committed artifact and wiring verified" (exit 0).vexis correct: it omits the package (not_applied).The cause is that the yarn extractor records a non-Socket block of the same name@version as
resolved_elsewhere, butcontest_across_locksonly contests a ref from a different lock file (e.file != r.source_file). An unpatched sibling block in the same yarn.lock never blocks attestation. The git-copy path (git_copies, #363 / #710) already handles this same-lock case for git blocks.Impact
A VEX document says the product isn't affected by the CVE while every install of that lock ships only the unpatched package. The trigger is a routine dependency edit made after the scan (pinning a dependency exactly, or a lock merge that brings in a separately keyed block), not a hand-edited lock. Re-running
socket-patch scanheals the lock, but nothing tells the user to.Expected vs actual
vex/discover/mod.rsrule 10) say a ref another lock "CONTESTS by resolving the same package elsewhere" is dropped. docs/ecosystems.md ("yarn classic git dependencies") applies the same rule inside one yarn.lock for a git copy: "vexnever attests the package from that lock while the git copy is there".git_copiesdoes), or the pin is reported as shadowed.not_affected, exit 0, no diagnostic.Repro
This uses the local mock patch API from the ledger, serving a
left-pad@1.3.0patch.For hosted, post-install
vexthen exits 1 withnot_applied, which is correct. For vendored, post-installvexstill attests.OS × version
Linux, main
9c43dfc, Node 22. Each hosted cell was run twice.add -Wdoesn't exist)With a hand-ordered lock where the root's pattern hits the pinned block, yarn installs the patched copy instead. Which bytes land depends on which pattern yarn resolves first, so attestation from the lock isn't sound either way. macOS and Windows weren't probed; the code path is platform-independent lock parsing. v4.0.0 isn't comparable (its
vexneeds a manifest).Suspect code
crates/socket-patch-core/src/vex/discover/mod.rs:606:&& e.file != r.source_fileexcludes same-file evidence, so a registry block in the same yarn.lock never contests the Socket ref.crates/socket-patch-core/src/vex/discover/yarn.rs:231: the comment says the non-Socket block is "evidence against another lock's wiring", andextract_classiconly post-filters refs againstgit_copies(git blocks), not against live registry blocks of the same purl.vendor --check(vendored) also reports the wiring as verified.Found by real-yarn runs, no probe branch (Linux only).