diff options
-rw-r--r-- | lib/rubygems/commands/install_command.rb | 5 | ||||
-rw-r--r-- | lib/rubygems/printable_uri.rb | 61 | ||||
-rw-r--r-- | lib/rubygems/remote_fetcher.rb | 17 | ||||
-rw-r--r-- | lib/rubygems/request.rb | 3 | ||||
-rw-r--r-- | lib/rubygems/uri.rb | 102 | ||||
-rw-r--r-- | lib/rubygems/uri_parser.rb | 42 | ||||
-rw-r--r-- | test/rubygems/test_gem_printable_uri.rb | 44 | ||||
-rw-r--r-- | test/rubygems/test_gem_uri.rb | 32 | ||||
-rw-r--r-- | test/rubygems/test_gem_uri_parser.rb | 17 |
9 files changed, 145 insertions, 178 deletions
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb index ee50cba472..adf2cdba84 100644 --- a/lib/rubygems/commands/install_command.rb +++ b/lib/rubygems/commands/install_command.rb @@ -5,7 +5,6 @@ require_relative '../dependency_installer' require_relative '../local_remote_options' require_relative '../validator' require_relative '../version_option' -require_relative '../printable_uri' ## # Gem installer command line tool @@ -261,8 +260,8 @@ You can use `i` command instead of `install`. errors.each do |x| return unless Gem::SourceFetchProblem === x - printable_uri = Gem::PrintableUri.parse_uri(x.source.uri.clone) - msg = "Unable to pull data from '#{printable_uri}': #{x.error.message}" + require_relative "../uri" + msg = "Unable to pull data from '#{Gem::Uri.new(x.source.uri).redacted}': #{x.error.message}" alert_warning msg end diff --git a/lib/rubygems/printable_uri.rb b/lib/rubygems/printable_uri.rb deleted file mode 100644 index 93c852e268..0000000000 --- a/lib/rubygems/printable_uri.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -require_relative 'uri_parser' - -class Gem::PrintableUri - def self.parse_uri(uri) - printable_uri = new(uri) - printable_uri.parse_uri - - printable_uri - end - - def initialize(original_uri) - @original_uri = original_uri - end - - def parse_uri - @original_uri = Gem::UriParser.parse_uri(@original_uri) - @uri = @original_uri.dup - redact_credential if valid_uri? - end - - def valid_uri? - @uri.respond_to?(:user) && - @uri.respond_to?(:user=) && - @uri.respond_to?(:password) && - @uri.respond_to?(:password=) - end - - def original_password - @original_uri.password - end - - def to_s - @uri.to_s - end - - private - - def redact_credential - if token? - @uri.user = 'REDACTED' - elsif oauth_basic? - @uri.user = 'REDACTED' - elsif password? - @uri.password = 'REDACTED' - end - end - - def password? - end - - def oauth_basic? - @uri.password == 'x-oauth-basic' - end - - def token? - [email protected]? && @uri.password.nil? - end -end diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 33c02b46f6..0724a51ee0 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -4,8 +4,7 @@ require_relative 'request' require_relative 'request/connection_pools' require_relative 's3_uri_signer' require_relative 'uri_formatter' -require_relative 'uri_parser' -require_relative 'printable_uri' +require_relative 'uri' require_relative 'user_interaction' ## @@ -26,12 +25,12 @@ class Gem::RemoteFetcher attr_accessor :uri, :original_uri def initialize(message, uri) - @original_uri = uri.dup - uri = Gem::PrintableUri.parse_uri(uri) + uri = Gem::Uri.new(uri) - super(uri.valid_uri? && uri.original_password ? message.sub(uri.original_password, 'REDACTED') : message) + super uri.redact_credentials_from(message) - @uri = uri.to_s + @original_uri = uri.to_s + @uri = uri.redacted.to_s end def to_s # :nodoc: @@ -127,7 +126,7 @@ class Gem::RemoteFetcher require "fileutils" FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir - source_uri = Gem::UriParser.parse_uri(source_uri) + source_uri = Gem::Uri.new(source_uri) scheme = source_uri.scheme @@ -222,7 +221,7 @@ class Gem::RemoteFetcher unless location = response['Location'] raise FetchError.new("redirecting but no redirect location was given", uri) end - location = Gem::UriParser.parse_uri location + location = Gem::Uri.new location if https?(uri) && !https?(location) raise FetchError.new("redirecting to non-https resource: #{location}", uri) @@ -240,7 +239,7 @@ class Gem::RemoteFetcher # Downloads +uri+ and returns it as a String. def fetch_path(uri, mtime = nil, head = false) - uri = Gem::UriParser.parse_uri uri + uri = Gem::Uri.new uri unless uri.scheme raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}" diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb index fdc4c55da0..72e25e20ab 100644 --- a/lib/rubygems/request.rb +++ b/lib/rubygems/request.rb @@ -184,7 +184,6 @@ class Gem::Request def perform_request(request) # :nodoc: connection = connection_for @uri - uri = Gem::PrintableUri.parse_uri(@uri) retried = false bad_response = false @@ -192,7 +191,7 @@ class Gem::Request begin @requests[connection.object_id] += 1 - verbose "#{request.method} #{uri}" + verbose "#{request.method} #{Gem::Uri.new(@uri).redacted}" file_name = File.basename(@uri.path) # perform download progress reporter only for gems diff --git a/lib/rubygems/uri.rb b/lib/rubygems/uri.rb new file mode 100644 index 0000000000..031d7e01c3 --- /dev/null +++ b/lib/rubygems/uri.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +## +# The Uri handles rubygems source URIs. +# + +class Gem::Uri + def initialize(source_uri) + @parsed_uri = parse(source_uri) + end + + def redacted + return self unless valid_uri? + + if token? || oauth_basic? + with_redacted_user + elsif password? + with_redacted_password + else + self + end + end + + def to_s + @parsed_uri.to_s + end + + def redact_credentials_from(text) + return text unless valid_uri? && password? + + text.sub(password, 'REDACTED') + end + + def method_missing(method_name, *args, &blk) + if @parsed_uri.respond_to?(method_name) + @parsed_uri.send(method_name, *args, &blk) + else + super + end + end + + def respond_to_missing?(method_name, include_private = false) + @parsed_uri.respond_to?(method_name, include_private) || super + end + + private + + ## + # Parses the #uri, raising if it's invalid + + def parse!(uri) + require "uri" + + raise URI::InvalidURIError unless uri + + # Always escape URI's to deal with potential spaces and such + # It should also be considered that source_uri may already be + # a valid URI with escaped characters. e.g. "{DESede}" is encoded + # as "%7BDESede%7D". If this is escaped again the percentage + # symbols will be escaped. + begin + URI.parse(uri) + rescue URI::InvalidURIError + URI.parse(URI::DEFAULT_PARSER.escape(uri)) + end + end + + ## + # Parses the #uri, returning the original uri if it's invalid + + def parse(uri) + return uri unless uri.is_a?(String) + + parse!(uri) + rescue URI::InvalidURIError + uri + end + + def with_redacted_user + clone.tap {|uri| uri.user = 'REDACTED' } + end + + def with_redacted_password + clone.tap {|uri| uri.password = 'REDACTED' } + end + + def valid_uri? + !@parsed_uri.is_a?(String) + end + + def password? + !!password + end + + def oauth_basic? + password == 'x-oauth-basic' + end + + def token? + !user.nil? && password.nil? + end +end diff --git a/lib/rubygems/uri_parser.rb b/lib/rubygems/uri_parser.rb deleted file mode 100644 index f51d77a4af..0000000000 --- a/lib/rubygems/uri_parser.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -## -# The UriParser handles parsing URIs. -# - -class Gem::UriParser - def self.parse_uri(source_uri) - return source_uri unless source_uri.is_a?(String) - - new.parse(source_uri) - end - - ## - # Parses the #uri, raising if it's invalid - - def parse!(uri) - require "uri" - - raise URI::InvalidURIError unless uri - - # Always escape URI's to deal with potential spaces and such - # It should also be considered that source_uri may already be - # a valid URI with escaped characters. e.g. "{DESede}" is encoded - # as "%7BDESede%7D". If this is escaped again the percentage - # symbols will be escaped. - begin - URI.parse(uri) - rescue URI::InvalidURIError - URI.parse(URI::DEFAULT_PARSER.escape(uri)) - end - end - - ## - # Parses the #uri, returning the original uri if it's invalid - - def parse(uri) - parse!(uri) - rescue URI::InvalidURIError - uri - end -end diff --git a/test/rubygems/test_gem_printable_uri.rb b/test/rubygems/test_gem_printable_uri.rb deleted file mode 100644 index 6d4be6efc2..0000000000 --- a/test/rubygems/test_gem_printable_uri.rb +++ /dev/null @@ -1,44 +0,0 @@ -require_relative 'helper' -require 'rubygems/printable_uri' - -class TestPrintableUri < Gem::TestCase - def test_parsed_uri - assert_equal true, Gem::PrintableUri.parse_uri("https://www.example.com").valid_uri? - end - - def test_valid_uri_with_invalid_uri - assert_equal false, Gem::PrintableUri.parse_uri("https://www.example.com:80index").valid_uri? - end - - def test_original_password_user_pass - assert_equal "pass", Gem::PrintableUri.parse_uri("https://user:[email protected]").original_password - end - - def test_original_password_with_token - assert_equal nil, Gem::PrintableUri.parse_uri("https://[email protected]").original_password - end - - def test_original_password_without_credential - assert_equal nil, Gem::PrintableUri.parse_uri("https://www.example.com").original_password - end - - def test_to_s_with_user_pass - assert_equal "https://user:[email protected]", Gem::PrintableUri.parse_uri("https://user:[email protected]").to_s - end - - def test_to_s_with_token - assert_equal "https://[email protected]", Gem::PrintableUri.parse_uri("https://[email protected]").to_s - end - - def test_to_s_with_user_x_oauth_basic - assert_equal "https://REDACTED:[email protected]", Gem::PrintableUri.parse_uri("https://token:[email protected]").to_s - end - - def test_to_s_without_credential - assert_equal "https://www.example.com", Gem::PrintableUri.parse_uri("https://www.example.com").to_s - end - - def test_to_s_with_invalid_uri - assert_equal "https://www.example.com:80index", Gem::PrintableUri.parse_uri("https://www.example.com:80index").to_s - end -end diff --git a/test/rubygems/test_gem_uri.rb b/test/rubygems/test_gem_uri.rb new file mode 100644 index 0000000000..0c70443f32 --- /dev/null +++ b/test/rubygems/test_gem_uri.rb @@ -0,0 +1,32 @@ +require_relative 'helper' +require 'rubygems/uri' + +class TestUri < Gem::TestCase + def test_to_s_not_string + assert_equal "not_a_uri", Gem::Uri.new(:not_a_uri).to_s + end + + def test_to_s_invalid_uri + assert_equal "https://www.example.com:80index", Gem::Uri.new("https://www.example.com:80index").to_s + end + + def test_redacted_with_user_pass + assert_equal "https://user:[email protected]", Gem::Uri.new("https://user:[email protected]").redacted.to_s + end + + def test_redacted_with_token + assert_equal "https://[email protected]", Gem::Uri.new("https://[email protected]").redacted.to_s + end + + def test_redacted_with_user_x_oauth_basic + assert_equal "https://REDACTED:[email protected]", Gem::Uri.new("https://token:[email protected]").redacted.to_s + end + + def test_redacted_without_credential + assert_equal "https://www.example.com", Gem::Uri.new("https://www.example.com").redacted.to_s + end + + def test_redacted_with_invalid_uri + assert_equal "https://www.example.com:80index", Gem::Uri.new("https://www.example.com:80index").redacted.to_s + end +end diff --git a/test/rubygems/test_gem_uri_parser.rb b/test/rubygems/test_gem_uri_parser.rb deleted file mode 100644 index 095b193666..0000000000 --- a/test/rubygems/test_gem_uri_parser.rb +++ /dev/null @@ -1,17 +0,0 @@ -require_relative 'helper' -require 'uri' -require 'rubygems/uri_parser' - -class TestUriParser < Gem::TestCase - def test_parse_uri_none_string - assert_equal :not_a_uri, Gem::UriParser.parse_uri(:not_a_uri) - end - - def test_parse_uri_invalid_uri - assert_equal "https://www.example.com:80index", Gem::UriParser.parse_uri("https://www.example.com:80index") - end - - def test_parse_uri - assert_equal URI::HTTPS, Gem::UriParser.parse_uri("https://www.example.com").class - end -end |