[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).
Summary
Bun runs dependency lifecycle scripts only for packages in trustedDependencies, plus Bun's built-in default-trusted list (better-sqlite3, esbuild, sharp, bcrypt, @prisma/client, electron, cypress, …). From Bun 1.3.5 on, the default list only applies to packages resolved from the npm registry. When socket-patch rewires one of these packages to a hosted URL tuple (scan --mode hosted) or a .socket/vendor/... local tarball (scan --mode vendored), Bun no longer trusts it. The package's install / postinstall script is skipped, and bun pm untrusted now lists it.
socket-patch reports success with no warning, and bun install --frozen-lockfile exits 0. But the installed package is broken at runtime whenever it needs its install script: native bindings aren't built or downloaded, binaries aren't fetched, and so on.
Impact
better-sqlite3@11.10.0: after rewiring, require('better-sqlite3') throws Could not locate the bindings file. The control (same lock, unrewired, fresh frozen install) loads fine.
- Any default-trusted package whose install script does real work breaks the same way. Nothing in the envelope, the lock or the frozen install's exit code signals it. It shows up only as a runtime crash, typically in CI or production after a fresh install.
Repro (Linux, main f6b7fb9, bun 1.4.2)
mkdir tr && cd tr
printf '{"name":"app","version":"1.0.0","dependencies":{"better-sqlite3":"11.10.0"}}\n' > package.json
bun install
node -e 'require("better-sqlite3")(":memory:");console.log("LOADS")' # LOADS
socket-patch scan --mode hosted --json --yes --api-url <mock> --org test-org --api-token fake # or --mode vendored
# status "success", redirect.warnings []
mkdir ../fresh && cp -a package.json bun.lock .socket ../fresh/ && cd ../fresh
bun install --frozen-lockfile; echo $? # 0
node -e 'require("better-sqlite3")(":memory:")' # Error: Could not locate the bindings file
bun pm untrusted
# ./node_modules/better-sqlite3 @http://…/better-sqlite3-11.10.0.tgz
# » [install]: prebuild-install || node-gyp rebuild --release
The mock serves a patched copy of the real registry tarball: the same files, with a marker prepended to lib/index.js, and the same scripts.
Workaround, which I verified: adding "trustedDependencies": ["better-sqlite3"] to package.json makes the fresh frozen install run the script again, and the module loads.
Expected vs actual
- Expected: rewiring changes the bytes of the patched package and nothing else. The docs (docs/testing/bun-compatibility.md, "Formats and rewrite behavior") describe the rewrite as keeping the meta object verbatim so the install behaves the same. A package that was trusted before the rewrite should stay trusted, for example by adding it to
trustedDependencies (and rollback removing it). At minimum the run should warn that the package's lifecycle scripts will now be blocked.
- Actual: trust is silently lost and the install scripts don't run.
OS × version (hosted and vendored behave identically)
| OS |
Bun 1.1.45 |
Bun 1.2.23 |
Bun 1.3.0 / 1.3.2 / 1.3.4 |
Bun 1.3.5 |
Bun 1.3.6 / 1.3.9 / 1.3.14 |
Bun 1.4.2 |
| Linux (sandbox) |
pass |
pass |
pass |
fail |
fail |
fail |
| ubuntu-latest |
— |
— |
pass (1.3.4) |
fail |
— |
fail |
| macos-latest |
— |
— |
pass (1.3.4) |
fail |
— |
fail |
| windows-latest |
— |
— |
pass (1.3.4) |
fail |
— |
fail |
First bad Bun release: 1.3.5 (1.3.4 passes, bisected on Linux and confirmed on all three runner OSes). This is an upstream Bun behaviour change that socket-patch doesn't compensate for. socket-patch release 4.0.0 behaves the same as main.
Suspect code
- The hosted text and binary rewriters,
crates/socket-patch-core/src/patch/redirect/mod.rs:4329 (rewrite_bun_lock) and patch/redirect/bun_binary.rs:19, plus the vendored bun backend (crates/socket-patch-core/src/vendor/bun_lock.rs). None of them looks at the package's scripts or at Bun's default-trust status, or touches trustedDependencies.
- The real-Bun matrix (
scripts/backtest-bun.py) uses --ignore-scripts for every measurement (docs/testing/bun-compatibility.md, "Installer boundaries"), so it can't observe this.
Probe run: https://github.com/SocketDev/socket-patch/actions/runs/36766578001 (3 OS × bun 1.3.4 / 1.3.5 / 1.4.2, main built on each runner).
[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).
Summary
Bun runs dependency lifecycle scripts only for packages in
trustedDependencies, plus Bun's built-in default-trusted list (better-sqlite3, esbuild, sharp, bcrypt, @prisma/client, electron, cypress, …). From Bun 1.3.5 on, the default list only applies to packages resolved from the npm registry. When socket-patch rewires one of these packages to a hosted URL tuple (scan --mode hosted) or a.socket/vendor/...local tarball (scan --mode vendored), Bun no longer trusts it. The package'sinstall/postinstallscript is skipped, andbun pm untrustednow lists it.socket-patch reports
successwith no warning, andbun install --frozen-lockfileexits 0. But the installed package is broken at runtime whenever it needs its install script: native bindings aren't built or downloaded, binaries aren't fetched, and so on.Impact
better-sqlite3@11.10.0: after rewiring,require('better-sqlite3')throwsCould not locate the bindings file. The control (same lock, unrewired, fresh frozen install) loads fine.Repro (Linux, main
f6b7fb9, bun 1.4.2)The mock serves a patched copy of the real registry tarball: the same files, with a marker prepended to
lib/index.js, and the samescripts.Workaround, which I verified: adding
"trustedDependencies": ["better-sqlite3"]to package.json makes the fresh frozen install run the script again, and the module loads.Expected vs actual
trustedDependencies(and rollback removing it). At minimum the run should warn that the package's lifecycle scripts will now be blocked.OS × version (hosted and vendored behave identically)
First bad Bun release: 1.3.5 (1.3.4 passes, bisected on Linux and confirmed on all three runner OSes). This is an upstream Bun behaviour change that socket-patch doesn't compensate for. socket-patch release 4.0.0 behaves the same as main.
Suspect code
crates/socket-patch-core/src/patch/redirect/mod.rs:4329(rewrite_bun_lock) andpatch/redirect/bun_binary.rs:19, plus the vendored bun backend (crates/socket-patch-core/src/vendor/bun_lock.rs). None of them looks at the package'sscriptsor at Bun's default-trust status, or touchestrustedDependencies.scripts/backtest-bun.py) uses--ignore-scriptsfor every measurement (docs/testing/bun-compatibility.md, "Installer boundaries"), so it can't observe this.Probe run: https://github.com/SocketDev/socket-patch/actions/runs/36766578001 (3 OS × bun 1.3.4 / 1.3.5 / 1.4.2, main built on each runner).