[rubygems/rubygems] Merge Gem::UriParser
and Gem::PrintableUri
into a Gem::Uri
class
The new class is a wrapper on top of an URI. And then, when you want credentials redacted, you call `#redacted` that returns a copy of itself, but with credentials redacted. https://github.com/rubygems/rubygems/commit/9581c2740a
This commit is contained in:
parent
f0c6cc14b1
commit
1e290c31f4
Notes:
git
2021-08-31 19:06:53 +09:00
@ -5,7 +5,6 @@ require_relative '../dependency_installer'
|
|||||||
require_relative '../local_remote_options'
|
require_relative '../local_remote_options'
|
||||||
require_relative '../validator'
|
require_relative '../validator'
|
||||||
require_relative '../version_option'
|
require_relative '../version_option'
|
||||||
require_relative '../printable_uri'
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Gem installer command line tool
|
# Gem installer command line tool
|
||||||
@ -261,8 +260,8 @@ You can use `i` command instead of `install`.
|
|||||||
errors.each do |x|
|
errors.each do |x|
|
||||||
return unless Gem::SourceFetchProblem === x
|
return unless Gem::SourceFetchProblem === x
|
||||||
|
|
||||||
printable_uri = Gem::PrintableUri.parse_uri(x.source.uri.clone)
|
require_relative "../uri"
|
||||||
msg = "Unable to pull data from '#{printable_uri}': #{x.error.message}"
|
msg = "Unable to pull data from '#{Gem::Uri.new(x.source.uri).redacted}': #{x.error.message}"
|
||||||
|
|
||||||
alert_warning msg
|
alert_warning msg
|
||||||
end
|
end
|
||||||
|
@ -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?
|
|
||||||
!!@uri.password
|
|
||||||
end
|
|
||||||
|
|
||||||
def oauth_basic?
|
|
||||||
@uri.password == 'x-oauth-basic'
|
|
||||||
end
|
|
||||||
|
|
||||||
def token?
|
|
||||||
!@uri.user.nil? && @uri.password.nil?
|
|
||||||
end
|
|
||||||
end
|
|
@ -4,8 +4,7 @@ require_relative 'request'
|
|||||||
require_relative 'request/connection_pools'
|
require_relative 'request/connection_pools'
|
||||||
require_relative 's3_uri_signer'
|
require_relative 's3_uri_signer'
|
||||||
require_relative 'uri_formatter'
|
require_relative 'uri_formatter'
|
||||||
require_relative 'uri_parser'
|
require_relative 'uri'
|
||||||
require_relative 'printable_uri'
|
|
||||||
require_relative 'user_interaction'
|
require_relative 'user_interaction'
|
||||||
|
|
||||||
##
|
##
|
||||||
@ -26,12 +25,12 @@ class Gem::RemoteFetcher
|
|||||||
attr_accessor :uri, :original_uri
|
attr_accessor :uri, :original_uri
|
||||||
|
|
||||||
def initialize(message, uri)
|
def initialize(message, uri)
|
||||||
@original_uri = uri.dup
|
uri = Gem::Uri.new(uri)
|
||||||
uri = Gem::PrintableUri.parse_uri(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
|
end
|
||||||
|
|
||||||
def to_s # :nodoc:
|
def to_s # :nodoc:
|
||||||
@ -127,7 +126,7 @@ class Gem::RemoteFetcher
|
|||||||
require "fileutils"
|
require "fileutils"
|
||||||
FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
|
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
|
scheme = source_uri.scheme
|
||||||
|
|
||||||
@ -222,7 +221,7 @@ class Gem::RemoteFetcher
|
|||||||
unless location = response['Location']
|
unless location = response['Location']
|
||||||
raise FetchError.new("redirecting but no redirect location was given", uri)
|
raise FetchError.new("redirecting but no redirect location was given", uri)
|
||||||
end
|
end
|
||||||
location = Gem::UriParser.parse_uri location
|
location = Gem::Uri.new location
|
||||||
|
|
||||||
if https?(uri) && !https?(location)
|
if https?(uri) && !https?(location)
|
||||||
raise FetchError.new("redirecting to non-https resource: #{location}", uri)
|
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.
|
# Downloads +uri+ and returns it as a String.
|
||||||
|
|
||||||
def fetch_path(uri, mtime = nil, head = false)
|
def fetch_path(uri, mtime = nil, head = false)
|
||||||
uri = Gem::UriParser.parse_uri uri
|
uri = Gem::Uri.new uri
|
||||||
|
|
||||||
unless uri.scheme
|
unless uri.scheme
|
||||||
raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}"
|
raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}"
|
||||||
|
@ -184,7 +184,6 @@ class Gem::Request
|
|||||||
|
|
||||||
def perform_request(request) # :nodoc:
|
def perform_request(request) # :nodoc:
|
||||||
connection = connection_for @uri
|
connection = connection_for @uri
|
||||||
uri = Gem::PrintableUri.parse_uri(@uri)
|
|
||||||
|
|
||||||
retried = false
|
retried = false
|
||||||
bad_response = false
|
bad_response = false
|
||||||
@ -192,7 +191,7 @@ class Gem::Request
|
|||||||
begin
|
begin
|
||||||
@requests[connection.object_id] += 1
|
@requests[connection.object_id] += 1
|
||||||
|
|
||||||
verbose "#{request.method} #{uri}"
|
verbose "#{request.method} #{Gem::Uri.new(@uri).redacted}"
|
||||||
|
|
||||||
file_name = File.basename(@uri.path)
|
file_name = File.basename(@uri.path)
|
||||||
# perform download progress reporter only for gems
|
# perform download progress reporter only for gems
|
||||||
|
102
lib/rubygems/uri.rb
Normal file
102
lib/rubygems/uri.rb
Normal file
@ -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
|
@ -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
|
|
@ -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:pass@example.com").original_password
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_original_password_with_token
|
|
||||||
assert_equal nil, Gem::PrintableUri.parse_uri("https://token@example.com").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:REDACTED@example.com", Gem::PrintableUri.parse_uri("https://user:pass@example.com").to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_to_s_with_token
|
|
||||||
assert_equal "https://REDACTED@example.com", Gem::PrintableUri.parse_uri("https://token@example.com").to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_to_s_with_user_x_oauth_basic
|
|
||||||
assert_equal "https://REDACTED:x-oauth-basic@example.com", Gem::PrintableUri.parse_uri("https://token:x-oauth-basic@example.com").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
|
|
32
test/rubygems/test_gem_uri.rb
Normal file
32
test/rubygems/test_gem_uri.rb
Normal file
@ -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:REDACTED@example.com", Gem::Uri.new("https://user:pass@example.com").redacted.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_redacted_with_token
|
||||||
|
assert_equal "https://REDACTED@example.com", Gem::Uri.new("https://token@example.com").redacted.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_redacted_with_user_x_oauth_basic
|
||||||
|
assert_equal "https://REDACTED:x-oauth-basic@example.com", Gem::Uri.new("https://token:x-oauth-basic@example.com").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
|
@ -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
|
|
Loading…
x
Reference in New Issue
Block a user