Class: LogStruct::ParamFilters

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/log_struct/param_filters.rb

Overview

This class contains methods for filtering sensitive data in logs It is used by Formatter to determine which keys should be filtered

Class Method Summary collapse

Class Method Details

.should_filter_key?(key, value = nil, logstruct_field: false, path: nil, root: nil) ⇒ Boolean

Check if a key should be filtered based on our defined sensitive keys. logstruct_field: the key is one of LogStruct's own fields at the top of a line. path: the keys from the line's top-level field down to this one (the key alone if not given). root: the top-level field's value, or the whole line for a top-level key.

Parameters:

  • key (T.untyped)
  • value (T.untyped) (defaults to: nil)
  • logstruct_field (Boolean) (defaults to: false)
  • path (Array<String>, nil) (defaults to: nil)
  • root (T.untyped) (defaults to: nil)

Returns:

  • (Boolean)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/log_struct/param_filters.rb', line 30

def should_filter_key?(key, value = nil, logstruct_field: false, path: nil, root: nil)
  filters = LogStruct.config.filters
  normalized_key = key.to_s

  words = KeyWords.normalize(normalized_key).to_sym
  # By its words too, so :api_key filters apiKey and X-Api-Key. Keys in filter_keys_with_hashes are
  # filtered too, with a hash of their value.
  [filters.filter_keys, filters.filter_keys_with_hashes].each do |keys|
    return true if keys.include?(normalized_key.downcase.to_sym) || keys.include?(words)
  end

  matched = filters.filter_matchers.any? do |matcher|
    next false if logstruct_field && !matcher.matches_logstruct_fields

    matcher.matches?(normalized_key, value)
  rescue => e
    handle_filter_matcher_error(e, matcher.label, normalized_key)
    false
  end
  return true if matched
  return false if filters.path_matchers.empty?

  path ||= [normalized_key]
  root = {} if root.nil?
  filters.path_matchers.any? do |matcher|
    matcher.matches?(path, value, root)
  rescue => e
    handle_filter_matcher_error(e, matcher.label, normalized_key)
    false
  end
end

.should_include_string_hash?(key) ⇒ Boolean

Check if a key should be hashed rather than completely filtered

Parameters:

  • key (T.untyped)

Returns:

  • (Boolean)


64
65
66
67
# File 'lib/log_struct/param_filters.rb', line 64

def should_include_string_hash?(key)
  keys = LogStruct.config.filters.filter_keys_with_hashes
  keys.include?(key.to_s.downcase.to_sym) || keys.include?(KeyWords.normalize(key.to_s).to_sym)
end

.summarize_array(array) ⇒ Hash{Symbol => T.untyped}

Summarize a filtered Array: how many items it has

Parameters:

  • array (Array<T.untyped>)

Returns:

  • (Hash{Symbol => T.untyped})


111
112
113
114
115
116
117
118
# File 'lib/log_struct/param_filters.rb', line 111

def summarize_array(array)
  return {_class: "Array", _empty: true} if array.empty?

  {
    _class: Array,
    _count: array.size
  }
end

.summarize_hash(hash) ⇒ Hash{Symbol => T.untyped}

Summarize a filtered Hash: its first 10 keys and how many there are. No size, which would say how long the secrets in it are.

Parameters:

  • hash (Hash{T.untyped => T.untyped})

Returns:

  • (Hash{Symbol => T.untyped})


99
100
101
102
103
104
105
106
107
# File 'lib/log_struct/param_filters.rb', line 99

def summarize_hash(hash)
  return {_class: "Hash", _empty: true} if hash.empty?

  {
    _class: Hash,
    _keys_count: hash.keys.size,
    _keys: hash.keys.take(10).map { |key| normalize_summary_key(StringScrubber.scrub_name(key)) }
  }
end

.summarize_json_attribute(key, data) ⇒ Hash{Symbol => T.untyped}

Convert a value to a filtered summary hash (e.g. { _filtered: { class: "String", ... }})

Parameters:

  • key (T.untyped)
  • data (T.untyped)

Returns:

  • (Hash{Symbol => T.untyped})


71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/log_struct/param_filters.rb', line 71

def summarize_json_attribute(key, data)
  case data
  when Hash
    summarize_hash(data)
  when Array
    summarize_array(data)
  when String
    summarize_string(data, should_include_string_hash?(key))
  else
    {_class: data.class}
  end
end

.summarize_string(string, include_hash) ⇒ Hash{Symbol => T.untyped}

Summarize a filtered String: its class, and an HMAC of it for keys in filter_keys_with_hashes. No length, which would say how long a password or token is.

Parameters:

  • string (String)
  • include_hash (Boolean)

Returns:

  • (Hash{Symbol => T.untyped})


87
88
89
90
91
92
93
94
# File 'lib/log_struct/param_filters.rb', line 87

def summarize_string(string, include_hash)
  filtered_string = {
    _class: String
  }
  filtered_string[:_hash] = StringScrubber.value_hash(string) if include_hash

  filtered_string
end