Skip to content

Fix yarn classic rewiring git-sourced lock blocks (#363) - #710

Open
Mikola Lysenko (mikolalysenko) wants to merge 3 commits into
mainfrom
agent/fix-yarn-classic-git-pattern-blocks
Open

Mikola Lysenko (mikolalysenko) wants to merge 3 commits into
mainfrom
agent/fix-yarn-classic-git-pattern-blocks

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #363

Summary

Yarn classic projects that depend on a package through git ("left-pad": "git+https://…/left-pad.git#v1.3.0") could no longer install anything after scan --mode hosted or scan --mode vendored. The scan reported success and the in-run VEX attested not_affected. These entries are now left alone with a named warning and are never attested. Rollback fails closed on a git entry an older release had already rewired, instead of "restoring" it to a registry tarball that still breaks the install.

Root cause

The yarn classic lock code picks a yarn.lock block by package name and version only. Three places do this: the hosted rewriter (patch::redirect::rewrite_yarn_classic), the vendored classifier (vendor::yarn_classic_lock::classify_classic_block) and the hosted restorer (upstream::npm::restore_classic). VEX discovery (vex::discover::yarn) then trusted whatever the block's resolved said.

Yarn 1, however, picks its fetcher from the key's pattern. A pattern that yarn's GitResolver.isVersion accepts goes through the git fetcher, which uses resolved as the git remote. Those patterns are git+<x>:, git:, ssh:, a url whose path ends in .git, or a bare https://github.com|gitlab.com|bitbucket.*/<owner>/<repo>. A tarball written into resolved makes yarn run git ls-remote on a .tgz (hosted) or spawn git inside one (vendored, ENOTDIR).

Fix

  • vendor::yarn_classic_lock::classic_block_is_git / yarn_classic_range_is_git is one shared model of yarn 1's git detection. It checks each key pattern's range (an npm: alias's target included) and the block's own resolved.
  • Hosted rewriter: a git block (an npm: alias of a git range included, checked before the alias gate) stays byte-identical, with redirect_yarn_classic_git_skipped naming it. If another block of the same package was rewired, the uuid goes into bundled_skipped_uuids, so the in-run --vex verifies the installed tree instead of assuming the patch applied.
  • Vendored: the new BlockClass::GitSkip gives vendor_yarn_classic_git_entry_skipped. A git-only lock is refused with vendor_lock_entry_not_found before any write.
  • VEX discovery: a git copy is never attested. It counts as resolved elsewhere for other locks, it drops any same-yarn.lock ref for that package (patched_ref_unattributable), and a Socket wiring on a git block is diagnosed instead of attested. Rollback, which plans from discovery, therefore fails such a pin closed with the git checkout -- yarn.lock remedy.
  • Restore (defense in depth, when handed such a pin directly): refuses instead of writing a registry tarball.
  • Hosted-git shorthands (owner/repo, github:owner/repo) lock to a codeload tarball that yarn fetches as a tarball, so they are still rewired. The issue's 2026-10-01 evidence called this scope out.
  • docs/ecosystems.md documents the behaviour.

No wrapper changes: npm/, pypi/ and gem/ only dispatch to the binary.

Tests (red → green)

Path Test Red without the fix
classifier vendor::yarn_classic_lock::tests::yarn_classic_git_ranges_are_recognized new function
hosted rewrite patch::redirect::tests::yarn_classic_git_pattern_block_is_skipped (git-only, git beside registry, an npm: alias of a git range, codeload shorthand) ✗ → ✓
vendored vendor::yarn_classic_lock::tests::git_pattern_block_is_skipped_with_warning, git_only_lock_is_refused_untouched, codeload_shorthand_block_is_still_rewritten (regression guard) ✗ ✗ → ✓ ✓ (the shorthand guard passed both ways)
VEX discovery vex::discover::yarn::tests::classic_git_pattern_copies_are_never_attested ✗ → ✓
restore upstream_restore_golden::yarn_classic_git_pattern_pin_is_refused ✗ (fetched the registry) → ✓
rollback in_process_rollback_hosted::a_git_pattern_hosted_pin_is_refused_not_restored_to_the_registry ✗ (exit 0) → ✓
e2e, real yarn 1.22.22 e2e_redirect_yarn_classic_build::classic_git_sourced_dependency_is_left_unrewired: local git+file: source, hosted scan leaves yarn.lock byte-identical, names the skip, attests nothing, fresh yarn install --frozen-lockfile succeeds —

Local runs:

  • cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test -p socket-patch-core --all-features: 4852 lib tests, 4848 pass. The 4 failures are chmod-based permission tests that cannot fail when run as root, which this sandbox is; they are untouched by this PR. Integration suites pass.
  • e2e_redirect_yarn_classic_build (13), e2e_vendor_yarn_classic_build (11), in_process_rollback_hosted (23), e2e_vex_lockfile (286), mode_migration_npm (15), upstream_restore_golden (43): all pass. The yarn e2e suites ran with SOCKET_PATCH_YARN_E2E_REQUIRED=1.
  • cargo fmt: the changed regions are rustfmt-clean. main itself is not rustfmt-clean under the pinned 1.93.1 rustfmt, and CI runs no fmt job, so unrelated files were left alone.

The e2e test skips on Windows, where git+file: urls over drive-letter paths are unreliable in yarn 1. The logic is OS-independent and unit-tested.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HfVbURTmD44zbwytAUWMcE


Note

Medium Risk
Changes lockfile rewrite, VEX attestation, and rollback for Yarn Classic git dependencies—security-sensitive paths—but behavior is narrowly scoped, fail-closed, and heavily tested.

Overview
Fixes #363: Yarn Classic lock entries that install from git are no longer rewritten to hosted or vendored tarballs, which previously broke yarn install because Yarn 1 still fetches those blocks via git using resolved as the remote.

Shared detection (classic_block_is_git / yarn_classic_range_is_git) mirrors Yarn 1’s git-pattern rules. Hosted redirect skips matching blocks with redirect_yarn_classic_git_skipped and treats the patch uuid like other unverified copies (bundled_skipped_uuids). Vendored flow adds GitSkip / vendor_yarn_classic_git_entry_skipped. VEX discovery does not attest packages when a live git copy exists in yarn.lock, including legacy Socket-wired git blocks (DIAG_REF_UNATTRIBUTABLE). Rollback / upstream restore refuses to “fix” git-pattern pins by swapping in registry tarballs. Codeload shorthand blocks remain rewritable.

Coverage adds unit, golden, rollback, and real Yarn 1 e2e tests; docs/ecosystems.md documents the behavior.

Reviewed by Cursor Bugbot for commit 15b5702. Configure here.

yarn 1 installs a git dependency (git+https:, git:, ssh:, a .git url,
or a github.com/<owner>/<repo> url) with its git fetcher, and uses the
lock entry's resolved value as the git remote. Hosted and vendored
scans rewrote that value to a tarball, so every later yarn install
failed while the scan reported success. Rollback then wrote a registry
tarball in the same place, which fails the same way.

Both rewriters now leave these entries byte-identical, with a warning
that names them. Rollback refuses a hosted pin on such an entry and
points to git checkout. Hosted codeload shorthands (owner/repo,
github:) are tarballs to yarn and are still rewired.

Refs #363

Assisted-by: Claude Code:claude-opus-5-5
yarn 1 installs a git-sourced yarn.lock entry from git, whatever its
resolved value says, so that copy stays unpatched. vex no longer
attests the package from yarn.lock while such a copy exists. A Socket
wiring that an older release wrote onto a git entry is reported
instead of attested, and rollback fails it closed. An end-to-end test
against real yarn 1 checks that a hosted scan leaves a git dependency
alone and that a frozen install still succeeds.

Fixes #363

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) force-pushed the agent/fix-yarn-classic-git-pattern-blocks branch from a0af7a6 to 15b5702 Compare October 3, 2026 18:27
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 3, 2026 18:30
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/patch/redirect/mod.rs
The hosted yarn classic rewriter checked for alias-only blocks before
git blocks. An npm: alias of a git range was therefore reported as an
alias skip, and its uuid was not excluded from the in-run --vex
assumption. That let the VEX attest a package whose aliased git copy
stays unpatched. The git check now runs first.

Refs #363

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit e96a709. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 3, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[burn-down agent] Ready for review at head e96a709.

  • CI: 491/491 check runs passed (485 success, 6 matrix/gated jobs skipped by design: e2e-full, e2e-docker, canary, downgrade, and two unexpanded matrix templates).
  • Bugbot: reviewed e96a709, no new issues. Its one earlier finding (an npm: alias of a git range bypassed the git skip and the VEX exclusion) was fixed in e96a709, and the thread is resolved.
  • Merges cleanly into main.
  • For reviewers: classic_block_is_git mirrors yarn 1's GitResolver.isVersion. Codeload shorthands (owner/repo) are deliberately still rewired.

Slack announcement not sent: this run has no Slack send tool.


Generated by Claude Code

This branch has not been deployed

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

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

3 participants