summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Newdigate <andrew@gitlab.com>2019-01-21 19:59:21 +0200
committerAndrew Newdigate <andrew@gitlab.com>2019-01-21 20:02:12 +0200
commitf15a5b2c270cc06ffa57d505d4fba161998889cd (patch)
treefe128b90139d30d91fce55a7bbf22005fbc11e86
parent33a6f23774c0e79f791da1b07dbdd48332467372 (diff)
downloadgitlab-ce-an-sanitize-url-parameters.tar.gz
Mask filterable parameters from sanitised URLsan-sanitize-url-parameters
Sanitised URLS are used for logging and display purposes only, and are intended to prevent sensitive information, such as credentials and access tokens. This change ensures that if URLs contain certain parameters, as configured by `Rails.application.config.filter_parameters`, these parameters in the sanitised URL will be masked with the phrase `[FILTERED]`. This is required for distributed tracing, which emits the `http.url` field, which is intended to include the full URL including querystring parameters. Since we want to avoid sensitive information was as `?private_token` values leaking, we need to mask the URL
-rw-r--r--changelogs/unreleased/an-sanitize-url-parameters.yml5
-rw-r--r--lib/gitlab/url_sanitizer.rb27
-rw-r--r--spec/lib/gitlab/url_sanitizer_spec.rb37
3 files changed, 55 insertions, 14 deletions
diff --git a/changelogs/unreleased/an-sanitize-url-parameters.yml b/changelogs/unreleased/an-sanitize-url-parameters.yml
new file mode 100644
index 00000000000..bb5dc280944
--- /dev/null
+++ b/changelogs/unreleased/an-sanitize-url-parameters.yml
@@ -0,0 +1,5 @@
+---
+title: Mask filterable parameters from sanitised URLs
+merge_request: 24537
+author:
+type: security
diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb
index 880712de5fe..cc58a69ee2e 100644
--- a/lib/gitlab/url_sanitizer.rb
+++ b/lib/gitlab/url_sanitizer.rb
@@ -3,11 +3,25 @@
module Gitlab
class UrlSanitizer
ALLOWED_SCHEMES = %w[http https ssh git].freeze
+ SINGLE_QUOTE = "'"
def self.sanitize(content)
regexp = URI::DEFAULT_PARSER.make_regexp(ALLOWED_SCHEMES)
- content.gsub(regexp) { |url| new(url).masked_url }
+ content.gsub(regexp) do |url|
+ # Unfortunately, URI::DEFAULT_PARSER.make_regexp returns a regular expression which hungrily consumes
+ # single quotes at the end of a string. When the querystring is filtered, the quote is converted into a %27
+ # which can affect error messages which quote the URL with singlequotes.
+ #
+ # Note the regular expression issue does not affect double-quotes nor backticks.
+ if url.end_with?(SINGLE_QUOTE)
+ url_without_quote = url.delete_suffix(SINGLE_QUOTE)
+ masked_unquoted_url = new(url_without_quote).masked_url
+ masked_unquoted_url + SINGLE_QUOTE
+ else
+ new(url).masked_url
+ end
+ end
rescue Addressable::URI::InvalidURIError
content.gsub(regexp, '')
end
@@ -40,6 +54,7 @@ module Gitlab
url = @url.dup
url.password = "*****" if url.password.present?
url.user = "*****" if url.user.present?
+ url.query = filter_query(url.query) if url.query.present?
url.to_s
end
@@ -53,6 +68,16 @@ module Gitlab
private
+ def param_filter
+ @param_filter ||= ActionDispatch::Http::ParameterFilter.new(Rails.application.config.filter_parameters)
+ end
+
+ def filter_query(query)
+ qs = CGI.parse(query)
+ qs = param_filter.filter(qs)
+ URI.encode_www_form(qs)
+ end
+
def parse_url(url)
url = url.to_s.strip
match = url.match(%r{\A(?:git|ssh|http(?:s?))\://(?:(.+)(?:@))?(.+)})
diff --git a/spec/lib/gitlab/url_sanitizer_spec.rb b/spec/lib/gitlab/url_sanitizer_spec.rb
index 6e98a999766..b2176a18918 100644
--- a/spec/lib/gitlab/url_sanitizer_spec.rb
+++ b/spec/lib/gitlab/url_sanitizer_spec.rb
@@ -4,12 +4,18 @@ describe Gitlab::UrlSanitizer do
using RSpec::Parameterized::TableSyntax
describe '.sanitize' do
- def sanitize_url(url)
+ def generate_message(url)
# We want to try with multi-line content because is how error messages are formatted
- described_class.sanitize(%Q{
- remote: Not Found
- fatal: repository '#{url}' not found
- })
+ %Q{
+ remote: Not Found
+ fatal: repository '#{url}' not found.
+ With Double Quotes: "#{url}"
+ With backticks: \`#{url}\`
+ }
+ end
+
+ def sanitize_url(url)
+ described_class.sanitize(generate_message(url))
end
where(:input, :output) do
@@ -27,10 +33,15 @@ describe Gitlab::UrlSanitizer do
# return an empty string for invalid URLs
'ssh://' | ''
+
+ # Filter secrets from the querystring
+ 'http://gitlab.com/api/v4/projects?private_token=1234&a=b' | 'http://gitlab.com/api/v4/projects?private_token=%5BFILTERED%5D&a=b'
+ 'http://gitlab.com/api/v4/projects?password=1234&a=b' | 'http://gitlab.com/api/v4/projects?password=%5BFILTERED%5D&a=b'
+ 'http://gitlab.com/api/v4/projects?token=1234&a=b' | 'http://gitlab.com/api/v4/projects?token=%5BFILTERED%5D&a=b'
end
with_them do
- it { expect(sanitize_url(input)).to include("repository '#{output}' not found") }
+ it { expect(sanitize_url(input)).to eq(generate_message(output)) }
end
end
@@ -51,6 +62,7 @@ describe Gitlab::UrlSanitizer do
true | 'git://foo:bar@example.com/group/group/project.git'
true | 'http://foo:bar@example.com/group/group/project.git'
true | 'https://foo:bar@example.com/group/group/project.git'
+ true | 'https://foo:bar@example.com/group/group/project.git?token=password'
end
with_them do
@@ -92,14 +104,13 @@ describe Gitlab::UrlSanitizer do
context 'credentials in URL' do
where(:url, :credentials) do
- 'http://foo:bar@example.com' | { user: 'foo', password: 'bar' }
+ 'http://foo:bar@example.com' | { user: 'foo', password: 'bar' }
'http://foo:bar:baz@example.com' | { user: 'foo', password: 'bar:baz' }
- 'http://:bar@example.com' | { user: nil, password: 'bar' }
- 'http://foo:@example.com' | { user: 'foo', password: nil }
- 'http://foo@example.com' | { user: 'foo', password: nil }
- 'http://:@example.com' | { user: nil, password: nil }
- 'http://@example.com' | { user: nil, password: nil }
- 'http://example.com' | { user: nil, password: nil }
+ 'http://foo:@example.com' | { user: 'foo', password: nil }
+ 'http://foo@example.com' | { user: 'foo', password: nil }
+ 'http://:@example.com' | { user: nil, password: nil }
+ 'http://@example.com' | { user: nil, password: nil }
+ 'http://example.com' | { user: nil, password: nil }
# Other invalid URLs
nil | { user: nil, password: nil }