Skip to content

Commit 55f8c3e

Browse files
authored
Merge pull request #6 from PayU/exclude
Support for exclude fields
2 parents f67e1ab + 3a8c8a0 commit 55f8c3e

File tree

5 files changed

+86
-8
lines changed

5 files changed

+86
-8
lines changed

Gemfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
fluent-plugin-masking (1.0.8)
4+
fluent-plugin-masking (1.1.0)
55
fluentd (>= 0.14.0)
66

77
GEM
@@ -10,7 +10,7 @@ GEM
1010
concurrent-ruby (1.1.5)
1111
cool.io (1.5.4)
1212
dig_rb (1.0.1)
13-
fluentd (1.7.2)
13+
fluentd (1.7.4)
1414
cool.io (>= 1.4.5, < 2.0.0)
1515
dig_rb (~> 1.0.0)
1616
http_parser.rb (>= 0.5.1, < 0.7.0)
@@ -26,7 +26,7 @@ GEM
2626
power_assert (1.1.5)
2727
rake (12.3.3)
2828
rr (1.2.1)
29-
serverengine (2.1.1)
29+
serverengine (2.2.0)
3030
sigdump (~> 0.2.2)
3131
sigdump (0.2.4)
3232
strptime (0.2.3)

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ Install with gem:
1919
## Setup
2020
In order to setup this plugin, the parameter `fieldsToMaskFilePath` needs to be a valid path to a file containing a list of all the fields to mask. The file should have a unique field on each line. These fields **are** case-sensitive (`Name` != `name`).
2121

22+
In addition, there's an optional parameter called `fieldsToExcludeJSONPaths` which receives as input a comma separated string of JSON fields that should be excluded in the masking procedure. Nested JSON fields are supported by `dot notation` (i.e: `path.to.excluded.field.in.record.nestedExcludedField`)
23+
The JSON fields that are excluded are comma separated.
24+
This can be used for logs of registration services or audit log entries which do not need to be masked.
2225
This is configured as shown below:
2326
```
2427
<filter "**">
2528
@type masking
2629
fieldsToMaskFilePath "/path/to/fields-to-mask-file"
30+
fieldsToExcludeJSONPaths "excludedField,exclude.path.nestedExcludedField"
2731
</filter>
2832
```
2933

