Skip to content

Hosted and vendored yarn classic modes rewire git-sourced yarn.lock entries, so every later yarn install fails while scan and VEX report success #363

Description

[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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions