Repository navigation
npm hosted pin next to a bundled copy can't be unwound: rollback/remove refuse it, and the vendored takeover skips the restore, so vendor --revert lands back on hosted and allow-remote=all stays #828
Description
Activity
- addedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:npmnpmnpm
on Oct 5, 2026 - added a commit that references this issue
on Oct 5, 2026 mikolalysenko commented
on Oct 5, 2026 CollaboratorAuthorMore actions[agent] Triaged: priority:p1 (npm). Not a duplicate. #325 / #669 and #588 / #589 are about VEX attestation. This issue is about unwinding hosted state: npm discovery drops a bundled-contested ref as
patched_ref_unattributable, so the management commands can't see the pin hosted mode wrote. That isHostedPin::allin the vendored takeover, plus theContestedWiringrefusal inredirect/upstream/mod.rs. No open PR covers it. Not clustered with any other open issue.
Generated by Claude Code
mikolalysenko commented
on Oct 5, 2026 CollaboratorAuthorMore actions[agent] Yarn classic bug-hunt routine (ledger #304): yarn classic reproduces this too, with a git-sourced copy of the same
name@versioninstead of a bundled one. I'm adding the matrix here rather than filing a duplicate, since the refusal is the sameContestedWiringpath. The trigger is new with #710 (the #363 fix,cfe060d). Before it, the git block was rewired too (and broke installs), so this state couldn't arise.Shape: a yarn workspace where
adepends onleft-pad@^1.3.0(registry) andbonleft-pad@git+https://github.com/stevemao/left-pad.git#v1.3.0. yarn.lock then has a registry block and a git block, both1.3.0. Local mock patch API,SOCKET_PATCH_SERVER_URLset to it.yarn install && git add -A && git commit -qm init socket-patch scan --mode hosted # exit 0, redirected: 1, redirect_yarn_classic_git_skipped; registry block pinned socket-patch scan --mode hosted # exit 0 (re-run) socket-patch list # exit 1, hosted_wiring_contested socket-patch rollback # exit 1, patched_ref_unattributable ("… installs from git …"); lock unchanged socket-patch remove pkg:npm/left-pad@1.3.0 # exit 1, hosted_wiring_contested socket-patch repair # exit 0, lock still pinned socket-patch scan --mode vendored --vendor-source service # exit 0, no vendor_takeover_reverted_redirect event; # .socket/vendor/state.json wiring original = the hosted URL socket-patch vendor --revert # exit 0; yarn.lock back on the hosted pin, not upstream socket-patch rollback # exit 1 again
Linux, Node 22, main
4646693:yarn hosted scan rollback / remove / list hosted → vendored takeover restores upstream vendor --revertlands on1.0.2 exit 0, pinned refused (exit 1) not run — 1.7.0 exit 0, pinned refused (exit 1) not run — 1.10.1 exit 0, pinned refused (exit 1) no (ledger original = hosted URL) hosted pin 1.22.22 exit 0, pinned (×2) refused (exit 1, ×2) no (ledger original = hosted URL) hosted pin The pure-vendored flow on the same project is fine: it wires the registry block, warns
vendor_yarn_classic_git_entry_skipped, and rollback is byte-exact.vexnever attests here, as documented. The yarn-side discovery that turns the git sibling intopatched_ref_unattributableiscrates/socket-patch-core/src/vex/discover/yarn.rs(added in #710). So a fix inupstream/mod.rs/ the takeover'shosted_pinsshould cover yarn classic too. Please include a yarn classic git-sibling case in its tests.
Generated by Claude Code
mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actionsBeing fixed in draft PR #1008 (batch fix for open
pm:npmissues).- added a commit that references this issue
on Oct 7, 2026 mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions[agent] Yarn classic bug-hunt routine (ledger #304): a third trigger for the same
ContestedWiringrefusal, annpm:self-alias. This needs no git or bundled copy, only yarn 1.22.22 and a plainpackage.json. Main05ecc6e, Linux, ×2. I'm adding it here so the in-progress fix (#1008) can cover it.echo '{"name":"p","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0","lp":"npm:left-pad@1.3.0"}}' > package.json yarn install # yarn 1.22.22 writes two blocks: `left-pad@1.3.0:` and `"lp@npm:left-pad@1.3.0":` socket-patch scan --mode hosted # pins left-pad@1.3.0, skips the alias block with redirect_yarn_classic_alias_skipped (documented), exit 0 socket-patch rollback # exit 1 patched_ref_unattributable: lock entry `"lp@npm:left-pad@1.3.0"` installs it from registry.yarnpkg.com … socket-patch remove pkg:npm/left-pad@1.3.0 # exit 1, same socket-patch list --json # exit 1 socket-patch scan --mode hosted # exit 0, writes the same state again (the suggested remedy loops) socket-patch scan --mode vendored # exit 0, "applied", no takeover-revert event socket-patch vendor --revert # exit 0, but yarn.lock's left-pad@1.3.0 block is back on the hosted URL, not upstream
So it's the same three symptoms as the npm bundled case: management commands refuse the pin the scan just wrote, the vendored takeover skips the upstream restore, and
vendor --revertlands on hosted.yarn 1.10.1 isn't affected: it merges the two keys into one block (
left-pad@1.3.0, "lp@npm:left-pad@1.3.0":), which hosted pins as a whole, and rollback works. Any yarn release that keeps the alias in its own block (1.22.22 here) hits it.
Generated by Claude Code
- added 5 commits that reference this issue
on Oct 7, 2026
[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
When a project installs
name@versiontwice, once as a normal registry entry and once bundled inside another package (inBundle: true), a bare hostedscanrewires the normal entry and warns that the bundled copy stays unpatched (redirect_npm_bundled_instance_skipped). That's the documented behaviour. But afterwards socket-patch can't manage the pin it just wrote:rollbackandremoverefuse it. They exit 1 with "package-lock.json wire(s) Socket-hosted patches that cannot be attributed to one package version … (patched_ref_unattributable: … also installs a bundled copy …)". The suggested fix is "re-runsocket-patch scan --mode hosted", which writes the same state again. The only way out isgit checkout.scan --mode vendored) quietly skips the upstream restore. It exits 0, emits novendor_takeover_reverted_redirectevent, and records the hosted URL (with its grant token) as the vendor ledger's wiringoriginal. The project.npmrcthat hosted mode created (exactlyallow-remote=all\n) is left in place.vendor --revertlands back on the hosted pin, not upstream..npmrcstill saysallow-remote=all, androllbackstill refuses, as in (1).Impact
npmitself bundlesansi-regex,semverand others) can't undo it with socket-patch, and every suggested remedy loops.allow-remote=allcommitted. On npm 12 that silently turns off theallow-remote=nonedefault for every URL dependency, though no hosted entry needs it any more.crates/socket-patch-cli/src/commands/vendor.rs:2380names this as exactly what the restore exists to prevent.Expected (CLI_CONTRACT.md)
originalandvendor --revertlands back on upstream registry state, never on hosted". A purl whose upstream entry can't be restored "failsredirect_revert_failed… (exit 1 /partial_failure, nothing vendored for it, the hosted wiring left in place)". So it should either restore or refuse loudly. Today it does neither.allow-remoteunwind: "oncerollback,removeor a hosted → vendored takeover has restored the last hosted entry … a project.npmrcholding exactlyallow-remote=all\n… is deleted".rollback/removeshould be able to restore a pin that a plain hostedscanwrote. Without a bundled copy they can, and the same takeover printsNote: pkg:npm/ms@2.1.3 was hosted; restored its upstream registry entry (.npmrc, package-lock.json) before vendoring (mode takeover)and deletes.npmrc.Repro (Linux, main
045d7ec, reproduced twice on each npm)The patch comes from a local mock of the public patch proxy (
SOCKET_PROXY_URL=http://127.0.0.1:8787, routes/patch/batch,/patch/view,/patch/blob,/patch/packageplus the hosted tarball), and every command gets--patch-server-url http://127.0.0.1:8787.Result, the same on every cell:
Control: the same project without
bundpasses. Rollback restoresregistry.npmjs.org, the takeover prints the restore note, and.npmrcis deleted.First bad: not bisected. v4.0.0 used the ledger-based hosted design (its
rollbackneeds.socket/manifest.json), so there's no like-for-like v4 cell.Suspect code
crates/socket-patch-core/src/vex/discover/npm.rs:104-125: a ref whose purl also has a bundled copy is turned into apatched_ref_unattributablediagnostic and dropped fromrefs. That's right for VEX, but the same discovery feeds the management commands.crates/socket-patch-cli/src/commands/vendor.rs:2200-2207: the takeover'shosted_pinscome only fromHostedPin::all(&discovery), i.e. attributable refs. A contested pin is invisible, sohosted_pin_ofreturnsNone, the restore block at:2385is skipped, and the backend vendors on top of the hosted entry. The eject path does checkinventory.contested_refusal()(vendor.rs:706), but the scan/get vendored takeover doesn't.crates/socket-patch-core/src/patch/redirect/upstream/mod.rs:235-260:rollback/removerefuse anyContestedWiring, including this one, which the hosted rewriter itself produced. Its remedy text ("re-runsocket-patch scan --mode hosted") loops.Related but separate: #325 / draft #669 (in-run VEX attesting the bundled-contested purl) and #588 (an unwired same-lock registry copy). This issue is about unwinding hosted state, not attestation.