@@ -52,6 +56,7 @@ phone
5256
<filter "**">
5357
@type masking
5458
fieldsToMaskFilePath "/path/to/fields-to-mask-file"
59+
fieldsToExcludeJSONPaths "excludedField,exclude.path.nestedExcludedField"
5560
</filter>
5661
5762
<match "**">
@@ -83,3 +88,12 @@ This sample result is created from the above configuration file `fluent.conf`. A
8388
```
8489
2019-09-15 16:12:50.359191000 +0300 maskme: {"message":"{ :body => \"{\\\"first_name\\\":\\\"*******\\\", \\\"type\\\":\\\"puggle\\\", \\\"last_name\\\":\\\"*******\\\", \\\"password\\\":\\\"*******\\\"}\"}"}
8590
```
91+
92+
A sample with exclude in use:
93+
fluentd -c fluent.conf
94+
echo '{ :body => "{\"first_name\":\"mickey\", \"type\":\"puggle\", \"last_name\":\"the-dog\", \"password\":\"d0g43u39\"}", "excludeMaskFields"=>"first_name,last_name"}' > /tmp/test.log
95+
```
96+
97+
```
98+
2019-12-01 14:25:53.385681000 +0300 maskme: {"message":"{ :body => \"{\\\"first_name\\\":\\\"mickey\\\", \\\"type\\\":\\\"puggle\\\", \\\"last_name\\\":\\\"the-dog\\\", \\\"password\\\":\\\"*******\\\"}\"}"}
99+
```

lib/fluent/plugin/filter_masking.rb

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,19 @@ def strToHash(str)
1515
# error safe method - if any error occurs the original record is returned
1616
def maskRecord(record)
1717
maskedRecord = record
18-
19-
begin
18+
excludedFields = []
19+
@fieldsToExcludeJSONPathsArray.each do | field |
20+
field_value = record.dig(*field)
21+
if field_value != nil
22+
excludedFields = excludedFields + field_value.split(',')
23+
end
24+
end
25+
begin
2026
recordStr = record.to_s
2127
@fields_to_mask_regex.each do | fieldToMaskRegex, fieldToMaskRegexStringReplacement |
22-
recordStr = recordStr.gsub(fieldToMaskRegex, fieldToMaskRegexStringReplacement)
28+
if !(excludedFields.include? @fields_to_mask_keys[fieldToMaskRegex])
29+
recordStr = recordStr.gsub(fieldToMaskRegex, fieldToMaskRegexStringReplacement)
30+
end
2331
end
2432

2533
maskedRecord = strToHash(recordStr)
@@ -35,12 +43,27 @@ def initialize
3543
super
3644
@fields_to_mask = []
3745
@fields_to_mask_regex = {}
46+
@fields_to_mask_keys = {}
47+
@fieldsToExcludeJSONPathsArray = []
3848
end
3949

4050
# this method only called ones (on startup time)
4151
def configure(conf)
4252
super
4353
fieldsToMaskFilePath = conf['fieldsToMaskFilePath']
54+
fieldsToExcludeJSONPaths = conf['fieldsToExcludeJSONPaths']
55+
56+
if fieldsToExcludeJSONPaths != nil && fieldsToExcludeJSONPaths.size() > 0
57+
fieldsToExcludeJSONPaths.split(",").each do | field |
58+
# To save splits we'll save the path as an array
59+
splitArray = field.split(".")
60+
symArray = []
61+
splitArray.each do | pathPortion |
62+
symArray.push(pathPortion.to_sym)
63+
end
64+
@fieldsToExcludeJSONPathsArray.push(symArray)
65+
end
66+
end
4467

4568
File.open(fieldsToMaskFilePath, "r") do |f|
4669
f.each_line do |line|
@@ -54,10 +77,12 @@ def configure(conf)
5477
hashObjectRegex = Regexp.new(/(?::#{value}=>")(.*?)(?:")/m) # mask element in hash object
5578
hashObjectRegexStringReplacement = ":#{value}=>\"#{MASK_STRING}\""
5679
@fields_to_mask_regex[hashObjectRegex] = hashObjectRegexStringReplacement
80+
@fields_to_mask_keys[hashObjectRegex] = value
5781

5882
innerJSONStringRegex = Regexp.new(/(\\+)"#{value}\\+":\\+.+?((?=(})|,( *|)(\s|\\+)\")|(?=}"$))/m) # mask element in json string using capture groups that count the level of escaping inside the json string
5983
innerJSONStringRegexStringReplacement = "\\1\"#{value}\\1\":\\1\"#{MASK_STRING}\\1\""
6084
@fields_to_mask_regex[innerJSONStringRegex] = innerJSONStringRegexStringReplacement
85+
@fields_to_mask_keys[innerJSONStringRegex] = value
6186
end
6287
end
6388

lib/fluent/plugin/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module FilterMasking
2-
VERSION = "1.0.8"
2+
VERSION = "1.1.0"
33
end

test/test_filter_masking.rb

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ def setup
1717
# default configuration for tests
1818
CONFIG = %[
1919
fieldsToMaskFilePath test/fields-to-mask
20+
fieldsToExcludeJSONPaths excludedField,exclude.path.nestedExcludedField
21+
]
22+
23+
# configuration for tests without exclude parameter
24+
CONFIG_NO_EXCLUDE = %[
25+
fieldsToMaskFilePath test/fields-to-mask
2026
]
2127

2228
def create_driver(conf = CONFIG)
@@ -35,7 +41,7 @@ def filter(config, messages)
3541

3642
sub_test_case 'plugin will mask all fields that need masking' do
3743
test 'mask field in hash object' do
38-
conf = CONFIG
44+
conf = CONFIG_NO_EXCLUDE
3945
messages = [
4046
{:not_masked_field=>"mickey-the-dog", :email=>"mickey-the-dog@zooz.com"}
4147
]
@@ -93,5 +99,38 @@ def filter(config, messages)
9399
filtered_records = filter(conf, messages)
94100
assert_equal(expected, filtered_records)
95101
end
102+
test 'mask field in hash object with exclude' do
103+
conf = CONFIG
104+
messages = [
105+
{:not_masked_field=>"mickey-the-dog", :email=>"mickey-the-dog@zooz.com", :first_name=>"Micky", :excludedField=>"first_name"}
106+
]
107+
expected = [
108+
{:not_masked_field=>"mickey-the-dog", :email=>MASK_STRING, :first_name=>"Micky", :excludedField=>"first_name"}
109+
]
110+
filtered_records = filter(conf, messages)
111+
assert_equal(expected, filtered_records)
112+
end
113+
test 'mask field in hash object with nested exclude' do
114+
conf = CONFIG
115+
messages = [
116+
{:not_masked_field=>"mickey-the-dog", :last_name=>"the dog", :email=>"mickey-the-dog@zooz.com", :first_name=>"Micky", :exclude=>{:path=>{:nestedExcludedField=>"first_name,last_name"}}}
117+
]
118+
expected = [
119+
{:not_masked_field=>"mickey-the-dog", :last_name=>"the dog", :email=>MASK_STRING, :first_name=>"Micky", :exclude=>{:path=>{:nestedExcludedField=>"first_name,last_name"}}}
120+
]
121+
filtered_records = filter(conf, messages)
122+
assert_equal(expected, filtered_records)
123+
end
124+
test 'mask field in hash object with base and nested exclude' do
125+
conf = CONFIG
126+
messages = [
127+
{:not_masked_field=>"mickey-the-dog", :last_name=>"the dog", :email=>"mickey-the-dog@zooz.com", :first_name=>"Micky", :excludedField=>"first_name", :exclude=>{:path=>{:nestedExcludedField=>"last_name"}}}
128+
]
129+
expected = [
130+
{:not_masked_field=>"mickey-the-dog", :last_name=>"the dog", :email=>MASK_STRING, :first_name=>"Micky", :excludedField=>"first_name", :exclude=>{:path=>{:nestedExcludedField=>"last_name"}}}
131+
]
132+
filtered_records = filter(conf, messages)
133+
assert_equal(expected, filtered_records)
134+
end
96135
end
97136
end

0 commit comments

Comments
 (0)