Skip to content

Commit

Permalink
Introduce Gem::PrintableUri that would redact URIs to be used on ou…
Browse files Browse the repository at this point in the history
…tputs

We need to redact URI credential in several places and copy pasting the code into each part of it is not ideal. This class is responsible for parsing URI strings and redacting credential from it. Also, it will handle URI object in the same manner. We will be reusing this class whenever we need to print/display a URI to users.
URI with the following format will be redacted:
- Token: `http://my-secure-token@example.com` => `http://REDACTED@example.com`
- Username & Password: `http://my-username:my-secure-password@example.com` => `http://my-username:REDACTED@example.com`
- x-oauth-basic: `http://my-secure-token:x-oauth-basic@example.com` => `http://REDACTED:x-oauth-basic@example.com`
  • Loading branch information
daniel-niknam authored and deivid-rodriguez committed Aug 24, 2021
1 parent 1ea73df commit f1e45d3
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ lib/rubygems/package/tar_writer.rb
lib/rubygems/package_task.rb
lib/rubygems/path_support.rb
lib/rubygems/platform.rb
lib/rubygems/printable_uri.rb
lib/rubygems/psych_additions.rb
lib/rubygems/psych_tree.rb
lib/rubygems/query_utils.rb
Expand Down Expand Up @@ -626,6 +627,7 @@ test/rubygems/test_gem_package_tar_writer.rb
test/rubygems/test_gem_package_task.rb
test/rubygems/test_gem_path_support.rb
test/rubygems/test_gem_platform.rb
test/rubygems/test_gem_printable_uri.rb
test/rubygems/test_gem_rdoc.rb
test/rubygems/test_gem_remote_fetcher.rb
test/rubygems/test_gem_request.rb
Expand Down
81 changes: 81 additions & 0 deletions lib/rubygems/printable_uri.rb
Original file line number Diff line number Diff line change
@@ -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
116 changes: 116 additions & 0 deletions test/rubygems/test_gem_printable_uri.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit f1e45d3

Please sign in to comment.