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 treats a `gitlab:` or custom `git_source` gem as patched, so Bundler keeps loading the unpatched git checkout while VEX attests `not_affected` · Issue #652 · SocketDev/socket-patch

Hosted gem redirect treats a gitlab: or custom git_source gem as patched, so Bundler keeps loading the unpatched git checkout while VEX attests not_affected #652

New issue
Copy link
New issue
Copy link
Closed
#731
Closed
Hosted gem redirect treats a gitlab: or custom git_source gem as patched, so Bundler keeps loading the unpatched git checkout while VEX attests not_affected#652
#731
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 3, 2026
Issue body actions

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

Summary

The hosted gem redirect refuses declarations that pick their own source (redirect_gem_source_option), because Bundler allows one source per gem and a git or path option inside the Socket source … do block overrides the block. The refusal is a fixed token list (path:, git:, github:, gist:, bitbucket:, source: and their :sym forms). That list misses:

  1. gitlab:, which has been a built-in Bundler git source since 2.x (add_git_sources in bundler/dsl.rb).
  2. Any custom git_source(:name) { … }, for example git_source(:internal) { |r| "https://git.corp/#{r}.git" } with gem "x", internal: "x". This is a common pattern for private git hosts.

In both cases scan --mode hosted --vex moves the line into the patch-registry block with the git option still attached, reports "redirected": 1, exits 0 and writes a not_affected VEX statement. Bundler still resolves the gem from the GIT source, so the app keeps running the vulnerable code.

A third, milder variant: with a string-keyed hash rocket, gem "x", "git" => "…", the refusal also doesn't fire. gem_line_trailing_options then treats "git" as a quoted version constraint and returns no options, so the git source (and any other string-keyed option, such as "require" => false) is silently dropped. Here the patched gem does get installed, but the user's declared source and options are rewritten without any warning, which is the same thing the refusal exists to prevent.

Impact

False attestation: the VEX says the vulnerability is mitigated by a Socket patch, but the patched bytes are never installed. The redirect envelope reports success, and only the usual no-CHECKSUMS caveats appear.

Repro

I used a temporary probe hook in crates/socket-patch-cli/tests/e2e_redirect_gem_build.rs (redirect_scanned_project, reverted afterwards) to replace the fixture Gemfile. For the git-sourced arms it puts the pristine vuln-gem 1.0.0 source in a local git repo. Everything else is the stock capstone: the real gem build, a wiremock compact index plus patch API, and the real bundle install.

Gemfile (custom git source):

source "http://127.0.0.1:PORT/upstream"

git_source(:local) { |r| "/tmp/…/repos/#{r}" }

gem "vuln-gem", local: "vuln-gem"

Gemfile (built-in gitlab:, with https://gitlab.com/vuln-gem/vuln-gem.git mapped to the local repo through GIT_CONFIG_* url.insteadOf, so the lock records the real gitlab remote):

source "http://127.0.0.1:PORT/upstream"
gem "vuln-gem", gitlab: "vuln-gem"

Then:

bundle install          # lock: GIT remote … specs: vuln-gem (1.0.0)
socket-patch scan --mode hosted --json --yes --vex out.vex.json --vex-product pkg:gem/[email protected] \
  --api-url <mock> --org test-org --api-token fake
bundle install
bundle exec ruby -e 'require "vuln_gem"; puts VulnGem.status; puts $LOADED_FEATURES.grep(/vuln_gem/)'

Actual result (custom arm; the gitlab arm is identical apart from the remote):

scan: exit 0, "redirected": 1, warnings: redirect_gem_no_checksums_section, redirect_gem_frozen_install
Gemfile after:
  source "http://127.0.0.1:…/patch-registry/gem/<token>/<uuid>/" do
    gem "vuln-gem", "1.0.0", local: "vuln-gem"
  end
out.vex.json: pkg:gem/[email protected] "status": "not_affected" ("Patched via Socket patch … (redirected)")
bundle exec: VULNERABLE
  …/vendor/bundle/ruby/3.3.0/bundler/gems/vuln-gem-e258a4451c09/lib/vuln_gem.rb

String-key arm (gem "vuln-gem", "git" => "/tmp/…/repos/vuln-gem"): the Gemfile becomes gem "vuln-gem", "1.0.0" inside the block (the git option is gone, with no warning), and bundle exec loads the patched registry gem.

Expected vs actual

  • Expected: the same refusal as github: / git:. redirect_gem_source_option should fire, nothing should be rewritten and nothing attested. The code's own comment on gem_tail_source_option says an option that selects a source "OVERRIDES the block and the redirect becomes a silent no-op". docs/ecosystems.md describes hosted gem as a "per-dep source block", which only works when the block actually decides the source.
  • Actual: gitlab: and custom git_source keys aren't recognized, so the run silently does nothing useful and attests success. String-keyed "git" => / "path" => aren't recognized either, and the option is dropped.

A sturdier approach than extending the token list would be to refuse whenever the lock lists the gem under a GIT or PATH section rather than GEM, because that's what Bundler actually resolves from.

OS × version

OS Ruby Bundler custom git_source gitlab: "git" =>
Linux 3.3.6 4.0.17 reproduces (×2) reproduces option dropped
Linux 3.3.6 2.5.22 reproduces untested untested
macOS / Windows — — not probed; the token matching is OS-independent

Suspect code

  • crates/socket-patch-core/src/patch/redirect/mod.rs:5187 (gem_tail_source_option: fixed token list, symbol-key spellings only)
  • crates/socket-patch-core/src/patch/redirect/mod.rs:5164 (gem_line_trailing_options: a quoted hash-rocket key ends the option scan)
  • The vendored backend has the same token list at crates/socket-patch-core/src/vendor/gem.rs:1544. I didn't verify vendored end to end; there, Bundler likely errors on path: + git rather than staying silent.

Main 045d7ec, latest release tag v4.0.0. Not covered by the open #340 / #637 or #577 / #621.

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 3, 2026
  2. Mikola Lysenko (mikolalysenko)
    added a commit that references this issue on Oct 3, 2026

    bundler: run 11 ledger (#652)

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

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

    [agent] Triaged: priority:p1 (Bundler, false VEX attestation). Not a duplicate: #340 / PR #637 covers multi-line and modifier gem lines, and #577 / PR #621 covers global config. This issue is about the source-option token list in gem_tail_source_option, which hosted (redirect/mod.rs) and vendored (vendor/gem.rs) both copy. A fix should go through one shared check, and ideally key on the lock's GIT/PATH sections, so the two modes can't drift apart again.


    Generated by Claude Code

  5. mikolalysenko commented on Oct 3, 2026

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

    [agent] Claiming this issue (shared root cause: the Gemfile source-option guard is a fixed token list shared by hosted and vendored modes, so it misses gitlab:, custom git_source keys and string-keyed "git" => options). Branch: agent/fix-gem-source-option-guard. Claim-ID: 2026-10-03T23:20:55Z-ca7ddd


    Generated by Claude Code

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

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

    [agent] Draft PR: #731


    Generated by Claude Code

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

    Refuse gem lines pulled from git sources

    This issue will close once commit 4741ee6 is merged into the 'main' branch.
    4741ee6

    Add e2e and escape tests for gem git sources

    This issue will close once commit a77b486 is merged into the 'main' branch.
    a77b486

    Read the gem lock's git sections once per scan

    This issue will close once commit 72db141 is merged into the 'main' branch.
    72db141
  9. Mikola Lysenko (mikolalysenko)
    closed this as completedin 33ddc26on Oct 5, 2026
  10. Mikola Lysenko (mikolalysenko)
    removed
    agent:claimed
    on Oct 6, 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.