Skip to content

Yarn classic hosted and vendored modes can't see or patch a yarn.lock block whose key has an empty range (left-pad@: from "left-pad": ""), so a lock-only scan reports no vulnerable package and an installed scan leaves it unpatched #1271

Description

[agent] Found by the scheduled Yarn classic (1.x) bug-hunt routine (ledger #304).

Summary

An empty version range is valid npm semver ("" means the same as *), and yarn 1 accepts it. A dependency declared as "left-pad": "" is locked under the key left-pad@:. When another manifest in the tree (another workspace member, or any transitive dependency) asks for a range that resolves to the same version, yarn merges both into one block: left-pad@, left-pad@^1.3.0:.

socket-patch's shared yarn key parser, split_pattern, rejects a pattern whose range is empty (crates/socket-patch-core/src/formats/yarn/patterns.rs:63, if name.is_empty() || range.is_empty() { return None; }). As a result:

  • Lock-only discovery (vendor/lock_inventory/yarn.rs:132, pattern_real_name on the first pattern) drops the block. Yarn sorts the empty pattern first, so a merged block is dropped too. On a fresh checkout scan reports packagesWithPatches: 0 and exits 0. The vulnerable package is invisible.
  • Vendored (vendor/yarn_classic_lock.rs:694, classic_key_real_name needs every pattern to parse) classifies the block as NoMatch. An installed scan exits 1 with vendor_lock_entry_not_found ("yarn.lock has no rewritable block for left-pad@1.3.0 — make sure the package is installed and locked"). The package is installed and locked.
  • Hosted finds no entry. It exits 0 with redirected: 0, redirect_yarn_classic_entry_not_found "no yarn.lock entry resolving left-pad@1.3.0", and the patch is unpinned / redirect_unconfirmed.

So one member's "" spec silently makes the shared block unpatchable for every member that depends on the package. There's no false VEX attestation (VEX exits 2 with nothing to attest), and agent mode patches the installed copy correctly, because it crawls node_modules.

Impact

  • A project or CI job that runs hosted / vendored scan on a lock-only checkout (the documented lockfile supplement) is told nothing is patchable while the vulnerable package is in the lock. Exit 0, no warning.
  • With an installed tree, hosted reports success with nothing pinned, and vendored fails with a remedy that's already satisfied (yarn install doesn't change the key).

Repro (Linux, main a80b89e, yarn 1.22.22; same on 1.7.0 / 1.10.1)

I used a local mock patch API that serves a pkg:npm/left-pad@1.3.0 patch (batch / view / patches/package grant with a tarball artifact), via SOCKET_API_URL / SOCKET_PROXY_URL / SOCKET_PATCH_SERVER_URL.

mkdir -p w/a w/b && cd w
echo '{"name":"root","version":"1.0.0","private":true,"workspaces":["a","b"]}' > package.json
echo '{"name":"a","version":"1.0.0","dependencies":{"left-pad":""}}'      > a/package.json
echo '{"name":"b","version":"1.0.0","dependencies":{"left-pad":"^1.3.0"}}' > b/package.json
yarn install
grep '^left-pad' yarn.lock            # left-pad@, left-pad@^1.3.0:

# lock-only checkout
rm -rf node_modules
socket-patch scan --mode hosted --json --yes     # exit 0, packagesWithPatches: 0, nothing rewritten
socket-patch scan --mode vendored --vendor-source service --json --yes   # exit 0, packagesWithPatches: 0

# installed tree
yarn install
socket-patch scan --mode hosted --json --yes     # exit 0, redirected 0, redirect_yarn_classic_entry_not_found
socket-patch scan --mode vendored --vendor-source service --json --yes   # exit 1, vendor_lock_entry_not_found

The single-member shape ("dependencies": {"left-pad": ""} alone, key left-pad@:) behaves the same way.

Expected vs actual

  • Expected: left-pad@: names left-pad with an empty range, the way yarn reads it. CLI_CONTRACT "Lockfile supplement (v3.4)" says yarn classic yarn.lock dependencies join discovery. docs/ecosystems.md "npm hosted-mode notes" says the yarn classic entry's resolved / integrity are rewritten. The block should be inventoried, pinned in hosted mode and wired in vendored mode, like left-pad@*:.
  • Actual: the block is invisible to lock-only discovery and unmatched by both rewriters.

Matrix (Linux, main a80b89e; ×2 on 1.22.22)

yarn key H lock-only H installed V lock-only V installed A
1.7.0 left-pad@, left-pad@^1.3.0: fail (0 found) fail (exit 0, unpinned) fail (0 found) fail (exit 1) untested
1.10.1 same fail fail fail fail untested
1.22.22 same fail fail fail fail pass
1.22.22 left-pad@: fail — fail — —
1.22.22 left-pad@*:, left-pad@latest:, "left-pad@>=1.0.0 <2":, "left-pad@1.2.0 || 1.3.0":, left-pad@v1.3.0:, left-pad@=1.3.0:, "left-pad@ 1.3.0":, "left-pad@1.3.0 - 1.3.0":, "left-pad@npm:left-pad@latest": (controls) pass — pass — —

Controls: H and V scan, then a fresh --frozen-lockfile install is patched and lock-only vex attests.

First bad release: not a regression. The published v4.0.0 behaves identically: lock-only finds nothing, hosted gives redirect_yarn_classic_entry_not_found, vendored gives vendor_lock_entry_not_found.

Suspect code

  • crates/socket-patch-core/src/formats/yarn/patterns.rs:63: split_pattern treats an empty range as unparseable. pattern_real_name / classic_key_real_name inherit this.
  • crates/socket-patch-core/src/vendor/lock_inventory/yarn.rs:132: lock-only inventory.
  • crates/socket-patch-core/src/vendor/yarn_classic_lock.rs:694: vendored block classification.
  • The hosted classic rewriter's block match, which emits redirect_yarn_classic_entry_not_found (patch/redirect/mod.rs:3917).

No probe run was needed: the parse doesn't depend on the OS or on line endings.

Activity

  1. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged: priority:p1 (yarn classic). Not a duplicate; no open PR covers it. Cause confirmed at formats/yarn/patterns.rs split_pattern rejecting an empty range, which every yarn classic lock consumer goes through.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: yarn key parser split_pattern rejects an empty range). Branch: agent/fix-yarn-classic-empty-range-key. Claim-ID: 2026-10-09T13:22:31Z-1c4fa9


    Generated by Claude Code

  3. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1274


    Generated by Claude Code

  4. added 2 commits that reference this issue on Oct 9, 2026
    db4ebb1
    291a108
  5. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    v5 triage: P2, not a release blocker. Drop P1 to P2 for the valid but unusual empty Yarn range. Keep the targeted fix in PR #1274; it need not block the entire v5 release.

    This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX.

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