diff --git a/lib/rubygems/printable_uri.rb b/lib/rubygems/printable_uri.rb new file mode 100644 index 0000000000..90b05ddd3c --- /dev/null +++ b/lib/rubygems/printable_uri.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +require 'uri' +require_relative 'uri_parser' + +class Gem::PrintableUri + def self.parse_uri(uri) + new(uri).parse_uri + end + + def initialize(original_uri) + @credential_redacted = false + @original_uri = original_uri + end + + def parse_uri + @original_uri = Gem::UriParser.parse_uri(@original_uri) + @uri = @original_uri.clone + redact_credential + + self + end + + def parsed_uri? + @uri.is_a? URI::Generic + end + + def credential_redacted? + @credential_redacted + end + + def original_password + return unless parsed_uri? + + @original_uri.password + end + + def to_s + @uri.to_s + end + + private + + def redact_credential + return unless redactable_credential? + + if token? + @uri.user = 'REDACTED' + elsif oauth_basic? + @uri.user = 'REDACTED' + elsif password? + @uri.password = 'REDACTED' if password? + end + + @credential_redacted = true + end + + def redactable_credential? + return false unless parsed_uri? + + password? || oauth_basic? || token? + end + + def password? + return false unless parsed_uri? + + !!@uri.password + end + + def oauth_basic? + return false unless parsed_uri? + + @uri.password == 'x-oauth-basic' + end + + def token? + return false unless parsed_uri? + + !@uri.user.nil? && @uri.password.nil? + end +end diff --git a/test/rubygems/test_gem_printable_uri.rb b/test/rubygems/test_gem_printable_uri.rb new file mode 100644 index 0000000000..59394547b9 --- /dev/null +++ b/test/rubygems/test_gem_printable_uri.rb @@ -0,0 +1,116 @@ +require_relative 'helper' +require 'uri' +require 'rubygems/printable_uri' + +class TestPrintableUri < Gem::TestCase + def test_parsed_uri + assert_equal true, Gem::PrintableUri.parse_uri("https://www.example.com").parsed_uri? + end + + def test_parsed_uri_with_empty_uri_object + assert_equal true, Gem::PrintableUri.parse_uri(URI("")).parsed_uri? + end + + def test_parsed_uri_with_valid_uri_object + assert_equal true, Gem::PrintableUri.parse_uri(URI("https://www.example.com")).parsed_uri? + end + + def test_parsed_uri_with_other_objects + assert_equal false, Gem::PrintableUri.parse_uri(Object.new).parsed_uri? + end + + def test_parsed_uri_with_invalid_uri + assert_equal false, Gem::PrintableUri.parse_uri("https://www.example.com:80index").parsed_uri? + end + + def test_credential_redacted_with_user_pass + assert_equal true, Gem::PrintableUri.parse_uri("https://user:pass@example.com").credential_redacted? + end + + def test_credential_redacted_with_token + assert_equal true, Gem::PrintableUri.parse_uri("https://token@example.com").credential_redacted? + end + + def test_credential_redacted_with_user_x_oauth_basic + assert_equal true, Gem::PrintableUri.parse_uri("https://token:x-oauth-basic@example.com").credential_redacted? + end + + def test_credential_redacted_without_credential + assert_equal false, Gem::PrintableUri.parse_uri("https://www.example.com").credential_redacted? + end + + def test_credential_redacted_with_empty_uri_object + assert_equal false, Gem::PrintableUri.parse_uri(URI("")).credential_redacted? + end + + def test_credential_redacted_with_valid_uri_object + assert_equal true, Gem::PrintableUri.parse_uri(URI("https://user:pass@example.com")).credential_redacted? + end + + def test_credential_redacted_with_other_objects + assert_equal false, Gem::PrintableUri.parse_uri(Object.new).credential_redacted? + 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_original_password_with_invalid_uri + assert_equal nil, Gem::PrintableUri.parse_uri("https://www.example.com:80index").original_password + end + + def test_original_password_with_empty_uri_object + assert_equal nil, Gem::PrintableUri.parse_uri(URI("")).original_password + end + + def test_original_password_with_valid_uri_object + assert_equal "pass", Gem::PrintableUri.parse_uri(URI("https://user:pass@example.com")).original_password + end + + def test_original_password_with_other_objects + assert_equal nil, Gem::PrintableUri.parse_uri(Object.new).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 + + def test_to_s_with_empty_uri_object + assert_equal "", Gem::PrintableUri.parse_uri(URI("")).to_s + end + + def test_to_s_with_valid_uri_object + assert_equal "https://user:REDACTED@example.com", Gem::PrintableUri.parse_uri(URI("https://user:pass@example.com")).to_s + end + + def test_to_s_with_other_objects + obj = Object.new + obj.stub(:to_s, "my-to-s") do + assert_equal "my-to-s", Gem::PrintableUri.parse_uri(obj).to_s + end + end +end