Repository navigation
Fix gem lock readers ignoring gems.locked (#736) - #750
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A gems.rb project's gems.locked was invisible to the lock inventory, ledger recovery read only Gemfile.lock, and VEX discovery read both locks. A leftover redirected Gemfile.lock beside gems.rb + gems.locked therefore made vex attest not_affected while bundle install installed the unpatched gem from gems.locked. Add one resolver for the lock Bundler loads (honouring BUNDLE_GEMFILE and the app config) and route the inventory, gem_remotes, VEX discovery and the hosted engine through it. VEX still reads the ignored twin, but any Socket wiring there is diagnosed as unattributable instead of attested. Fixes #736 Assisted-by: Claude Code:claude-opus-5-5
9ea1929 to
4834b26
Compare
Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
Ready for review — burn-down agent.
Reviewers should focus on the twin-lock handling in Slack announcement: not sent. This session's Slack connector has no send tool, so the next run will retry. Generated by Claude Code |
Assisted-by: Claude Code:claude-opus-5-5
Release notes are written when a release is cut, from the merged PR log and the code, so PRs no longer edit CHANGELOG.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Resolve the keep_bundler_loaded_gem_files conflict to the shared resolver, and pass the new global-config argument (None for a memory view) that #577 added to manifest::classify. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Ao6g9qAnawPfNxv11f3wM
|
CI status after merging
PDM Generated by Claude Code |
main is red since 4646693 (#605): two commands::vex_consumed tests assumed the name-keyed resolver never returns npm-aliased copies, which #605 changed. Same tests-only change as #851; it no-ops once main carries it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Ao6g9qAnawPfNxv11f3wM
|
bugbot run 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 1eedea8. Configure here.
|
Burn-down agent: Ready for review at
Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #736
Summary
socket-patch now reads the gem lock Bundler actually loads. Before this, a
gems.rbproject'sgems.lockedwas invisible to the lock inventory (scan's lockfile supplement, the in-memory hosted engine, VEX ledger liveness), and ledger recovery read onlyGemfile.lock. VEX discovery read both locks, so a leftover redirectedGemfile.lockbesidegems.rb+gems.lockedmadevexattestnot_affectedwhilebundle installinstalled the unpatched gem fromgems.locked. That repro is in the #736 comments, on real Bundler 2.6.9 and 4.0.17.Root cause
Bundler loads one manifest/lock pair:
gems.rb+gems.lockedwhen the root holds agems.rb, otherwiseGemfile+Gemfile.lock, unlessBUNDLE_GEMFILE(env or.bundle/config) says otherwise.LoadedManifest::pairalready models this, and the writers use it (#341, #390). The readers each chose their own lock: they hard-codedGemfile.lock, or read both.Change
crawlers/ruby_crawler.rs:bundler_loaded_manifest_in(view): on disk or a snapshot, the ambient env, the app config and (since Gem settings resolution skips Bundler's global config (~/.bundle/config/BUNDLE_USER_CONFIG), so a globalcache_pathorgemfilegets no warning or refusal and VEX attests an unpatched install #577) the global config; in a memory view, its own.bundle/config. This logic moved out of the hosted engine.bundler_loaded_lock_in(view): the lock of that pair, orNonefor an unsupportedBUNDLE_GEMFILE.lock_inventory/gem.rs:inventory_gemfile_lock_raw_inandgem_remotesread only the loaded lock.vex/discover/gem.rs: only the loaded lock yields refs. The twin Bundler ignores is still read through the guarded reader, so its Socket uuids stay recognized (rule 11) and a ledger claim can't attest them. Each ref the twin would have yielded becomes apatched_ref_unattributablediagnostic that names the lock Bundler loads. Bundler picks one pair deterministically, so rule 1 ("read every lock") applies only within that pair here, and the module docs now say so.hosted/engine.rs:keep_bundler_loaded_gem_filesreuses the shared resolver. Its behavior is unchanged.polyglot_project_discovers_the_union_of_every_package_managerused to add a vendoredgems.lockednext to the bundler fixture'sGemfile.lockwith nogems.rb. Bundler never reads that file, so it is now an ignored twin. The test keeps its vendored coverage through thePipfile.lockwheel, and gem coverage through the hosted bundler fixture.main's redsocket-patch-cli --libdoesn't block this PR): twocommands::vex_consumedtests adjusted after Fix npm store copies missed by agent apply and vex (#601, #603) #605. It becomes a no-op once Fix vex alias tests broken by store-copy merge #851 lands.Per-issue checklist (#736 acceptance criteria)
inventory_projectongems.rb+gems.lockedreturns its gems:lock_inventory::tests::gem_inventory_reads_the_lock_bundler_loadsGemfile.lockbesidegems.rb+gems.locked: inventory, every-lock inventory and VEX discovery read onlygems.locked: same test, plusvex::discover::gem::tests::only_the_lock_bundler_loads_is_readanda_stale_redirected_gemfile_lock_beside_gems_rb_is_not_attested(the issue-comment repro).bundle/configBUNDLE_GEMFILE: Gemfilebeside agems.rbreadsGemfile.lockon disk and in memory:gem_inventory_reads_the_lock_bundler_loads,gem_inventory_memory_view_reads_the_lock_bundler_loadsgems.rbproject yields a gem candidate, with or without a stale twin:hosted_memory_engine::gems_rb_project_yields_its_gem_candidatesgem_remotes_reads_the_lock_bundler_loadsgems.rb, a straygems.lockedis ignored:gem_inventory_ignores_gems_locked_without_gems_rbTest evidence
gem_inventory_reads…,…memory_view…,gem_remotes_reads…,only_the_lock_bundler_loads_is_read,a_stale_redirected…). The engine test failed withleft: 0, right: 1(no candidate) when onlylock_inventory/gem.rswas reverted.cargo clippy --workspace --all-features -- -D warnings: clean, also after the merges ofmain.--all-targetsreports only pre-existing hits onmain, none in the touched files.cargo test --workspace --all-features --no-fail-fast: all binaries pass except 12 permission-injection tests (chmod 0o555/ unremovable-file write failures incovgap_commands_vendor,copy_tree,vlt_heal,pypi_poetry,pypi_requirements,repair_invariants). Those can't fail as root (the sandbox runs as uid 0), none touches gem code, and CI runs as non-root.e2e_redirect_gem_build -- --ignored(11 passed),e2e_vendor_gem_build -- --ignored(6 passed),e2e_vex_lockfile gem(9 passed).nativejobs failed one random live-API case each and passed on a single re-run (details in the PR comments).cargo fmt --all -- --checkisn't usable as a gate here:mainitself isn't rustfmt-clean, and CI doesn't run it. The touched hunks are rustfmt-formatted.npm/,pypi/,gem/only dispatch to the binary).🤖 Generated with Claude Code
https://claude.ai/code/session_012Ao6g9qAnawPfNxv11f3wM
Note
Medium Risk
Changes which gem lock drives inventory, VEX attestation, and hosted scan—incorrect choice previously allowed false
not_affectedwhilebundle installused an unpatched lock; behavior is now security-sensitive but narrowly scoped to Bundler discovery rules with extensive tests.Overview
Fixes #736 by aligning all Ruby gem lock readers with the single manifest/lock pair Bundler actually loads (
gems.rb+gems.lockedwhen present, elseGemfile+Gemfile.lock, honoringBUNDLE_GEMFILE/.bundle/config).Adds shared helpers
bundler_loaded_manifest_inandbundler_loaded_lock_ininruby_crawler.rs(hosted engine now calls the manifest helper instead of duplicating logic). Lock inventory andgem_remotesread only that lock; VEX gem discovery attests refs from the loaded lock only, while wiring in the ignored twin still parses for UUID recognition but surfacespatched_ref_unattributablediagnostics instead of refs.Regression coverage includes
gems.rbin-memory hosted redirect, inventory/memory-view/BUNDLE_GEMFILEcases, and stale-twin repros. npmvex_consumedtests are adjusted post-#605 so alias expansion is still exercised when the name-keyed resolver already finds aliases. The polyglot discovery test drops a vendoredgems.lockedthat Bundler would never read.Reviewed by Cursor Bugbot for commit 1eedea8. Configure here.