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
{{ message }}
Repository navigation
Gem VEX judges an unused system gem-home copy when the project sets a Bundler path, so standalone vex never attests #1098
For a hosted gem purl, vex requires every copy in list 1 to verify (vex_consumed.rs#L28, vex.rs#L624-L630). #1002 fixed the #1001 symptom for scan --mode hosted --vex, but the standalone vex command still has it.
Proved by execution. I ran a throwaway test twice on e61a845. It reuses the harness of e2e_redirect_gem_stale_install.rs: stage_system_home_copy provides a fake gem env gemdir home holding the unpatched stale-probe-gem-1.0.0, plus the mock API. The steps were:
exit 1, not_applied ("the patched files still hold the original content"), no_applicable_patches
installed: BUNDLE_PATH: gems, with the patched copy in gems/ruby/3.3.0/gems/
exit 1, not_applied
control: same as the row above, with the system-home copy removed
exit 0, verified, not_affected
In both failing cases, Bundler never loads the system-home copy (the premise of #1001 and #1002). The project is patched, or about to be, yet vex refuses it on every run. On the fresh checkout, the unused copy also prevents the hosted lockfile basis from applying, because the copy counts as "installed".
The agent apply path reads the same list 1. In an explicit-path project, it therefore also writes to (and rollback restores) a same-version copy in the shared gem home, which Bundler doesn't load for that project. I found this by reading the code; I didn't execute it.
Impact: fail-closed (no false attestation), but a false negative. Any developer or CI machine whose system gem home holds an old copy of a patched gem never gets vex output for that gem, in projects that set path (common in CI caches, bundle config set --local path vendor/bundle). Size: one model function plus three call sites.
Proposed change
Make bundler_install_homes (or one BundlerHomes { loaded, default_gem_homes } value built once) the single answer to "which homes does Bundler load for this project".
Make the gem branch of the vex installed lookup (find_manifest_package_copies_reusing and hosted_consumed_copies) judge only those homes.
Leave get_gem_paths with only its documented default-gem reason for keeping the gem env homes under an explicit path. Either restrict those homes to gems whose spec is under specifications/default/, or keep them for apply only. The PR decides which and documents it in CLI_CONTRACT's gem section.
Delete the duplicated tier read: bundler_install_homes re-reads the app, env and global tiers that discover_bundle_stores_impl has already resolved. BundleStoreDiscovery should carry explicit_path so the rule lives in one place.
Size and scope
crawlers/ruby_crawler.rs, ecosystem_dispatch.rs (the gem branch) and commands/vex_consumed.rs (the gem row), plus tests.
Standalone vex attests the gem in both failing rows above, and in each BUNDLE_PATH spelling (env, .bundle/config, global config). Add a regression e2e test beside gem_hosted_explicit_bundle_path_ignores_system_home_copy.
Without a Bundler path (system gems), an unpatched gem env copy still blocks attestation (not_applied), and so does path.system: true outranking env BUNDLE_PATH. This mirrors gem_hosted_system_install_still_flags_system_home_copy.
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: register comment.
Kind: bug (inconsistent logic within one ecosystem). Source: new finding; register E90.
Problem
The question "which gem homes does Bundler load this project's gems from" now has two answers on main @
e61a845:RubyCrawler::get_gem_paths(ruby_crawler.rs#L43-L58).It appends every `gem env` home whenever the default `vendor/bundle` holds no store ([`#L136-L143`](https://github.com/SocketDev/socket-patch/blob/e61a8458651f78a8d42e4aad437e66a9f9894f6b/crates/socket-patch-core/src/crawlers/ruby_crawler.rs#L136-L143)).`` It does this even when the project sets an explicit Bundlerpath, where Bundler disables shared gems. This list feeds scan discovery, agentapply, andvex's installed-copy lookup (ecosystem_dispatch.rs#L618-L660).``RubyCrawler::bundler_install_homes(#L645-L690).`` It was added by Fix gem stale-install guard home selection (#1001, #729) #1002 for Hosted gem stale-install guard flags an unused system gem-home copy when the project sets a Bundlerpaththat isn't installed yet, soscan --mode hosted --vexfails withno_applicable_patcheson fresh checkouts #1001 and counts thegem envhomes only when Bundler uses system gems: no deployment store, and no explicit `path` according to `bundler_sets_explicit_path`. Only the hosted stale-install guard uses it (`scan/hosted.rs#L379`).For a hosted gem purl,
vexrequires every copy in list 1 to verify (vex_consumed.rs#L28,vex.rs#L624-L630). #1002 fixed the #1001 symptom forscan --mode hosted --vex, but the standalonevexcommand still has it.Proved by execution. I ran a throwaway test twice on
e61a845. It reuses the harness ofe2e_redirect_gem_stale_install.rs:stage_system_home_copyprovides a fakegem env gemdirhome holding the unpatchedstale-probe-gem-1.0.0, plus the mock API. The steps were:.bundle/configsetsBUNDLE_PATH;scan --mode hosted --vexexits 0, with no stale warning, as Hosted gem stale-install guard flags an unused system gem-home copy when the project sets a Bundlerpaththat isn't installed yet, soscan --mode hosted --vexfails withno_applicable_patcheson fresh checkouts #1001 intends;GEMsection);vexruns with the samePATH.vexBUNDLE_PATH: vendor/bundle(nothing installed yet)not_applied("the patched files still hold the original content"),no_applicable_patchesBUNDLE_PATH: gems, with the patched copy ingems/ruby/3.3.0/gems/not_appliedverified,not_affectedIn both failing cases, Bundler never loads the system-home copy (the premise of #1001 and #1002). The project is patched, or about to be, yet
vexrefuses it on every run. On the fresh checkout, the unused copy also prevents the hosted lockfile basis from applying, because the copy counts as "installed".The agent
applypath reads the same list 1. In an explicit-path project, it therefore also writes to (androllbackrestores) a same-version copy in the shared gem home, which Bundler doesn't load for that project. I found this by reading the code; I didn't execute it.Symptoms
paththat isn't installed yet, soscan --mode hosted --vexfails withno_applicable_patcheson fresh checkouts #1001 / Fix gem stale-install guard home selection (#1001, #729) #1002: the same defect in the stale guard, fixed there only.gem "x", "~> 1.0"to the older patched"0.8.1", so the prescribedbundle installdowngrades the project's locked gem #1055: also shared-gem-home state leaking into a project's answer (version selection; different path).Impact: fail-closed (no false attestation), but a false negative. Any developer or CI machine whose system gem home holds an old copy of a patched gem never gets
vexoutput for that gem, in projects that setpath(common in CI caches,bundle config set --local path vendor/bundle). Size: one model function plus three call sites.Proposed change
bundler_install_homes(or oneBundlerHomes { loaded, default_gem_homes }value built once) the single answer to "which homes does Bundler load for this project".vexinstalled lookup (find_manifest_package_copies_reusingandhosted_consumed_copies) judge only those homes.get_gem_pathswith only its documented default-gem reason for keeping thegem envhomes under an explicit path. Either restrict those homes to gems whose spec is underspecifications/default/, or keep them for apply only. The PR decides which and documents it in CLI_CONTRACT's gem section.bundler_install_homesre-reads the app, env and global tiers thatdiscover_bundle_stores_implhas already resolved.BundleStoreDiscoveryshould carryexplicit_pathso the rule lives in one place.Size and scope
crawlers/ruby_crawler.rs,ecosystem_dispatch.rs(the gem branch) andcommands/vex_consumed.rs(the gem row), plus tests.applywrite targets beyond the default-gem rule, and Hosted gem scan pins a version that only another project installed into the shared gem home, rewritinggem "x", "~> 1.0"to the older patched"0.8.1", so the prescribedbundle installdowngrades the project's locked gem #1055's version selection.Acceptance criteria
vexattests the gem in both failing rows above, and in eachBUNDLE_PATHspelling (env,.bundle/config, global config). Add a regression e2e test besidegem_hosted_explicit_bundle_path_ignores_system_home_copy.path(system gems), an unpatchedgem envcopy still blocks attestation (not_applied), and so doespath.system: trueoutranking envBUNDLE_PATH. This mirrorsgem_hosted_system_install_still_flags_system_home_copy.not_affectedfor an unpatched install when.bundle/configsets an out-of-treepath(absolute or~/…), because the skipped bundle root counts as "nothing installed" #709's out-of-tree config root is still read for verification.vextake the home list from the same function: a unit test asserts that they agree on each tier combination.e2e_redirect_gem_stale_install, the ruby crawler unit tests and the gem VEX discovery tests stay green.Dependencies
BUNDLE_GEMFILE=gemfiles/x.gemfilemovesBundler.root, so a relative bundle path resolves to the wrong dir,applypatches the system copy andvexattestsnot_affectedwhile Bundler loads the unpatchedgemfiles/vendor/bundlecopy #952 (BUNDLE_GEMFILEandBundler.root) and PR Fix hosted gem pinning a version the lock doesn't resolve (#1055) #1060 (Hosted gem scan pins a version that only another project installed into the shared gem home, rewritinggem "x", "~> 1.0"to the older patched"0.8.1", so the prescribedbundle installdowngrades the project's locked gem #1055). Rebase on whichever lands first.Backlog review — 2026-10-08
Priority: P1 → P2. An unused system gem copy makes VEX reject a correctly patched Bundler path; false negative.