From e7702be5b133d1d6412c62c69c1d9a029a6b99b8 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 4 Jun 2026 17:32:19 +0900 Subject: [PATCH] Avoid duplicate relative path prefix in bundle exec When bundle exec resolves an explicit relative path like ./script and falls back to Kernel.exec, Bundler currently prepends another ./ because the resolved path is not absolute. That turns ./script into ././script, which leaks into the executed process as an unexpectedly modified argv[0]. Only prepend ./ for non-absolute paths that do not already begin with a relative path marker. This preserves the existing behavior for bare executables found in the current directory while leaving explicit relative paths unchanged. Add a regression spec that disables exec-load so the Kernel.exec path is exercised and verifies that ./script remains ./script. Closes https://github.com/ruby/rubygems/issues/8930 --- bundler/lib/bundler/cli/exec.rb | 2 +- spec/commands/exec_spec.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/bundler/lib/bundler/cli/exec.rb b/bundler/lib/bundler/cli/exec.rb index 2fdc4162868a..aecab5ee897b 100644 --- a/bundler/lib/bundler/cli/exec.rb +++ b/bundler/lib/bundler/cli/exec.rb @@ -23,7 +23,7 @@ def run bin_path.delete_suffix!(".bat") if Gem.win_platform? kernel_load(bin_path, *args) else - bin_path = "./" + bin_path unless File.absolute_path?(bin_path) + bin_path = "./" + bin_path unless File.absolute_path?(bin_path) || bin_path.start_with?("./", "../") kernel_exec(bin_path, *args) end else diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb index aa35685be8ff..cc3a402629a5 100644 --- a/spec/commands/exec_spec.rb +++ b/spec/commands/exec_spec.rb @@ -341,6 +341,32 @@ expect(out).to include(rubylib) end + it "does not duplicate a relative path prefix when exec'ing to a relative path" do + skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform? + + create_file("script", "#!/usr/bin/env ruby\nputs $0") + + install_gemfile <<-G + source "https://gem.repo1" + G + + bundle "exec ./script", env: { "BUNDLE_DISABLE_EXEC_LOAD" => "true" } + expect(out).to eq("./script") + end + + it "prepends a relative path prefix when exec'ing to a hidden file in the current directory" do + skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform? + + create_file(".script", "#!/usr/bin/env ruby\nputs $0") + + install_gemfile <<-G + source "https://gem.repo1" + G + + bundle "exec .script", env: { "BUNDLE_DISABLE_EXEC_LOAD" => "true" } + expect(out).to eq("./.script") + end + it "errors nicely when the argument doesn't exist" do install_gemfile <<-G source "https://gem.repo1"