From 5c007637aad3dbfe9831b4c48e705b780e54cdd7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 19 Jun 2026 10:35:09 +0900 Subject: [PATCH 1/4] [ruby/json] Read ASCII-incompatible strings in binary mode https://github.com/ruby/json/commit/46aa46d3da --- test/json/json_minefield_parser_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/json/json_minefield_parser_test.rb b/test/json/json_minefield_parser_test.rb index 17619b2db1dbaa..71590325573edf 100644 --- a/test/json/json_minefield_parser_test.rb +++ b/test/json/json_minefield_parser_test.rb @@ -78,7 +78,7 @@ def define_test(name, &block) end fixtures.each do |path| - payload = File.read(path) + payload = File.binread(path) name = File.basename(path, '.json') if COMMENT_TESTS.include?(name) @@ -111,4 +111,4 @@ def define_test(name, &block) raise "Unexpected minefield test: #{name}" end end -end \ No newline at end of file +end From e602af1ffd1b5124e57dc48f6ec2026ef0848340 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 19 Jun 2026 11:23:31 +0900 Subject: [PATCH 2/4] [ruby/rubygems] Suggest access issues, not only yanking, for missing locked gems A private source returning 403/404 for an inaccessible gem is indistinguishable from a real 404, so the previous message wrongly told users the author had removed the gem. Mention the credentials and access possibility and point at the source to check. https://github.com/ruby/rubygems/issues/9628 https://github.com/ruby/rubygems/commit/fae8f8fdbb Co-Authored-By: Claude Opus 4.8 --- lib/bundler/definition.rb | 7 ++++--- spec/bundler/install/yanked_spec.rb | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index d0470f3f3426af..0d37f89772fb3c 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -711,9 +711,10 @@ def materialize(dependencies) "available locally before rerunning Bundler." else "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \ - "no longer be found in that source. That means the author of #{locked_gem} has removed it. " \ - "You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \ - "removed in order to install." + "no longer be found in that source. That means either the author of #{locked_gem} has removed it, " \ + "or you no longer have access to that source. You'll need to update your bundle to a version other " \ + "than #{locked_gem} that hasn't been removed, or check your credentials and access rights for " \ + "#{locked_gem.source}, in order to install." end raise GemNotFound, message diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb index c92af7bfb09526..fb9316cced8a4c 100644 --- a/spec/bundler/install/yanked_spec.rb +++ b/spec/bundler/install/yanked_spec.rb @@ -26,6 +26,8 @@ G expect(err).to include("Your bundle is locked to foo (10.0.0)") + expect(err).to include("either the author of foo (10.0.0) has removed it, or you no longer have access to that source") + expect(err).to include("check your credentials and access rights") end context "when a platform specific yanked version is included in the lockfile, and a generic variant is available remotely" do From fc24706a54aac0219c1d6fdbab9e01922ecc238b Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 19 Jun 2026 12:18:20 +0900 Subject: [PATCH 3/4] [ruby/rubygems] Resolve Git LFS files in git sources from the real remote A git source working copy is cloned from the local bare cache, whose origin holds no LFS objects, so git-lfs looked there and checkout failed with "smudge filter lfs failed". Reset origin to the real remote so git-lfs derives the right endpoint, using the credential-filtered URI so no secrets land in the copy's .git/config. https://github.com/ruby/rubygems/issues/9612 https://github.com/ruby/rubygems/commit/8401e5b700 Co-Authored-By: Claude Opus 4.8 --- lib/bundler/source/git/git_proxy.rb | 6 ++++ .../bundler/source/git/git_proxy_spec.rb | 28 +++++++++++++++++++ spec/bundler/install/gemfile/git_spec.rb | 8 ++++++ 3 files changed, 42 insertions(+) diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb index 8094dcaa9d94ff..78ab747215ca7d 100644 --- a/lib/bundler/source/git/git_proxy.rb +++ b/lib/bundler/source/git/git_proxy.rb @@ -144,6 +144,12 @@ def copy_to(destination, submodules = false) FileUtils.rm_rf(p) end git "clone", "--no-checkout", "--quiet", path.to_s, destination.to_s + # The copy is cloned from the local bare cache, which holds no Git LFS + # objects, so point origin back at the real remote and let git-lfs derive + # its endpoint from there when checking out. Use the credential-filtered + # URI to avoid persisting secrets in the copy's .git/config; auth is left + # to git's credential helper. + git "remote", "set-url", "origin", credential_filtered_uri, dir: destination File.chmod((File.stat(destination).mode | 0o777) & ~File.umask, destination) rescue Errno::EEXIST => e file_path = e.message[%r{.*?((?:[a-zA-Z]:)?/.*)}, 1] diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb index 1f10ca4b0776fb..f3fa2a694aed3c 100644 --- a/spec/bundler/bundler/source/git/git_proxy_spec.rb +++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb @@ -98,6 +98,34 @@ end end + describe "#copy_to" do + let(:revision) { "abc123" } + let(:destination) { tmp("git-proxy-copy") } + + before do + # The bare cache (`path`) is the clone source, so stub it away and only + # exercise the post-clone wiring of the working copy at `destination`. + allow(File).to receive(:stat).and_call_original + allow(File).to receive(:stat).with(destination).and_return(double("File::Stat", mode: 0o755)) + allow(File).to receive(:chmod) + allow(git_proxy).to receive(:capture).and_return(["", "", clone_result]) + end + + it "points the working copy's origin back at the real remote" do + expect(git_proxy).to receive(:capture).with(["remote", "set-url", "origin", uri], destination).and_return(["", "", clone_result]) + git_proxy.copy_to(destination) + end + + it "does not persist credentials in the working copy's origin" do + Bundler.settings.temporary(uri => "u:p") do + credentialed_uri = "https://u:p@github.com/ruby/rubygems.git" + expect(git_proxy).not_to receive(:capture).with(["remote", "set-url", "origin", credentialed_uri], destination) + expect(git_proxy).to receive(:capture).with(["remote", "set-url", "origin", uri], destination).and_return(["", "", clone_result]) + git_proxy.copy_to(destination) + end + end + end + describe "#version" do context "with a normal version number" do before do diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb index b2a82caf017b91..f2138b5fda2088 100644 --- a/spec/bundler/install/gemfile/git_spec.rb +++ b/spec/bundler/install/gemfile/git_spec.rb @@ -31,6 +31,14 @@ expect(out).to eq("WIN") end + it "points the installed copy's origin at the real remote, not the local cache" do + install_base_gemfile + + installed = Pathname.glob(default_bundle_path("bundler/gems/foo-1.0-*")).first + origin = git("config --get remote.origin.url", installed).strip + expect(origin).to eq(lib_path("foo-1.0").to_s) + end + it "does not (yet?) enforce CHECKSUMS" do build_git "foo" revision = revision_for(lib_path("foo-1.0")) From b49a2721f6cf0754635fa0d4284afae5e973fe30 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 19 Jun 2026 12:46:14 +0900 Subject: [PATCH 4/4] [ruby/rubygems] Verify embedded credentials are stripped from copied git origin The existing copy_to specs only proved we avoid the credential-bearing configured URI. Add a case where the remote URI itself embeds a password so the credential_filtered_uri stripping is exercised directly. https://github.com/ruby/rubygems/commit/3515d369d4 Co-Authored-By: Claude Opus 4.8 --- spec/bundler/bundler/source/git/git_proxy_spec.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb index f3fa2a694aed3c..e2d3bbb6e7efaf 100644 --- a/spec/bundler/bundler/source/git/git_proxy_spec.rb +++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb @@ -124,6 +124,17 @@ git_proxy.copy_to(destination) end end + + context "when the remote URI embeds credentials" do + let(:uri) { "https://user:secret@github.com/ruby/rubygems.git" } + + it "strips the password before writing origin" do + filtered_uri = "https://user@github.com/ruby/rubygems.git" + expect(git_proxy).not_to receive(:capture).with(["remote", "set-url", "origin", uri], destination) + expect(git_proxy).to receive(:capture).with(["remote", "set-url", "origin", filtered_uri], destination).and_return(["", "", clone_result]) + git_proxy.copy_to(destination) + end + end end describe "#version" do