diff options
Diffstat (limited to 'spec/bundler/bundler/source')
-rw-r--r-- | spec/bundler/bundler/source/git/git_proxy_spec.rb | 157 | ||||
-rw-r--r-- | spec/bundler/bundler/source/git_spec.rb | 45 | ||||
-rw-r--r-- | spec/bundler/bundler/source/rubygems/remote_spec.rb | 20 | ||||
-rw-r--r-- | spec/bundler/bundler/source/rubygems_spec.rb | 14 |
4 files changed, 179 insertions, 57 deletions
diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb index 97f06973cb..f7c883eed4 100644 --- a/spec/bundler/bundler/source/git/git_proxy_spec.rb +++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb @@ -3,29 +3,84 @@ RSpec.describe Bundler::Source::Git::GitProxy do let(:path) { Pathname("path") } let(:uri) { "https://github.com/rubygems/rubygems.git" } - let(:ref) { "HEAD" } + let(:ref) { nil } + let(:branch) { nil } + let(:tag) { nil } + let(:options) { { "ref" => ref, "branch" => branch, "tag" => tag }.compact } let(:revision) { nil } let(:git_source) { nil } - subject { described_class.new(path, uri, ref, revision, git_source) } + let(:clone_result) { double(Process::Status, success?: true) } + let(:base_clone_args) { ["clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--depth", "1", "--single-branch"] } + subject(:git_proxy) { described_class.new(path, uri, options, revision, git_source) } + + context "with explicit ref" do + context "with branch only" do + let(:branch) { "main" } + it "sets explicit ref to branch" do + expect(git_proxy.explicit_ref).to eq(branch) + end + end + + context "with ref only" do + let(:ref) { "HEAD" } + it "sets explicit ref to ref" do + expect(git_proxy.explicit_ref).to eq(ref) + end + end + + context "with tag only" do + let(:tag) { "v1.0" } + it "sets explicit ref to ref" do + expect(git_proxy.explicit_ref).to eq(tag) + end + end + + context "with tag and branch" do + let(:tag) { "v1.0" } + let(:branch) { "main" } + it "raises error" do + expect { git_proxy }.to raise_error(Bundler::Source::Git::AmbiguousGitReference) + end + end + + context "with tag and ref" do + let(:tag) { "v1.0" } + let(:ref) { "HEAD" } + it "raises error" do + expect { git_proxy }.to raise_error(Bundler::Source::Git::AmbiguousGitReference) + end + end + + context "with branch and ref" do + let(:branch) { "main" } + let(:ref) { "HEAD" } + it "honors ref over branch" do + expect(git_proxy.explicit_ref).to eq(ref) + end + end + end context "with configured credentials" do it "adds username and password to URI" do Bundler.settings.temporary(uri => "u:p") do - expect(subject).to receive(:git_retry).with("clone", "https://u:p@github.com/rubygems/rubygems.git", any_args) + allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0") + expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result]) subject.checkout end end it "adds username and password to URI for host" do Bundler.settings.temporary("github.com" => "u:p") do - expect(subject).to receive(:git_retry).with("clone", "https://u:p@github.com/rubygems/rubygems.git", any_args) + allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0") + expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result]) subject.checkout end end it "does not add username and password to mismatched URI" do Bundler.settings.temporary("https://u:p@github.com/rubygems/rubygems-mismatch.git" => "u:p") do - expect(subject).to receive(:git_retry).with("clone", uri, any_args) + allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0") + expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "", clone_result]) subject.checkout end end @@ -33,9 +88,10 @@ RSpec.describe Bundler::Source::Git::GitProxy do it "keeps original userinfo" do Bundler.settings.temporary("github.com" => "u:p") do original = "https://orig:info@github.com/rubygems/rubygems.git" - subject = described_class.new(Pathname("path"), original, "HEAD") - expect(subject).to receive(:git_retry).with("clone", original, any_args) - subject.checkout + git_proxy = described_class.new(Pathname("path"), original, options) + allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0") + expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", original, path.to_s], nil).and_return(["", "", clone_result]) + git_proxy.checkout end end end @@ -43,46 +99,46 @@ RSpec.describe Bundler::Source::Git::GitProxy do describe "#version" do context "with a normal version number" do before do - expect(subject).to receive(:git).with("--version"). + expect(git_proxy).to receive(:git_local).with("--version"). and_return("git version 1.2.3") end it "returns the git version number" do - expect(subject.version).to eq("1.2.3") + expect(git_proxy.version).to eq("1.2.3") end it "does not raise an error when passed into Gem::Version.create" do - expect { Gem::Version.create subject.version }.not_to raise_error + expect { Gem::Version.create git_proxy.version }.not_to raise_error end end context "with a OSX version number" do before do - expect(subject).to receive(:git).with("--version"). + expect(git_proxy).to receive(:git_local).with("--version"). and_return("git version 1.2.3 (Apple Git-BS)") end it "strips out OSX specific additions in the version string" do - expect(subject.version).to eq("1.2.3") + expect(git_proxy.version).to eq("1.2.3") end it "does not raise an error when passed into Gem::Version.create" do - expect { Gem::Version.create subject.version }.not_to raise_error + expect { Gem::Version.create git_proxy.version }.not_to raise_error end end context "with a msysgit version number" do before do - expect(subject).to receive(:git).with("--version"). + expect(git_proxy).to receive(:git_local).with("--version"). and_return("git version 1.2.3.msysgit.0") end it "strips out msysgit specific additions in the version string" do - expect(subject.version).to eq("1.2.3") + expect(git_proxy.version).to eq("1.2.3") end it "does not raise an error when passed into Gem::Version.create" do - expect { Gem::Version.create subject.version }.not_to raise_error + expect { Gem::Version.create git_proxy.version }.not_to raise_error end end end @@ -90,62 +146,69 @@ RSpec.describe Bundler::Source::Git::GitProxy do describe "#full_version" do context "with a normal version number" do before do - expect(subject).to receive(:git).with("--version"). + expect(git_proxy).to receive(:git_local).with("--version"). and_return("git version 1.2.3") end it "returns the git version number" do - expect(subject.full_version).to eq("1.2.3") + expect(git_proxy.full_version).to eq("1.2.3") end end context "with a OSX version number" do before do - expect(subject).to receive(:git).with("--version"). + expect(git_proxy).to receive(:git_local).with("--version"). and_return("git version 1.2.3 (Apple Git-BS)") end it "does not strip out OSX specific additions in the version string" do - expect(subject.full_version).to eq("1.2.3 (Apple Git-BS)") + expect(git_proxy.full_version).to eq("1.2.3 (Apple Git-BS)") end end context "with a msysgit version number" do before do - expect(subject).to receive(:git).with("--version"). + expect(git_proxy).to receive(:git_local).with("--version"). and_return("git version 1.2.3.msysgit.0") end it "does not strip out msysgit specific additions in the version string" do - expect(subject.full_version).to eq("1.2.3.msysgit.0") + expect(git_proxy.full_version).to eq("1.2.3.msysgit.0") end end end - describe "#copy_to" do - let(:cache) { tmpdir("cache_path") } - let(:destination) { tmpdir("copy_to_path") } - let(:submodules) { false } - - context "when given a SHA as a revision" do - let(:revision) { "abcd" * 10 } - let(:command) { ["reset", "--hard", revision] } - let(:command_for_display) { "git #{command.shelljoin}" } - - it "fails gracefully when resetting to the revision fails" do - expect(subject).to receive(:git_retry).with("clone", any_args) { destination.mkpath } - expect(subject).to receive(:git_retry).with("fetch", any_args, :dir => destination) - expect(subject).to receive(:git).with(*command, :dir => destination).and_raise(Bundler::Source::Git::GitCommandError.new(command_for_display, destination)) - expect(subject).not_to receive(:git) - - expect { subject.copy_to(destination, submodules) }. - to raise_error( - Bundler::Source::Git::MissingGitRevisionError, - "Git error: command `#{command_for_display}` in directory #{destination} has failed.\n" \ - "Revision #{revision} does not exist in the repository #{uri}. Maybe you misspelled it?\n" \ - "If this error persists you could try removing the cache directory '#{destination}'" - ) - end + it "doesn't allow arbitrary code execution through Gemfile uris with a leading dash" do + gemfile <<~G + gem "poc", git: "-u./pay:load.sh" + G + + file = bundled_app("pay:load.sh") + + create_file file, <<~RUBY + #!/bin/sh + + touch #{bundled_app("canary")} + RUBY + + FileUtils.chmod("+x", file) + + bundle :lock, raise_on_error: false + + expect(Pathname.new(bundled_app("canary"))).not_to exist + end + + context "URI is HTTP" do + let(:uri) { "http://github.com/rubygems/rubygems.git" } + let(:without_depth_arguments) { ["clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--single-branch"] } + let(:fail_clone_result) { double(Process::Status, success?: false) } + + it "retries without --depth when git url is http and fails" do + allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0") + allow(git_proxy).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "dumb http transport does not support shallow capabilities", fail_clone_result]) + expect(git_proxy).to receive(:capture).with([*without_depth_arguments, "--", uri, path.to_s], nil).and_return(["", "", clone_result]) + + subject.checkout end end end diff --git a/spec/bundler/bundler/source/git_spec.rb b/spec/bundler/bundler/source/git_spec.rb index 6668b6e69a..feef54bbf4 100644 --- a/spec/bundler/bundler/source/git_spec.rb +++ b/spec/bundler/bundler/source/git_spec.rb @@ -24,5 +24,50 @@ RSpec.describe Bundler::Source::Git do expect(subject.to_s).to eq "https://x-oauth-basic@github.com/foo/bar.git" end end + + context "when the source has a glob specifier" do + let(:glob) { "bar/baz/*.gemspec" } + let(:options) do + { "uri" => uri, "glob" => glob } + end + + it "includes it" do + expect(subject.to_s).to eq "https://github.com/foo/bar.git (glob: bar/baz/*.gemspec)" + end + end + + context "when the source has a reference" do + let(:git_proxy_stub) do + instance_double(Bundler::Source::Git::GitProxy, revision: "123abc", branch: "v1.0.0") + end + let(:options) do + { "uri" => uri, "ref" => "v1.0.0" } + end + + before do + allow(Bundler::Source::Git::GitProxy).to receive(:new).and_return(git_proxy_stub) + end + + it "includes it" do + expect(subject.to_s).to eq "https://github.com/foo/bar.git (at v1.0.0@123abc)" + end + end + + context "when the source has both reference and glob specifiers" do + let(:git_proxy_stub) do + instance_double(Bundler::Source::Git::GitProxy, revision: "123abc", branch: "v1.0.0") + end + let(:options) do + { "uri" => uri, "ref" => "v1.0.0", "glob" => "gems/foo/*.gemspec" } + end + + before do + allow(Bundler::Source::Git::GitProxy).to receive(:new).and_return(git_proxy_stub) + end + + it "includes both" do + expect(subject.to_s).to eq "https://github.com/foo/bar.git (at v1.0.0@123abc, glob: gems/foo/*.gemspec)" + end + end end end diff --git a/spec/bundler/bundler/source/rubygems/remote_spec.rb b/spec/bundler/bundler/source/rubygems/remote_spec.rb index 07ce4f968e..56f3bee459 100644 --- a/spec/bundler/bundler/source/rubygems/remote_spec.rb +++ b/spec/bundler/bundler/source/rubygems/remote_spec.rb @@ -11,8 +11,8 @@ RSpec.describe Bundler::Source::Rubygems::Remote do allow(Digest(:MD5)).to receive(:hexdigest).with(duck_type(:to_s)) {|string| "MD5HEX(#{string})" } end - let(:uri_no_auth) { Bundler::URI("https://gems.example.com") } - let(:uri_with_auth) { Bundler::URI("https://#{credentials}@gems.example.com") } + let(:uri_no_auth) { Gem::URI("https://gems.example.com") } + let(:uri_with_auth) { Gem::URI("https://#{credentials}@gems.example.com") } let(:credentials) { "username:password" } context "when the original URI has no credentials" do @@ -89,11 +89,11 @@ RSpec.describe Bundler::Source::Rubygems::Remote do end context "when the original URI has only a username" do - let(:uri) { Bundler::URI("https://SeCrEt-ToKeN@gem.fury.io/me/") } + let(:uri) { Gem::URI("https://SeCrEt-ToKeN@gem.fury.io/me/") } describe "#anonymized_uri" do it "returns the URI without username and password" do - expect(remote(uri).anonymized_uri).to eq(Bundler::URI("https://gem.fury.io/me/")) + expect(remote(uri).anonymized_uri).to eq(Gem::URI("https://gem.fury.io/me/")) end end @@ -105,9 +105,9 @@ RSpec.describe Bundler::Source::Rubygems::Remote do end context "when a mirror with inline credentials is configured for the URI" do - let(:uri) { Bundler::URI("https://rubygems.org/") } - let(:mirror_uri_with_auth) { Bundler::URI("https://username:password@rubygems-mirror.org/") } - let(:mirror_uri_no_auth) { Bundler::URI("https://rubygems-mirror.org/") } + let(:uri) { Gem::URI("https://rubygems.org/") } + let(:mirror_uri_with_auth) { Gem::URI("https://username:password@rubygems-mirror.org/") } + let(:mirror_uri_no_auth) { Gem::URI("https://rubygems-mirror.org/") } before { Bundler.settings.temporary("mirror.https://rubygems.org/" => mirror_uri_with_auth.to_s) } @@ -131,9 +131,9 @@ RSpec.describe Bundler::Source::Rubygems::Remote do end context "when a mirror with configured credentials is configured for the URI" do - let(:uri) { Bundler::URI("https://rubygems.org/") } - let(:mirror_uri_with_auth) { Bundler::URI("https://#{credentials}@rubygems-mirror.org/") } - let(:mirror_uri_no_auth) { Bundler::URI("https://rubygems-mirror.org/") } + let(:uri) { Gem::URI("https://rubygems.org/") } + let(:mirror_uri_with_auth) { Gem::URI("https://#{credentials}@rubygems-mirror.org/") } + let(:mirror_uri_no_auth) { Gem::URI("https://rubygems-mirror.org/") } before do Bundler.settings.temporary("mirror.https://rubygems.org/" => mirror_uri_no_auth.to_s) diff --git a/spec/bundler/bundler/source/rubygems_spec.rb b/spec/bundler/bundler/source/rubygems_spec.rb index 7c457a7265..884fa81046 100644 --- a/spec/bundler/bundler/source/rubygems_spec.rb +++ b/spec/bundler/bundler/source/rubygems_spec.rb @@ -30,4 +30,18 @@ RSpec.describe Bundler::Source::Rubygems do end end end + + describe "#no_remotes?" do + context "when no remote provided" do + it "returns a truthy value" do + expect(described_class.new("remotes" => []).no_remotes?).to be_truthy + end + end + + context "when a remote provided" do + it "returns a falsey value" do + expect(described_class.new("remotes" => ["https://rubygems.org"]).no_remotes?).to be_falsey + end + end + end end |