You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
With Bun's isolated linker, vex attests a hosted patch as not_affected (verified) while the installed copy under node_modules/.bun is still unpatched (v5 regression) #405
[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).
Summary
With Bun's isolated linker, socket-patch vex attests a hosted-mode patch as not_affected and reports it verified, even though the copy Bun actually installed (under node_modules/.bun/<name>@<version>/node_modules/<name>) does not carry the patch. The isolated linker is opt-in on Bun 1.2.x (linker = "isolated") and the default for fresh workspaces on Bun 1.3.x and 1.4.x.
v5 hosted mode is the default for scan. In that mode, vex treats a purl with no consumed copy as "nothing installed" and attests it from the lockfile pin. The npm copy lookup doesn't look inside Bun's .bun/ store, so a transitive package there is never found. That makes the pin the only evidence even when a stale (pre-reinstall) or tampered copy is installed. A hoisted install of the same project is handled correctly: not_applied, omitted from the document.
This is a regression from 4.0.0. 4.0.0 omits the same purl from the document (package_not_found).
Related: #366 shares the root cause (agent mode can't see packages under node_modules/.bun). That issue is about agent-mode scan/apply. This one is about hosted-mode VEX making a false attestation. #373 is the Deno counterpart of #366.
Impact
The document says not_affected / "Patched via Socket patch (redirected)" for a dependency the running code loads unpatched. That happens in two cases, both with exit 0 and status: success:
Stale tree: right after scan rewrote bun.lock and before the user re-installs. The installed copy still has the vulnerable bytes, and vex already attests.
Tampered or wrong install: after a real fresh bun install --frozen-lockfile, the hosted copy lives at node_modules/.bun/is-number@http+++…/node_modules/is-number. Removing the patch from that file doesn't change the verdict: it's still verified. With the hoisted linker the same edit gives not_applied.
Every Bun ≥ 1.3 workspace that runs socket-patch scan && socket-patch vex hits case 1, because fresh workspaces default to the isolated linker.
Repro (Linux, Bun 1.4.2, main 2463257)
The patch API is a local mock that serves the batch, patches/package, patches/view and hosted tarball routes. SOCKET_PATCH_SERVER_URL points at it, so its tarball URL counts as a hosted pin. The patch prepends /* SOCKET-PATCHED … */ to is-number@7.0.0/index.js.
mkdir app &&cd app
echo'{"name":"app","version":"1.0.0","dependencies":{"to-regex-range":"5.0.1"}}'> package.json
printf'[install]\nlinker = "isolated"\n'> bunfig.toml # omit on a Bun >=1.3 workspace: it's the default there
bun install
# node_modules/.bun/is-number@7.0.0/node_modules/is-number <- transitive, only in the store
socket-patch scan --mode hosted --json --yes --api-url $MOCK --org org --api-token fake
# status success, redirect.redirected 1 (bun.lock now pins the hosted URL)
grep -c SOCKET-PATCHED node_modules/.bun/is-number@7.0.0/node_modules/is-number/index.js # 0 (not reinstalled yet)
socket-patch vex --json --output out.vex.json --api-url $MOCK --org org --api-token fake
# status "success", events: [{"action":"verified","purl":"pkg:npm/is-number@7.0.0", ... "status":"not_affected"}]# out.vex.json: not_affected, "Patched via Socket patch 2222…(redirected)"
Control in the same state: linker = "hoisted" gives is-number at node_modules/is-number and vex → skipped not_applied, no_applicable_patches, no document.
Workspace variant, with no bunfig on Bun 1.3.14 or 1.4.2: the root has workspaces: ["packages/*"], and packages/a depends on to-regex-range@5.0.1 and left-pad@1.3.0. After a hosted scan and before a reinstall, vex reports left-pad as not_applied, because the direct dep is reached through its node_modules/left-pad symlink. It reports is-number as verified / not_affected, although neither store copy is patched.
Tamper variant (measured on Linux, Bun 1.4.2): run a fresh checkout and bun install --frozen-lockfile, so the hosted copy is installed and patched. Then delete the marker line from node_modules/.bun/is-number@http+++…/node_modules/is-number/index.js. vex still reports verified. The hoisted control reports not_applied.
Expected vs actual
Expected (CLI_CONTRACT.md, "Patched via Socket patch (redirected)" row and "Manifest-less VEX"): "a post-install socket-patch vex re-proves the lockfile wiring and hash-verifies the installed copy the build consumes". Only "with nothing installed" may it attest a discovered reference from its pin. HostedCopies (crates/socket-patch-core/src/vex/verify.rs:83-105) says a shared-location ecosystem's pristine copy "IS what runs" and must fail verification.
Actual: the consumed copy under node_modules/.bun/ is invisible to the lookup, so vex takes the "nothing installed" branch and attests from the pin, with action: verified.
OS × version (hosted scan → vex before reinstall)
OS
Bun 1.2.23 (linker = "isolated")
Bun 1.3.14 (isolated / default workspace)
Bun 1.4.2 (isolated / default workspace)
hoisted control
Linux
fail
fail / fail
fail / fail
pass (not_applied)
macOS (macos-latest)
fail
fail / fail
fail / fail
pass
Windows (windows-latest)
fail
fail / fail
fail / fail
pass
On Bun 1.2.23 a default workspace still installs hoisted, so that cell is correctly not_applied on all 3 OSes. That's expected, not a fix.
Regression check (Linux, Bun 1.4.2, isolated): release 4.0.0 → omitted (package_not_found), pass. main 2463257 (#277, v5) → attested, fail. So the first bad commit is the v5 consolidation (#277), the one that added manifest-less VEX's "attest from the pin when nothing is installed".
Suspect code
crates/socket-patch-cli/src/commands/vex_consumed.rs:69-121 (hosted_consumed_copies): npm's shared-location copies come from the crawler's installed map plus the alias walk and identity fallback. None of these walks node_modules/.bun/*/node_modules/<name>. with_store_variants (:323) only expands copies that were already found.
A fix for #366 that teaches the crawler the .bun store would probably fix this too. Even so, vex should probably refuse to take the "nothing installed" branch while node_modules/.bun/ exists.
[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).
Summary
With Bun's isolated linker,
socket-patch vexattests a hosted-mode patch asnot_affectedand reports itverified, even though the copy Bun actually installed (undernode_modules/.bun/<name>@<version>/node_modules/<name>) does not carry the patch. The isolated linker is opt-in on Bun 1.2.x (linker = "isolated") and the default for fresh workspaces on Bun 1.3.x and 1.4.x.v5 hosted mode is the default for
scan. In that mode,vextreats a purl with no consumed copy as "nothing installed" and attests it from the lockfile pin. The npm copy lookup doesn't look inside Bun's.bun/store, so a transitive package there is never found. That makes the pin the only evidence even when a stale (pre-reinstall) or tampered copy is installed. A hoisted install of the same project is handled correctly:not_applied, omitted from the document.This is a regression from 4.0.0. 4.0.0 omits the same purl from the document (
package_not_found).Related: #366 shares the root cause (agent mode can't see packages under
node_modules/.bun). That issue is about agent-modescan/apply. This one is about hosted-mode VEX making a false attestation. #373 is the Deno counterpart of #366.Impact
The document says
not_affected/ "Patched via Socket patch (redirected)" for a dependency the running code loads unpatched. That happens in two cases, both with exit 0 andstatus: success:scanrewrotebun.lockand before the user re-installs. The installed copy still has the vulnerable bytes, andvexalready attests.bun install --frozen-lockfile, the hosted copy lives atnode_modules/.bun/is-number@http+++…/node_modules/is-number. Removing the patch from that file doesn't change the verdict: it's stillverified. With the hoisted linker the same edit givesnot_applied.Every Bun ≥ 1.3 workspace that runs
socket-patch scan && socket-patch vexhits case 1, because fresh workspaces default to the isolated linker.Repro (Linux, Bun 1.4.2, main
2463257)The patch API is a local mock that serves the batch,
patches/package,patches/viewand hosted tarball routes.SOCKET_PATCH_SERVER_URLpoints at it, so its tarball URL counts as a hosted pin. The patch prepends/* SOCKET-PATCHED … */tois-number@7.0.0/index.js.Control in the same state:
linker = "hoisted"givesis-numberatnode_modules/is-numberandvex→skipped not_applied,no_applicable_patches, no document.Workspace variant, with no bunfig on Bun 1.3.14 or 1.4.2: the root has
workspaces: ["packages/*"], andpackages/adepends onto-regex-range@5.0.1andleft-pad@1.3.0. After a hosted scan and before a reinstall,vexreportsleft-padasnot_applied, because the direct dep is reached through itsnode_modules/left-padsymlink. It reportsis-numberasverified/not_affected, although neither store copy is patched.Tamper variant (measured on Linux, Bun 1.4.2): run a fresh checkout and
bun install --frozen-lockfile, so the hosted copy is installed and patched. Then delete the marker line fromnode_modules/.bun/is-number@http+++…/node_modules/is-number/index.js.vexstill reportsverified. The hoisted control reportsnot_applied.Expected vs actual
socket-patch vexre-proves the lockfile wiring and hash-verifies the installed copy the build consumes". Only "with nothing installed" may it attest a discovered reference from its pin.HostedCopies(crates/socket-patch-core/src/vex/verify.rs:83-105) says a shared-location ecosystem's pristine copy "IS what runs" and must fail verification.node_modules/.bun/is invisible to the lookup, sovextakes the "nothing installed" branch and attests from the pin, withaction: verified.OS × version (hosted
scan→vexbefore reinstall)linker = "isolated")not_applied)On Bun 1.2.23 a default workspace still installs hoisted, so that cell is correctly
not_appliedon all 3 OSes. That's expected, not a fix.Regression check (Linux, Bun 1.4.2, isolated): release 4.0.0 → omitted (
package_not_found), pass. main2463257(#277, v5) → attested, fail. So the first bad commit is the v5 consolidation (#277), the one that added manifest-less VEX's "attest from the pin when nothing is installed".Suspect code
crates/socket-patch-cli/src/commands/vex_consumed.rs:69-121(hosted_consumed_copies): npm's shared-location copies come from the crawler'sinstalledmap plus the alias walk and identity fallback. None of these walksnode_modules/.bun/*/node_modules/<name>.with_store_variants(:323) only expands copies that were already found.crates/socket-patch-core/src/vex/verify.rs:98-105: an emptyHostedCopies.pathsmeans "none installed", which the lockfile basis then excuses. The npm crawler's missing.bunstore discovery (the same gap as Bun isolated linker: transitive packages under node_modules/.bun are "not installed" in agent mode, and scan --mode agent exits 0 with them unpatched #366,crates/socket-patch-core/src/crawlers/npm_crawler.rs) turns that into a false attestation.A fix for #366 that teaches the crawler the
.bunstore would probably fix this too. Even so,vexshould probably refuse to take the "nothing installed" branch whilenode_modules/.bun/exists.Probe run (3 OS × Bun 1.2.23 / 1.3.14 / 1.4.2, main built on each runner; cases
iso-bunfig,ws-default,hoisted-control): https://github.com/SocketDev/socket-patch/actions/runs/36801259018