Repository navigation
Yarn berry drops a relative portal: / link: copy whose target directory is missing (e.g. an uninitialized git submodule), so hosted and vendored scans succeed and VEX attests not_affected while that copy installs unpatched #1561
Description
[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).
Summary
When a yarn berry portal: / link: dependency under another name ("lp2": "portal:./vend/lp") points at a relative directory that doesn't exist at scan or vex time, socket-patch drops that copy. It's then neither named as a copy nor treated as unidentified. Hosted and vendored scans report success, and the in-run and lock-only VEX attest not_affected. Once the directory exists and yarn installs, node_modules/lp2 holds unpatched left-pad@1.3.0.
The file: twin ("lp2": "file:./vend/lp", same missing directory) fails closed as #1516 intends: the scan warns redirect_copy_unpatched and VEX refuses. Only the linked protocols fail open.
A realistic way to hit this is a portal target that is a git submodule. actions/checkout doesn't fetch submodules by default, so a lock-only socket-patch vex or scan in CI sees no vend/lp, while the build that installs with submodules ships the unpatched copy. A portal into a generated or gitignored directory works the same way.
Impact
This produces a false not_affected VEX statement and a clean scan, while the shipped tree holds an unpatched copy of the patched package@version. It's the same fail-open class as #1516 and #1540, through a path those fixes left open.
Repro (Linux, real yarn 4.18.1 / 4.0.2)
# project: dependencies {"left-pad":"1.3.0","lp2":"portal:./vend/lp"}, nodeLinker: node-modules
mkdir -p vend && npm pack left-pad@1.3.0 && tar xzf left-pad-1.3.0.tgz && mv package vend/lp
yarn install # lock: "lp2@portal:./vend/lp::locator=app%40workspace%3A." (linkType: soft)
# lock-only checkout without the portal target (submodule not checked out):
mkdir ../co && cp package.json yarn.lock .yarnrc.yml ../co/
socket-patch scan --mode hosted --json --yes --cwd ../co --api-url $API --org org --api-token x \
--vex ../co/out.vex.json # exit 0, status success, no redirect_copy_unpatched; VEX: not_affected pkg:npm/left-pad@1.3.0
socket-patch vex --cwd ../co ... # exit 0, 1 statement, not_affected
# restore the target, then install:
cp -r vend ../co/ && (cd ../co && yarn install --immutable)
head -c 60 ../co/node_modules/lp2/index.js # unpatched left-pad 1.3.0I used a wiremock patch API (the berry e2e fixture from e2e_redirect_yarn_berry_build.rs with a marker-prepended index.js) and the real yarn bundles from @yarnpkg/cli-dist.
Expected vs actual
docs/ecosystems.md ("yarn copies under another dependency name"): "A copy whose package can't be read (a non-registry URL, a missing directory) is treated as possibly any package at its lock version … and vex attests nothing that lock wires", and "a portal: / link: copy whose package.json isn't read has no version either, so it may be any version of any package and vex attests nothing that lock wires".
- Expected: a missing
portal:/link:target is an unidentified copy of any version, the same as an absolute one (Yarn berry renamed-copy detection ignores aportal:/link:copy with an absolute path, so hosted and vendored scans succeed and every VEX path attests not_affected while that copy installs unpatched #1540). Hosted warnsredirect_copy_unpatched, vendored warnsvendor_copy_unpatched, andvexwithholds the lock's packages. - Actual: the copy is dropped. The scan exits 0 with
success, and every VEX path attestsnot_affected.
Matrix (Linux, main 7b6371c)
| Cell | yarn 4.0.2 | yarn 4.18.1 |
|---|---|---|
portal:./vend/lp missing, lock-only vex |
fail (exit 0, attests) | fail ×2 |
link:./vend/lp missing, lock-only vex |
– | fail |
portal: missing, scan --mode hosted --vex |
fail (exit 0, attests) | fail |
link: missing, scan --mode hosted --vex |
– | fail |
portal: missing, scan --mode vendored --vex |
– | fail (exit 0, attests) |
link: missing, scan --mode vendored --vex |
fail | – |
Control: file:./vend/lp missing, hosted / vendored / vex |
pass (fails closed) | pass |
Control: portal:/link: target present |
pass (redirect_copy_unpatched, vex refuses) |
pass |
#1540 absolute portal: (target present) |
pass | pass |
In every failing cell, a later yarn install --immutable with the target restored puts unpatched bytes in node_modules/lp2 and patched bytes in node_modules/left-pad.
macOS and Windows weren't probed. The code path is OS-independent.
First bad
The copy has been dropped since berry renamed-copy detection landed (#1489). #1551 (fix for #1540) widened the fail-closed path only to absolute (read_path() == None) linked copies. A relative linked copy whose read fails still hits the old continue.
Suspect code
crates/socket-patch-core/src/vex/discover/yarn.rs:894-897:if matches!(copy.source, CopyRef::Linked(_)) && !unread_linked { continue; }treats "read failed: NotFound / not a directory" the same as "read and found to hold no versioned package".crates/socket-patch-core/src/vendor/yarn_copies.rs:113: the same condition on the vendored and hosted side.crates/socket-patch-core/src/formats/yarn/copy.rs:64is_unread_linkedonly covers anchored paths.
To keep the #1551 review intent (a link: to an existing plain directory with no package.json stays ignored), the check probably needs to tell "the directory exists but has no manifest" apart from "the directory is missing or unreadable".
Activity
mikolalysenko commentedon Oct 15, 2026
[agent] Triaged: priority:p1 (yarn berry, npm family). Confirmed on main
7b6371c:CopyRef::is_unread_linked(formats/yarn/copy.rs) is true only for an anchored path, so a relativeportal:/link:copy whosepackage.jsonread fails (missing or unreadable target) hits the "read and found to hold no versioned package" skip in bothvendor/yarn_copies.rs(other_name_copies,None if Linked && !is_unread_linked => continue) andvex/discover/yarn.rs(record_copies). The fix needs to tell "the read failed" apart from "the manifest was read and names no versioned package". Follows #1540 / #1551, not a duplicate. No open PR addresses it.Generated by Claude Code
mikolalysenko commentedon Oct 15, 2026
[agent] Claiming this issue (shared root cause: a relative
portal:/link:copy whose target read fails is treated like one read and found to hold no versioned package). Branch: agent/fix-yarn-berry-missing-linked-copy. Claim-ID: 2026-10-15T08:21:15Z-625c53Generated by Claude Code
mikolalysenko commentedon Oct 15, 2026
[agent] Draft PR: #1571
Generated by Claude Code
Test yarn berry missing portal:/link: copies
Flag yarn berry portal:/link: copies with no target
Docs: missing yarn portal:/link: targets
Flag empty and unparseable portal:/link: targets
Docs: empty and LFS portal:/link: targets