Module: Philiprehberger::Mask
- Defined in:
- lib/philiprehberger/mask.rb,
lib/philiprehberger/mask/version.rb,
lib/philiprehberger/mask/detector.rb,
lib/philiprehberger/mask/scrubber.rb,
lib/philiprehberger/mask/configuration.rb,
lib/philiprehberger/mask/deep_scrubber.rb
Defined Under Namespace
Modules: DeepScrubber, Detector, Scrubber Classes: Configuration, Error
Constant Summary collapse
- VERSION =
'0.7.0'
Class Method Summary collapse
-
.add_locale(locale, patterns) ⇒ Object
Register locale-specific patterns.
-
.batch_scrub(strings, mode: :full, locale: nil) ⇒ Array<String>
Process an array of strings in one call with shared compiled patterns.
-
.configure {|Configuration| ... } ⇒ Object
Configure custom patterns.
-
.configure_priority(detector_order) ⇒ Object
Set detector evaluation priority.
-
.detect(string, locale: nil) ⇒ Array<Hash>
Scan a string for PII without modifying it.
-
.detokenize(string, tokens:) ⇒ String
Reverse tokenization using a token lookup table.
-
.reset_configuration! ⇒ Object
Reset configuration to defaults.
-
.scrub(string, mode: :full, reveal: 4) ⇒ String
Detect and redact PII patterns in a string.
-
.scrub_hash(data, keys: nil, mode: :full, reveal: 4) ⇒ Hash, Array
Deep-walk a hash/array and redact sensitive values.
-
.scrub_hash_with_audit(data, keys: nil) ⇒ Hash
Deep-walk a hash/array and redact sensitive values with audit trail.
-
.scrub_io(io, mode: :full, locale: nil) ⇒ Array<String>
Read from IO line by line, scrub each line.
-
.scrub_log(path, output: nil, mode: :full, locale: nil) ⇒ Hash
Read a file line by line, scrub each line, and write the result.
-
.scrub_with_audit(string) ⇒ Hash
Scrub a string and return an audit trail of what was masked.
-
.tokenize(string) ⇒ Hash
Replace PII with reversible tokens.
Class Method Details
.add_locale(locale, patterns) ⇒ Object
Register locale-specific patterns
143 144 145 |
# File 'lib/philiprehberger/mask.rb', line 143 def self.add_locale(locale, patterns) Configuration.instance.add_locale(locale, patterns) end |
.batch_scrub(strings, mode: :full, locale: nil) ⇒ Array<String>
Process an array of strings in one call with shared compiled patterns
Raises ArgumentError when strings is not an Array. An empty Array returns [].
124 125 126 127 128 129 130 |
# File 'lib/philiprehberger/mask.rb', line 124 def self.batch_scrub(strings, mode: :full, locale: nil) raise ArgumentError, 'strings must be an Array' unless strings.is_a?(Array) patterns = Configuration.instance.patterns(locale: locale) compiled = patterns.map { |pat| pat.merge(pattern: Regexp.new(pat[:pattern].source, pat[:pattern].)) } strings.map { |s| Scrubber.call(s, patterns: compiled, mode: mode) } end |
.configure {|Configuration| ... } ⇒ Object
Configure custom patterns
209 210 211 |
# File 'lib/philiprehberger/mask.rb', line 209 def self.configure(&block) block.call(Configuration.instance) end |
.configure_priority(detector_order) ⇒ Object
Set detector evaluation priority
135 136 137 |
# File 'lib/philiprehberger/mask.rb', line 135 def self.configure_priority(detector_order) Configuration.instance.set_priority(detector_order) end |
.detect(string, locale: nil) ⇒ Array<Hash>
Scan a string for PII without modifying it
Returns the list of detector matches in detection order. Each entry has
:detector, :match, and :position. Useful for "should this be
redacted?" checks before the cost of substitution. The input string is
not mutated.
41 42 43 |
# File 'lib/philiprehberger/mask.rb', line 41 def self.detect(string, locale: nil) Scrubber.scan(string, patterns: Configuration.instance.patterns(locale: locale)) end |
.detokenize(string, tokens:) ⇒ String
Reverse tokenization using a token lookup table
106 107 108 109 110 |
# File 'lib/philiprehberger/mask.rb', line 106 def self.detokenize(string, tokens:) result = string.dup tokens.each { |token, original| result = result.gsub(token) { original } } result end |
.reset_configuration! ⇒ Object
Reset configuration to defaults
214 215 216 |
# File 'lib/philiprehberger/mask.rb', line 214 def self.reset_configuration! Configuration.reset! end |
.scrub(string, mode: :full, reveal: 4) ⇒ String
Detect and redact PII patterns in a string
23 24 25 |
# File 'lib/philiprehberger/mask.rb', line 23 def self.scrub(string, mode: :full, reveal: 4) Scrubber.call(string, patterns: Configuration.instance.patterns, mode: mode, reveal: reveal) end |
.scrub_hash(data, keys: nil, mode: :full, reveal: 4) ⇒ Hash, Array
Deep-walk a hash/array and redact sensitive values
55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/philiprehberger/mask.rb', line 55 def self.scrub_hash(data, keys: nil, mode: :full, reveal: 4) config = Configuration.instance DeepScrubber.call( data, patterns: config.patterns, sensitive_keys: keys || config.sensitive_keys, mode: mode, reveal: reveal, placeholder: config.filtered_placeholder ) end |
.scrub_hash_with_audit(data, keys: nil) ⇒ Hash
Deep-walk a hash/array and redact sensitive values with audit trail
72 73 74 75 76 77 78 79 80 |
# File 'lib/philiprehberger/mask.rb', line 72 def self.scrub_hash_with_audit(data, keys: nil) config = Configuration.instance DeepScrubber.call_with_audit( data, patterns: config.patterns, sensitive_keys: keys || config.sensitive_keys, placeholder: config.filtered_placeholder ) end |
.scrub_io(io, mode: :full, locale: nil) ⇒ Array<String>
Read from IO line by line, scrub each line
Raises ArgumentError when io is nil. An IO that is already at EOF (or empty)
returns an empty Array rather than raising.
160 161 162 163 164 165 166 |
# File 'lib/philiprehberger/mask.rb', line 160 def self.scrub_io(io, mode: :full, locale: nil) raise ArgumentError, 'io is required' if io.nil? return [] if io.respond_to?(:eof?) && io.eof? patterns = Configuration.instance.patterns(locale: locale) io.each_line.map { |line| Scrubber.call(line, patterns: patterns, mode: mode) } end |
.scrub_log(path, output: nil, mode: :full, locale: nil) ⇒ Hash
Read a file line by line, scrub each line, and write the result
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/philiprehberger/mask.rb', line 175 def self.scrub_log(path, output: nil, mode: :full, locale: nil) patterns = Configuration.instance.patterns(locale: locale) lines_processed = 0 lines_modified = 0 detections = 0 scrubbed_lines = File.open(path, 'r') do |f| f.each_line.map do |line| lines_processed += 1 scrubbed = Scrubber.call(line, patterns: patterns, mode: mode) if scrubbed != line lines_modified += 1 detections += Scrubber.call_with_audit(line, patterns: patterns)[:audit].length end scrubbed end end if output.nil? Tempfile.open([File.basename(path), '.tmp'], File.dirname(path)) do |tmp| tmp.write(scrubbed_lines.join) tmp.flush File.rename(tmp.path, path) end else File.write(output, scrubbed_lines.join) end { lines_processed: lines_processed, lines_modified: lines_modified, detections: detections } end |
.scrub_with_audit(string) ⇒ Hash
Scrub a string and return an audit trail of what was masked
86 87 88 |
# File 'lib/philiprehberger/mask.rb', line 86 def self.scrub_with_audit(string) Scrubber.call_with_audit(string, patterns: Configuration.instance.patterns) end |
.tokenize(string) ⇒ Hash
Replace PII with reversible tokens
97 98 99 |
# File 'lib/philiprehberger/mask.rb', line 97 def self.tokenize(string) Scrubber.call_with_tokens(string, patterns: Configuration.instance.patterns) end |