module Airbrake::Filters::KeysFilter

This is a filter helper that endows a class ability to filter notices' payload based on the return value of the should_filter? method that a class that includes this module must implement.

@see Notice @see KeysAllowlist @see KeysBlocklist @api private

Constants

FILTERABLE_CONTEXT_KEYS

@return [Array<Symbol>] parts of a Notice's context payload that can

be modified by blocklist/allowlist filters
FILTERABLE_KEYS

@return [Array<Symbol>] parts of a Notice's payload that can be modified

by blocklist/allowlist filters
FILTERED

@return [String] The label to replace real values of filtered payload

VALID_PATTERN_CLASSES

@return [Array<String,Symbol,Regexp>] the array of classes instances of

which can compared with payload keys

Attributes

weight[R]

@return [Integer]

Public Class Methods

new(patterns) click to toggle source

Creates a new KeysBlocklist or KeysAllowlist filter that uses the given patterns for filtering a notice's payload.

@param [Array<String,Regexp,Symbol>] patterns

# File lib/airbrake-ruby/filters/keys_filter.rb, line 49
def initialize(patterns)
  @patterns = patterns
  @valid_patterns = false
end

Public Instance Methods

call(notice) click to toggle source

@!macro call_filter

This is a mandatory method required by any filter integrated with
FilterChain.

@param [Notice] notice the notice to be filtered
@return [void]
@see FilterChain
# File lib/airbrake-ruby/filters/keys_filter.rb, line 61
def call(notice)
  unless @valid_patterns
    eval_proc_patterns!
    validate_patterns
  end

  FILTERABLE_KEYS.each do |key|
    notice[key] = filter_hash(notice[key])
  end

  FILTERABLE_CONTEXT_KEYS.each { |key| filter_context_key(notice, key) }

  return unless notice[:context][:url]

  filter_url(notice)
end
should_filter?(_key) click to toggle source

@raise [NotImplementedError] if called directly

# File lib/airbrake-ruby/filters/keys_filter.rb, line 79
def should_filter?(_key)
  raise NotImplementedError, 'method must be implemented in the included class'
end

Private Instance Methods

eval_proc_patterns!() click to toggle source
# File lib/airbrake-ruby/filters/keys_filter.rb, line 125
def eval_proc_patterns!
  return unless @patterns.any? { |pattern| pattern.is_a?(Proc) }

  @patterns = @patterns.flat_map do |pattern|
    next(pattern) unless pattern.respond_to?(:call)

    pattern.call
  end
end
filter_context_key(notice, key) click to toggle source
# File lib/airbrake-ruby/filters/keys_filter.rb, line 148
def filter_context_key(notice, key)
  return unless notice[:context][key]
  return if notice[:context][key] == FILTERED
  unless should_filter?(key)
    return notice[:context][key] = filter_hash(notice[:context][key])
  end

  notice[:context][key] = FILTERED
end
filter_hash(hash) click to toggle source
# File lib/airbrake-ruby/filters/keys_filter.rb, line 85
def filter_hash(hash) # rubocop:disable Metrics/AbcSize
  return hash unless hash.is_a?(Hash)

  hash_copy = hash.dup

  hash.each_key do |key|
    if should_filter?(key.to_s)
      hash_copy[key] = FILTERED
    elsif hash_copy[key].is_a?(Hash)
      hash_copy[key] = filter_hash(hash_copy[key])
    elsif hash[key].is_a?(Array)
      hash_copy[key].each_with_index do |h, i|
        hash_copy[key][i] = filter_hash(h)
      end
    end
  end

  hash_copy
end
filter_url(notice) click to toggle source
# File lib/airbrake-ruby/filters/keys_filter.rb, line 113
def filter_url(notice)
  begin
    url = URI(notice[:context][:url])
  rescue URI::InvalidURIError
    return
  end

  return unless url.query

  notice[:context][:url] = filter_url_params(url)
end
filter_url_params(url) click to toggle source
# File lib/airbrake-ruby/filters/keys_filter.rb, line 105
def filter_url_params(url)
  url.query = URI.decode_www_form(url.query).to_h.map do |key, val|
    should_filter?(key) ? "#{key}=[Filtered]" : "#{key}=#{val}"
  end.join('&')

  url.to_s
end
validate_patterns() click to toggle source
# File lib/airbrake-ruby/filters/keys_filter.rb, line 135
def validate_patterns
  @valid_patterns = @patterns.all? do |pattern|
    VALID_PATTERN_CLASSES.any? { |c| pattern.is_a?(c) }
  end

  return if @valid_patterns

  logger.error(
    "#{LOG_LABEL} one of the patterns in #{self.class} is invalid. " \
    "Known patterns: #{@patterns}",
  )
end