[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).
Summary
When a project's registry (npmRegistryServer) returns dist.tarball URLs that aren't at yarn's conventional path (<registry>/<name>/-/<name>-<version>.tgz), yarn locks the package as resolution: "left-pad@npm:1.3.0::__archiveUrl=<percent-encoded tarball URL>", and fetches from that URL.
scan --mode hosted pins such an entry correctly. But rollback and remove rebuild the original entry in restore_berry as a bare resolution: "left-pad@npm:1.3.0" and drop the ::__archiveUrl= binding. Yarn trusts the locked locator, so a cold-cache yarn install --immutable then fetches <registry>/left-pad/-/left-pad-1.3.0.tgz. On a registry that only serves the URLs it advertises, that fails with YN0001. The lock from before the hosted pin installs fine against the same registry.
Release 4.0.0 kept the original entry verbatim in .socket/vendor/redirect-state.json ("original": "...::__archiveUrl=http%3A%2F%2F127.0.0.1%3A8766%2Ffiles%2F..."), so its revert was exact. v5 dropped the hosted ledger and rebuilds the entry from registry metadata (SOCKET_NPM_REGISTRY, npmjs by default), which never sees the project's registry or its tarball URL.
Impact
- After
socket-patch rollback or remove, the restored yarn.lock differs from what yarn wrote: the locator loses the binding.
- On registries whose advertised tarball URLs differ from the conventional path and which don't also serve the conventional path, every fresh or CI install (cold cache) fails after the revert. Proxies that point
dist.tarball at another host or path are one example; GitHub Packages-style /download/@scope/name/<v>/<hash> URLs are another. Warm caches hide it, because yarn finds the zip by checksum.
yarn install --immutable does not flag the lock change (hardened mode and --refresh-lockfile accept it too), so the broken lock is committed silently.
This is the yarn-berry counterpart of #557 (pnpm rollback drops tarball:).
Repro
The repro uses a local registry proxy that rewrites every dist.tarball to http://127.0.0.1:8766/files/<url-encoded npmjs tarball URL> and serves only those URLs. It also uses a mock patch API serving batch, by-package, view, patches/package (with the yarn-berry-zip yarnBerry10c0), the hosted tarball and /upstream/npm/<uuid>.json.
mkdir hD && cd hD
echo '{"name":"hD","private":true,"dependencies":{"left-pad":"^1.3.0"}}' > package.json
printf 'nodeLinker: node-modules\nenableGlobalCache: false\nnpmRegistryServer: "http://127.0.0.1:8766"\nunsafeHttpWhitelist: ["127.0.0.1"]\n' > .yarnrc.yml
yarn install # lock: resolution "left-pad@npm:1.3.0::__archiveUrl=http%3A%2F%2F127.0.0.1%3A8766%2Ffiles%2F..."
cp yarn.lock /tmp/lock.orig
SOCKET_NPM_REGISTRY=http://127.0.0.1:8766 socket-patch scan --mode hosted --json --yes \
--api-url $MOCK --api-token x --org org --patch-server-url $MOCK # success, redirected: 1
# (fresh checkout + yarn install --immutable here: patched, exit 0)
SOCKET_NPM_REGISTRY=http://127.0.0.1:8766 socket-patch rollback --json \
--api-url $MOCK --api-token x --org org --patch-server-url $MOCK # success, reverted: [pkg:npm/left-pad@1.3.0]
git diff --no-index /tmp/lock.orig yarn.lock
# - resolution: "left-pad@npm:1.3.0::__archiveUrl=http%3A%2F%2F127.0.0.1%3A8766%2Ffiles%2Fhttps%253A%252F%252Fregistry.npmjs.org%252Fleft-pad%252F-%252Fleft-pad-1.3.0.tgz"
# + resolution: "left-pad@npm:1.3.0"
# fresh checkout, cold cache:
YARN_GLOBAL_FOLDER=$(mktemp -d) yarn install --immutable
# ➤ YN0001: │ RequestError: socket hang up (GET /left-pad/-/left-pad-1.3.0.tgz, which the registry doesn't serve)
# exit 1
With /tmp/lock.orig restored, the same cold-cache yarn install --immutable exits 0. remove pkg:npm/left-pad@1.3.0 behaves the same as rollback. Vendored vendor --revert on the same project is byte-exact and installs fine.
Expected vs actual
- Expected: per docs/testing/yarn-berry-compatibility.md,
rollback/remove restore the "upstream entries", meaning the entry yarn resolves from the project's registry. The project must be installable afterwards, as it was before the pin (release 4.0.0 restored the entry byte-exact). At minimum, when the pinned entry's original locator can't be rebuilt, the revert should refuse loudly, as it already does for other unrebuildable entries ("restore it from version control").
- Actual: exit 0,
reverted, and a lock whose locator yarn fetches from a URL the registry never advertised.
Matrix (Linux; main 045d7ec)
| yarn |
rollback |
remove |
control (original lock, cold cache) |
| 4.0.2 |
fail (YN0001) |
fail (YN0001) |
pass |
| 4.12.0 |
fail (YN0001) |
fail (YN0001) |
pass |
| 4.18.1 |
fail (YN0001) |
fail (YN0001) |
pass |
Each cell was run twice. A control project on the default registry (conventional URLs) rolls back byte-exact. Release 4.0.0 recorded the original entry in redirect-state.json, so this behaviour dates from the v5 removal of the hosted ledger. I didn't bisect the exact commit. macOS and Windows weren't probed (the restore is platform-independent string surgery).
Suspect code
crates/socket-patch-core/src/patch/redirect/upstream/npm.rs:557: restore_berry writes resolution: "{name}@npm:{version}" unconditionally. The project's .yarnrc.yml npmRegistryServer / npmScopes and the registry's dist.tarball are never consulted, and npm_dist (client.rs:280) reads SOCKET_NPM_REGISTRY rather than the project's registry.
[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).
Summary
When a project's registry (
npmRegistryServer) returnsdist.tarballURLs that aren't at yarn's conventional path (<registry>/<name>/-/<name>-<version>.tgz), yarn locks the package asresolution: "left-pad@npm:1.3.0::__archiveUrl=<percent-encoded tarball URL>", and fetches from that URL.scan --mode hostedpins such an entry correctly. Butrollbackandremoverebuild the original entry inrestore_berryas a bareresolution: "left-pad@npm:1.3.0"and drop the::__archiveUrl=binding. Yarn trusts the locked locator, so a cold-cacheyarn install --immutablethen fetches<registry>/left-pad/-/left-pad-1.3.0.tgz. On a registry that only serves the URLs it advertises, that fails withYN0001. The lock from before the hosted pin installs fine against the same registry.Release 4.0.0 kept the original entry verbatim in
.socket/vendor/redirect-state.json("original": "...::__archiveUrl=http%3A%2F%2F127.0.0.1%3A8766%2Ffiles%2F..."), so its revert was exact. v5 dropped the hosted ledger and rebuilds the entry from registry metadata (SOCKET_NPM_REGISTRY, npmjs by default), which never sees the project's registry or its tarball URL.Impact
socket-patch rollbackorremove, the restoredyarn.lockdiffers from what yarn wrote: the locator loses the binding.dist.tarballat another host or path are one example; GitHub Packages-style/download/@scope/name/<v>/<hash>URLs are another. Warm caches hide it, because yarn finds the zip by checksum.yarn install --immutabledoes not flag the lock change (hardened mode and--refresh-lockfileaccept it too), so the broken lock is committed silently.This is the yarn-berry counterpart of #557 (pnpm rollback drops
tarball:).Repro
The repro uses a local registry proxy that rewrites every
dist.tarballtohttp://127.0.0.1:8766/files/<url-encoded npmjs tarball URL>and serves only those URLs. It also uses a mock patch API serving batch, by-package, view,patches/package(with theyarn-berry-zipyarnBerry10c0), the hosted tarball and/upstream/npm/<uuid>.json.With
/tmp/lock.origrestored, the same cold-cacheyarn install --immutableexits 0.remove pkg:npm/left-pad@1.3.0behaves the same asrollback. Vendoredvendor --reverton the same project is byte-exact and installs fine.Expected vs actual
rollback/removerestore the "upstream entries", meaning the entry yarn resolves from the project's registry. The project must be installable afterwards, as it was before the pin (release 4.0.0 restored the entry byte-exact). At minimum, when the pinned entry's original locator can't be rebuilt, the revert should refuse loudly, as it already does for other unrebuildable entries ("restore it from version control").reverted, and a lock whose locator yarn fetches from a URL the registry never advertised.Matrix (Linux; main
045d7ec)Each cell was run twice. A control project on the default registry (conventional URLs) rolls back byte-exact. Release 4.0.0 recorded the original entry in
redirect-state.json, so this behaviour dates from the v5 removal of the hosted ledger. I didn't bisect the exact commit. macOS and Windows weren't probed (the restore is platform-independent string surgery).Suspect code
crates/socket-patch-core/src/patch/redirect/upstream/npm.rs:557:restore_berrywritesresolution: "{name}@npm:{version}"unconditionally. The project's.yarnrc.ymlnpmRegistryServer/npmScopesand the registry'sdist.tarballare never consulted, andnpm_dist(client.rs:280) readsSOCKET_NPM_REGISTRYrather than the project's registry.