[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).
Summary
Once a Bun project's lock carries hosted pins (bun.lock URL 3-tuples, or the rewritten bun.lockb records), lockfile-only discovery no longer inventories those packages. In a checkout without node_modules (the usual CI shape, and the one bun-compatibility.md calls lockfile-only), every later scan sees scannedPackages: 0 for them and exits 0 with status: "success":
- Hosted re-run: when a superseding patch (new uuid) is published,
scan --mode hosted reports redirected: 0, updates: 0 and leaves the lock pinned to the old uuid. With node_modules present, the same re-run upgrades (updates: 1). The npm package-lock.json lockfile-only re-run also upgrades.
- Hosted → vendored takeover:
scan --mode vendored reports success with 0 packages and writes nothing, so the project silently stays hosted. socket-patch vendor on the same checkout does take it over (applied 3), and so does scan --mode vendored with node_modules present.
Vendored pins aren't affected: a lockfile-only vendored re-run upgrades to the new uuid through the vendor ledger.
Impact
CI jobs that run socket-patch scan --mode hosted on a fresh checkout (no install) never pick up a superseding patch for a package that is already hosted-pinned. Until somebody runs the scan in a tree with node_modules, the project keeps the superseded patch, and the JSON envelope gives no hint: success, 0 scanned, no warning. The same goes for a requested hosted → vendored migration. Nothing unpatched gets attested, because vex still correctly verifies the old patch, so this is a silent no-op rather than a false attestation.
Repro (Linux, Bun 1.4.2; mock patch API at SOCKET_PROXY_URL / SOCKET_PATCH_SERVER_URL)
mkdir t && cd t
echo '{"name":"t","version":"1.0.0","dependencies":{"left-pad":"1.3.0","is-odd":"3.0.1"}}' > package.json
bun install && rm -rf node_modules # lockfile-only checkout
socket-patch scan --mode hosted --json # scannedPackages 3, redirected 3 (ok)
socket-patch scan --mode hosted --json # scannedPackages 0, redirected 0 (was 3)
# publish a superseding patch for left-pad@1.3.0 (new uuid) on the API, then:
socket-patch scan --mode hosted --json # success, scanned 0, updates 0; lock still pins the old uuid
socket-patch scan --mode vendored --json # success, scanned 0; bun.lock unchanged (still hosted)
socket-patch vendor --json # applied 3: the takeover works through this path
Control: bun install (populating node_modules), then the same hosted re-run → scanned 3, updates 1, the lock moves to the new uuid, and a fresh frozen install has the new patch's bytes.
Expected vs actual
- Expected: docs/ecosystems.md says Bun locks are patched "in hosted and vendored modes, including lockfile-only discovery". docs/testing/bun-compatibility.md ("Mode conversion") says
scan / get --mode vendored over a hosted bun.lock restores the hosted line and vendors. A re-run should rediscover the hosted pins, as npm lockfile-only does and as Bun does with node_modules.
- Actual: hosted-pinned packages are invisible to lockfile-only discovery, the run reports
success with 0 packages, and the upgrade or takeover silently doesn't happen.
Matrix (Linux, main 045d7ec)
| Lock |
Bun writer |
Lockfile-only hosted re-run (superseding patch) |
Lockfile-only scan --mode vendored over hosted |
Same with node_modules |
| text v2 |
1.4.2 |
fail (0 scanned, old uuid kept) |
fail (0 scanned, still hosted) |
pass |
| text v1 workspace |
1.3.14 |
fail |
not run |
not run |
bun.lockb |
1.1.45 |
fail |
fail |
not run |
npm package-lock.json (control) |
npm |
pass (updates: 1) |
pass (vendor applied 3) |
pass |
| vendored text lock (control) |
1.4.2 |
pass (lockfile-only vendored re-run upgrades) |
n/a |
n/a |
Reproduced several times on main and on the published 4.0.0 binary. 3.3.0 predates --mode, so the first bad release is 4.0.0, the first release with hosted mode. It isn't a regression. macOS/Windows weren't probed (the bug is in platform-independent lock parsing).
Suspect code
crates/socket-patch-core/src/vendor/lock_inventory/bun.rs:94: text bun.lock inventory keeps only registry 4-tuples. The hosted pin is the URL 3-tuple ["name@http…/name-ver.tgz", {deps}, "sha512-…"], so it is skipped (and its spec "version" is a URL anyway, line 107).
crates/socket-patch-core/src/vendor/lock_inventory/bun.rs:61-65: bun.lockb inventory drops records whose version isn't a registry version, which covers the rewritten hosted records.
- The npm inventory still sees hosted entries because
package-lock.json keeps version next to the hosted resolved URL. A Bun hosted 3-tuple could be recognised as a Socket hosted URL and mapped back to name@version (the version is in the URL path), the way vendored tuples are recovered from their ledger.
No probe runs: the sandbox can't delete probe branches yet (see ledger #306), so this is Linux-only evidence.
[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).
Summary
Once a Bun project's lock carries hosted pins (
bun.lockURL 3-tuples, or the rewrittenbun.lockbrecords), lockfile-only discovery no longer inventories those packages. In a checkout withoutnode_modules(the usual CI shape, and the onebun-compatibility.mdcallslockfile-only), every laterscanseesscannedPackages: 0for them and exits 0 withstatus: "success":scan --mode hostedreportsredirected: 0, updates: 0and leaves the lock pinned to the old uuid. Withnode_modulespresent, the same re-run upgrades (updates: 1). The npmpackage-lock.jsonlockfile-only re-run also upgrades.scan --mode vendoredreportssuccesswith 0 packages and writes nothing, so the project silently stays hosted.socket-patch vendoron the same checkout does take it over (applied 3), and so doesscan --mode vendoredwithnode_modulespresent.Vendored pins aren't affected: a lockfile-only vendored re-run upgrades to the new uuid through the vendor ledger.
Impact
CI jobs that run
socket-patch scan --mode hostedon a fresh checkout (no install) never pick up a superseding patch for a package that is already hosted-pinned. Until somebody runs the scan in a tree withnode_modules, the project keeps the superseded patch, and the JSON envelope gives no hint: success, 0 scanned, no warning. The same goes for a requested hosted → vendored migration. Nothing unpatched gets attested, becausevexstill correctly verifies the old patch, so this is a silent no-op rather than a false attestation.Repro (Linux, Bun 1.4.2; mock patch API at
SOCKET_PROXY_URL/SOCKET_PATCH_SERVER_URL)Control:
bun install(populatingnode_modules), then the same hosted re-run →scanned 3, updates 1, the lock moves to the new uuid, and a fresh frozen install has the new patch's bytes.Expected vs actual
scan/get --mode vendoredover a hostedbun.lockrestores the hosted line and vendors. A re-run should rediscover the hosted pins, as npm lockfile-only does and as Bun does withnode_modules.successwith 0 packages, and the upgrade or takeover silently doesn't happen.Matrix (Linux, main
045d7ec)scan --mode vendoredover hostednode_modulesbun.lockbpackage-lock.json(control)updates: 1)Reproduced several times on main and on the published 4.0.0 binary. 3.3.0 predates
--mode, so the first bad release is 4.0.0, the first release with hosted mode. It isn't a regression. macOS/Windows weren't probed (the bug is in platform-independent lock parsing).Suspect code
crates/socket-patch-core/src/vendor/lock_inventory/bun.rs:94: textbun.lockinventory keeps only registry 4-tuples. The hosted pin is the URL 3-tuple["name@http…/name-ver.tgz", {deps}, "sha512-…"], so it is skipped (and its spec "version" is a URL anyway, line 107).crates/socket-patch-core/src/vendor/lock_inventory/bun.rs:61-65:bun.lockbinventory drops records whose version isn't a registry version, which covers the rewritten hosted records.package-lock.jsonkeepsversionnext to the hostedresolvedURL. A Bun hosted 3-tuple could be recognised as a Socket hosted URL and mapped back toname@version(the version is in the URL path), the way vendored tuples are recovered from their ledger.No probe runs: the sandbox can't delete probe branches yet (see ledger #306), so this is Linux-only evidence.