Module: PWN::Redaction

Defined in:
lib/pwn/redaction.rb

Overview

Shared write-boundary redaction. Never opens a credential store.

Constant Summary collapse

PATTERNS =
{
  pem: /-----BEGIN [A-Z ]*PRIVATE KEY-----.*?(?:-----END [A-Z ]*PRIVATE KEY-----|\z)/m,
  authorization: /\b(?:Proxy-)?Authorization\s*[:=]\s*(?!\[REDACTED:)[^\r\n"'\\]+/i,
  jwt: /\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/,
  aws: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/,
  bearer: %r{\bBearer\s+[A-Za-z0-9._~+/=-]+}i,
  api_key: /\b(?:sk|rk|xai|xox[baprs]|ghp|gho|ghu|ghs|ghr|glpat)[-_][A-Za-z0-9_-]{16,}/,
  password: /\b(?:password|passwd|api[_-]?key|access[_-]?token|refresh[_-]?token|secret)\s*[:=]\s*(?!\[REDACTED:)[^\s,"'}]+/i,
  cookie: /\bSet-Cookie:\s*(?!\[REDACTED:)[^\r\n]+/i
}.freeze
SECRET_FIELD =
/\A(?:password|passwd|secret|api[_-]?key|authorization|proxy-authorization|access[_-]?token|refresh[_-]?token|private[_-]?key|client[_-]?secret)\z/i

Class Method Summary collapse

Class Method Details

.authorsObject



50
51
52
# File 'lib/pwn/redaction.rb', line 50

public_class_method def self.authors
  "AUTHOR(S):\n  0day Inc. <[email protected]>\n"
end

.helpObject



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/pwn/redaction.rb', line 54

public_class_method def self.help
  puts "USAGE:
    # Recursively redact secret patterns before persistence.
    #{self}.redact(
      value: 'required - string or nested JSON-compatible data'
    )
    # Create a correlatable non-reversible replacement marker.
    #{self}.token(
      kind: 'required - secret type label',
      value: 'required - sensitive value to hash'
    )
    # Print the author information.
    #{self}.authors
  "
end

.redact(opts = {}) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/pwn/redaction.rb', line 20

public_class_method def self.redact(opts = {})
  value = opts[:value]
  case value
  when Hash
    value.to_h do |key, item|
      clean = if key.to_s.match?(SECRET_FIELD) && !item.nil?
                token(kind: key.to_s.downcase, value: item.to_s)
              else
                redact(value: item)
              end
      [key.is_a?(String) ? redact(value: key) : key, clean]
    end
  when Array then value.map { |item| redact(value: item) }
  when String
    PATTERNS.reduce(value.dup) do |text, (kind, regex)|
      text.split(/(\[REDACTED:[^:\]]+:[0-9a-f]{8}\])/).map do |part|
        part.start_with?('[REDACTED:') ? part : part.gsub(regex) { |match| token(kind: kind, value: match) }
      end.join
    end
  else value
  end
end

.token(opts = {}) ⇒ Object



43
44
45
46
47
48
# File 'lib/pwn/redaction.rb', line 43

public_class_method def self.token(opts = {})
  value = opts[:value].to_s
  return value if value.match?(/\A\[REDACTED:[^:]+:[0-9a-f]{8}\]\z/)

  "[REDACTED:#{opts[:kind]}:#{Digest::SHA256.hexdigest(value)[0, 8]}]"
end