require 'cgi' require 'uri' require 'rollbar/language_support' module Rollbar module Scrubbers class URL def self.call(*args) new.call(*args) end def call(options = {}) url = options[:url] return url unless Rollbar::LanguageSupport.can_scrub_url? filter(url, build_regex(options[:scrub_fields]), options[:scrub_user], options[:scrub_password], options.fetch(:randomize_scrub_length, true), options[:whitelist]) rescue => e Rollbar.logger.error("[Rollbar] There was an error scrubbing the url: #{e}, options: #{options.inspect}") url end private def filter(url, regex, scrub_user, scrub_password, randomize_scrub_length, whitelist) uri = URI.parse(url) uri.user = filter_user(uri.user, scrub_user, randomize_scrub_length) uri.password = filter_password(uri.password, scrub_password, randomize_scrub_length) uri.query = filter_query(uri.query, regex, randomize_scrub_length, whitelist) uri.to_s end # Builds a regex to match with any of the received fields. # The built regex will also match array params like 'user_ids[]'. def build_regex(fields) fields_or = fields.map { |field| "#{field}(\\[\\])?" }.join('|') Regexp.new("^#{fields_or}$") end def filter_user(user, scrub_user, randomize_scrub_length) scrub_user && user ? filtered_value(user, randomize_scrub_length) : user end def filter_password(password, scrub_password, randomize_scrub_length) scrub_password && password ? filtered_value(password, randomize_scrub_length) : password end def filter_query(query, regex, randomize_scrub_length, whitelist) return query unless query params = decode_www_form(query) encoded_query = encode_www_form(filter_query_params(params, regex, randomize_scrub_length, whitelist)) # We want this to rebuild array params like foo[]=1&foo[]=2 URI.escape(CGI.unescape(encoded_query)) end def decode_www_form(query) URI.decode_www_form(query) end def encode_www_form(params) URI.encode_www_form(params) end def filter_query_params(params, regex, randomize_scrub_length, whitelist) params.map do |key, value| if whitelist [key, filter_key?(key, regex) ? value : filtered_value(value, randomize_scrub_length)] else [key, filter_key?(key, regex) ? filtered_value(value, randomize_scrub_length) : value] end end end def filter_key?(key, regex) !!(key =~ regex) end def filtered_value(value, randomize_scrub_length) if randomize_scrub_length random_filtered_value else '*' * (value.length rescue 8) end end def random_filtered_value '*' * (rand(5) + 3) end end end end