Skip to content
Snippets Groups Projects
Commit a42ede83 authored by Luke Duncalfe's avatar Luke Duncalfe Committed by GitLab Release Tools Bot
Browse files

Redact InvalidURIError error messages

Merge branch 'security-37261-clean-passwords-from-invalid-uri-error-14-7' into '14-7-stable-ee'

See merge request gitlab-org/security/gitlab!2297

Changelog: security
parent 2de2e279
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -19,7 +19,8 @@ module Gitlab
PROCESSORS = [
::Gitlab::ErrorTracking::Processor::SidekiqProcessor,
::Gitlab::ErrorTracking::Processor::GrpcErrorProcessor,
::Gitlab::ErrorTracking::Processor::ContextPayloadProcessor
::Gitlab::ErrorTracking::Processor::ContextPayloadProcessor,
::Gitlab::ErrorTracking::Processor::SanitizeErrorMessageProcessor
].freeze
 
class << self
Loading
Loading
# frozen_string_literal: true
module Gitlab
module ErrorTracking
module Processor
module Concerns
module ProcessesExceptions
private
def extract_exceptions_from(event)
exceptions = event.instance_variable_get(:@interfaces)[:exception]&.values
Array.wrap(exceptions)
end
def valid_exception?(exception)
case exception
when Raven::SingleExceptionInterface
exception&.value.present?
else
false
end
end
end
end
end
end
end
Loading
Loading
@@ -4,6 +4,8 @@ module Gitlab
module ErrorTracking
module Processor
module GrpcErrorProcessor
extend Gitlab::ErrorTracking::Processor::Concerns::ProcessesExceptions
DEBUG_ERROR_STRING_REGEX = RE2('(.*) debug_error_string:(.*)')
 
class << self
Loading
Loading
@@ -18,10 +20,7 @@ module Gitlab
# only the first one since that's what is used for grouping.
def process_first_exception_value(event)
# Better in new version, will be event.exception.values
exceptions = event.instance_variable_get(:@interfaces)[:exception]&.values
return unless exceptions.is_a?(Array)
exceptions = extract_exceptions_from(event)
exception = exceptions.first
 
return unless valid_exception?(exception)
Loading
Loading
@@ -68,15 +67,6 @@ module Gitlab
 
[match[1], match[2]]
end
def valid_exception?(exception)
case exception
when Raven::SingleExceptionInterface
exception&.value
else
false
end
end
end
end
end
Loading
Loading
# frozen_string_literal: true
module Gitlab
module ErrorTracking
module Processor
module SanitizeErrorMessageProcessor
extend Gitlab::ErrorTracking::Processor::Concerns::ProcessesExceptions
class << self
def call(event)
exceptions = extract_exceptions_from(event)
exceptions.each do |exception|
next unless valid_exception?(exception)
exception.value = Gitlab::Sanitizers::ExceptionMessage.clean(exception.type, exception.value)
end
event
end
end
end
end
end
end
Loading
Loading
@@ -10,7 +10,7 @@ module Gitlab
# Use periods to flatten the fields.
payload.merge!(
'exception.class' => exception.class.name,
'exception.message' => exception.message
'exception.message' => sanitize_message(exception)
)
 
