GeezerVison is showing github.com — Gem agent crawl ignores that `BUNDLE_GEMFILE=gemfiles/x.gemfile` moves `Bundler.root`, so a relative bundle path resolves to the wrong dir, `apply` patches the system copy and `vex` attests `not_affected` while Bundler loads the unpatched `gemfiles/vendor/bundle` copy · Issue #952 · SocketDev/socket-patch
Gem agent crawl ignores that BUNDLE_GEMFILE=gemfiles/x.gemfile moves Bundler.root, so a relative bundle path resolves to the wrong dir, apply patches the system copy and vex attests not_affected while Bundler loads the unpatched gemfiles/vendor/bundle copy #952Description[agent] Found by the scheduled Bundler (RubyGems) bug-hunt routine (ledger #316).
Summary
Multi-Gemfile projects (the Appraisal / gemfiles/*.gemfile CI-matrix layout) run Bundler with BUNDLE_GEMFILE=gemfiles/rails_7.gemfile. That moves Bundler.root to gemfiles/, which has two effects:
- the app config Bundler reads is
gemfiles/.bundle/config (Bundler.app_config_path = root.join(".bundle"));
- a relative bundle path, whether from that config or from the
BUNDLE_PATH env var, resolves against gemfiles/. bundle install with path vendor/bundle therefore installs into gemfiles/vendor/bundle/ruby/<abi>.
The agent-mode crawler always uses the --cwd root instead. It reads <cwd>/.bundle/config, resolves BUNDLE_PATH against <cwd>, and probes <cwd>/vendor/bundle. The code's own doc comment says a relative value resolves "against the directory of the Gemfile (Bundler.root), not the process cwd" (ruby_crawler.rs:1391). It finds no store, falls back to the gem env homes, and patches the system copy. vex then attests not_affected while bundle exec loads the unpatched gemfiles/vendor/bundle copy. Nothing warns.
Hosted and vendored modes already treat a BUNDLE_GEMFILE outside the root pair as unsupported (redirect_gem_bundle_gemfile_unsupported / gemfile_not_loaded). Agent mode and vex don't consult it at all.
Impact
Silent: the patch lands in a copy the app doesn't load, and the VEX document claims the vulnerability is mitigated. This shape is the standard GitHub Actions matrix, env: BUNDLE_GEMFILE: gemfiles/<x>.gemfile. A relative BUNDLE_PATH: vendor/bundle export, or bundle config set --local path vendor/bundle run under that env, both land in gemfiles/vendor/bundle.
Repro (Linux, Ruby 3.3.6; no API needed)
mkdir app && cd app && mkdir gemfiles
printf 'source "https://rubygems.org"\ngem "colorize", "0.8.1"\n' > Gemfile
cp Gemfile gemfiles/alt.gemfile
gem install colorize -v 0.8.1 # a system copy also exists
export BUNDLE_GEMFILE=gemfiles/alt.gemfile
# variant cfg: bundle config set --local path vendor/bundle (writes gemfiles/.bundle/config)
# variant env: export BUNDLE_PATH=vendor/bundle
bundle install # → gemfiles/vendor/bundle/ruby/3.3.0/gems/colorize-0.8.1
# hand-written agent manifest: one patch to package/lib/colorize.rb (appends a marker),
# .socket/manifest.json + .socket/blobs/<afterHash>
socket-patch apply --offline # exit 0, "applied"
socket-patch vex --product pkg:gem/[email protected] --output vex.json # exit 0, not_affected
bundle exec ruby -e 'require "colorize"; f=$LOADED_FEATURES.grep(/colorize.rb$/).first; puts f, File.read(f).include?("SOCKET_PATCHED_MARKER")'
# → ./gemfiles/vendor/bundle/ruby/3.3.0/gems/colorize-0.8.1/lib/colorize.rb false
grep -c SOCKET_PATCHED_MARKER "$(gem env gemdir)/gems/colorize-0.8.1/lib/colorize.rb" # 1 (unused copy)
Expected vs actual
- Expected: agent install-root discovery follows
Bundler::Settings (CLI_CONTRACT.md: the app config is $BUNDLE_APP_CONFIG/config, else .bundle/config, and the settings are resolved in Bundler's priority; ruby_crawler.rs:1391: a relative path resolves against Bundler.root). With an ambient BUNDLE_GEMFILE in a subdirectory, that means gemfiles/.bundle/config and gemfiles/vendor/bundle. Failing that, the command should refuse or warn the way hosted and vendored do, not attest.
- Actual:
apply patches the gem env copy and vex exits 0 with not_affected for a copy Bundler never loads.
OS × version
| OS |
Ruby |
Bundler |
Path from gemfiles/.bundle/config |
Path from env BUNDLE_PATH=vendor/bundle |
| Linux |
3.3.6 |
4.0.22 |
fail (×2) |
fail |
| Linux |
3.3.6 |
2.6.9 |
fail |
fail |
| macOS / Windows |
— |
— |
untested (path resolution is OS-independent) |
— |
Control: the same project without BUNDLE_GEMFILE (root .bundle/config with path vendor/bundle) patches the loaded copy (the e2e suites cover it). Tested on main 9c43dfc. I didn't bisect: no released version models Bundler.root for this.
Suspect code
crates/socket-patch-core/src/crawlers/ruby_crawler.rs:354 (discover_bundle_stores_impl): cwd is used as Bundler.root for the app config, the env BUNDLE_PATH and the default root.
ruby_crawler.rs:1320 (bundler_app_config_dir) and :1398 (resolve_bundle_path): both join onto the project root, not onto the directory of the effective BUNDLE_GEMFILE.
vex's installed-copy lookup goes through the same discovery.
No probe runs: this is OS-independent path resolution. Activitymikolalysenko commented on Oct 6, 2026 CollaboratorAuthor More actions [agent] Triaged as priority:p1 (Bundler). No duplicate and no open PR. #896 also touches BUNDLE_GEMFILE, but that one is about the hosted gate comparing paths lexically; this one is about agent/VEX store discovery using --cwd instead of the effective Bundler.root. They have different causes, so they aren't clustered. #951, which is about comment stripping in the config value, is also a separate cause.
Generated by Claude Code mikolalysenko commented on Oct 8, 2026 CollaboratorAuthor More actions [agent] Re-triage on main b96a785 (Bundler 4.0.18 / Ruby 3.3.6): this still reproduces, and the new apply --check gate from #1029 inherits it. With BUNDLE_GEMFILE=$PWD/gemfiles/alt.gemfile, gemfiles/.bundle/config BUNDLE_PATH: "vendor/bundle", and colorize 0.8.1 installed into gemfiles/vendor/bundle:
socket-patch apply --offline --ecosystems gem # 1 of 1 applied (to the system gem-home copy)
socket-patch apply --check --ecosystems gem # "Patches are in sync (1 patch checked)." exit 0
socket-patch vex --offline --product pkg:gem/[email protected] # not_affected
bundle exec ruby -e '…' # LOADED …/gemfiles/vendor/bundle/ruby/3.3.0/gems/colorize-0.8.1 patched=false
So CI's apply --check also passes while Bundler loads the unpatched copy.
Generated by Claude Code mikolalysenko commented on Oct 9, 2026 CollaboratorAuthor More actions
v5 triage: P2, not a release blocker. Retain alternate nested BUNDLE_GEMFILE agent scope at P2. Standard Bundler install-home/VEX failures have separate release blockers.
This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX. mikolalysenko commented on Oct 12, 2026 CollaboratorAuthor More actions [agent] Claiming this issue (shared root cause: agent-mode bundle store discovery treats --cwd as Bundler.root instead of the directory of the effective BUNDLE_GEMFILE). Branch: agent/fix-gem-bundle-gemfile-root. Claim-ID: 2026-10-12T12:21:43Z-39893f
Generated by Claude Code mikolalysenko commented on Oct 12, 2026 CollaboratorAuthor More actions added a commit that references this issue on Oct 12, 2026This issue will close once commit e086d49 is merged into the 'main' branch.e086d49
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|