[agent] Found by the scheduled Yarn classic (1.x) bug-hunt routine (ledger #304).
Summary
Both yarn classic lock rewriters match a yarn.lock block by package name and version only. A dependency installed from git ("left-pad": "git+https://…/left-pad.git#v1.3.0", github:owner/repo#tag, and so on) gets a block like this:
"left-pad@git+file:///…/lpgit#v1.3.0":
version "1.3.0"
resolved "git+file:///…/lpgit#a380ff32159b9beb078ec6ce294cf6fbdad19c55"
That block matches a pkg:npm/[email protected] patch, so:
scan --mode hosted replaces resolved with the hosted https://…/left-pad-1.3.0.tgz#<sha1> URL and adds an integrity line.
scan --mode vendored replaces it with file:./.socket/vendor/npm/<uuid>/left-pad-1.3.0.tgz#<sha1>.
Both runs report status: success (redirected: 1 / applied: 1) with no warning, and the in-run --vex attests not_affected.
Yarn 1, however, chooses the fetcher from the key's pattern (a git pattern → the git resolver/fetcher), not from the resolved value. It then tries to use the rewritten value as a git remote:
- hosted:
git ls-remote against the tarball URL (the patch host sees GET …/left-pad-1.3.0.tgz/info/refs?service=git-upload-pack) → error Command failed. (exit 1 or 128)
- vendored:
error Error: spawn ENOTDIR (git is spawned with the .tgz path as its cwd)
So after the scan, every yarn install fails, frozen or not, on a fresh checkout and in place. That includes the "re-run your package manager's install" remedy that the vendored vex warning suggests.
This is the yarn-classic counterpart of npm #326, but the failure is different: npm silently installs the unpatched git bytes, while yarn 1 hard-fails every install. PR #345 only changes the npm lock code (npm_lock.rs / npm_origin.rs); rewrite_yarn_classic and vendor/yarn_classic_lock.rs still match git blocks.
Impact
- Any yarn-classic project with a git-sourced copy of a patched
name@version can no longer install at all after scan --mode hosted or scan --mode vendored, and CI breaks on the next run.
- False attestation: the in-run
scan --vex says not_affected in both modes. After the scan, vendored socket-patch vex still says not_affected for a tree that holds the original git bytes, with only the vendored_tree_out_of_sync warning. A fresh checkout can't install anything.
file: directory and link: blocks are already skipped (classify_classic_block), but git patterns are not.
Repro (Linux, main f6b7fb9, yarn 1.22.22)
# a local git source of left-pad 1.3.0 (a github: / git+https: spec behaves the same)
mkdir lpgit && npm pack [email protected] && tar xzf left-pad-1.3.0.tgz -C lpgit --strip-components=1
(cd lpgit && git init -q && git add -A && git commit -qm v && git tag v1.3.0)
mkdir app && cd app
echo '{"name":"g","version":"1.0.0","private":true,"dependencies":{"left-pad":"git+file://'"$PWD"'/../lpgit#v1.3.0"}}' > package.json
yarn install # ok; lock: resolved "git+file:///…/lpgit#a380ff3…"
socket-patch scan --mode hosted --json --yes --vex inrun.json \
--api-url $MOCK --org test-org --api-token fake
# → status success, redirect.redirected 1, no warnings; inrun.json: not_affected
# yarn.lock: resolved "http://…/patch/npm/left-pad/1.3.0/<token>/<uuid>/left-pad-1.3.0.tgz#f6cceb…"
yarn install --frozen-lockfile # exit 128/1: "error Command failed." (git ls-remote on the tgz URL)
# vendored twin: socket-patch scan --mode vendored --detached … → applied 1, success
yarn install --frozen-lockfile # exit 1: "error Error: spawn ENOTDIR"
socket-patch vex # vendored: not_affected (+ vendored_tree_out_of_sync)
Control: the same pristine lock with no socket-patch step installs fine (--frozen-lockfile, exit 0).
$MOCK is a local mock of the patch API serving a patched left-pad-1.3.0.tgz (the same shape as e2e_redirect_yarn_classic_build.rs).
Expected vs actual
- Expected: a lock block whose key pattern is a git (or other non-registry, non-tarball) source is not rewired. It is left byte-identical, with a warning that names it (the way
redirect_yarn_classic_alias_skipped does for aliases, or the way classify_classic_block already LinkSkips link: / file: directories), and it is not counted as redirected, vendored or attested. docs/ecosystems.md describes the vlt backend refusing exactly this shape ("a git, remote-tarball or local-directory node of the same package"). CLI_CONTRACT.md's VEX section says a lockfile reference is attested only when the lockfile actually wires the patched artifact.
- Actual: the git block is rewritten, the scan reports success, VEX attests
not_affected, and every later yarn install fails.
OS × version
| OS |
yarn |
hosted: fresh --frozen-lockfile |
vendored: fresh --frozen-lockfile |
in-run VEX |
| Linux |
1.0.2 |
fail (Command failed) |
fail (Command failed) |
not_affected |
| Linux |
1.7.0 |
fail |
fail (spawn ENOTDIR) |
not_affected |
| Linux |
1.10.1 |
fail |
fail (spawn ENOTDIR) |
not_affected |
| Linux |
1.22.22 |
fail (exit 128) |
fail (spawn ENOTDIR) |
not_affected |
| macOS (probe) |
1.10.1 |
fail (Command failed) |
fail (spawn ENOTDIR) |
not_affected |
| macOS (probe) |
1.22.22 |
fail (exit 128) |
fail (spawn ENOTDIR) |
not_affected |
| Windows (probe) |
1.10.1 / 1.22.22 |
blocked: the probe's fixture yarn install of the git dep couldn't find git ("Couldn't find the binary git"), so the cell was never reached |
blocked |
— |
| Linux (probe) |
1.10.1 / 1.22.22 |
fail |
fail (spawn ENOTDIR) |
not_affected |
Released 4.0.0 behaves the same (hosted exit 128 / vendored ENOTDIR), so this isn't a recent regression.
Suspect code
- Hosted:
crates/socket-patch-core/src/patch/redirect/mod.rs:3744, in rewrite_yarn_classic. The block is selected on real_name == fname plus version_re. The key pattern's range (git+…, github:, git://, …/repo.git#…) and the existing resolved scheme are never checked.
- Vendored:
crates/socket-patch-core/src/vendor/yarn_classic_lock.rs:676-701, in classify_classic_block. It skips link: and file: directories only, so git ranges fall through to Candidate.
- VEX discovery (
crates/socket-patch-core/src/vex/discover/yarn.rs) then trusts the rewired block.
Probe run (ubuntu/macos/windows × yarn 1.10.1/1.22.22): https://github.com/SocketDev/socket-patch/actions/runs/36761888480
[agent] Found by the scheduled Yarn classic (1.x) bug-hunt routine (ledger #304).
Summary
Both yarn classic lock rewriters match a
yarn.lockblock by package name andversiononly. A dependency installed from git ("left-pad": "git+https://…/left-pad.git#v1.3.0",github:owner/repo#tag, and so on) gets a block like this:That block matches a
pkg:npm/[email protected]patch, so:scan --mode hostedreplacesresolvedwith the hostedhttps://…/left-pad-1.3.0.tgz#<sha1>URL and adds anintegrityline.scan --mode vendoredreplaces it withfile:./.socket/vendor/npm/<uuid>/left-pad-1.3.0.tgz#<sha1>.Both runs report
status: success(redirected: 1/applied: 1) with no warning, and the in-run--vexattestsnot_affected.Yarn 1, however, chooses the fetcher from the key's pattern (a git pattern → the git resolver/fetcher), not from the
resolvedvalue. It then tries to use the rewritten value as a git remote:git ls-remoteagainst the tarball URL (the patch host seesGET …/left-pad-1.3.0.tgz/info/refs?service=git-upload-pack) →error Command failed.(exit 1 or 128)error Error: spawn ENOTDIR(git is spawned with the.tgzpath as its cwd)So after the scan, every
yarn installfails, frozen or not, on a fresh checkout and in place. That includes the "re-run your package manager's install" remedy that the vendoredvexwarning suggests.This is the yarn-classic counterpart of npm #326, but the failure is different: npm silently installs the unpatched git bytes, while yarn 1 hard-fails every install. PR #345 only changes the npm lock code (
npm_lock.rs/npm_origin.rs);rewrite_yarn_classicandvendor/yarn_classic_lock.rsstill match git blocks.Impact
name@versioncan no longer install at all afterscan --mode hostedorscan --mode vendored, and CI breaks on the next run.scan --vexsaysnot_affectedin both modes. After the scan, vendoredsocket-patch vexstill saysnot_affectedfor a tree that holds the original git bytes, with only thevendored_tree_out_of_syncwarning. A fresh checkout can't install anything.file:directory andlink:blocks are already skipped (classify_classic_block), but git patterns are not.Repro (Linux, main
f6b7fb9, yarn 1.22.22)Control: the same pristine lock with no socket-patch step installs fine (
--frozen-lockfile, exit 0).$MOCKis a local mock of the patch API serving a patchedleft-pad-1.3.0.tgz(the same shape ase2e_redirect_yarn_classic_build.rs).Expected vs actual
redirect_yarn_classic_alias_skippeddoes for aliases, or the wayclassify_classic_blockalreadyLinkSkipslink:/file:directories), and it is not counted as redirected, vendored or attested. docs/ecosystems.md describes the vlt backend refusing exactly this shape ("a git, remote-tarball or local-directory node of the same package"). CLI_CONTRACT.md's VEX section says a lockfile reference is attested only when the lockfile actually wires the patched artifact.not_affected, and every lateryarn installfails.OS × version
--frozen-lockfile--frozen-lockfileyarn installof the git dep couldn't findgit("Couldn't find the binary git"), so the cell was never reachedReleased 4.0.0 behaves the same (hosted exit 128 / vendored ENOTDIR), so this isn't a recent regression.
Suspect code
crates/socket-patch-core/src/patch/redirect/mod.rs:3744, inrewrite_yarn_classic. The block is selected onreal_name == fnameplusversion_re. The key pattern's range (git+…,github:,git://,…/repo.git#…) and the existingresolvedscheme are never checked.crates/socket-patch-core/src/vendor/yarn_classic_lock.rs:676-701, inclassify_classic_block. It skipslink:andfile:directories only, so git ranges fall through toCandidate.crates/socket-patch-core/src/vex/discover/yarn.rs) then trusts the rewired block.Probe run (ubuntu/macos/windows × yarn 1.10.1/1.22.22): https://github.com/SocketDev/socket-patch/actions/runs/36761888480