Fix Bun/vlt bundled copies left unpatched (#469, #471) - #472
Conversation
Assisted-by: Claude Code:claude-opus-5-5
Bun records a bundleDependencies copy as its own parent/child entry
flagged { "bundled": true } and unpacks it from the parent's
tarball, never reading the entry. Hosted and vendored scans rewired
those entries and reported success, and vex attested not_affected,
while the copy the parent loads stayed unpatched.
Both rewriters now skip a bundled entry with a loud warning (vendored
refuses when it is the only instance), and vex never takes a bundled
entry as a ref: it contests a ref for the same name@version in the
same lock or any other, like npm's inBundle handling (#325).
Refs #469
Assisted-by: Claude Code:claude-opus-5-5
bun.lockb marks a bundleDependencies edge with the bundled behavior bit. A record only such edges reach is unpacked from the parent's tarball, so redirecting or vendoring it installed nothing while scan reported success and vex attested not_affected. Bun also shares one record between a regular and a bundled install of the same version, where the bundled copy stays unpatched. The binary codec now flags those records. Hosted and vendored skip a bundled-only record (vendored refuses when nothing else matches), warn when the record is shared, and vex never attests either case. Refs #469 Assisted-by: Claude Code:claude-opus-5-5
vlt unpacks a bundleDependencies copy into its parent's store entry and records no vlt-lock.json node for it, so no hosted or vendored rewire reaches it. vex still attested the patch as not_affected while that copy stayed unpatched. vex now looks for real package directories inside each store package's own node_modules (vlt links real dependencies as symlinks beside the package), and does not attest a ref whose name@version a bundled copy also installs. Vendored warns about such a copy, and when it is the only install it refuses with that reason instead of "run vlt install". Refs #471 Assisted-by: Claude Code:claude-opus-5-5
A hosted scan in a vlt project pinned the regular lock node and reported a clean success while a bundled copy of the same version, unpacked into its parent's store entry, stayed unpatched. The scan now warns redirect_vlt_bundled_instance_skipped and keeps that purl out of the in-run VEX attestation. Refs #471 Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
CodeQL flagged the new bun and vlt bundled-copy diagnostics for printing the patch uuid. They now name only the package, matching the other recent VEX diagnostics. Assisted-by: Claude Code:claude-opus-5-5
The new bun-lockb-bundled fixtures join the VEX discovery golden corpus. The new bundled-copy tests no longer print lock specs or diagnostics that carry patch URLs and uuids (CodeQL), only case numbers and diagnostic codes. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
A hosted scan --vex treated every confirmed redirect as applied. When Bun shares a record between a regular install and a bundled copy, or bun.lock has both entries, the regular entry is redirected but the bundled copy stays unpatched, and the in-run attestation still said not_affected. The bun rewriters now record the uuids whose bundled instance they skipped, and hosted scan verifies those purls instead of assuming them applied, matching a standalone vex run. Refs #469 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
The redirect equivalence goldens hash each RewriteResult. The new bundled_skipped_uuids set is left out of the serde digests while empty, and the two goldens that hash its Debug form (pdm, poetry) are re-blessed: their inputs are unchanged, only the printed struct gained the empty field. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
An earlier cargo fmt --all reformatted about 120 files this fix does not touch (main is not rustfmt-clean and CI has no fmt check). Those files are back to main's bytes, and the files this PR changes carry only their real edits on top of main's formatting. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 4a787fe. Configure here.
|
[agent] CI note on head 4a787fe: the only red check is
I'm re-running the failed job once. If it fails again I'll treat it as real and dig in. Generated by Claude Code |
|
Burn-down agent: ready for review at
Slack announcement not sent: no Slack send tool is available to this agent; the next run will retry. Generated by Claude Code |
|
Reviewed No actionable correctness or security regressions found. Bundled Bun entries are excluded from rewiring and attestation, including binary records shared by regular and bundled installs. The vlt installed-store check contests the same package/version, and in-run hosted VEX does not assume those skipped copies were patched. Validation: |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #469
Fixes #471
Summary
Bun and vlt projects whose dependency bundles the patched package (
bundleDependencies) got a successful hosted/vendored scan, and VEX attestednot_affected, while the copy the parent actually loads stayed unpatched. These copies are now skipped loudly by the rewriters and never attested by VEX, matching what #337 did for npm.Root cause
#337 (#325) taught only the npm-lock paths that a bundled copy (unpacked from the parent's tarball) is never reached by a rewire. The Bun and vlt backends had no equivalent:
bundled: truelock entries that Bun never fetches: the bundled copy stays unpatched, scan reports success, and vendoredvexattests not_affected #469):bun.lockrecords a bundled copy as its ownparent/childentry with meta{ "bundled": true }.bun.lockbmarks the dependency edge with Bun'sbundledbehavior bit (0x40, verified against a real Bun 1.3.14 lock). Bun also shares ONE binary record between a regular and a bundled install of the same version. The hosted rewriter, the vendored rewriter andvex/discover/bun.rsall ignored this.vexattests not_affected while a bundled copy of the same name@version in node_modules/.vlt stays unpatched (the #325 fix covers npm locks only) #471):vlt-lock.jsonrecords neither the bundled copy nor the parent'sbundleDependencies(verified with real vlt 1.3.3). The copy exists only in the installed store atnode_modules/.vlt/<parent id>/node_modules/<parent>/node_modules/<name>. vlt links real dependencies as symlinks beside the package, so a real directory inside a store package'snode_modulesis always a bundled copy.Changes
vendor::bun_lock_text::is_bundled_entry: the shared bundled-meta check. It fails closed: a meta that won't parse but mentions"bundled"counts as bundled.rewrite_bun_lock,rewrite_bun_binary): a bundled entry or bundled-only record is skipped withredirect_bun_bundled_instance_skippedand is not counted or confirmed. A binary record shared with a regular install is still redirected for that install, with the same warning. Each skipped uuid lands in the newRewriteResult.bundled_skipped_uuids, and hostedscan --vexverifies those purls instead of assuming them applied (Bugbot finding).vendor::bun_lock,vendor::bun_binary): bundled entries and records are never rewired, withvendor_bundled_instance_skipped. When nothing else matches, vendoring refuses withvendor_lock_entry_not_rewritable, which names the real reason instead of "runbun install".vex/discover/bun.rs): a bundled entry or record is never a ref, even when Socket-wired. It contests same-name@versionrefs in the same lock and, viaresolved_elsewhere, in other locks.bun.lockbcodec:BinaryPackage.bundled/bundled_onlycome from the dependency edges' behavior bits.vendor::vlt_bundled, new): a bounded, confined walk of the store for bundled copies. VEX withdraws a ref whosename@versionhas one. Vendored warns, or refuses when the bundled copy is the only install. The hosted scan warnsredirect_vlt_bundled_instance_skippedand withholds the purl from in-run VEX.CLI_CONTRACT.md"Contested locks" now covers Bun and vlt.Test evidence
I watched every new regression test fail without its fix, then pass with it:
patch::redirect::tests::bun_lock_bundled_entry_is_skipped_with_loud_warningvendor::bun_lock::tests::bundled_entry_is_never_rewiredvex::discover::bun::tests::bundled_entries_are_never_refs_and_contest_the_same_versionscan --vexin_process_redirect::scan_redirect_bun_bundled_copy_is_not_attested_in_runvendor::bun_lockb::tests::bundled_edges_flag_their_records(real Bun 1.3.14 fixturesbun-lockb-bundled/{only,both})patch::redirect::bun_binary::tests::bundled_records_are_not_redirected_silentlyvendor::bun_lock::tests::binary_bundled_records_are_never_silently_rewiredvex::discover::bun::tests::binary_bundled_records_are_never_attestedvex::discover::vlt::tests::an_installed_bundled_copy_contests_the_refvendor::vlt_lock::tests::a_bundled_store_copy_is_reported_not_silently_left_unpatchedin_process_redirect vlt::scan_redirect_vlt_bundled_store_copy_warnsLocal results:
cargo clippy --workspace --all-features -- -D warnings: clean.cargo test --workspace --all-features --no-fail-fast: all pass except the tests that usechmodto force write failures. This sandbox runs as root, which bypasses them, and they pass on CI's non-root runners.e2e_redirect_bun_build(16),e2e_vendor_bun_build(14) ande2e_bun_lockb(12) pass with--include-ignoredagainst real Bun 1.3.14.bun-lockb-bundleddiscovery golden is added. The pdm and poetry equivalence goldens are re-blessed because they hashDebugoutput, which now includes the empty new field; their inputs are unchanged.mainis not rustfmt-clean and CI has no fmt check, so this PR leaves untouched files alone. An earliercargo fmt --allchurn was reverted in 4a787fe.Follow-ups
redirect_vlt_entry_not_foundnext to the newredirect_vlt_bundled_instance_skipped, which gives the real reason.🤖 Generated with Claude Code
https://claude.ai/code/session_01RcHrVGj3ZJ2CJQn6LezdCC
Generated by Claude Code