Repository navigation
Hosted yarn classic rescan and VEX accept a pin written by v4.0.0 whose patched package.json adds a dependency the lock doesn't have, so yarn installs the patched package without it #1462
Description
[agent] Found by the scheduled Yarn classic (1.x) bug-hunt routine (ledger #304).
Summary
This is the hosted-mode counterpart of #1379, which covered vendored mode only.
Since #1328 / #1363 (#591), a fresh hosted scan refuses a yarn classic pin when the served tarball's package.json depends on a descriptor that yarn.lock doesn't lock (redirect_yarn_classic_dep_manifest_unlocked). A lock that v4.0.0 already pinned in that state isn't caught, though. That matters because v4.0.0 is the current npm latest, so every existing hosted user upgrades from it.
- Grant with a sha1: the rescan selects which served tarballs to fetch, and it skips any artifact the lock already pins as
"<url>#…"(select_classic_artifacts). The servedpackage.jsonis never read, so the Yarn classic: a patch that adds a dependency to the package's own package.json leaves vendored yarn.lock with a dangling dependency (offline frozen install fails, lock churns), and hosted silently installs without it #591 check never runs. The scan reportsapplied/successwith no warning. - Grant without a sha1: the tarball is fetched and the scan warns
redirect_yarn_classic_dep_manifest_unlocked("not pinned and stays unpatched"). But it leaves v4's pin in the lock and exitssuccess. - In both shapes,
vexandscan --vexattestnot_affectedfor the package once it's installed.
Impact
yarn install --frozen-lockfile exits 0 and installs the patched package without the dependency its own package.json declares. A real patch that requires that dependency crashes at runtime. Meanwhile CI sees a green scan and a not_affected VEX statement.
Repro (Linux, yarn 1.22.22, local mock API)
The mock serves a left-pad@1.3.0 patch whose package/package.json adds "is-number": "^7.0.0". The grant carries sha512 + sha1. The mock is the ledger's run-18 one plus an ADD_DEP switch.
M=http://127.0.0.1:8787; export SOCKET_API_URL=$M SOCKET_PROXY_URL=$M SOCKET_PATCH_SERVER_URL=$M
API="--api-url $M --proxy-url $M --patch-server-url $M"
echo '{"name":"p","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0"}}' > package.json
yarn install
npx @socketsecurity/socket-patch@4.0.0 scan --mode hosted --json --yes $API # pins resolved "<mock>/…/left-pad-1.3.0.tgz#<sha1>"; no is-number block
socket-patch scan --mode hosted --json --yes $API # main: status success, events [applied], warnings [berry_migration_risk only]
rm -rf node_modules && yarn install --frozen-lockfile # exit 0; node_modules: left-pad only
grep is-number node_modules/left-pad/package.json # "is-number": "^7.0.0" declared, not installed
socket-patch vex --output v.json $API # exit 0, not_affected
socket-patch scan --mode hosted --vex v2.json --json --yes $API # exit 0, not_affectedControl: the same main scan on a fresh, unpinned lock refuses correctly (redirect_unconfirmed plus redirect_yarn_classic_dep_manifest_unlocked, with the lock untouched).
Expected vs actual
- Expected: CLI_CONTRACT says
redirect_yarn_classic_dep_manifest_unlockedfires when "the served tarball's ownpackage.jsondepends on a descriptor noyarn.lockblock is keyed by… The dep is not pinned and never confirmed". An existing pin in that state should be flagged on rescan (warning, non-success, or un-pinned), not confirmed asapplied.vexshouldn't attest a pin yarn installs without the patch's dependencies. That's the hosted equivalent of what Fix yarn classic check of dangling descriptors (#1379) #1388 added tovendor --check. - Actual: with a sha1 grant, the rescan reports
appliedwith no warning. With a no-sha1 grant, it warns but keeps the pin and exitssuccess. In both shapesvex/scan --vexgivenot_affected.
Matrix (main 15c2c83, Linux; lock pinned by v4.0.0)
| yarn | sha1 grant: rescan | no-sha1 grant: rescan | frozen install | vex / scan --vex |
|---|---|---|---|---|
| 1.7.0 | applied, no warning |
not run | dep missing, exit 0 | not_affected |
| 1.10.1 | applied, no warning |
not run | dep missing, exit 0 | not_affected |
| 1.22.22 | applied, no warning (×3) |
warns, pin kept, success |
dep missing, exit 0 | not_affected |
macOS and Windows weren't probed. The logic is platform-independent.
First bad version
The hole has existed since #1328 (the #591 fix) landed. v5.0.0 includes that fix and has the same gap. v4.0.0 is what wrote the dangling pins: before #591 was fixed, hosted mode never checked the served manifest.
Suspect code
crates/socket-patch-core/src/hosted/engine.rs:1307(select_classic_artifacts): an already-pinned artifact with a sha1 is never fetched, somanifestshas no entry for it.crates/socket-patch-core/src/patch/redirect/mod.rs:4085: the Yarn classic: a patch that adds a dependency to the package's own package.json leaves vendored yarn.lock with a dangling dependency (offline frozen install fails, lock churns), and hosted silently installs without it #591 check runs onlyif let Some(pkg) = &served_manifest, so a missing manifest silently passes. In the no-sha1 path the refusalcontinues, but the existing pin stays in the lock.- Hosted
vexdoesn't re-check the dependency graph of the pinned block.
Activity
mikolalysenko commentedon Oct 13, 2026
[agent] Triage: priority:p1 (yarn classic, npm family). Not a duplicate: #1379 (fixed by #1388) covered vendored mode only; this is the hosted rescan/VEX side, where an artifact already pinned with a sha1 is never fetched in
select_classic_artifacts, so the #591 served-manifest check never runs. No open PR addresses it.Generated by Claude Code
mikolalysenko commentedon Oct 13, 2026
[agent] Claiming this issue (shared root cause: the hosted yarn classic rescan never re-reads the served package.json of an artifact the lock already pins, and a refused pin left by an earlier release stays in the lock and is attested). Branch: agent/fix-yarn-classic-pinned-manifest-check. Claim-ID: 2026-10-13T02:21:15Z-bb537d
Generated by Claude Code
mikolalysenko commentedon Oct 13, 2026
[agent] Draft PR: #1463
Generated by Claude Code
Check v4 yarn classic pins against served deps
Test node lookup for the dependency check
Resolve npm deps from the copy's real path
Read the copy's package.json FIFO-safely