if exception.backtrace
Loading
Loading
@@ -38,6 +38,10 @@ module Gitlab
rescue PgQuery::ParseError
sql
end
def sanitize_message(exception)
Gitlab::Sanitizers::ExceptionMessage.clean(exception.class.name, exception.message)
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Sanitizers
module ExceptionMessage
FILTERED_STRING = '[FILTERED]'
EXCEPTION_NAMES = %w(URI::InvalidURIError Addressable::URI::InvalidURIError).freeze
MESSAGE_REGEX = %r{(\A[^:]+:\s).*\Z}.freeze
class << self
def clean(exception_name, message)
return message unless exception_name.in?(EXCEPTION_NAMES)
message.sub(MESSAGE_REGEX, '\1' + FILTERED_STRING)
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::ErrorTracking::Processor::SanitizeErrorMessageProcessor, :sentry do
describe '.call' do
let(:exception) { StandardError.new('raw error') }
let(:event) { Raven::Event.from_exception(exception, raven_required_options) }
let(:result_hash) { described_class.call(event).to_hash }
let(:raven_required_options) do
{
configuration: Raven.configuration,
context: Raven.context,
breadcrumbs: Raven.breadcrumbs
}
end
it 'cleans the exception message' do
expect(Gitlab::Sanitizers::ExceptionMessage).to receive(:clean).with('StandardError', 'raw error').and_return('cleaned')
expect(result_hash[:exception][:values].first).to include(
type: 'StandardError',
value: 'cleaned'
)
end
context 'when event is invalid' do
let(:event) { instance_double('Raven::Event', to_hash: { invalid: true }) }
it 'does nothing' do
extracted_exception = instance_double('Raven::SingleExceptionInterface', value: nil)
allow(described_class).to receive(:extract_exceptions_from).and_return([extracted_exception])
expect(Gitlab::Sanitizers::ExceptionMessage).not_to receive(:clean)
expect(result_hash).to eq(invalid: true)
end
end
end
end
Loading
Loading
@@ -301,5 +301,48 @@ RSpec.describe Gitlab::ErrorTracking do
end
end
end
context 'when processing invalid URI exceptions' do
let(:invalid_uri) { 'http://foo:bar' }
let(:sentry_exception_values) { sentry_event['exception']['values'] }
context 'when the error is a URI::InvalidURIError' do
let(:exception) do
URI.parse(invalid_uri)
rescue URI::InvalidURIError => error
error
end
it 'filters the URI from the error message' do
track_exception
expect(sentry_exception_values).to include(
hash_including(
'type' => 'URI::InvalidURIError',
'value' => 'bad URI(is not URI?): [FILTERED]'
)
)
end
end
context 'when the error is a Addressable::URI::InvalidURIError' do
let(:exception) do
Addressable::URI.parse(invalid_uri)
rescue Addressable::URI::InvalidURIError => error
error
end
it 'filters the URI from the error message' do
track_exception
expect(sentry_exception_values).to include(
hash_including(
'type' => 'Addressable::URI::InvalidURIError',
'value' => 'Invalid port number: [FILTERED]'
)
)
end
end
end
end
end
Loading
Loading
@@ -22,6 +22,14 @@ RSpec.describe Gitlab::ExceptionLogFormatter do
expect(payload['exception.sql']).to be_nil
end
 
it 'cleans the exception message' do
expect(Gitlab::Sanitizers::ExceptionMessage).to receive(:clean).with('RuntimeError', 'bad request').and_return('cleaned')
described_class.format!(exception, payload)
expect(payload['exception.message']).to eq('cleaned')
end
context 'when exception is ActiveRecord::StatementInvalid' do
let(:exception) { ActiveRecord::StatementInvalid.new(sql: 'SELECT "users".* FROM "users" WHERE "users"."id" = 1 AND "users"."foo" = $1') }
 
Loading
Loading
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rspec-parameterized'
RSpec.describe Gitlab::Sanitizers::ExceptionMessage do
describe '.clean' do
let(:exception_name) { exception.class.name }
let(:exception_message) { exception.message }
subject { described_class.clean(exception_name, exception_message) }
context 'when error is a URI::InvalidURIError' do
let(:exception) do
URI.parse('http://foo:bar')
rescue URI::InvalidURIError => error
error
end
it { is_expected.to eq('bad URI(is not URI?): [FILTERED]') }
end
context 'when error is an Addressable::URI::InvalidURIError' do
using RSpec::Parameterized::TableSyntax
let(:exception) do
Addressable::URI.parse(uri)
rescue Addressable::URI::InvalidURIError => error
error
end
where(:uri, :result) do
'http://foo:bar' | 'Invalid port number: [FILTERED]'
'http://foo:%eb' | 'Invalid encoding in port'
'ht%0atp://foo' | 'Invalid scheme format: [FILTERED]'
'http:' | 'Absolute URI missing hierarchical segment: [FILTERED]'
'::http' | 'Cannot assemble URI string with ambiguous path: [FILTERED]'
'http://foo bar' | 'Invalid character in host: [FILTERED]'
end
with_them do
it { is_expected.to eq(result) }
end
end
context 'with any other exception' do
let(:exception) { StandardError.new('Error message: http://foo@bar:baz@ex:ample.com') }
it 'is not invoked and does nothing' do
is_expected.to eq('Error message: http://foo@bar:baz@ex:ample.com')
end
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment