[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).
Summary
A Bun project can depend on a package by remote tarball URL ("is-number": "https://registry.npmjs.org/is-number/-/is-number-6.0.0.tgz") or by file: tarball. If a registry copy of the same name@version is also in bun.lock (here, nested under a dependent), hosted and vendored scans rewire only the registry tuple.
- The user's URL/
file: tuple is left alone. That's correct in itself, since Bun installs it from its spec.
- But there's no warning:
status: success, redirect.warnings: [], no vendor warning event.
- In vendored mode,
vex (default and --no-verify) then attests not_affected for that name@version, even though the root copy the app loads (require('is-number')) is still the original bytes after a cold bun install --frozen-lockfile.
- Hosted default
vex correctly refuses (not_applied), but vex --no-verify also attests.
#326 / #345 fixed exactly this for package-lock.json: a redirect_npm_non_registry_entry_skipped / vendor_non_registry_entry_skipped warning, and vex attests nothing while a non-registry copy is in the lock. The Bun rewriters and the Bun VEX discovery never got the equivalent. It's the same class as #469 (bundled) and #471 (vlt bundled).
Impact
A false VEX attestation, plus a silent "success" that leaves the copy the application actually loads unpatched.
Repro (Bun 1.4.2, Linux)
cat > package.json <<'EOF'
{"name":"app","version":"1.0.0","dependencies":{
"is-odd":"3.0.1",
"is-number":"https://registry.npmjs.org/is-number/-/is-number-6.0.0.tgz"}}
EOF
bun install # is-odd@3.0.1 depends on is-number@^6 → nested registry copy
grep -E '"(is-number|is-odd/is-number)": \[' bun.lock
# "is-number": ["is-number@https://registry.npmjs.org/is-number/-/is-number-6.0.0.tgz", {}, "sha512-Wu1V…"]
# "is-odd/is-number": ["is-number@6.0.0", "", {}, "sha512-Wu1V…"]
# a patch exists for pkg:npm/is-number@6.0.0 (and is-odd@3.0.1)
socket-patch scan --mode vendored --yes --json # exit 0, status success, no warning
# fresh checkout (package.json, bun.lock, .socket/), empty cache:
bun install --frozen-lockfile # exit 0
node -e "console.log(require('fs').readFileSync(require.resolve('is-number'),'utf8').split('\n')[0])"
# /*! ← root copy, unpatched
# is-odd's nested is-number → /* SOCKET-PATCHED */
socket-patch vex --json --output vex.json
# status success; pkg:npm/is-number@6.0.0 verified not_affected
The patch data came from a local mock of the patch API: batch, by-package, patches/package, view and the hosted tarball route. The patch prepends /* SOCKET-PATCHED */ to index.js.
Expected vs actual
Controls:
- With only the URL dependency (no registry copy), vendored refuses with
vendor_lock_entry_not_found and hosted warns redirect_bun_entry_not_found, both correctly.
- Hosted default
vex → not_applied for is-number, which is correct.
Matrix (Linux; main 61cfb9b; each cell run twice on 1.3.14 / 1.4.2)
| Bun |
lock |
spec |
hosted scan warns |
vendored scan warns |
root copy patched after frozen install |
vendored vex |
hosted vex |
hosted vex --no-verify |
| 1.1.45 |
text v0 |
URL |
no ✗ |
no ✗ |
no |
attests ✗ |
not_applied ✓ |
attests ✗ |
| 1.2.23 |
text v1 |
URL |
no ✗ |
no ✗ |
no |
attests ✗ |
not_applied ✓ |
attests ✗ |
| 1.3.14 |
text v1 |
URL |
no ✗ |
no ✗ |
no |
attests ✗ |
not_applied ✓ |
— |
| 1.4.2 |
text v2 |
URL |
no ✗ |
no ✗ |
no |
attests ✗ |
not_applied ✓ |
attests ✗ |
| 1.4.2 |
text v2 |
file:./is-number-6.0.0.tgz |
— |
no ✗ |
no |
attests ✗ |
— |
— |
Release 4.0.0 behaves the same on 1.4.2 (vendored attests, no warnings). It isn't a regression. macOS and Windows weren't probed, because this is lock-parsing logic, not OS-specific code.
Suspect code
crates/socket-patch-core/src/vex/discover/bun.rs:249: a non-Socket tuple only counts as resolved_elsewhere when it has a recorded or digit-leading version. A name@https://…/name-6.0.0.tgz or name@./x.tgz tuple has neither, so it never contests the vendored/hosted ref for the same name@version. Compare drop_non_registry_installs in vex/discover/npm.rs:202.
crates/socket-patch-core/src/patch/redirect/mod.rs:3773 (rewrite_bun_lock): warns only when no tuple matched (!matched_any). There's no stays-unpatched warning when a non-registry tuple of the same name@version is skipped beside a rewired one.
crates/socket-patch-core/src/vendor/bun_lock.rs:679 (preflight_package / classify): same, on the vendored side.
[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).
Summary
A Bun project can depend on a package by remote tarball URL (
"is-number": "https://registry.npmjs.org/is-number/-/is-number-6.0.0.tgz") or byfile:tarball. If a registry copy of the samename@versionis also inbun.lock(here, nested under a dependent), hosted and vendored scans rewire only the registry tuple.file:tuple is left alone. That's correct in itself, since Bun installs it from its spec.status: success,redirect.warnings: [], no vendor warning event.vex(default and--no-verify) then attestsnot_affectedfor thatname@version, even though the root copy the app loads (require('is-number')) is still the original bytes after a coldbun install --frozen-lockfile.vexcorrectly refuses (not_applied), butvex --no-verifyalso attests.#326 / #345 fixed exactly this for
package-lock.json: aredirect_npm_non_registry_entry_skipped/vendor_non_registry_entry_skippedwarning, andvexattests nothing while a non-registry copy is in the lock. The Bun rewriters and the Bun VEX discovery never got the equivalent. It's the same class as #469 (bundled) and #471 (vlt bundled).Impact
A false VEX attestation, plus a silent "success" that leaves the copy the application actually loads unpatched.
Repro (Bun 1.4.2, Linux)
The patch data came from a local mock of the patch API: batch, by-package,
patches/package,viewand the hosted tarball route. The patch prepends/* SOCKET-PATCHED */toindex.js.Expected vs actual
vexrow: "derived from … the hosted / vendored patch references the project's lockfiles wire"): when a non-registry copy of the patchedname@versionstays installed, both modes emit a stays-UNPATCHED warning andvexattests nothing for thatname@version, as it already does for npm.vex(and--no-verifyin either mode) attestsnot_affected.Controls:
vendor_lock_entry_not_foundand hosted warnsredirect_bun_entry_not_found, both correctly.vex→not_appliedforis-number, which is correct.Matrix (Linux; main
61cfb9b; each cell run twice on 1.3.14 / 1.4.2)vexvexvex --no-verifynot_applied✓not_applied✓not_applied✓not_applied✓file:./is-number-6.0.0.tgzRelease 4.0.0 behaves the same on 1.4.2 (vendored attests, no warnings). It isn't a regression. macOS and Windows weren't probed, because this is lock-parsing logic, not OS-specific code.
Suspect code
crates/socket-patch-core/src/vex/discover/bun.rs:249: a non-Socket tuple only counts asresolved_elsewherewhen it has a recorded or digit-leading version. Aname@https://…/name-6.0.0.tgzorname@./x.tgztuple has neither, so it never contests the vendored/hosted ref for the samename@version. Comparedrop_non_registry_installsinvex/discover/npm.rs:202.crates/socket-patch-core/src/patch/redirect/mod.rs:3773(rewrite_bun_lock): warns only when no tuple matched (!matched_any). There's no stays-unpatched warning when a non-registry tuple of the samename@versionis skipped beside a rewired one.crates/socket-patch-core/src/vendor/bun_lock.rs:679(preflight_package/classify): same, on the vendored side.