Repository navigation
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_nameon the first pattern) drops the block. Yarn sorts the empty pattern first, so a merged block is dropped too. On a fresh checkoutscanreportspackagesWithPatches: 0and exits 0. The vulnerable package is invisible. - Vendored (
vendor/yarn_classic_lock.rs:694,classic_key_real_nameneeds every pattern to parse) classifies the block asNoMatch. An installed scan exits 1 withvendor_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 isunpinned/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
scanon 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 installdoesn'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_foundThe single-member shape ("dependencies": {"left-pad": ""} alone, key left-pad@:) behaves the same way.
Expected vs actual
- Expected:
left-pad@:namesleft-padwith an empty range, the way yarn reads it. CLI_CONTRACT "Lockfile supplement (v3.4)" says yarn classicyarn.lockdependencies join discovery. docs/ecosystems.md "npm hosted-mode notes" says the yarn classic entry'sresolved/integrityare rewritten. The block should be inventoried, pinned in hosted mode and wired in vendored mode, likeleft-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_patterntreats an empty range as unparseable.pattern_real_name/classic_key_real_nameinherit 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
mikolalysenko commentedon Oct 9, 2026
[agent] Triaged: priority:p1 (yarn classic). Not a duplicate; no open PR covers it. Cause confirmed at
formats/yarn/patterns.rssplit_patternrejecting an empty range, which every yarn classic lock consumer goes through.Generated by Claude Code
mikolalysenko commentedon Oct 9, 2026
[agent] Claiming this issue (shared root cause: yarn key parser
split_patternrejects an empty range). Branch: agent/fix-yarn-classic-empty-range-key. Claim-ID: 2026-10-09T13:22:31Z-1c4fa9Generated by Claude Code
mikolalysenko commentedon Oct 9, 2026
[agent] Draft PR: #1274
Generated by Claude Code
Patch yarn 1 lock blocks with an empty range
Move empty-range tests out of other test bodies
mikolalysenko commentedon Oct 9, 2026
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.