Skip to content

Gem .bundle/config reader keeps a trailing # comment in the value, so a commented BUNDLE_PATH is missed, agent apply patches the system copy and vex attests not_affected while Bundler loads the unpatched project copy #951

Description

[agent] Found by the scheduled Bundler (RubyGems) bug-hunt routine (ledger #316).

Summary

Bundler's config loader drops a trailing # comment from a .bundle/config value. socket-patch's line scraper (bundle_config_setting* / parse_bundle_config_path → unquote_bundle_config_value) keeps it. Take BUNDLE_PATH: .gems # project-local gems. Bundler installs into and loads from .gems/ruby/<abi>/. The crawler resolves the root to a directory literally named .gems # project-local gems, which doesn't exist, and falls back to the gem env homes.

Agent apply therefore patches the system copy of the gem and reports success. vex then attests not_affected while bundle exec loads the unpatched project copy. Nothing warns.

Bundler 4.1 writes .bundle/config values unquoted (BUNDLE_PATH: .gems), so a hand-added trailing comment produces exactly this shape. Bundler 2.4 through 4.1 all honour the commented value (verified below).

Impact

Repro (Linux, Ruby 3.3.6; no API needed)

mkdir app && cd app && mkdir .bundle
printf 'source "https://rubygems.org"\ngem "colorize", "0.8.1"\n' > Gemfile
printf -- '---\nBUNDLE_PATH: .gems # project-local gems\n' > .bundle/config
gem install colorize -v 0.8.1          # a system copy also exists (common on dev machines / images)
bundle install                          # installs into ./.gems/ruby/3.3.0
# Hand-written agent manifest: one patch to package/lib/colorize.rb (appends a marker line),
# .socket/manifest.json + .socket/blobs/<afterHash> (git-sha256 hashes).
socket-patch apply --offline            # exit 0, "applied"
socket-patch vex --product pkg:gem/app@1.0.0 --output vex.json   # exit 0, not_affected
grep -c SOCKET_PATCHED_MARKER .gems/ruby/3.3.0/gems/colorize-0.8.1/lib/colorize.rb   # 0  (loaded copy)
grep -c SOCKET_PATCHED_MARKER "$(gem env gemdir)/gems/colorize-0.8.1/lib/colorize.rb" # 1  (unused copy)
bundle exec ruby -e 'require "colorize"; puts $LOADED_FEATURES.grep(/colorize.rb$/)'   # → ./.gems/... (unpatched)

Bundler's own view: bundle config get path → ".gems", and Bundler::YAMLSerializer.load("---\nBUNDLE_PATH: .gems # c") → {"BUNDLE_PATH"=>".gems"} (strip_comment in bundler/yaml_serializer.rb, 2.5+; 2.4.22's loader gives the same result).

Expected vs actual

  • Expected: install-root discovery follows Bundler's settings (CLI_CONTRACT.md: the bundle path, cache path and gemfile are resolved "in Bundler::Settings priority", and .bundle/config is read like Bundler reads it). So apply patches .gems/ruby/3.3.0/gems/colorize-0.8.1, and vex attests only if that copy is patched.
  • Actual: the comment becomes part of the path. The project copy is never crawled, the gem env copy is patched instead, and vex exits 0 with not_affected.

OS × version

OS Ruby Bundler BUNDLE_PATH: .gems # c Control BUNDLE_PATH: .gems
Linux 3.3.6 4.1.0.beta1 (written by bundle config set --local path .gems, comment appended) fail (loaded copy unpatched, VEX not_affected) —
Linux 3.3.6 4.0.22 fail (×2) pass
Linux 3.3.6 2.6.9 fail pass
Linux 3.3.6 2.4.22 fail pass
macOS / Windows — — untested (the parsing is OS-independent) —

The v4.0.0 release (npm @socketsecurity/socket-patch-linux-x64-gnu@4.0.0) behaves the same, so this isn't a regression. Tested on main 9c43dfc.

Suspect code

No probe runs: the logic is OS-independent string parsing.

Activity

  1. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Follow-up from the same run: the cache_path variant is now verified, not just traced.

    • Bundler: with BUNDLE_CACHE_PATH: vendor/gems # committed gem cache in .bundle/config, Bundler.app_cache resolves to <proj>/vendor/gems on both 4.0.22 and 2.4.22, and bundle cache writes the archives there.
    • socket-patch (main 9c43dfc): I added one case to gem_hosted_stale_archive_at_configured_cache_path_warns_and_is_not_attested (e2e_redirect_gem_stale_install.rs) with that config line and a stale vendor/gems/<gem>.gem. The run is a hosted scan --vex. It emits no redirect_gem_stale_install warning and finishes "status":"success", so the committed stale archive goes unflagged. It failed at the warning-count assertion on both runs (2/2). The existing quoted BUNDLE_CACHE_PATH: "vendor/gems" case passes. The edit was local only and has been reverted.

    So the trailing-comment divergence affects at least two readers: the agent install root (BUNDLE_PATH) and the hosted stale-install guard (BUNDLE_CACHE_PATH).


    Generated by Claude Code

  2. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged as priority:p1 (Bundler). Confirmed on main 9c43dfc: unquote_bundle_config_value (crates/socket-patch-core/src/crawlers/ruby_crawler.rs:1520) trims and unquotes the value but never strips a # … tail the way Bundler's YAMLSerializer#strip_comment does, so every .bundle/config reader on this helper (path, cache_path, path.system, gemfile, global tier) is affected. No duplicate and no open PR. Open PRs #684 and #916 add readers on the same helper, so a fix at the helper covers them too. This is a different cause from #952, which is about Bundler.root.


    Generated by Claude Code

  3. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: unquote_bundle_config_value doesn't apply Bundler's strip_comment to .bundle/config values). Branch: agent/fix-bundle-config-trailing-comment. Claim-ID: 20261006T172655Z-2a8716


    Generated by Claude Code

  4. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft fix PR: #953


    Generated by Claude Code

  5. added 2 commits that reference this issue on Oct 6, 2026
    ff4f2c4
    52542db
  6. mikolalysenko commented on Oct 6, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Re-check (Bundler bug-hunt, ledger #316): I verified PR #953 at head 52542db against the original agent repro (Linux, Ruby 3.3.6, Bundler 4.0.22, .bundle/config = BUNDLE_PATH: .gems # project-local gems, real bundle install / bundle exec).

    Binary Copy bundle exec loads (.gems/ruby/3.3.0/…) vex
    main 9c43dfc unpatched (system copy patched instead) not_affected (false)
    PR #953 52542db patched not_affected (now true)

    The issue stays open until the PR merges.


    Generated by Claude Code

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

    agent:triagedbugSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentpm:bundlerBundler (RubyGems)priority:p1

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions