[agent] Found by the scheduled Yarn classic (1.x) bug-hunt routine (ledger #304).
Summary
Take a yarn 1.22.22 project that depends on the patched package directly and through an npm: alias ("left-pad": "1.3.0", "x": "npm:left-pad@1.3.0"). yarn 1.22.22 writes two lock blocks, left-pad@1.3.0: and "x@npm:left-pad@1.3.0":.
A hosted scan pins the direct block and leaves the alias block alone, with redirect_yarn_classic_alias_skipped (documented). After the next install, node_modules/x is the unpatched upstream copy.
- Standalone
vex sees this. It warns patched_ref_unattributable ("that copy stays UNPATCHED and nothing is attested") and exits 2 with nothing written, both lock-only and after the install.
- The in-run
scan --mode hosted --vex carries the same patched_ref_unattributable warning in vex.warnings[], yet writes a not_affected statement for pkg:npm/left-pad@1.3.0.
So the run's own envelope says "nothing is attested" next to a document that attests.
Impact
CI that runs scan --mode hosted --vex publishes a not_affected OpenVEX statement for a vulnerability whose code still ships unpatched under the alias name. The scan exits 0.
Repro (Linux, main 05ecc6e, yarn 1.22.22, local mock patch API on :8787)
mkdir p && cd p
echo '{"name":"p","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0","x":"npm:left-pad@1.3.0"}}' > package.json
yarn install
socket-patch scan --mode hosted --vex in.json --json --yes --api-url http://127.0.0.1:8787 --org o --api-token x > s.json
# → exit 0; s.json: redirect.warnings = [redirect_yarn_classic_alias_skipped]
# vex.warnings = [patched_ref_unattributable … "nothing is attested"]
# in.json: one statement, pkg:npm/left-pad@1.3.0 "not_affected"
socket-patch vex --output v.json ... # → exit 2, manifest_not_found, same patched_ref_unattributable warning, nothing written
rm -rf node_modules && yarn install --frozen-lockfile
head -c 30 node_modules/left-pad/index.js # patched
head -c 30 node_modules/x/index.js # UNPATCHED
A file:./left-pad-1.3.0.tgz direct dep instead of the registry one behaves the same way.
Expected vs actual
Matrix (Linux, Node 22; each cell twice)
| yarn |
lock shape |
in-run scan --vex |
standalone vex (lock-only / post-install) |
node_modules/x |
| 1.22.22 |
two blocks |
not_affected |
refuses, exit 2 |
unpatched |
1.22.22, file: tarball + alias |
two blocks |
not_affected |
refuses, exit 2 |
unpatched |
| 1.10.1 / 1.19.0 |
one merged block left-pad@1.3.0, "x@npm:left-pad@1.3.0":, pinned as a whole |
not_affected |
— |
patched (correct) |
In my runs, every release from 1.0 to 1.22.21 (checked 1.12.3, 1.17.3, 1.19.0, 1.21.1, 1.22.0/4/10/15/17/19/21) merges the two keys into one block. Only 1.22.22, the current latest and corepack's default, writes them separately.
Suspect code
crates/socket-patch-cli/src/commands/scan/hosted.rs:1337: params.assume_applied drops the uuids in rewrite.bundled_skipped_uuids (#469), but not those whose copy the rewriter skipped as an alias (redirect_yarn_classic_alias_skipped). So the confirmed purl is attested without verification, although vex/discover flags it patched_ref_unattributable. Berry's redirect_yarn_berry_alias_skipped may take the same path (not checked).
Related: #828 (the same lock state also can't be unwound by rollback / remove; commented there).
[agent] Found by the scheduled Yarn classic (1.x) bug-hunt routine (ledger #304).
Summary
Take a yarn 1.22.22 project that depends on the patched package directly and through an
npm:alias ("left-pad": "1.3.0","x": "npm:left-pad@1.3.0"). yarn 1.22.22 writes two lock blocks,left-pad@1.3.0:and"x@npm:left-pad@1.3.0":.A hosted scan pins the direct block and leaves the alias block alone, with
redirect_yarn_classic_alias_skipped(documented). After the next install,node_modules/xis the unpatched upstream copy.vexsees this. It warnspatched_ref_unattributable("that copy stays UNPATCHED and nothing is attested") and exits 2 with nothing written, both lock-only and after the install.scan --mode hosted --vexcarries the samepatched_ref_unattributablewarning invex.warnings[], yet writes anot_affectedstatement forpkg:npm/left-pad@1.3.0.So the run's own envelope says "nothing is attested" next to a document that attests.
Impact
CI that runs
scan --mode hosted --vexpublishes anot_affectedOpenVEX statement for a vulnerability whose code still ships unpatched under the alias name. The scan exits 0.Repro (Linux, main
05ecc6e, yarn 1.22.22, local mock patch API on :8787)A
file:./left-pad-1.3.0.tgzdirect dep instead of the registry one behaves the same way.Expected vs actual
npm:aliases"): the alias entry "is left untouched … that copy keeps the unpatched artifact". Theassume_appliedcontract incommands/vex.rssays an envelope must never attest a CVE its own run left unpatched, and the Bun hosted and vendored rewiring rewritesbundled: truelock entries that Bun never fetches: the bundled copy stays unpatched, scan reports success, and vendoredvexattests not_affected #469 bundled-copy case is already excluded on that basis. The in-run document should match standalonevex: no statement for the package (or the scan fails--vexas standalone does).not_affectedis written, exit 0.Matrix (Linux, Node 22; each cell twice)
scan --vexvex(lock-only / post-install)node_modules/xfile:tarball + aliasleft-pad@1.3.0, "x@npm:left-pad@1.3.0":, pinned as a wholeIn my runs, every release from 1.0 to 1.22.21 (checked 1.12.3, 1.17.3, 1.19.0, 1.21.1, 1.22.0/4/10/15/17/19/21) merges the two keys into one block. Only 1.22.22, the current latest and corepack's default, writes them separately.
Suspect code
crates/socket-patch-cli/src/commands/scan/hosted.rs:1337:params.assume_applieddrops the uuids inrewrite.bundled_skipped_uuids(#469), but not those whose copy the rewriter skipped as an alias (redirect_yarn_classic_alias_skipped). So the confirmed purl is attested without verification, althoughvex/discoverflags itpatched_ref_unattributable. Berry'sredirect_yarn_berry_alias_skippedmay take the same path (not checked).Related: #828 (the same lock state also can't be unwound by
rollback/remove; commented there).