diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb index eeb548b8f7..ca858cfda5 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -2,105 +2,14 @@ require_relative "helper" -require "webrick" -require "webrick/https" if Gem::HAVE_OPENSSL - -unless Gem::HAVE_OPENSSL - warn "Skipping Gem::RemoteFetcher tests. openssl not found." -end - require "rubygems/remote_fetcher" require "rubygems/package" -# = Testing Proxy Settings -# -# These tests check the proper proxy server settings by running two -# web servers. The web server at http://localhost:#{SERVER_PORT} -# represents the normal gem server and returns a gemspec with a rake -# version of 0.4.11. The web server at http://localhost:#{PROXY_PORT} -# represents the proxy server and returns a different dataset where -# rake has version 0.4.2. This allows us to detect which server is -# returning the data. -# -# Note that the proxy server is not a *real* proxy server. But our -# software doesn't really care, as long as we hit the proxy URL when a -# proxy is configured. - class TestGemRemoteFetcher < Gem::TestCase include Gem::DefaultUserInteraction - SERVER_DATA = <<-EOY ---- !ruby/object:Gem::Cache -gems: - rake-0.4.11: !ruby/object:Gem::Specification - rubygems_version: "0.7" - specification_version: 1 - name: rake - version: !ruby/object:Gem::Version - version: 0.4.11 - date: 2004-11-12 - summary: Ruby based make-like utility. - require_paths: - - lib - author: Jim Weirich - email: jim@weirichhouse.org - homepage: http://rake.rubyforge.org - description: Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax. - autorequire: - bindir: bin - has_rdoc: true - required_ruby_version: !ruby/object:Gem::Version::Requirement - requirements: - - - - ">" - - !ruby/object:Gem::Version - version: 0.0.0 - version: - platform: ruby - files: - - README - test_files: [] - library_stubs: - rdoc_options: - extra_rdoc_files: - executables: - - rake - extensions: [] - requirements: [] - dependencies: [] - EOY - - PROXY_DATA = SERVER_DATA.gsub(/0.4.11/, "0.4.2") - - # Generated via: - # x = OpenSSL::PKey::DH.new(2048) # wait a while... - # x.to_s => pem - TEST_KEY_DH2048 = OpenSSL::PKey::DH.new <<-_END_OF_PEM_ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEA3Ze2EHSfYkZLUn557torAmjBgPsqzbodaRaGZtgK1gEU+9nNJaFV -G1JKhmGUiEDyIW7idsBpe4sX/Wqjnp48Lr8IeI/SlEzLdoGpf05iRYXC8Cm9o8aM -cfmVgoSEAo9YLBpzoji2jHkO7Q5IPt4zxbTdlmmGFLc/GO9q7LGHhC+rcMcNTGsM -49AnILNn49pq4Y72jSwdmvq4psHZwwFBbPwLdw6bLUDDCN90jfqvYt18muwUxDiN -NP0fuvVAIB158VnQ0liHSwcl6+9vE1mL0Jo/qEXQxl0+UdKDjaGfTsn6HIrwTnmJ -PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== ------END DH PARAMETERS----- - _END_OF_PEM_ - def setup - @proxies = %w[https_proxy http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY] - @old_proxies = @proxies.map {|k| ENV[k] } - @proxies.each {|k| ENV[k] = nil } - super - start_servers - self.enable_yaml = true - self.enable_zip = false - - base_server_uri = "http://localhost:#{normal_server_port}" - @proxy_uri = "http://localhost:#{proxy_server_port}" - - @server_uri = base_server_uri + "/yaml" - @server_z_uri = base_server_uri + "/yaml.Z" @cache_dir = File.join @gemhome, "cache" @@ -116,14 +25,6 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== @fetcher = Gem::RemoteFetcher.fetcher end - def teardown - @fetcher.close_all - stop_servers - super - Gem.configuration[:http_proxy] = nil - @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] } - end - def test_self_fetcher fetcher = Gem::RemoteFetcher.fetcher refute_nil fetcher @@ -140,6 +41,8 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== refute_nil fetcher assert_kind_of Gem::RemoteFetcher, fetcher assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy).to_s + ensure + Gem.configuration[:http_proxy] = nil end def test_fetch_path_bad_uri @@ -153,14 +56,6 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== assert_equal "uri scheme is invalid: nil", e.message end - def test_no_proxy - use_ui @stub_ui do - assert_data_from_server @fetcher.fetch_path(@server_uri) - response = @fetcher.fetch_path(@server_uri, nil, true) - assert_equal SERVER_DATA.size, response["content-length"].to_i - end - end - def test_cache_update_path uri = Gem::URI "http://example/file" path = File.join @tempdir, "file" @@ -616,41 +511,6 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== assert_nil fetcher.fetch_path(Gem::URI.parse(@gem_repo), Time.at(0)) end - def test_implicit_no_proxy - use_ui @stub_ui do - ENV["http_proxy"] = "http://fakeurl:12345" - fetcher = Gem::RemoteFetcher.new :no_proxy - @fetcher = fetcher - assert_data_from_server fetcher.fetch_path(@server_uri) - end - end - - def test_implicit_proxy - use_ui @stub_ui do - ENV["http_proxy"] = @proxy_uri - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - assert_data_from_proxy fetcher.fetch_path(@server_uri) - end - end - - def test_implicit_upper_case_proxy - use_ui @stub_ui do - ENV["HTTP_PROXY"] = @proxy_uri - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - assert_data_from_proxy fetcher.fetch_path(@server_uri) - end - end - - def test_implicit_proxy_no_env - use_ui @stub_ui do - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - assert_data_from_server fetcher.fetch_path(@server_uri) - end - end - def test_fetch_http fetcher = Gem::RemoteFetcher.new nil @fetcher = fetcher @@ -712,34 +572,6 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== assert_equal "redirecting but no redirect location was given (#{url})", e.message end - def test_fetch_http_with_additional_headers - ENV["http_proxy"] = @proxy_uri - ENV["no_proxy"] = Gem::URI.parse(@server_uri).host - fetcher = Gem::RemoteFetcher.new nil, nil, { "X-Captain" => "murphy" } - @fetcher = fetcher - assert_equal "murphy", fetcher.fetch_path(@server_uri) - end - - def test_observe_no_proxy_env_single_host - use_ui @stub_ui do - ENV["http_proxy"] = @proxy_uri - ENV["no_proxy"] = Gem::URI.parse(@server_uri).host - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - assert_data_from_server fetcher.fetch_path(@server_uri) - end - end - - def test_observe_no_proxy_env_list - use_ui @stub_ui do - ENV["http_proxy"] = @proxy_uri - ENV["no_proxy"] = "fakeurl.com, #{Gem::URI.parse(@server_uri).host}" - fetcher = Gem::RemoteFetcher.new nil - @fetcher = fetcher - assert_data_from_server fetcher.fetch_path(@server_uri) - end - end - def test_request_block fetcher = Gem::RemoteFetcher.new nil @fetcher = fetcher @@ -754,112 +586,12 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== def test_yaml_error_on_size use_ui @stub_ui do - self.enable_yaml = false fetcher = Gem::RemoteFetcher.new nil @fetcher = fetcher assert_error { fetcher.size } end end - def test_ssl_connection - ssl_server = start_ssl_server - temp_ca_cert = File.join(__dir__, "ca_cert.pem") - with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") - end - end - - def test_ssl_client_cert_auth_connection - ssl_server = start_ssl_server( - { SSLVerifyClient: OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT } - ) - - temp_ca_cert = File.join(__dir__, "ca_cert.pem") - temp_client_cert = File.join(__dir__, "client.pem") - - with_configured_fetcher( - ":ssl_ca_cert: #{temp_ca_cert}\n" \ - ":ssl_client_cert: #{temp_client_cert}\n" - ) do |fetcher| - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") - end - end - - def test_do_not_allow_invalid_client_cert_auth_connection - ssl_server = start_ssl_server( - { SSLVerifyClient: OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT } - ) - - temp_ca_cert = File.join(__dir__, "ca_cert.pem") - temp_client_cert = File.join(__dir__, "invalid_client.pem") - - with_configured_fetcher( - ":ssl_ca_cert: #{temp_ca_cert}\n" \ - ":ssl_client_cert: #{temp_client_cert}\n" - ) do |fetcher| - assert_raise Gem::RemoteFetcher::FetchError do - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") - end - end - end - - def test_do_not_allow_insecure_ssl_connection_by_default - ssl_server = start_ssl_server - with_configured_fetcher do |fetcher| - assert_raise Gem::RemoteFetcher::FetchError do - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") - end - end - end - - def test_ssl_connection_allow_verify_none - ssl_server = start_ssl_server - with_configured_fetcher(":ssl_verify_mode: 0") do |fetcher| - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") - end - end - - def test_do_not_follow_insecure_redirect - ssl_server = start_ssl_server - temp_ca_cert = File.join(__dir__, "ca_cert.pem") - expected_error_message = - "redirecting to non-https resource: #{@server_uri} (https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri})" - - with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| - err = assert_raise Gem::RemoteFetcher::FetchError do - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri}") - end - - assert_equal(err.message, expected_error_message) - end - end - - def test_nil_ca_cert - ssl_server = start_ssl_server - temp_ca_cert = nil - - with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| - assert_raise Gem::RemoteFetcher::FetchError do - fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}") - end - end - end - - def with_configured_fetcher(config_str = nil, &block) - if config_str - temp_conf = File.join @tempdir, ".gemrc" - File.open temp_conf, "w" do |fp| - fp.puts config_str - end - Gem.configuration = Gem::ConfigFile.new %W[--config-file #{temp_conf}] - end - fetcher = Gem::RemoteFetcher.new - yield fetcher - ensure - fetcher.close_all - Gem.configuration = nil - end - def assert_error(exception_class=Exception) got_exception = false @@ -871,152 +603,4 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== assert got_exception, "Expected exception conforming to #{exception_class}" end - - def assert_data_from_server(data) - assert_match(/0\.4\.11/, data, "Data is not from server") - end - - def assert_data_from_proxy(data) - assert_match(/0\.4\.2/, data, "Data is not from proxy") - end - - class NilLog < WEBrick::Log - def log(level, data) # Do nothing - end - end - - private - - attr_reader :normal_server, :proxy_server - attr_accessor :enable_zip, :enable_yaml - - def start_servers - @normal_server ||= start_server(SERVER_DATA) - @proxy_server ||= start_server(PROXY_DATA) - @enable_yaml = true - @enable_zip = false - @ssl_server = nil - @ssl_server_thread = nil - end - - def stop_servers - if @normal_server - @normal_server.kill.join - @normal_server = nil - end - if @proxy_server - @proxy_server.kill.join - @proxy_server = nil - end - if @ssl_server - @ssl_server.stop - @ssl_server = nil - end - if @ssl_server_thread - @ssl_server_thread.kill.join - @ssl_server_thread = nil - end - WEBrick::Utils::TimeoutHandler.terminate - end - - def normal_server_port - @normal_server[:server].config[:Port] - end - - def proxy_server_port - @proxy_server[:server].config[:Port] - end - - def start_ssl_server(config = {}) - pend "starting this test server fails randomly on jruby" if Gem.java_platform? - - null_logger = NilLog.new - server = WEBrick::HTTPServer.new({ - Port: 0, - Logger: null_logger, - AccessLog: [], - SSLEnable: true, - SSLCACertificateFile: File.join(__dir__, "ca_cert.pem"), - SSLCertificate: cert("ssl_cert.pem"), - SSLPrivateKey: key("ssl_key.pem"), - SSLVerifyClient: nil, - SSLCertName: nil, - }.merge(config)) - server.mount_proc("/yaml") do |_req, res| - res.body = "--- true\n" - end - server.mount_proc("/insecure_redirect") do |req, res| - res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, req.query["to"]) - end - server.ssl_context.tmp_dh_callback = proc { TEST_KEY_DH2048 } - t = Thread.new do - server.start - rescue StandardError => ex - puts "ERROR during server thread: #{ex.message}" - raise - ensure - server.shutdown - end - while server.status != :Running - sleep 0.1 - unless t.alive? - t.join - raise - end - end - @ssl_server = server - @ssl_server_thread = t - server - end - - def start_server(data) - null_logger = NilLog.new - s = WEBrick::HTTPServer.new( - Port: 0, - DocumentRoot: nil, - Logger: null_logger, - AccessLog: null_logger - ) - s.mount_proc("/kill") {|_req, _res| s.shutdown } - s.mount_proc("/yaml") do |req, res| - if req["X-Captain"] - res.body = req["X-Captain"] - elsif @enable_yaml - res.body = data - res["Content-Type"] = "text/plain" - res["content-length"] = data.size - else - res.status = "404" - res.body = "

