Skip to content

Bun hosted and vendored modes skip a URL or file: tarball copy of the patched package without warning, and vendored vex attests not_affected (the #326 fix covers npm locks only) #497

Description

[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.

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