Skip to content

On Bun 1.4, a bun patch made on a hosted or vendored package is silently dropped by the next superseding re-run, takeover, rollback or revert (the #367 guard only matches name@version keys) #1019

Description

[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).

Summary

#873 (fix for #367) leaves a package on its registry entry when the root package.json / bun.lock patchedDependencies has a key for its name@version or bare name. But on Bun 1.4.x, running bun patch <pkg> after socket-patch has rewired the package keys the user's patch by the package's current resolution:

"patchedDependencies": {
  "plainpkg@https://patch.socket.dev/patch/npm/plainpkg/1.0.0/<tok>/<uuid>/plainpkg-1.0.0.tgz": "patches/plainpkg@https%3A%2F%2F…tgz.patch"
}

(vendored: "plainpkg@.socket/vendor/npm/<uuid>/plainpkg-1.0.0.tgz"). Bun 1.4.2 applies that patch on top of the Socket-patched bytes, so the project has both fixes. patched_dependency_key matches neither spelling, so every later socket-patch operation that changes the resolution rewrites the entry, exits 0 with no warning, and orphans the key. Bun then silently ignores the orphaned key on every install, so the user's own patch is gone.

Impact

This is the same silent loss #367 fixed, reached through the normal order of events: patch with socket-patch first, bun patch something else in that package later. A routine hosted re-run that picks up a superseding Socket patch (new uuid → new URL) is enough. CI frozen installs succeed, vex attests the Socket patch, and nothing flags that the project's own patch stopped applying.

Repro (Linux, main db83f01, Bun 1.4.2)

Local npm registry on :4873 (bunfig [install] registry) and a patch-API mock on :4874 (SOCKET_PATCH_SERVER_URL), serving plainpkg@1.0.0 with a Socket patch.

printf '[install]\nregistry = "http://127.0.0.1:4873/"\n' > bunfig.toml
echo '{"name":"proj","version":"1.0.0","dependencies":{"plainpkg":"1.0.0","otherpkg":"2.0.0"}}' > package.json
bun install && git init -q && echo node_modules > .gitignore && git add -A && git commit -qm init
socket-patch scan --mode hosted --yes                     # plainpkg → hosted URL tuple
git add -A && git commit -qm hosted
bun patch plainpkg && echo '// USERPATCH' >> node_modules/plainpkg/index.js
bun patch --commit node_modules/plainpkg                  # key: "plainpkg@http://…/tok/<uuid>/plainpkg-1.0.0.tgz"
git add -A && git commit -qm bunpatch
# fresh clone + cold `bun install --frozen-lockfile`: index.js = PATCHED + "// USERPATCH"   (both apply)

# the Socket patch is superseded by a new uuid, then the usual re-run:
socket-patch scan --mode hosted --yes --json              # exit 0, status success, warnings []
git commit -qam rerun
# fresh clone + cold `bun install --frozen-lockfile`: exit 0, index.js = PATCHED only, "// USERPATCH" gone

bun.lock still has the old-URL patchedDependencies key; the package line now names the new URL.

Expected vs actual

  • Expected (docs/testing/bun-compatibility.md, the Hosted and vendored Bun rewiring silently discards the project's own bun patch (patchedDependencies): fresh frozen installs drop the user's patch with exit 0 #367 row): a package the project patches with bun patch is never rewired in a way that "would silently drop that patch from every install with exit 0". The run should warn redirect_bun_patched_dependency_skipped (hosted) or refuse vendor_lock_entry_unsupported (vendored), naming the key. Rollback/revert should at least warn that the key will stop applying. The row's premise, "Bun applies the user's patch only to the registry name@version", doesn't hold on Bun 1.4: Bun keys and applies patches on URL and local-tarball resolutions too.
  • Actual: exit 0, status: success, no warning code at all; the user's patch silently stops applying.

Matrix (Linux, cold-cache fresh clone after each step; "drop" = USERPATCH lost, exit 0, no warning)

Bun lock start bun patch key Operation Result
1.4.2 text v2 hosted name@<hosted url> hosted re-run, superseding uuid drop (×2)
1.4.2 bun.lockb hosted name@<hosted url> hosted re-run, superseding uuid drop
1.4.2 text v2 hosted name@<hosted url> scan --mode vendored (takeover) drop (×2)
1.4.2 bun.lockb hosted name@<hosted url> scan --mode vendored (takeover) drop
1.4.2 text v2 hosted name@<hosted url> rollback drop (×2; only reinstall_required)
1.4.2 text v2 vendored name@.socket/vendor/…tgz vendored re-run, superseding uuid drop
1.4.2 text v2 vendored name@.socket/vendor/…tgz scan --mode hosted (takeover) drop (only redirect_takeover_reverted_vendored)
1.4.2 text v2 vendored name@.socket/vendor/…tgz vendor --revert drop
1.4.2 text v2 registry name@1.0.0 hosted / vendored scan pass (#873 guard fires)
1.2.23, 1.3.9 text v1 hosted — — n/a: Bun's own bun patch --commit crashes (SIGILL) on a URL-resolved package, so the state can't be created
macOS / Windows — — — — untested (no probe branches this run); the key match is platform-independent string logic

First bad: not a regression, a gap in #873's matching. 4.0.0 predates the guard entirely (#367).

Suspect code

  • crates/socket-patch-core/src/vendor/bun_lock_text.rs:123 patched_dependency_key only matches "{name}@{version}" or "{name}". Keys "{name}@{current resolution}" (hosted URL / .socket/vendor/… path) are ignored.
  • Callers: crates/socket-patch-core/src/patch/redirect/mod.rs:4813 skip_bun_user_patched (text + binary hosted), crates/socket-patch-core/src/vendor/bun_lock.rs:689 (vendored), and nothing on the rollback / vendor --revert / takeover-unwind paths.
  • Possible direction: treat any key whose name part is the package and whose spec part equals the entry's current resolution (or any URL / path spec for that name) as user-patched; keep the entry and warn, or refuse with the bun patch --commit remedy.

Not covered by the open draft #1009 (its scope is the earlier pm:bun issues).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions