GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Hosted gem redirect rewrites only the first of a gem's declarations, so a gem listed in two `group` blocks makes every `bundle install` fail with "You cannot specify the same gem twice" · Issue #548 · SocketDev/socket-patch

Hosted gem redirect rewrites only the first of a gem's declarations, so a gem listed in two group blocks makes every bundle install fail with "You cannot specify the same gem twice" #548

New issue
Copy link
New issue
Copy link
Closed
#552
Closed
Hosted gem redirect rewrites only the first of a gem's declarations, so a gem listed in two group blocks makes every bundle install fail with "You cannot specify the same gem twice"#548
#552
Copy link
Labels
agent:triagedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:bundlerBundler (RubyGems)Bundler (RubyGems)priority:p1

Description

@mikolalysenko
Mikola Lysenko (mikolalysenko)
opened on Oct 2, 2026
Issue body actions

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

Summary

Bundler accepts a Gemfile that declares the same gem more than once with the same requirement. It only prints "Your Gemfile lists the gem … more than once". A common example is a gem listed in both group :development and group :test. scan --mode hosted / get --mode hosted rewrite only the first matching gem line into the per-dep source "<patch registry>" do gem "x", "<ver>" end block, and leave the other declaration as it was. The two declarations then have different requirements (= 1.0.0 vs >= 0). Bundler refuses to parse the Gemfile, so every later bundle install (frozen or not) and every bundle exec exits 4.

The scan exits 0 with redirected: 1, no warning, and an in-run --vex that attests not_affected.

Vendored mode already detects this shape and fails closed with gemfile_declaration_not_editable: gem "x" is declared more than once in the Gemfile (crates/socket-patch-core/src/vendor/gem.rs:1371). Hosted mode has no equivalent check.

Impact

A project that commits the hosted rewrite can no longer install at all: CI breaks, the deploy breaks, and the VEX document from the same run claims the CVE is not exploitable. This is a different root cause from #482. #482 appends a second declaration when the first one isn't visible (eval_gemfile / a loop). Here both declarations are plain lines that the rewriter can see, but it edits only one of them.

Repro

This uses a hold-open copy of crates/socket-patch-cli/tests/e2e_redirect_gem_build.rs (the redirect_scanned_project fixture with a CHECKSUMS lock: real gem build, mock upstream + patch registry + API on loopback), then a real Bundler:

cat > Gemfile <<'EOF'
source "$UPSTREAM"

group :development do
  gem "vuln-gem"
end

group :test do
  gem "vuln-gem"
end
EOF
bundle config set --local path vendor/bundle
bundle lock                 # warns "lists the gem vuln-gem (>= 0) more than once", writes the lock
socket-patch scan --mode hosted --json --yes --api-url $API --org test-org --api-token fake \
  --vex out.vex.json --vex-product pkg:gem/[email protected]
# exit 0, redirect.redirected = 1, warnings = [], VEX: GHSA-redirect-gem-real not_affected
# fresh checkout (Gemfile, Gemfile.lock, .socket, .bundle):
bundle install              # exit 4

The Gemfile after the scan:

source "http://127.0.0.1:33109/upstream"

group :development do
source "http://127.0.0.1:33109/patch-registry/gem/<token>/<uuid>/" do
  gem "vuln-gem", "1.0.0"
end
end

group :test do
  gem "vuln-gem"
end

The output of bundle install:

[!] There was an error parsing `Gemfile`: You cannot specify the same gem twice with different version requirements.
You specified: vuln-gem (= 1.0.0) and vuln-gem (>= 0). Bundler cannot continue.
 #  from …/Gemfile:10
 >    gem "vuln-gem"

A top-level declaration plus one inside group :test do … end gives the same result (the top-level line is rewritten and the group line is left alone, so install exits 4).

Expected vs actual

  • Expected: the hosted rewrite must leave a Gemfile that Bundler installs. docs/ecosystems.md describes hosted gem wiring as a "per-dep source block", and CLI_CONTRACT.md requires that an envelope never attests a CVE that its own wiring doesn't fix. For a gem declared more than once, hosted mode should either rewrite every declaration consistently or fail closed before writing anything, as vendored mode does (gemfile_declaration_not_editable). In the fail-closed case it should also leave the purl out of the in-run --vex set.
  • Actual: only the first declaration is rewritten; the scan exits 0 with no warning; --vex attests not_affected; every install exits 4.

Matrix (Linux, Ruby 3.3.6)

Bundler Lock Two group blocks Top-level + group Vendored, same shape
4.0.17 CHECKSUMS fail (install exit 4, ×2) fail (install exit 4) pass (refused, nothing written)
2.6.9 CHECKSUMS (--add-checksums) fail (install exit 4) untested untested
2.4.22 no CHECKSUMS fail (install exit 4) untested untested

The bug isn't OS-specific: it comes from the Gemfile text rewriter, and macOS / Windows weren't probed. It also reproduces with the released v4.0.0 (npm @socketsecurity/[email protected]) and with the head of draft PR #532, so it isn't a recent regression.

Suspect code

  • crates/socket-patch-core/src/patch/redirect/mod.rs:5007: if let Some(m) = gem_line_re.captures(gf) takes only the first match of the gem-line regex, so later declarations of the same name are never examined.
  • Compare with crates/socket-patch-core/src/vendor/gem.rs:1371 (if found.len() > 1 { return Err("… declared more than once …") }).

No probe branch was needed (the bug doesn't depend on the OS).

Activity

  1. Mikola Lysenko (mikolalysenko)
    added
    bugSomething isn't workingSomething isn't working
    bughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agent
    pm:bundlerBundler (RubyGems)Bundler (RubyGems)
    on Oct 2, 2026
  2. mikolalysenko commented on Oct 2, 2026

    @mikolalysenko
    Mikola Lysenko (mikolalysenko)
    on Oct 2, 2026
    CollaboratorAuthor
    More actions

    [agent] Triaged as priority:p1 (Bundler). Confirmed on main 61cfb9b: in the hosted gem rewriter (crates/socket-patch-core/src/patch/redirect/mod.rs, around L5007), gem_line_re.captures(gf) only looks at the first match. A second literal declaration of the same gem is never examined, and the code has no equivalent of vendored mode's found.len() > 1 refusal (vendor/gem.rs:1371). No open or merged PR covers this.

    Shares root cause with #482: the hosted Gemfile rewriter chooses between "rewrite" and "append" from a single regex probe of the root Gemfile text. It never counts every declaration Bundler will actually see, so it misses both the second literal line here and the eval_gemfile/loop declaration in #482, and it never checks the lock's DEPENDENCIES. Will be fixed together.


    Generated by Claude Code

  3. Mikola Lysenko (mikolalysenko)
    added
    agent:triaged
    priority:p1
    on Oct 2, 2026
  4. mikolalysenko commented on Oct 2, 2026

    @mikolalysenko
    Mikola Lysenko (mikolalysenko)
    on Oct 2, 2026
    CollaboratorAuthor
    More actions

    [agent] Claiming this issue (with #482; shared root cause: the Gemfile rewriters pick rewrite or append from one regex probe of the root Gemfile instead of counting every declaration Bundler sees). Branch: agent/fix-gemfile-declaration-census. Claim-ID: 2026-10-02T11:21:53Z-e56953


    Generated by Claude Code

  5. Mikola Lysenko (mikolalysenko)
    added
    agent:claimed
    on Oct 2, 2026
  6. mikolalysenko commented on Oct 2, 2026

    @mikolalysenko
    Mikola Lysenko (mikolalysenko)
    on Oct 2, 2026
    CollaboratorAuthor
    More actions

    [agent] Draft fix PR: #552


    Generated by Claude Code

  7. Mikola Lysenko (mikolalysenko)
    added 2 commits that reference this issue on Oct 2, 2026

    Stop Gemfile rewrites declaring a gem twice

    94270b4

    Test hosted gem refusals with real Bundler

    5d814c7
  8. Mikola Lysenko (mikolalysenko)
    closed this as completedin #552on Oct 2, 2026
  9. Mikola Lysenko (mikolalysenko)
    removed
    agent:claimed
    on Oct 2, 2026
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 workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:bundlerBundler (RubyGems)Bundler (RubyGems)priority:p1

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.