Module: JsonLogging::Sanitizer
- Defined in:
- lib/json_logging/sanitizer.rb
Constant Summary collapse
- CONTROL_CHARS =
Control characters that should be escaped or removed from log messages
/[\x00-\x1F\x7F]/- MAX_STRING_LENGTH =
Maximum string length before truncation
10_000- MAX_CONTEXT_SIZE =
Maximum context hash size (number of keys)
50- MAX_DEPTH =
Maximum depth for nested structures
10- MAX_BACKTRACE_LINES =
Maximum backtrace lines to include
20- SENSITIVE_KEY_PATTERNS =
Common sensitive key patterns (case insensitive) - fallback when Rails ParameterFilter not available
/\b(password|passwd|pwd|secret|token|api_key|apikey|access_token|auth_token|private_key|credential)\b/i
Class Method Summary collapse
-
.rails_parameter_filter ⇒ Object
Get Rails ParameterFilter if available, nil otherwise.
-
.sanitize_backtrace(backtrace) ⇒ Object
Sanitize backtrace - truncate and remove sensitive paths.
-
.sanitize_exception(ex) ⇒ Object
Sanitize exception, including backtrace.
-
.sanitize_hash(hash, depth: 0) ⇒ Object
Sanitize a hash, removing sensitive keys and limiting size/depth Uses Rails ParameterFilter when available, falls back to pattern matching.
-
.sanitize_string(str) ⇒ Object
Sanitize a string by removing/escaping control characters and truncating.
-
.sanitize_value(value, depth: 0) ⇒ Object
Sanitize a value (handles strings, hashes, arrays, etc.) Preserves numeric, boolean, and nil types.
-
.sensitive_key?(key) ⇒ Boolean
Check if a key looks sensitive.
Class Method Details
.rails_parameter_filter ⇒ Object
Get Rails ParameterFilter if available, nil otherwise
24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/json_logging/sanitizer.rb', line 24 def rails_parameter_filter return nil unless defined?(Rails) && Rails.respond_to?(:application) return nil unless Rails.application.respond_to?(:config) filter_params = Rails.application.config.filter_parameters return nil if filter_params.empty? ActiveSupport::ParameterFilter.new(filter_params) rescue nil end |
.sanitize_backtrace(backtrace) ⇒ Object
Sanitize backtrace - truncate and remove sensitive paths
142 143 144 145 146 147 148 149 150 151 |
# File 'lib/json_logging/sanitizer.rb', line 142 def sanitize_backtrace(backtrace) return [] unless backtrace.is_a?(Array) # Take first MAX_BACKTRACE_LINES, sanitize each backtrace.first(MAX_BACKTRACE_LINES).map do |line| sanitize_string(line.to_s) end rescue [] end |
.sanitize_exception(ex) ⇒ Object
Sanitize exception, including backtrace
129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/json_logging/sanitizer.rb', line 129 def sanitize_exception(ex) { "error" => { "class" => ex.class.name, "message" => sanitize_string(ex..to_s), "backtrace" => sanitize_backtrace(ex.backtrace) } } rescue {"error" => {"class" => "Exception", "message" => "<sanitization_failed>"}} end |
.sanitize_hash(hash, depth: 0) ⇒ Object
Sanitize a hash, removing sensitive keys and limiting size/depth Uses Rails ParameterFilter when available, falls back to pattern matching
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/json_logging/sanitizer.rb', line 55 def sanitize_hash(hash, depth: 0) return hash unless hash.is_a?(Hash) # Prevent excessive nesting return {"error" => "max_depth_exceeded"} if depth > MAX_DEPTH # Limit hash size first limited_hash = if hash.size > MAX_CONTEXT_SIZE truncated = hash.first(MAX_CONTEXT_SIZE).to_h truncated["_truncated"] = true truncated else hash end # Use Rails ParameterFilter if available (handles encrypted attributes automatically) filter = rails_parameter_filter if filter # ParameterFilter will filter based on Rails.config.filter_parameters # This includes encrypted attributes automatically # Create a deep copy since filter modifies in place (Rails 6+) filtered = limited_hash.respond_to?(:deep_dup) ? limited_hash.deep_dup : limited_hash.dup filtered = filter.filter(filtered) # Then sanitize values (strings, control chars, etc.) preserving filtered structure filtered.each_with_object({}) do |(key, value), result| result[key] = sanitize_value(value, depth: depth + 1) end else # Fallback: use pattern matching for sensitive keys limited_hash.each_with_object({}) do |(key, value), result| key_str = key.to_s # Skip sensitive keys if SENSITIVE_KEY_PATTERNS.match?(key_str) result[key_str.gsub(/(?<!^)(?=[A-Z])/, "_").downcase + "_filtered"] = "[FILTERED]" next end result[key] = sanitize_value(value, depth: depth + 1) end end rescue {"sanitization_error" => true} end |
.sanitize_string(str) ⇒ Object
Sanitize a string by removing/escaping control characters and truncating
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/json_logging/sanitizer.rb', line 37 def sanitize_string(str) return str unless str.is_a?(String) # Remove or replace control characters sanitized = str.gsub(CONTROL_CHARS, "") # Truncate if too long if sanitized.length > MAX_STRING_LENGTH sanitized = sanitized[0, MAX_STRING_LENGTH] + "...[truncated]" end sanitized rescue "<sanitization_error>" end |
.sanitize_value(value, depth: 0) ⇒ Object
Sanitize a value (handles strings, hashes, arrays, etc.) Preserves numeric, boolean, and nil types
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/json_logging/sanitizer.rb', line 104 def sanitize_value(value, depth: 0) case value when String sanitize_string(value) when Hash sanitize_hash(value, depth: depth) when Array # Limit array size sanitized = value.first(MAX_CONTEXT_SIZE).map { |v| sanitize_value(v, depth: depth + 1) } sanitized << "[truncated]" if value.size > MAX_CONTEXT_SIZE sanitized when Exception sanitize_exception(value) when Numeric, TrueClass, FalseClass, NilClass # Preserve numeric, boolean, and nil types value else # For other types, convert to string and sanitize sanitize_string(value.to_s) end rescue "<unprintable>" end |
.sensitive_key?(key) ⇒ Boolean
Check if a key looks sensitive
154 155 156 |
# File 'lib/json_logging/sanitizer.rb', line 154 def sensitive_key?(key) SENSITIVE_KEY_PATTERNS.match?(key.to_s) end |