Module: Bootprint::Sanitizer
- Defined in:
- lib/bootprint/sanitizer.rb
Constant Summary collapse
- REDACTED =
"[REDACTED]"- JWT_PATTERN =
/\AeyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\z/- PRIVATE_KEY_PATTERN =
/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/- URL_CREDENTIAL_PATTERN =
%r{\A([a-z][a-z0-9+.-]*://)([^/@\s]+)@}i- HIGH_ENTROPY_PATTERN =
%r{\A[A-Za-z0-9+/_=-]{32,}\z}
Class Method Summary collapse
- .boolean?(value) ⇒ Boolean
- .high_entropy?(value) ⇒ Boolean
- .hostname_like?(value) ⇒ Boolean
- .path(value) ⇒ Object
- .path_variants(path) ⇒ Object
- .recursive(value, patterns: Bootprint.configuration.redaction_patterns, privacy: Bootprint.configuration.privacy) ⇒ Object
- .redaction_metadata(value) ⇒ Object
- .replacements ⇒ Object
- .safe_digest_name?(name) ⇒ Boolean
- .sanitize_value(value, privacy: :standard) ⇒ Object
- .secret_name?(name, patterns = Bootprint.configuration.redaction_patterns) ⇒ Boolean
- .sensitive_value?(value) ⇒ Boolean
- .text(value) ⇒ Object
Class Method Details
.boolean?(value) ⇒ Boolean
59 60 61 |
# File 'lib/bootprint/sanitizer.rb', line 59 def boolean?(value) [true, false].include?(value) end |
.high_entropy?(value) ⇒ Boolean
77 78 79 80 81 |
# File 'lib/bootprint/sanitizer.rb', line 77 def high_entropy?(value) return false unless HIGH_ENTROPY_PATTERN.match?(value) value.chars.uniq.length >= 16 end |
.hostname_like?(value) ⇒ Boolean
83 84 85 |
# File 'lib/bootprint/sanitizer.rb', line 83 def hostname_like?(value) value.match?(/\A(?=.{1,253}\z)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}\z/i) end |
.path(value) ⇒ Object
23 24 25 |
# File 'lib/bootprint/sanitizer.rb', line 23 def path(value) text(value) end |
.path_variants(path) ⇒ Object
101 102 103 |
# File 'lib/bootprint/sanitizer.rb', line 101 def path_variants(path) [path, path.tr("\\", "/"), path.tr("/", "\\")].uniq.sort_by { |variant| -variant.length } end |
.recursive(value, patterns: Bootprint.configuration.redaction_patterns, privacy: Bootprint.configuration.privacy) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/bootprint/sanitizer.rb', line 27 def recursive(value, patterns: Bootprint.configuration.redaction_patterns, privacy: Bootprint.configuration.privacy) case value when Hash value.each_with_object({}) do |(key, nested), result| name = key.to_s result[name] = if secret_name?(name, patterns) && !boolean?(nested) (nested) elsif safe_digest_name?(name) && !nested.is_a?(Hash) && !nested.is_a?(Array) nested.to_s else recursive(nested, patterns:, privacy:) end end when Array value.map { |nested| recursive(nested, patterns:, privacy:) } when String sanitize_value(value, privacy:) else value end end |
.redaction_metadata(value) ⇒ Object
87 88 89 |
# File 'lib/bootprint/sanitizer.rb', line 87 def (value) { "present" => !value.nil?, "redacted" => true } end |
.replacements ⇒ Object
91 92 93 94 95 96 97 98 99 |
# File 'lib/bootprint/sanitizer.rb', line 91 def replacements app_root = File.(Dir.pwd) home = begin File.(Dir.home) rescue ArgumentError nil end [[app_root, "<APP_ROOT>"], [home, "<HOME>"]].reject { |path, _marker| path.nil? || path.empty? } end |
.safe_digest_name?(name) ⇒ Boolean
55 56 57 |
# File 'lib/bootprint/sanitizer.rb', line 55 def safe_digest_name?(name) name.match?(/(?:checksum|sha256|digest)\z/i) end |
.sanitize_value(value, privacy: :standard) ⇒ Object
69 70 71 72 73 74 75 |
# File 'lib/bootprint/sanitizer.rb', line 69 def sanitize_value(value, privacy: :standard) result = text(value) result = result.gsub(URL_CREDENTIAL_PATTERN, "\\1#{REDACTED}@") result = REDACTED if PRIVATE_KEY_PATTERN.match?(result) || JWT_PATTERN.match?(result) || high_entropy?(result) result = "<HOST>" if privacy.to_sym == :strict && hostname_like?(result) result end |
.secret_name?(name, patterns = Bootprint.configuration.redaction_patterns) ⇒ Boolean
49 50 51 52 53 |
# File 'lib/bootprint/sanitizer.rb', line 49 def secret_name?(name, patterns = Bootprint.configuration.redaction_patterns) return false if Bootprint.configuration.redaction_safe_list.any? { |entry| File.fnmatch?(entry, name, File::FNM_CASEFOLD) } patterns.any? { |pattern| name.upcase.include?(pattern.to_s.upcase) } end |
.sensitive_value?(value) ⇒ Boolean
63 64 65 66 67 |
# File 'lib/bootprint/sanitizer.rb', line 63 def sensitive_value?(value) string = value.to_s PRIVATE_KEY_PATTERN.match?(string) || JWT_PATTERN.match?(string) || URL_CREDENTIAL_PATTERN.match?(string) || high_entropy?(string) end |
.text(value) ⇒ Object
13 14 15 16 17 18 19 20 21 |
# File 'lib/bootprint/sanitizer.rb', line 13 def text(value) sanitized = value.to_s.dup replacements.each do |path, marker| path_variants(path).each do |variant| sanitized.gsub!(variant, marker) end end sanitized.gsub(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/, "?") end |