aboutsummaryrefslogtreecommitdiffstats
path: root/spec/bundler
diff options
context:
space:
mode:
authorMartin Emde <martin.emde@gmail.com>2023-09-01 15:15:49 -0700
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2023-10-23 13:59:01 +0900
commitc667de72ff9de195e1cab4b1937973e841ff89ae (patch)
tree6dc6a88dbf8c44109593352055c8e798d562a83f /spec/bundler
parent6362bfdc337c1929a381734ded417b796f9767bf (diff)
downloadruby-c667de72ff9de195e1cab4b1937973e841ff89ae.tar.gz
[rubygems/rubygems] Improve errors and register checksums reliably
Improve error reporting for checksums, raises a new error class. Solve for multi-source checksum errors. Add CHECKSUMS to tool/bundler/(dev|standard|rubocop)26_gems.rb https://github.com/rubygems/rubygems/commit/26ceee0e76 Co-authored-by: Samuel Giddins <segiddins@segiddins.me>
Diffstat (limited to 'spec/bundler')
-rw-r--r--spec/bundler/bundler/lockfile_parser_spec.rb51
-rw-r--r--spec/bundler/cache/gems_spec.rb19
-rw-r--r--spec/bundler/commands/lock_spec.rb6
-rw-r--r--spec/bundler/commands/update_spec.rb2
-rw-r--r--spec/bundler/install/gemfile/gemspec_spec.rb42
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb187
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb8
-rw-r--r--spec/bundler/install/gems/compact_index_spec.rb32
-rw-r--r--spec/bundler/lock/lockfile_spec.rb2
-rw-r--r--spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb3
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index.rb12
-rw-r--r--spec/bundler/support/checksums.rb20
12 files changed, 277 insertions, 107 deletions
diff --git a/spec/bundler/bundler/lockfile_parser_spec.rb b/spec/bundler/bundler/lockfile_parser_spec.rb
index c05d5a01d1..a42bdad6e4 100644
--- a/spec/bundler/bundler/lockfile_parser_spec.rb
+++ b/spec/bundler/bundler/lockfile_parser_spec.rb
@@ -119,6 +119,12 @@ RSpec.describe Bundler::LockfileParser do
let(:bundler_version) { Gem::Version.new("1.12.0.rc.2") }
let(:ruby_version) { "ruby 2.1.3p242" }
let(:lockfile_path) { Bundler.default_lockfile.relative_path_from(Dir.pwd) }
+ let(:rake_checksum) do
+ Bundler::Checksum.from_lock(
+ "sha256-814828c34f1315d7e7b7e8295184577cc4e969bad6156ac069d02d63f58d82e8",
+ "#{lockfile_path}:??:1"
+ )
+ end
shared_examples_for "parsing" do
it "parses correctly" do
@@ -129,11 +135,9 @@ RSpec.describe Bundler::LockfileParser do
expect(subject.platforms).to eq platforms
expect(subject.bundler_version).to eq bundler_version
expect(subject.ruby_version).to eq ruby_version
- checksums = subject.sources.last.checksum_store.checksums("rake-10.3.2")
- expect(checksums.size).to eq(1)
- expected_checksum = Bundler::Checksum.new("sha256", "814828c34f1315d7e7b7e8295184577cc4e969bad6156ac069d02d63f58d82e8", "#{lockfile_path}:??:1")
- expect(checksums.first).to be_match(expected_checksum)
- expect(checksums.first.sources.first).to match(/#{Regexp.escape(lockfile_path.to_s)}:\d+:\d+/)
+ checksum = subject.sources.last.checksum_store.fetch(specs.last)
+ expect(checksum).to be_match(rake_checksum)
+ expect(checksum.sources.first.to_s).to match(/the lockfile CHECKSUMS at #{Regexp.escape(lockfile_path.to_s)}:\d+:\d+/)
end
end
@@ -159,29 +163,28 @@ RSpec.describe Bundler::LockfileParser do
include_examples "parsing"
end
- context "when CHECKSUMS has duplicate checksums that don't match" do
- let(:lockfile_contents) { super().split(/(?<=CHECKSUMS\n)/m).insert(1, " rake (10.3.2) sha256-69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b6\n").join }
+ context "when CHECKSUMS has duplicate checksums in the lockfile that don't match" do
+ let(:bad_checksum) { "sha256-c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11" }
+ let(:lockfile_contents) { super().split(/(?<=CHECKSUMS\n)/m).insert(1, " rake (10.3.2) #{bad_checksum}\n").join }
it "raises a security error" do
expect { subject }.to raise_error(Bundler::SecurityError) do |e|
expect(e.message).to match <<~MESSAGE
- Bundler found multiple different checksums for rake-10.3.2.
- This means that there are multiple different `rake-10.3.2.gem` files.
- This is a potential security issue, since Bundler could be attempting to install a different gem than what you expect.
-
- sha256-814828c34f1315d7e7b7e8295184577cc4e969bad6156ac069d02d63f58d82e8 (from #{lockfile_path}:21:1 CHECKSUMS rake (10.3.2))
- sha256-69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b6 from:
- * #{lockfile_path}:20:1 CHECKSUMS rake (10.3.2)
-
- To resolve this issue:
- 1. delete any downloaded gems referenced above
- 2. run `bundle install`
-
- If you are sure that the new checksum is correct, you can remove the `rake-10.3.2` entry under the lockfile `CHECKSUMS` section and rerun `bundle install`.
-
- If you wish to continue installing the downloaded gem, and are certain it does not pose a security issue despite the mismatching checksum, do the following:
- 1. run `bundle config set --local disable_checksum_validation true` to turn off checksum verification
- 2. run `bundle install`
+ Bundler found mismatched checksums. This is a potential security risk.
+ rake (10.3.2) #{bad_checksum}
+ from the lockfile CHECKSUMS at #{lockfile_path}:20:17
+ rake (10.3.2) #{rake_checksum.to_lock}
+ from the lockfile CHECKSUMS at #{lockfile_path}:21:17
+
+ To resolve this issue you can either:
+ 1. remove the matching checksum in #{lockfile_path}:21:17
+ 2. run `bundle install`
+ or if you are sure that the new checksum from the lockfile CHECKSUMS at #{lockfile_path}:21:17 is correct:
+ 1. remove the matching checksum in #{lockfile_path}:20:17
+ 2. run `bundle install`
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
MESSAGE
end
end
diff --git a/spec/bundler/cache/gems_spec.rb b/spec/bundler/cache/gems_spec.rb
index 6053c4c761..2f5da4e7e4 100644
--- a/spec/bundler/cache/gems_spec.rb
+++ b/spec/bundler/cache/gems_spec.rb
@@ -281,9 +281,26 @@ RSpec.describe "bundle cache" do
build_gem "rack", "1.0.0",
:path => bundled_app("vendor/cache"),
:rubygems_version => "1.3.2"
+ # This test is only really valid if the checksum isn't saved. It otherwise can't be the same gem. Tested below.
+ bundled_app_lock.write remove_checksums_from_lockfile(bundled_app_lock.read, "rack (1.0.0)")
simulate_new_machine
- pending "Causes checksum mismatch exception"
+ bundle :install
+ expect(cached_gem("rack-1.0.0")).to exist
+ end
+
+ it "raises an error when the gem file is altered and produces a different checksum" do
+ cached_gem("rack-1.0.0").rmtree
+ build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache")
+ simulate_new_machine
+
+ bundle :install, :raise_on_error => false
+ expect(exitstatus).to eq(37)
+ expect(err).to include("Bundler found mismatched checksums.")
+ expect(err).to include("1. remove the gem at #{cached_gem("rack-1.0.0")}")
+
+ expect(cached_gem("rack-1.0.0")).to exist
+ cached_gem("rack-1.0.0").rmtree
bundle :install
expect(cached_gem("rack-1.0.0")).to exist
end
diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb
index 1e98df75d7..1cfc702a80 100644
--- a/spec/bundler/commands/lock_spec.rb
+++ b/spec/bundler/commands/lock_spec.rb
@@ -91,14 +91,16 @@ RSpec.describe "bundle lock" do
bundle "lock --update"
- expect(read_lockfile).to eq(@lockfile)
+ expect(read_lockfile).to eq(remove_checksums_from_lockfile(@lockfile, "(2.3.2)"))
+ end
+ it "writes a lockfile when there is an outdated lockfile using a bundle is frozen" do
lockfile @lockfile.gsub("2.3.2", "2.3.1")
bundle "lock --update", :env => { "BUNDLE_FROZEN" => "true" }
# No checksums for the updated gems
- expect(read_lockfile).to eq(remove_checksums_from_lockfile(@lockfile, " (2.3.2)"))
+ expect(read_lockfile).to eq(remove_checksums_from_lockfile(@lockfile, "(2.3.2)"))
end
it "does not fetch remote specs when using the --local option" do
diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb
index b1043ae71b..c1836ea309 100644
--- a/spec/bundler/commands/update_spec.rb
+++ b/spec/bundler/commands/update_spec.rb
@@ -551,7 +551,7 @@ RSpec.describe "bundle update" do
lockfile original_lockfile
bundle "lock --update"
expect(the_bundle).to include_gems("activesupport 6.0.4.1", "tzinfo 1.2.9")
- expect(lockfile).to eq expected_lockfile
+ expect(lockfile).to eq(expected_lockfile)
end
end
diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb
index f1e68bbec1..ae53130bf3 100644
--- a/spec/bundler/install/gemfile/gemspec_spec.rb
+++ b/spec/bundler/install/gemfile/gemspec_spec.rb
@@ -28,6 +28,16 @@ RSpec.describe "bundle install from an existing gemspec" do
x64_mingw_archs.join("\n ")
end
+ let(:x64_mingw_checksums) do
+ x64_mingw_archs.map do |arch|
+ if arch == "x64-mingw-ucrt"
+ gem_no_checksum "platform_specific", "1.0", arch
+ else
+ checksum_for_repo_gem gem_repo2, "platform_specific", "1.0", arch
+ end
+ end.join("\n ")
+ end
+
it "should install runtime and development dependencies" do
build_lib("foo", :path => tmp.join("foo")) do |s|
s.write("Gemfile", "source :rubygems\ngemspec")
@@ -449,12 +459,6 @@ RSpec.describe "bundle install from an existing gemspec" do
it "keeps all platform dependencies in the lockfile" do
expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
- expected_checksums = checksum_section do |c|
- c.repo_gem gem_repo2, "platform_specific", "1.0"
- c.repo_gem gem_repo2, "platform_specific", "1.0", "java"
- c.repo_gem gem_repo2, "platform_specific", "1.0", x64_mingw32
- end
-
expect(lockfile).to eq <<~L
PATH
remote: .
@@ -479,7 +483,9 @@ RSpec.describe "bundle install from an existing gemspec" do
CHECKSUMS
foo (1.0)
- #{expected_checksums}
+ #{checksum_for_repo_gem gem_repo2, "platform_specific", "1.0"}
+ #{checksum_for_repo_gem gem_repo2, "platform_specific", "1.0", "java"}
+ #{x64_mingw_checksums}
BUNDLED WITH
#{Bundler::VERSION}
@@ -493,12 +499,6 @@ RSpec.describe "bundle install from an existing gemspec" do
it "keeps all platform dependencies in the lockfile" do
expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
- expected_checksums = checksum_section do |c|
- c.repo_gem gem_repo2, "platform_specific", "1.0"
- c.repo_gem gem_repo2, "platform_specific", "1.0", "java"
- c.repo_gem gem_repo2, "platform_specific", "1.0", x64_mingw32
- end
-
expect(lockfile).to eq <<~L
PATH
remote: .
@@ -523,7 +523,9 @@ RSpec.describe "bundle install from an existing gemspec" do
CHECKSUMS
foo (1.0)
- #{expected_checksums}
+ #{checksum_for_repo_gem gem_repo2, "platform_specific", "1.0"}
+ #{checksum_for_repo_gem gem_repo2, "platform_specific", "1.0", "java"}
+ #{x64_mingw_checksums}
BUNDLED WITH
#{Bundler::VERSION}
@@ -538,13 +540,6 @@ RSpec.describe "bundle install from an existing gemspec" do
it "keeps all platform dependencies in the lockfile" do
expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY"
- expected_checksums = checksum_section do |c|
- c.repo_gem gem_repo2, "indirect_platform_specific", "1.0"
- c.repo_gem gem_repo2, "platform_specific", "1.0"
- c.repo_gem gem_repo2, "platform_specific", "1.0", "java"
- c.repo_gem gem_repo2, "platform_specific", "1.0", x64_mingw32
- end
-
expect(lockfile).to eq <<~L
PATH
remote: .
@@ -571,7 +566,10 @@ RSpec.describe "bundle install from an existing gemspec" do
CHECKSUMS
foo (1.0)
- #{expected_checksums}
+ #{checksum_for_repo_gem gem_repo2, "indirect_platform_specific", "1.0"}
+ #{checksum_for_repo_gem gem_repo2, "platform_specific", "1.0"}
+ #{checksum_for_repo_gem gem_repo2, "platform_specific", "1.0", "java"}
+ #{x64_mingw_checksums}
BUNDLED WITH
#{Bundler::VERSION}
diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb
index 72658aebd9..16b9d82104 100644
--- a/spec/bundler/install/gemfile/sources_spec.rb
+++ b/spec/bundler/install/gemfile/sources_spec.rb
@@ -27,27 +27,55 @@ RSpec.describe "bundle install with gems on multiple sources" do
G
end
- it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", :bundler => "< 3" do
- bundle :install, :artifice => "compact_index"
+ it "refuses to install mismatched checksum because one gem has been tampered with", :bundler => "< 3" do
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
- expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
- expect(err).to include("Installed from: https://gem.repo1")
- expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
+ expect(exitstatus).to eq(37)
+ expect(err).to eq <<~E.strip
+ [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source.
+ Bundler found mismatched checksums. This is a potential security risk.
+ #{checksum_for_repo_gem(gem_repo1, "rack", "1.0.0")}
+ from the API at https://gem.repo1/
+ #{checksum_for_repo_gem(gem_repo3, "rack", "1.0.0")}
+ from the API at https://gem.repo3/
+
+ Mismatched checksums each have an authoritative source:
+ 1. the API at https://gem.repo1/
+ 2. the API at https://gem.repo3/
+ You may need to alter your Gemfile sources to resolve this issue.
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
end
- it "does not use the full index unnecessarily", :bundler => "< 3" do
- bundle :install, :artifice => "compact_index", :verbose => true
+ context "when checksum validation is disabled" do
+ before do
+ bundle "config set --local disable_checksum_validation true"
+ end
- expect(out).to include("https://gem.repo1/versions")
- expect(out).to include("https://gem.repo3/versions")
- expect(out).not_to include("https://gem.repo1/quick/Marshal.4.8/")
- expect(out).not_to include("https://gem.repo3/quick/Marshal.4.8/")
- end
+ it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", :bundler => "< 3" do
+ bundle :install, :artifice => "compact_index"
- it "fails", :bundler => "3" do
- bundle :install, :artifice => "compact_index", :raise_on_error => false
- expect(err).to include("Each source after the first must include a block")
- expect(exitstatus).to eq(4)
+ expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(err).to include("Installed from: https://gem.repo1")
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
+ end
+
+ it "does not use the full index unnecessarily", :bundler => "< 3" do
+ bundle :install, :artifice => "compact_index", :verbose => true
+
+ expect(out).to include("https://gem.repo1/versions")
+ expect(out).to include("https://gem.repo3/versions")
+ expect(out).not_to include("https://gem.repo1/quick/Marshal.4.8/")
+ expect(out).not_to include("https://gem.repo3/quick/Marshal.4.8/")
+ end
+
+ it "fails", :bundler => "3" do
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
+ expect(err).to include("Each source after the first must include a block")
+ expect(exitstatus).to eq(4)
+ end
end
end
@@ -101,7 +129,8 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
it "works in standalone mode", :bundler => "< 3" do
- bundle "install --standalone", :artifice => "compact_index"
+ gem_checksum = checksum_for_repo_gem(gem_repo4, "foo", "1.0").split("-").last
+ bundle "install --standalone", :artifice => "compact_index", :env => { "BUNDLER_SPEC_FOO_CHECKSUM" => gem_checksum }
end
end
@@ -279,8 +308,55 @@ RSpec.describe "bundle install with gems on multiple sources" do
G
end
- it "installs from the other source and warns about ambiguous gems", :bundler => "< 3" do
- bundle :install, :artifice => "compact_index"
+ it "fails when the two sources don't have the same checksum", :bundler => "< 3" do
+ bundle :install, :artifice => "compact_index", :raise_on_error => false
+
+ expect(err).to eq(<<~E.strip)
+ [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source.
+ Bundler found mismatched checksums. This is a potential security risk.
+ #{checksum_for_repo_gem(gem_repo2, "rack", "1.0.0")}
+ from the API at https://gem.repo2/
+ #{checksum_for_repo_gem(gem_repo1, "rack", "1.0.0")}
+ from the API at https://gem.repo1/
+
+ Mismatched checksums each have an authoritative source:
+ 1. the API at https://gem.repo2/
+ 2. the API at https://gem.repo1/
+ You may need to alter your Gemfile sources to resolve this issue.
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
+ expect(exitstatus).to eq(37)
+ end
+
+ it "fails when the two sources agree, but the local gem calculates a different checksum", :bundler => "< 3" do
+ rack_checksum = "c0ffee11" * 8
+ bundle :install, :artifice => "compact_index", :env => { "BUNDLER_SPEC_RACK_CHECKSUM" => rack_checksum }, :raise_on_error => false
+
+ expect(err).to eq(<<~E.strip)
+ [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source.
+ Bundler found mismatched checksums. This is a potential security risk.
+ rack (1.0.0) sha256-#{rack_checksum}
+ from the API at https://gem.repo2/
+ and the API at https://gem.repo1/
+ #{checksum_for_repo_gem(gem_repo2, "rack", "1.0.0")}
+ from the gem at #{default_bundle_path("cache", "rack-1.0.0.gem")}
+
+ If you trust the API at https://gem.repo2/, to resolve this issue you can:
+ 1. remove the gem at #{default_bundle_path("cache", "rack-1.0.0.gem")}
+ 2. run `bundle install`
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
+ expect(exitstatus).to eq(37)
+ end
+
+ it "installs from the other source and warns about ambiguous gems when the sources have the same checksum", :bundler => "< 3" do
+ gem_checksum = checksum_for_repo_gem(gem_repo2, "rack", "1.0.0").split("-").last
+ bundle :install, :artifice => "compact_index", :env => { "BUNDLER_SPEC_RACK_CHECKSUM" => gem_checksum, "DEBUG" => "1" }
+
expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
expect(err).to include("Installed from: https://gem.repo2")
@@ -320,6 +396,49 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(lockfile).to eq(previous_lockfile)
end
+ it "installs from the other source and warns about ambiguous gems when checksum validation is disabled", :bundler => "< 3" do
+ bundle "config set --local disable_checksum_validation true"
+ bundle :install, :artifice => "compact_index"
+
+ expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(err).to include("Installed from: https://gem.repo2")
+
+ expected_checksums = checksum_section do |c|
+ c.no_checksum "depends_on_rack", "1.0.1"
+ c.no_checksum "rack", "1.0.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo1/
+ remote: https://gem.repo2/
+ specs:
+ rack (1.0.0)
+
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ depends_on_rack (1.0.1)
+ rack
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ depends_on_rack!
+
+ CHECKSUMS
+ #{expected_checksums}
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ previous_lockfile = lockfile
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ expect(lockfile).to eq(previous_lockfile)
+ end
+
it "fails", :bundler => "3" do
bundle :install, :artifice => "compact_index", :raise_on_error => false
expect(err).to include("Each source after the first must include a block")
@@ -1167,8 +1286,9 @@ RSpec.describe "bundle install with gems on multiple sources" do
lockfile aggregate_gem_section_lockfile
end
- it "installs the existing lockfile but prints a warning", :bundler => "< 3" do
+ it "installs the existing lockfile but prints a warning when checksum validation is disabled", :bundler => "< 3" do
bundle "config set --local deployment true"
+ bundle "config set --local disable_checksum_validation true"
bundle "install", :artifice => "compact_index"
@@ -1177,6 +1297,33 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(the_bundle).to include_gems("rack 0.9.1", :source => "remote3")
end
+ it "prints a checksum warning when the checksums from both sources do not match", :bundler => "< 3" do
+ bundle "config set --local deployment true"
+
+ bundle "install", :artifice => "compact_index", :raise_on_error => false
+
+ api_checksum1 = checksum_for_repo_gem(gem_repo1, "rack", "0.9.1").split("sha256-").last
+ api_checksum3 = checksum_for_repo_gem(gem_repo3, "rack", "0.9.1").split("sha256-").last
+
+ expect(exitstatus).to eq(37)
+ expect(err).to eq(<<~E.strip)
+ [DEPRECATED] Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure.
+ Bundler found mismatched checksums. This is a potential security risk.
+ rack (0.9.1) sha256-#{api_checksum3}
+ from the API at https://gem.repo3/
+ rack (0.9.1) sha256-#{api_checksum1}
+ from the API at https://gem.repo1/
+
+ Mismatched checksums each have an authoritative source:
+ 1. the API at https://gem.repo3/
+ 2. the API at https://gem.repo1/
+ You may need to alter your Gemfile sources to resolve this issue.
+
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
+ end
+
it "refuses to install the existing lockfile and prints an error", :bundler => "3" do
bundle "config set --local deployment true"
diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb
index cb6b18cda2..d4e2f25179 100644
--- a/spec/bundler/install/gemfile/specific_platform_spec.rb
+++ b/spec/bundler/install/gemfile/specific_platform_spec.rb
@@ -759,6 +759,11 @@ RSpec.describe "bundle install with specific platforms" do
bundle "update"
+ expected_checksums = checksum_section do |c|
+ c.repo_gem gem_repo4, "nokogiri", "1.14.0", "x86_64-linux"
+ c.repo_gem gem_repo4, "sorbet-static", "0.5.10696", "x86_64-linux"
+ end
+
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
@@ -773,6 +778,9 @@ RSpec.describe "bundle install with specific platforms" do
nokogiri
sorbet-static
+ CHECKSUMS
+ #{expected_checksums}
+
BUNDLED WITH
#{Bundler::VERSION}
L
diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb
index 4a345824ce..03c25d53bf 100644
--- a/spec/bundler/install/gems/compact_index_spec.rb
+++ b/spec/bundler/install/gems/compact_index_spec.rb
@@ -890,25 +890,21 @@ The checksum of /versions does not match the checksum provided by the server! So
default_cache_path.dirname.join("rack-1.0.0.gem")
end
- expect(exitstatus).to eq(19)
- expect(err).
- to eq <<~E.strip
- Bundler cannot continue installing rack (1.0.0).
- The checksum for the downloaded `rack-1.0.0.gem` does not match the known checksum for the gem.
- This means the contents of the downloaded gem is different from what was uploaded to the server or first used by your teammates, and could be a potential security issue.
-
- To resolve this issue:
- 1. delete the downloaded gem located at: `#{gem_path}`
+ expect(exitstatus).to eq(37)
+ expect(err).to eq <<~E.strip
+ Bundler found mismatched checksums. This is a potential security risk.
+ rack (1.0.0) sha256-2222222222222222222222222222222222222222222222222222222222222222
+ from the API at http://localgemserver.test/
+ rack (1.0.0) sha256-#{api_checksum}
+ from the gem at #{gem_path}
+
+ If you trust the API at http://localgemserver.test/, to resolve this issue you can:
+ 1. remove the gem at #{gem_path}
2. run `bundle install`
- If you are sure that the new checksum is correct, you can remove the `rack (1.0.0)` entry under the lockfile `CHECKSUMS` section and rerun `bundle install`.
-
- If you wish to continue installing the downloaded gem, and are certain it does not pose a security issue despite the mismatching checksum, do the following:
- 1. run `bundle config set --local disable_checksum_validation true` to turn off checksum verification
- 2. run `bundle install`
-
- (More info: The expected SHA256 checksum was "69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b69b", but the checksum for the downloaded gem was "#{api_checksum}". The expected checksum came from: API response from http://localgemserver.test/)
- E
+ To ignore checksum security warnings, disable checksum validation with
+ `bundle config set --local disable_checksum_validation true`
+ E
end
it "raises when the checksum is the wrong length" do
@@ -917,7 +913,7 @@ The checksum of /versions does not match the checksum provided by the server! So
gem "rack"
G
expect(exitstatus).to eq(14)
- expect(err).to include("The given checksum for rack-0.9.1 (\"checksum!\") is not a valid SHA256 hexdigest nor base64digest")
+ expect(err).to include("Invalid checksum for rack-0.9.1: \"checksum!\" is not a valid SHA256 hexdigest nor base64digest")
end
it "does not raise when disable_checksum_validation is set" do
diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb
index bab0d915ab..f81b34b6d3 100644
--- a/spec/bundler/lock/lockfile_spec.rb
+++ b/spec/bundler/lock/lockfile_spec.rb
@@ -1769,7 +1769,7 @@ RSpec.describe "the lockfile format" do
gem "rack"
G
- expect(err).to match(/your lockfile contains merge conflicts/i)
+ expect(err).to match(/your Gemfile.lock contains merge conflicts/i)
expect(err).to match(/git checkout HEAD -- Gemfile.lock/i)
end
diff --git a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
index acc13a56ff..9bd2ca0a9d 100644
--- a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
+++ b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb
@@ -7,7 +7,8 @@ class CompactIndexWrongGemChecksum < CompactIndexAPI
etag_response do
name = params[:name]
gem = gems.find {|g| g.name == name }
- checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") { "ab" * 22 }
+ # This generates the hexdigest "2222222222222222222222222222222222222222222222222222222222222222"
+ checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") { "IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI=" }
versions = gem ? gem.versions : []
versions.each {|v| v.checksum = checksum }
CompactIndex.info(versions)
diff --git a/spec/bundler/support/artifice/helpers/compact_index.rb b/spec/bundler/support/artifice/helpers/compact_index.rb
index ef507ca12d..dd9e94ef9b 100644
--- a/spec/bundler/support/artifice/helpers/compact_index.rb
+++ b/spec/bundler/support/artifice/helpers/compact_index.rb
@@ -79,11 +79,13 @@ class CompactIndexAPI < Endpoint
reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ")
CompactIndex::Dependency.new(d.name, reqs)
end
- checksum = begin
- Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").hexdigest
- rescue StandardError
- nil
- end
+ begin
+ checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") do
+ Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").hexdigest
+ end
+ rescue StandardError
+ checksum = nil
+ end
CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
end
diff --git a/spec/bundler/support/checksums.rb b/spec/bundler/support/checksums.rb
index 7f5ecc14dd..b5579d5671 100644
--- a/spec/bundler/support/checksums.rb
+++ b/spec/bundler/support/checksums.rb
@@ -9,26 +9,22 @@ module Spec
end
def repo_gem(repo, name, version, platform = Gem::Platform::RUBY)
- gem_file = File.join(repo, "gems", "#{Bundler::GemHelpers.spec_full_name(name, version, platform)}.gem")
+ name_tuple = Gem::NameTuple.new(name, version, platform)
+ gem_file = File.join(repo, "gems", "#{name_tuple.full_name}.gem")
File.open(gem_file, "rb") do |f|
- checksums = Bundler::Checksum.from_io(f, "ChecksumsBuilder")
- checksum_entry(checksums, name, version, platform)
+ @checksums[name_tuple] = Bundler::Checksum.from_gem(f, "#{gem_file} (via ChecksumsBuilder#repo_gem)")
end
end
def no_checksum(name, version, platform = Gem::Platform::RUBY)
- checksum_entry(nil, name, version, platform)
- end
-
- def checksum_entry(checksums, name, version, platform = Gem::Platform::RUBY)
- lock_name = Bundler::GemHelpers.lock_name(name, version, platform)
- @checksums[lock_name] = checksums
+ name_tuple = Gem::NameTuple.new(name, version, platform)
+ @checksums[name_tuple] = nil
end
def to_lock
- @checksums.map do |lock_name, checksums|
- checksums &&= " #{checksums.map(&:to_lock).join(",")}"
- " #{lock_name}#{checksums}\n"
+ @checksums.map do |name_tuple, checksum|
+ checksum &&= " #{checksum.to_lock}"
+ " #{name_tuple.lock_name}#{checksum}\n"
end.sort.join.strip
end
end