class InfinumGraylog::Cleaner

Constants

ENCODING_OPTIONS
FILTERED
OBJECT
RAISED
RECURSION

Public Class Methods

clean_object_encoding(obj) click to toggle source
# File lib/infinum_graylog/cleaner.rb, line 74
def self.clean_object_encoding(obj)
  new(nil).clean_object(obj)
end
new(filters) click to toggle source
# File lib/infinum_graylog/cleaner.rb, line 11
def initialize(filters)
  @filters = Array(filters)
  @deep_filters = @filters.any? {|f| f.kind_of?(Regexp) && f.to_s.include?("\\.".freeze) }
end

Public Instance Methods

clean_object(obj) click to toggle source
# File lib/infinum_graylog/cleaner.rb, line 16
def clean_object(obj)
  traverse_object(obj, {}, nil)
end
clean_string(str) click to toggle source
# File lib/infinum_graylog/cleaner.rb, line 60
def clean_string(str)
  if defined?(str.encoding) && defined?(Encoding::UTF_8)
    if str.encoding == Encoding::UTF_8
      str.valid_encoding? ? str : str.encode('utf-16', ENCODING_OPTIONS).encode('utf-8')
    else
      str.encode('utf-8', ENCODING_OPTIONS)
    end
  elsif defined?(Iconv)
    Iconv.conv('UTF-8//IGNORE', 'UTF-8', str) || str
  else
    str
  end
end
clean_url(url) click to toggle source
# File lib/infinum_graylog/cleaner.rb, line 78
def clean_url(url)
  return url if @filters.empty?

  uri = URI(url)
  return url unless uri.query

  query_params = uri.query.split('&').map { |pair| pair.split('=') }
  query_params.map! do |key, val|
    if filters_match?(key)
      "#{key}=#{FILTERED}"
    else
      "#{key}=#{val}"
    end
  end

  uri.query = query_params.join('&')
  uri.to_s
end
traverse_object(obj, seen, scope) click to toggle source
# File lib/infinum_graylog/cleaner.rb, line 20
def traverse_object(obj, seen, scope)
  return nil if obj.nil?

  # Protect against recursion of recursable items
  protection = if obj.is_a?(Hash) || obj.is_a?(Array) || obj.is_a?(Set)
    return seen[obj] if seen[obj]
    seen[obj] = RECURSION
  end

  value = case obj
  when Hash
    clean_hash = {}
    obj.each do |k,v|
      if filters_match_deeply?(k, scope)
        clean_hash[k] = FILTERED
      else
        clean_hash[k] = traverse_object(v, seen, [scope, k].compact.join('.'))
      end
    end
    clean_hash
  when Array, Set
    obj.map { |el| traverse_object(el, seen, scope) }
  when Numeric, TrueClass, FalseClass
    obj
  when String
    clean_string(obj)
  else
    str = obj.to_s rescue RAISED
    # avoid leaking potentially sensitive data from objects' #inspect output
    if str =~ /#<.*>/
      OBJECT
    else
      clean_string(str)
    end
  end

  seen[obj] = value if protection
  value
end

Private Instance Methods

filters_match?(key) click to toggle source
# File lib/infinum_graylog/cleaner.rb, line 99
def filters_match?(key)
  str = key.to_s

  @filters.any? do |f|
    case f
    when Regexp
      str.match(f)
    else
      str.include?(f.to_s)
    end
  end
end
filters_match_deeply?(key, scope) click to toggle source

If someone has a Rails filter like /^stuff.secret/, it won't match “request.params.stuff.secret”, so we try it both with and without the “request.params.” bit.

# File lib/infinum_graylog/cleaner.rb, line 114
def filters_match_deeply?(key, scope)
  return true if filters_match?(key)
  return false unless @deep_filters

  long = [scope, key].compact.join('.')
  short = long.sub(/^request\.params\./, '')
  filters_match?(long) || filters_match?(short)
end