Skip to content

Hosted gem on Bundler < 2.2 locks: once two gems are redirected, vex finds no patch refs and every re-scan skips both gems as redirect_unattributable (exit 0), so superseding patches never land #1749

Open

Description

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

Summary

Bundler 1.17–2.1 write all rubygems remotes into one merged GEM section. #1688 / #1690 made hosted mode work on that shape when one gem is redirected. If two gems are redirected (each with its own source "<patch-registry>/<uuid>/" do block, e.g. a direct gem and its transitive dep, or two direct gems), the converged lock's merged section holds two Socket remotes, and lockfile discovery gives up on the whole section:

Impact

On Bundler ≤ 2.1, a hosted project with more than one patched gem can't produce a VEX document and never picks up newer patches, and the scan exits 0, so CI never notices. The only signal is a per-package skipped event.

Repro

This uses the repo's own real-Bundler harness (e2e_redirect_gem_build.rs: redirect_scanned_project + mount_patch_generation).

  1. Fixture Driver::ScanVex, no CHECKSUMS (Bundler 2.1.4).
  2. Mount a generation serving vuln-gem (uuid 10000000-…) and tiny-dep (uuid 80000000-…). scan --mode hosted, then bundle install (the prescribed converge). The lock becomes:
GEM
  remote: http://127.0.0.1:…/upstream/
  remote: http://127.0.0.1:…/patch-registry/gem/<token>/10000000-…/
  remote: http://127.0.0.1:…/patch-registry/gem/<token>/80000000-…/
  specs:
    tiny-dep (1.0.0)
    vuln-gem (1.0.0)
      tiny-dep
DEPENDENCIES
  tiny-dep (= 1.0.0)!
  vuln-gem (= 1.0.0)!
BUNDLED WITH
   2.1.4

bundle exec loads both patched gems.
3. Re-run scan --mode hosted with the same generation. Both gems come back skipped / redirect_unattributable (Gemfile.lock: line 1: GEM section lists 3 remotes (a merged multi-source lock), and no gem in it is source-pinned to Socket patch 10000000-… … by both DEPENDENCIES (!) and a Gemfile source … do block), with status: success and exit 0.
4. vex --product pkg:gem/app@1.0.0 --output vex.json --patch-server-url <mock> … exits 2 with patched_ref_unattributable and then manifest_not_found.
5. Mount a superseding generation for vuln-gem only (uuid 9a9a9a9a-…, new bytes) and scan: vuln-gem is skipped as redirect_unattributable, rewrittenFiles: [], exit 0. A fresh frozen install still loads the gen-2 bytes.

Each cell reproduced twice.

Expected vs actual

OS × version

OS Ruby Bundler 2 hosted gems: re-scan / supersede / vex
Linux 3.1.6 1.17.3 fail
Linux 3.1.6 2.1.4 fail
Linux 3.3.6 2.1.4 fail
Linux 3.3.6 2.5.22 pass (separate sections, vex → not_affected)
Linux 3.3.6 4.0.23 pass (separate sections, vex → not_affected)
Linux 3.3.6 2.1.4, one hosted gem (gem_hosted_fresh_checkout_…_vex_verifies, supersede tests) pass

Tested on main 4654b7c. macOS and Windows weren't probed, because the logic is OS-independent text parsing.

Suspect code

crates/socket-patch-core/src/vex/discover/gem.rs:503: if socket_count == 1 && is_patch_registry_index(remote, &uuid). A merged section with more than one Socket remote yields an empty pinned set and DIAG_REF_UNATTRIBUTABLE, and the function only ever returns the first Socket remote's ref per section (socket.into_iter().next()?). Per-gem attribution through each gem's own source block URL would cover this shape. refresh_merged_gem_lock_remote (formats/gem/hosted.rs) already handles several remotes. It never runs here because the pre-write discovery check refuses first.

Bundler 2.2+ keeps a lock that started merged merged (module docs, "Merged sections"), so upgraded projects probably hit this too. I haven't verified that.

Activity

  1. mikolalysenko commented on Oct 27, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged as priority:p1 (Bundler). Confirmed on main (b787454): hosted_wiring in crates/socket-patch-core/src/vex/discover/gem.rs takes only the first Socket remote of a merged GEM section (socket.into_iter().next()?), and attributes gems only when socket_count == 1, so a merged section with two hosted remotes is reported patched_ref_unattributable. Not a duplicate: #1688/#1690 fixed the one-remote case only.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 27, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: gem lockfile discovery attributes at most one Socket remote per merged GEM section). Branch: agent/fix-gem-merged-multi-socket-remotes. Claim-ID: 2026-10-27T21:21:07Z-4d6d31


    Generated by Claude Code

  3. mikolalysenko commented on Oct 27, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1750


    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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions