Skip to content

Commit

Permalink
Allows whitelisting hash values based on the key
Browse files Browse the repository at this point in the history
  • Loading branch information
alxberardi committed Dec 26, 2016
1 parent 99f14dc commit 5f2bd2f
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This changelog adheres to [Keep a CHANGELOG](http://keepachangelog.com/).

## [Unreleased]
### Changed
- Allows whitelisting hash values based on the key
- Updates README for usage with Rails middleware stack

## [0.2.4] - 2016-12-22
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ SensitiveDataFilter.config do |config|
# Report occurrence
end
config.whitelist pattern1, pattern2 # Allows specifying patterns to whitelist matches
config.whitelist_key key_pattern1, key_pattern2 # Allows specifying patterns to whitelist hash values based on their keys
config.register_parser('yaml', -> params { YAML.load params }, -> params { YAML.dump params })
end
```
Expand Down Expand Up @@ -91,6 +92,9 @@ filtered_body_params = if @occurrence.filtered_body_params.is_a? Hash
A list of whitelisting patterns can be passed to `config.whitelist`.
Any sensitive data match which also matches any of these patterns will be ignored.

A list of whitelisting patterns can be passed to `config.whitelist_key`.
When scanning and matching hashes, any value whose key matches any of these patterns will be ignored.

#### Parameter Parsing

Parsers for parameters encoded for a specific content type can be defined.
Expand Down
12 changes: 12 additions & 0 deletions lib/sensitive_data_filter/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ def self.whitelisted?(value)
config.whitelist_patterns.any? { |pattern| value.match pattern }
end

def self.whitelisted_key?(key)
config.whitelist_key_patterns.any? { |pattern| key.match pattern }
end

class Config
DEFAULT_TYPES = %i(credit_card).freeze

Expand All @@ -45,6 +49,14 @@ def whitelist_patterns
@whitelist_patterns ||= []
end

def whitelist_key(*patterns)
@whitelist_key_patterns = patterns
end

def whitelist_key_patterns
@whitelist_key_patterns ||= []
end

def register_parser(content_type, parser, unparser)
SensitiveDataFilter::Middleware::ParameterParser
.register_parser(content_type, parser, unparser)
Expand Down
8 changes: 7 additions & 1 deletion lib/sensitive_data_filter/mask.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ module Mask
end

module_function def mask_hash(hash)
hash.map { |key, value| [mask(key), mask(value)] }.to_h
hash.map { |key, value| mask_key_value(key, value) }.to_h
end

module_function def mask_key_value(key, value)
masked_key = mask(key)
return [masked_key, value] if SensitiveDataFilter.whitelisted_key? key
[masked_key, mask(value)]
end
end
end
8 changes: 7 additions & 1 deletion lib/sensitive_data_filter/scan.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ def self.scan_array(array)
end

def self.scan_hash(hash)
hash.map { |key, value| scan(key).collate(scan(value)) }.inject(:collate) || {}
hash.map { |key, value| scan_key_value(key, value) }.inject(:collate) || {}
end

def self.scan_key_value(key, value)
key_scan = scan(key)
return key_scan if SensitiveDataFilter.whitelisted_key? key
key_scan.collate(scan(value))
end

def self.whitelist(matches)
Expand Down
14 changes: 14 additions & 0 deletions spec/sensitive_data_filter/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@
specify { expect(SensitiveDataFilter.whitelisted?(non_allowed_value)).to be false }
end

describe '#whitelisted_key?' do
before do
SensitiveDataFilter.config do |config|
config.whitelist_key 'is allowed', 'is acceptable'
end
end

let(:allowed_key) { 'this is allowed' }
let(:non_allowed_key) { 'this is not allowed' }

specify { expect(SensitiveDataFilter.whitelisted_key?(allowed_key)).to be true }
specify { expect(SensitiveDataFilter.whitelisted_key?(non_allowed_key)).to be false }
end

describe '#register_parser' do
let(:parameter_parser) { double }
let(:parse) { double }
Expand Down
13 changes: 13 additions & 0 deletions spec/sensitive_data_filter/mask_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
}

allow(SensitiveDataFilter).to receive(:enabled_types).and_return enabled_types
allow(SensitiveDataFilter).to receive(:whitelisted_key?) { |key| key.match /phone|mobile/}
end

describe '#mask' do
Expand Down Expand Up @@ -59,6 +60,18 @@

specify { expect(result).to eq expected_result }
specify { expect(value).to eq original_value }

context 'when keys are whitelisted' do
let(:value) {
{ credit_card: 'maskable', phone: 'maskable' }
}

let(:expected_result) {
{ credit_card: masked_value, phone: 'maskable' }
}

specify { expect(result).to eq expected_result }
end
end

context 'with an array' do
Expand Down
29 changes: 20 additions & 9 deletions spec/sensitive_data_filter/scan_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,23 @@
describe SensitiveDataFilter::Scan do
let(:credit_card_scanner) { double name: 'CreditCard' }
let(:enabled_types) { [credit_card_scanner] }
let(:whitelisted?) { false }
let(:credit_card) { '4111 1111 1111 1111' }
let(:whitelisted?) { false }
let(:pattern) { /4111[\s-]*1111[\s-]*1111[\s-]*1111|5555[\s-]*5555[\s-]*5555[\s-]*4444/ }
let(:visa) { '4111 1111 1111 1111' }
let(:mastercard) { '5555 5555 5555 4444' }

before do
allow(SensitiveDataFilter).to receive(:enabled_types).and_return enabled_types
allow(credit_card_scanner).to receive(:scan) { |value| value.to_s.scan credit_card }
allow(credit_card_scanner).to receive(:scan) { |value| value.to_s.scan pattern }
allow(SensitiveDataFilter).to receive(:whitelisted?).and_return whitelisted?
allow(SensitiveDataFilter).to receive(:whitelisted_key?) { |key| key.match /phone|mobile/}
end

let(:scan) { SensitiveDataFilter::Scan.new(value) }

context 'when there are matches' do
let(:value) { 'Credit card 4111 1111 1111 1111' }
let(:matches) { ['4111 1111 1111 1111'] }
let(:value) { "Credit card #{visa}" }
let(:matches) { [visa] }

context 'after scanning' do
before do
Expand Down Expand Up @@ -53,9 +56,8 @@
end

context 'with complex values' do
let(:credit_card) { '4111 1111 1111 1111' }
let(:matchable_value) { 'Credit card ' + credit_card }
let(:matches) { [credit_card] }
let(:matchable_value) { 'Credit card ' + visa }
let(:matches) { [visa] }
let(:value) {
{
a: nil,
Expand All @@ -80,6 +82,15 @@
end

specify { expect(scan.matches?).to be true }
specify { expect(scan.matches).to eq 'CreditCard' => [credit_card] * 5 }
specify { expect(scan.matches).to eq 'CreditCard' => [visa] * 5 }
end

context 'when keys are whitelisted' do
let(:value) {
{ credit_card: mastercard, phone: "+#{visa}" }
}

specify { expect(scan.matches?).to be true }
specify { expect(scan.matches).to eq 'CreditCard' => [mastercard] }
end
end

0 comments on commit 5f2bd2f

Please sign in to comment.