Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Gem `BUNDLE_GEMFILE` check compares paths lexically, so a symlinked spelling of the project's own Gemfile (e.g. macOS `/tmp/app/Gemfile`) is refused, and `vex` / `rollback` reject the hosted patch Bundler is loading

Open Beginner friendly
#896 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
85/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
ruby, rust
Domain
cli

Research direction

Read crates/socket-patch-core/src/formats/gem/manifest.rs starting from the resolve_against function around lines 138-147 and the manifest comparison at lines 183-189. The fix should canonicalize paths (or use file identity) when comparing BUNDLE_GEMFILE against the project root, so symlinked paths to the same Gemfile are accepted. Done looks like the reproducer in the issue: BUNDLE_GEMFILE=$PWD/Gemfile on a symlinked project no longer returns redirect_gem_bundle_gemfile_unsupported.

Written by the indexing model from the issue text.

Description

agent:triaged bug bughunt pm:bundler priority:p1

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

Summary

formats::gem::manifest::classify decides whether BUNDLE_GEMFILE names the project's own Gemfile / gems.rb by comparing std::path::absolute + normalize_lexically of the setting against the project root. It never resolves symlinks. The root comes from the process cwd (getcwd, the physical path) or --cwd. A shell's $PWD, and paths people type, are often the logical path through a symlink. So BUNDLE_GEMFILE=$PWD/Gemfile names the same file Bundler loads, but socket-patch classifies it as "another manifest" (LoadedManifest::Unsupported).

On macOS this is the default for anything under /tmp (→ /private/tmp) or $TMPDIR (/var/folders/… → /private/var/…). On Linux it hits any project reached through a symlinked directory (a symlinked workspace or home, /app → volume, and so on).

Impact

All of these fail closed, but each one stops socket-patch from working on a correctly configured project, with a message that's factually wrong:

  • Hosted scan redirects nothing: redirect_gem_bundle_gemfile_unsupported ("bundler loads /tmp/bh-app/Gemfile … not the project's Gemfile or gems.rb"). That is the project's Gemfile.
  • vex on an already-redirected project whose install is patched (Bundler loads the patched gem) refuses with exit 2: "BUNDLE_GEMFILE points bundler at another manifest, so this wiring is never installed and the patch is not attested". It also suggests deleting Gemfile.lock, which is the lock Bundler uses.
  • rollback / remove error out with patched_ref_unattributable for the same reason, so the hosted patch can't be unwound while the variable is set.

The same function backs bundler_loaded_lock_in (lock inventory, ledger recovery, VEX discovery) and the vendored manifest check, so every gem lock reader inherits this.

Repro (Linux; the ln -s stands in for macOS /tmp)

mkdir -p real/app && ln -s "$PWD/real" link && cd link/app
printf 'source "https://rubygems.org"\n\ngem "colorize", "0.8.1"\ngem "rainbow"\n' > Gemfile
bundle config set --local path vendor/bundle && bundle install && bundle lock --add-checksums
echo "PWD=$PWD physical=$(pwd -P)"

BUNDLE_GEMFILE=$PWD/Gemfile bundle exec ruby -e 'puts Bundler.default_lockfile'   # → link/app/Gemfile.lock (same file)

A="--api-url <mock> --org org --api-token fake --patch-server-url <mock>"
socket-patch scan --mode hosted --json --yes --dry-run $A                              # redirected: 1
BUNDLE_GEMFILE=$PWD/Gemfile socket-patch scan --mode hosted --json --yes --dry-run $A  # redirected: 0, redirect_gem_bundle_gemfile_unsupported
BUNDLE_GEMFILE=$(pwd -P)/Gemfile socket-patch scan --mode hosted --json --yes --dry-run $A  # redirected: 1

# after a real redirect (no env) + bundle install → installed gem is patched:
BUNDLE_GEMFILE=$PWD/Gemfile socket-patch vex --product pkg:gem/app@1 $A      # exit 2 ("never installed")
BUNDLE_GEMFILE=$PWD/Gemfile socket-patch rollback --dry-run --json --yes $A  # status error, patched_ref_unattributable

The same refusal happens for --cwd <symlinked path> with BUNDLE_GEMFILE=<physical path>, and for .bundle/config BUNDLE_GEMFILE: "<symlinked abs path>/Gemfile" (as written by bundle config set --local gemfile "$PWD/Gemfile"), with no environment variable at all.

Expected vs actual

  • Expected, per docs/ecosystems.md (RubyGems row): BUNDLE_GEMFILE "is followed when it names the project's Gemfile / gems.rb, and any other configured manifest is refused". Bundler expands the path and resolves it to the same file and the same Gemfile.lock (Bundler.default_lockfile above), so socket-patch should treat it as the project's Gemfile.
  • Actual: the refusal fires when it shouldn't (a false Unsupported), and the VEX / rollback messages claim Bundler uses a different manifest.

Matrix (Bundler 4.0.22)

OS Setup Unset BUNDLE_GEMFILE=$PWD/Gemfile (logical) BUNDLE_GEMFILE=$(pwd -P)/Gemfile
macos-latest, Ruby 3.4.9 project in /tmp/bh-app (physical /private/tmp/bh-app) redirected 1 refused redirected 1
ubuntu-latest, Ruby 3.4 project via symlinked dir redirected 1 refused redirected 1
Linux sandbox, Ruby 3.3.6 same, plus vex / rollback after a real redirect + install (2/2) vex ok vex exit 2, rollback error (installed gem is patched) —
Linux sandbox --cwd <link> + env <real> / config abs <link> path — refused / refused —

Probe run: https://github.com/SocketDev/socket-patch/actions/runs/37382646373 (the probe's own vex/rollback cells hit a stale vendor/bundle harness artifact; the Linux sandbox rows cover them).

First bad commit

9d718cf5 (#431, the fix for #341 / #390), which introduced the BUNDLE_GEMFILE classification with a lexical compare; cbf1f748 (#532) kept it. The v4.0.0 release binary ignores BUNDLE_GEMFILE and redirects this project (it predates the classification), so this hasn't shipped in a release yet.

Suspect code

  • crates/socket-patch-core/src/formats/gem/manifest.rs:138-147 (resolve_against: absolute + normalize_lexically only) and :183-189 (target == root.join(manifest)).
  • The same lexical compare is in env_keeps_root (manifest.rs:163-166), which decides whether the env var moves Bundler's root.

Comparing canonicalized paths (or file identity, same_file-style) whenever both exist would match what Bundler does. A lexical match could stay as the fast path, falling back to canonicalization only when it fails, so a symlinked Gemfile file (already refused elsewhere) keeps its own handling.

Dominant language
Rust
Stars
8
Forks
0
Avg merge
19h 21m
Merged PRs (30d)
421

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from SocketDev/socket-patch

All issues in SocketDev/socket-patch

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.