NOT FOUND

" - res["Content-Type"] = "text/html" - end - end - s.mount_proc("/yaml.Z") do |_req, res| - if @enable_zip - res.body = Zlib::Deflate.deflate(data) - res["Content-Type"] = "text/plain" - else - res.status = "404" - res.body = "

NOT FOUND

" - res["Content-Type"] = "text/html" - end - end - th = Thread.new do - s.start - rescue StandardError => ex - abort "ERROR during server thread: #{ex.message}" - ensure - s.shutdown - end - th[:server] = s - th - end - - def cert(filename) - OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, filename))) - end - - def key(filename) - OpenSSL::PKey::RSA.new(File.read(File.join(__dir__, filename))) - end -end if Gem::HAVE_OPENSSL +end diff --git a/test/rubygems/test_gem_remote_fetcher_local_server.rb b/test/rubygems/test_gem_remote_fetcher_local_server.rb new file mode 100644 index 0000000000..a6bc626ede --- /dev/null +++ b/test/rubygems/test_gem_remote_fetcher_local_server.rb @@ -0,0 +1,444 @@ +# frozen_string_literal: true + +require_relative "helper" + +require "webrick" +require "webrick/https" if Gem::HAVE_OPENSSL + +unless Gem::HAVE_OPENSSL + warn "Skipping Gem::RemoteFetcher tests. openssl not found." +end + +require "rubygems/remote_fetcher" +require "rubygems/package" + +# = Testing Proxy Settings +# +# These tests check the proper proxy server settings by running two +# web servers. The web server at http://localhost:#{SERVER_PORT} +# represents the normal gem server and returns a gemspec with a rake +# version of 0.4.11. The web server at http://localhost:#{PROXY_PORT} +# represents the proxy server and returns a different dataset where +# rake has version 0.4.2. This allows us to detect which server is +# returning the data. +# +# Note that the proxy server is not a *real* proxy server. But our +# software doesn't really care, as long as we hit the proxy URL when a +# proxy is configured. + +class TestGemRemoteFetcherLocalServer < Gem::TestCase + include Gem::DefaultUserInteraction + + SERVER_DATA = <<-EOY +--- !ruby/object:Gem::Cache +gems: + rake-0.4.11: !ruby/object:Gem::Specification + rubygems_version: "0.7" + specification_version: 1 + name: rake + version: !ruby/object:Gem::Version + version: 0.4.11 + date: 2004-11-12 + summary: Ruby based make-like utility. + require_paths: + - lib + author: Jim Weirich + email: jim@weirichhouse.org + homepage: http://rake.rubyforge.org + description: Rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax. + autorequire: + bindir: bin + has_rdoc: true + required_ruby_version: !ruby/object:Gem::Version::Requirement + requirements: + - + - ">" + - !ruby/object:Gem::Version + version: 0.0.0 + version: + platform: ruby + files: + - README + test_files: [] + library_stubs: + rdoc_options: + extra_rdoc_files: + executables: + - rake + extensions: [] + requirements: [] + dependencies: [] + EOY + + PROXY_DATA = SERVER_DATA.gsub(/0.4.11/, "0.4.2") + + # Generated via: + # x = OpenSSL::PKey::DH.new(2048) # wait a while... + # x.to_s => pem + TEST_KEY_DH2048 = OpenSSL::PKey::DH.new <<-_END_OF_PEM_ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA3Ze2EHSfYkZLUn557torAmjBgPsqzbodaRaGZtgK1gEU+9nNJaFV +G1JKhmGUiEDyIW7idsBpe4sX/Wqjnp48Lr8IeI/SlEzLdoGpf05iRYXC8Cm9o8aM +cfmVgoSEAo9YLBpzoji2jHkO7Q5IPt4zxbTdlmmGFLc/GO9q7LGHhC+rcMcNTGsM +49AnILNn49pq4Y72jSwdmvq4psHZwwFBbPwLdw6bLUDDCN90jfqvYt18muwUxDiN +NP0fuvVAIB158VnQ0liHSwcl6+9vE1mL0Jo/qEXQxl0+UdKDjaGfTsn6HIrwTnmJ +PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg== +-----END DH PARAMETERS----- + _END_OF_PEM_ + + def setup + @proxies = %w[https_proxy http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY] + @old_proxies = @proxies.map {|k| ENV[k] } + @proxies.each {|k| ENV[k] = nil } + + super + start_servers + self.enable_yaml = true + self.enable_zip = false + + base_server_uri = "http://localhost:#{normal_server_port}" + @proxy_uri = "http://localhost:#{proxy_server_port}" + + @server_uri = base_server_uri + "/yaml" + @server_z_uri = base_server_uri + "/yaml.Z" + + @cache_dir = File.join @gemhome, "cache" + + # TODO: why does the remote fetcher need it written to disk? + @a1, @a1_gem = util_gem "a", "1" do |s| + s.executables << "a_bin" + end + + @a1.loaded_from = File.join(@gemhome, "specifications", @a1.full_name) + + Gem::RemoteFetcher.fetcher = nil + @stub_ui = Gem::MockGemUi.new + @fetcher = Gem::RemoteFetcher.fetcher + end + + def teardown + @fetcher.close_all + stop_servers + super + Gem.configuration[:http_proxy] = nil + @proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] } + end + + def test_no_proxy + use_ui @stub_ui do + assert_data_from_server @fetcher.fetch_path(@server_uri) + response = @fetcher.fetch_path(@server_uri, nil, true) + assert_equal SERVER_DATA.size, response["content-length"].to_i + end + end + + def test_implicit_no_proxy + use_ui @stub_ui do + ENV["http_proxy"] = "http://fakeurl:12345" + fetcher = Gem::RemoteFetcher.new :no_proxy + @fetcher = fetcher + assert_data_from_server fetcher.fetch_path(@server_uri) + end + end + + def test_implicit_proxy + use_ui @stub_ui do + ENV["http_proxy"] = @proxy_uri + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + assert_data_from_proxy fetcher.fetch_path(@server_uri) + end + end + + def test_implicit_upper_case_proxy + use_ui @stub_ui do + ENV["HTTP_PROXY"] = @proxy_uri + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + assert_data_from_proxy fetcher.fetch_path(@server_uri) + end + end + + def test_implicit_proxy_no_env + use_ui @stub_ui do + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + assert_data_from_server fetcher.fetch_path(@server_uri) + end + end + + def test_fetch_http_with_additional_headers + ENV["http_proxy"] = @proxy_uri + ENV["no_proxy"] = Gem::URI.parse(@server_uri).host + fetcher = Gem::RemoteFetcher.new nil, nil, { "X-Captain" => "murphy" } + @fetcher = fetcher + assert_equal "murphy", fetcher.fetch_path(@server_uri) + end + + def test_observe_no_proxy_env_single_host + use_ui @stub_ui do + ENV["http_proxy"] = @proxy_uri + ENV["no_proxy"] = Gem::URI.parse(@server_uri).host + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + assert_data_from_server fetcher.fetch_path(@server_uri) + end + end + + def test_observe_no_proxy_env_list + use_ui @stub_ui do + ENV["http_proxy"] = @proxy_uri + ENV["no_proxy"] = "fakeurl.com, #{Gem::URI.parse(@server_uri).host}" + fetcher = Gem::RemoteFetcher.new nil + @fetcher = fetcher + assert_data_from_server fetcher.fetch_path(@server_uri) + end + end + + def test_ssl_connection + ssl_server = start_ssl_server + temp_ca_cert = File.join(__dir__, "ca_cert.pem") + with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| + fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") + end + end + + def test_ssl_client_cert_auth_connection + ssl_server = start_ssl_server( + { SSLVerifyClient: OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT } + ) + + temp_ca_cert = File.join(__dir__, "ca_cert.pem") + temp_client_cert = File.join(__dir__, "client.pem") + + with_configured_fetcher( + ":ssl_ca_cert: #{temp_ca_cert}\n" \ + ":ssl_client_cert: #{temp_client_cert}\n" + ) do |fetcher| + fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") + end + end + + def test_do_not_allow_invalid_client_cert_auth_connection + ssl_server = start_ssl_server( + { SSLVerifyClient: OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT } + ) + + temp_ca_cert = File.join(__dir__, "ca_cert.pem") + temp_client_cert = File.join(__dir__, "invalid_client.pem") + + with_configured_fetcher( + ":ssl_ca_cert: #{temp_ca_cert}\n" \ + ":ssl_client_cert: #{temp_client_cert}\n" + ) do |fetcher| + assert_raise Gem::RemoteFetcher::FetchError do + fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") + end + end + end + + def test_do_not_allow_insecure_ssl_connection_by_default + ssl_server = start_ssl_server + with_configured_fetcher do |fetcher| + assert_raise Gem::RemoteFetcher::FetchError do + fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") + end + end + end + + def test_ssl_connection_allow_verify_none + ssl_server = start_ssl_server + with_configured_fetcher(":ssl_verify_mode: 0") do |fetcher| + fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") + end + end + + def test_do_not_follow_insecure_redirect + ssl_server = start_ssl_server + temp_ca_cert = File.join(__dir__, "ca_cert.pem") + expected_error_message = + "redirecting to non-https resource: #{@server_uri} (https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri})" + + with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| + err = assert_raise Gem::RemoteFetcher::FetchError do + fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/insecure_redirect?to=#{@server_uri}") + end + + assert_equal(err.message, expected_error_message) + end + end + + def test_nil_ca_cert + ssl_server = start_ssl_server + temp_ca_cert = nil + + with_configured_fetcher(":ssl_ca_cert: #{temp_ca_cert}") do |fetcher| + assert_raise Gem::RemoteFetcher::FetchError do + fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}") + end + end + end + + def with_configured_fetcher(config_str = nil, &block) + if config_str + temp_conf = File.join @tempdir, ".gemrc" + File.open temp_conf, "w" do |fp| + fp.puts config_str + end + Gem.configuration = Gem::ConfigFile.new %W[--config-file #{temp_conf}] + end + fetcher = Gem::RemoteFetcher.new + yield fetcher + ensure + fetcher.close_all + Gem.configuration = nil + end + + def assert_data_from_server(data) + assert_match(/0\.4\.11/, data, "Data is not from server") + end + + def assert_data_from_proxy(data) + assert_match(/0\.4\.2/, data, "Data is not from proxy") + end + + class NilLog < WEBrick::Log + def log(level, data) # Do nothing + end + end + + private + + attr_reader :normal_server, :proxy_server + attr_accessor :enable_zip, :enable_yaml + + def start_servers + @normal_server ||= start_server(SERVER_DATA) + @proxy_server ||= start_server(PROXY_DATA) + @enable_yaml = true + @enable_zip = false + @ssl_server = nil + @ssl_server_thread = nil + end + + def stop_servers + if @normal_server + @normal_server.kill.join + @normal_server = nil + end + if @proxy_server + @proxy_server.kill.join + @proxy_server = nil + end + if @ssl_server + @ssl_server.stop + @ssl_server = nil + end + if @ssl_server_thread + @ssl_server_thread.kill.join + @ssl_server_thread = nil + end + WEBrick::Utils::TimeoutHandler.terminate + end + + def normal_server_port + @normal_server[:server].config[:Port] + end + + def proxy_server_port + @proxy_server[:server].config[:Port] + end + + def start_ssl_server(config = {}) + pend "starting this test server fails randomly on jruby" if Gem.java_platform? + + null_logger = NilLog.new + server = WEBrick::HTTPServer.new({ + Port: 0, + Logger: null_logger, + AccessLog: [], + SSLEnable: true, + SSLCACertificateFile: File.join(__dir__, "ca_cert.pem"), + SSLCertificate: cert("ssl_cert.pem"), + SSLPrivateKey: key("ssl_key.pem"), + SSLVerifyClient: nil, + SSLCertName: nil, + }.merge(config)) + server.mount_proc("/yaml") do |_req, res| + res.body = "--- true\n" + end + server.mount_proc("/insecure_redirect") do |req, res| + res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, req.query["to"]) + end + server.ssl_context.tmp_dh_callback = proc { TEST_KEY_DH2048 } + t = Thread.new do + server.start + rescue StandardError => ex + puts "ERROR during server thread: #{ex.message}" + raise + ensure + server.shutdown + end + while server.status != :Running + sleep 0.1 + unless t.alive? + t.join + raise + end + end + @ssl_server = server + @ssl_server_thread = t + server + end + + def start_server(data) + null_logger = NilLog.new + s = WEBrick::HTTPServer.new( + Port: 0, + DocumentRoot: nil, + Logger: null_logger, + AccessLog: null_logger + ) + s.mount_proc("/kill") {|_req, _res| s.shutdown } + s.mount_proc("/yaml") do |req, res| + if req["X-Captain"] + res.body = req["X-Captain"] + elsif @enable_yaml + res.body = data + res["Content-Type"] = "text/plain" + res["content-length"] = data.size + else + res.status = "404" + res.body = "

NOT FOUND

" + res["Content-Type"] = "text/html" + end + end + s.mount_proc("/yaml.Z") do |_req, res| + if @enable_zip + res.body = Zlib::Deflate.deflate(data) + res["Content-Type"] = "text/plain" + else + res.status = "404" + res.body = "

NOT FOUND

" + res["Content-Type"] = "text/html" + end + end + th = Thread.new do + s.start + rescue StandardError => ex + abort "ERROR during server thread: #{ex.message}" + ensure + s.shutdown + end + th[:server] = s + th + end + + def cert(filename) + OpenSSL::X509::Certificate.new(File.read(File.join(__dir__, filename))) + end + + def key(filename) + OpenSSL::PKey::RSA.new(File.read(File.join(__dir__, filename))) + end +end if Gem::HAVE_OPENSSL