Class: Expect::Redactor
- Inherits:
-
Object
- Object
- Expect::Redactor
- Defined in:
- lib/expect/redactor.rb
Overview
单个日志字节流的过滤器。保留最长秘密长度减一的尾部,跨 write/read 分片仍可识别。 掩码与原字节一起留存;重叠命中的区间取并集,已经输出的掩码不重复生成。 不依赖会话或 IO;每个日志目标或诊断方向使用独立实例。
Class Method Summary collapse
-
.redact(data, patterns, replacement: "[FILTERED]") ⇒ Object
完整诊断文本只匹配完整秘密;流边界的疑似秘密前缀由 finish 的默认策略保护。.
Instance Method Summary collapse
-
#append(data) ⇒ Object
追加一个原始字节块,返回已经可以确定的安全前缀;新秘密可能跨越此前保留的尾部。.
-
#finish(partial: true) ⇒ Object
EOF、日志目标替换及关闭是流边界;尾部疑似秘密前缀也遮盖,不能因 flush 泄露片段。.
-
#initialize(patterns, replacement: "[FILTERED]") ⇒ Redactor
constructor
pending 保存尚不能安全输出的原字节,hidden 的对应字节用 0/1 表示是否需要遮盖。.
-
#inspect ⇒ Object
过滤器公开后仍不在诊断摘要中展开注册秘密或尚未交付的原始字节。.
-
#patterns=(patterns) ⇒ Object
更新后续匹配规则并保留已有尾部与掩码;不能追溯修改已经交付给日志目标的内容。.
Constructor Details
#initialize(patterns, replacement: "[FILTERED]") ⇒ Redactor
pending 保存尚不能安全输出的原字节,hidden 的对应字节用 0/1 表示是否需要遮盖。
15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/expect/redactor.rb', line 15 def initialize(patterns, replacement: "[FILTERED]") unless replacement.is_a?(String) && !replacement.empty? raise ArgumentError, "replacement must be a nonempty String" end @replacement = replacement.b.freeze self.patterns = patterns @pending = "".b @hidden = "".b @masking = false end |
Class Method Details
.redact(data, patterns, replacement: "[FILTERED]") ⇒ Object
完整诊断文本只匹配完整秘密;流边界的疑似秘密前缀由 finish 的默认策略保护。
9 10 11 12 |
# File 'lib/expect/redactor.rb', line 9 def self.redact(data, patterns, replacement: "[FILTERED]") filter = new(patterns, replacement: replacement) filter.append(data) + filter.finish(partial: false) end |
Instance Method Details
#append(data) ⇒ Object
追加一个原始字节块,返回已经可以确定的安全前缀;新秘密可能跨越此前保留的尾部。
38 39 40 41 42 43 44 45 |
# File 'lib/expect/redactor.rb', line 38 def append(data) raise ArgumentError, "data must be a String" unless data.is_a?(String) @pending << data.b @hidden << ("\0" * data.bytesize) mark_secrets release([@pending.bytesize - @lookbehind, 0].max) end |
#finish(partial: true) ⇒ Object
EOF、日志目标替换及关闭是流边界;尾部疑似秘密前缀也遮盖,不能因 flush 泄露片段。
48 49 50 51 52 53 54 55 56 |
# File 'lib/expect/redactor.rb', line 48 def finish(partial: true) raise ArgumentError, "partial must be true or false" unless [true, false].include?(partial) mark_secrets mark_partial_secrets if partial output = release(@pending.bytesize) @masking = false output end |
#inspect ⇒ Object
过滤器公开后仍不在诊断摘要中展开注册秘密或尚未交付的原始字节。
59 |
# File 'lib/expect/redactor.rb', line 59 def inspect = "#<#{self.class}>" |
#patterns=(patterns) ⇒ Object
更新后续匹配规则并保留已有尾部与掩码;不能追溯修改已经交付给日志目标的内容。
28 29 30 31 32 33 34 35 |
# File 'lib/expect/redactor.rb', line 28 def patterns=(patterns) unless patterns.is_a?(Array) && patterns.all? { |pattern| pattern.is_a?(String) && !pattern.empty? } raise ArgumentError, "patterns must be an Array of nonempty Strings" end @patterns = patterns.map { |pattern| pattern.b.freeze }.uniq.freeze @lookbehind = [(@patterns.map(&:bytesize).max || 0) - 1, 0].max end |