fluent-plugin-anonymizer 0.4.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a1fc717a156c6b19fea2d19f4522c3b2d8be7dda
4
+ data.tar.gz: 9c7ff4fda067571c4becc5f29c5be0a5452e9653
5
+ SHA512:
6
+ metadata.gz: bc870e37f4a1b30b543d53497208e9af52ac1c901bc77ec32c1bb772fb30d9cd4a8ebd2eb1782550757fd4763c93e84248c8e2f2c46826e3c3b688113e1f6a5c
7
+ data.tar.gz: 0bcc4eaecfceb6f5a00a14d587bd81f77efe17d8500384d73bee0d8f4e63c0e92897fb81aaa7e5809362fdbaf2cf329a85b0361d085ffdae55ab3450993064f7
data/.travis.yml CHANGED
@@ -1,7 +1,15 @@
1
+ sudo: false
1
2
  language: ruby
2
3
 
3
4
  rvm:
5
+ - 2.4.0
6
+ - 2.3.3
4
7
  - 2.2
5
8
  - 2.1
6
- - 2.0.0
7
- - 1.9.3
9
+
10
+ # to avoid travis-ci issue since 2015-12-25
11
+ before_install:
12
+ - gem update bundler
13
+
14
+ gemfile:
15
+ - Gemfile
data/Appraisals ADDED
@@ -0,0 +1,7 @@
1
+ appraise "fluentd v0.10" do
2
+ gem "fluentd", "~> 0.10.0"
3
+ end
4
+
5
+ appraise "fluentd v0.12" do
6
+ gem "fluentd", "~> 0.12.0"
7
+ end
data/README.md CHANGED
@@ -2,37 +2,52 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- Fluentd filter output plugin to anonymize records with [OpenSSL::HMAC](http://docs.ruby-lang.org/ja/1.9.3/class/OpenSSL=3a=3aHMAC.html) of MD5/SHA1/SHA256/SHA384/SHA512 algorithms. This data masking plugin protects privacy data such as UserID, Email, Phone number, IPv4/IPv6 address and so on.
5
+ Fluentd filter output plugin to anonymize records with [OpenSSL::Digest](https://docs.ruby-lang.org/ja/latest/class/OpenSSL=3a=3aDigest.html) of MD5/SHA1/SHA256/SHA384/SHA512 algorithms. This data masking plugin protects privacy data such as UserID, Email, Phone number, IPv4/IPv6 address and so on.
6
+
7
+ ## Requirements
8
+
9
+ | fluent-plugin-anonymizer | fluentd | ruby |
10
+ |--------------------|------------|--------|
11
+ | 1.0.0 | v0.14.x | >= 2.1 |
12
+ | 0.5.1 | v0.12.x | >= 1.9 |
13
+
6
14
 
7
15
  ## Installation
8
16
 
9
- install with gem or fluent-gem command as:
17
+ install with gem or td-agent-gem command as:
10
18
 
11
19
  `````
12
- ### native gem
13
- gem install fluent-plugin-anonymizer
20
+ # for system installed fluentd
21
+ $ gem install fluent-plugin-anonymizer
14
22
 
15
- ### td-agent gem
16
- /usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-anonymizer
23
+ # for td-agent2 (with fluentd v0.12)
24
+ $ sudo td-agent-gem install fluent-plugin-anonymizer -v 0.5.1
25
+
26
+ # for td-agent3 (with fluentd v0.14)
27
+ $ sudo td-agent-gem install fluent-plugin-anonymizer -v 1.0.0
17
28
  `````
18
29
 
30
+ For more details, see [Plugin Management](https://docs.fluentd.org/v0.14/articles/plugin-management)
31
+
19
32
  ## Tutorial
20
33
 
21
- ### Output Plugin
34
+ ### Filter Plugin
22
35
 
23
36
  #### configuration
24
37
 
25
- It is a sample to hash record with sha1 for `user_id`, `member_id` and `mail`. For IP address, auto-detecting IPv4/IPv6 and rounding number with 24bit(IPv4) or 104bit(IPv6) netmask using `ipaddr_mask_keys` and `ipv4_mask_subnet`, `ipv6_mask_subnet` option.
26
-
27
- `````
38
+ ```text
28
39
  <source>
29
- type forward
30
- port 24224
40
+ @type dummy
41
+ tag raw.dummy
42
+ dummy [
43
+ {"host":"10.102.3.80","member_id":"12345", "mail":"example@example.com"},
44
+ {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
45
+ ]
31
46
  </source>
32
47
 
33
- <match test.message>
34
- type anonymizer
35
-
48
+ <filter raw.**>
49
+ @type anonymizer
50
+
36
51
  # Specify hashing keys with comma
37
52
  sha1_keys user_id, member_id, mail
38
53
 
@@ -43,45 +58,40 @@ It is a sample to hash record with sha1 for `user_id`, `member_id` and `mail`. F
43
58
  ipaddr_mask_keys host
44
59
  ipv4_mask_subnet 24
45
60
  ipv6_mask_subnet 104
46
-
47
- # Set tag rename pattern
48
- remove_tag_prefix test.
49
- add_tag_prefix anonymized.
50
- </match>
61
+ </filter>
51
62
 
52
- <match anonymized.message>
53
- type stdout
63
+ <match raw.**>
64
+ @type stdout
54
65
  </match>
55
- `````
66
+ ```
56
67
 
57
68
  #### result
58
69
 
59
- `````
60
- $ echo '{"host":"10.102.3.80","member_id":"12345", "mail":"example@example.com"}' | fluent-cat test.message
61
- $ echo '{"host":"2001:db8:0:8d3:0:8a2e:70:7344","member_id":"12345", "mail":"example@example.com"}' | fluent-cat test.message
70
+ This sample result has made with the above configuration into "fluent.conf".
62
71
 
63
- $ tail -f /var/log/td-agent/td-agent.log
64
- 2014-01-06 18:30:21 +0900 anonymized.message: {"host":"10.102.3.0","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
65
- 2014-01-06 18:30:22 +0900 anonymized.message: {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
66
- `````
72
+ ```text
73
+ $ fluentd -c fluent.conf
74
+ 2017-02-27 22:59:18.070132000 +0900 raw.dummy: {"host":"10.102.3.0","member_id":"5ab2cebb0537866c4a0cd2e2f3502c0976b788da","mail":"7e9d6dbefa72d56056c8c740b34b5c0bbfec8d87"}
75
+ 2017-02-27 22:59:19.079251000 +0900 raw.dummy: {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"445514dfcd82b2a8b94ec6763afa6e349e78c5f8","mail":"54608576c8d815a4ffd595a3c1fe72751ed04424"}
76
+ 2017-02-27 22:59:20.086747000 +0900 raw.dummy: {"host":"10.102.3.0","member_id":"b14a8f98019ec84c6fe329d5af62c46bb45348f8","mail":"723da8084da3438d9287b44e5a714b70e10a9755"}
77
+ 2017-02-27 22:59:21.094767000 +0900 raw.dummy: {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"d38ebb9b96c0cbffd4136935c7f6fe9dd05980cd","mail":"b6f9d777831cbecfd2ea806f5f62f79a275bbb82"}
78
+ ```
67
79
 
68
- ### Filter Plugin
80
+ ### Output Plugin
69
81
 
70
82
  #### configuration
71
83
 
72
- ```text
84
+ It is a sample to hash record with sha1 for `user_id`, `member_id` and `mail`. For IP address, auto-detecting IPv4/IPv6 and rounding number with 24bit(IPv4) or 104bit(IPv6) netmask using `ipaddr_mask_keys` and `ipv4_mask_subnet`, `ipv6_mask_subnet` option.
85
+
86
+ `````
73
87
  <source>
74
- type dummy
75
- tag raw.dummy
76
- dummy [
77
- {"host":"10.102.3.80","member_id":"12345", "mail":"example@example.com"},
78
- {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
79
- ]
88
+ @type forward
89
+ port 24224
80
90
  </source>
81
91
 
82
- <filter raw.**>
83
- type anonymizer
84
-
92
+ <match test.message>
93
+ @type anonymizer
94
+
85
95
  # Specify hashing keys with comma
86
96
  sha1_keys user_id, member_id, mail
87
97
 
@@ -92,18 +102,27 @@ $ tail -f /var/log/td-agent/td-agent.log
92
102
  ipaddr_mask_keys host
93
103
  ipv4_mask_subnet 24
94
104
  ipv6_mask_subnet 104
95
- </filter>
105
+
106
+ # Set tag rename pattern
107
+ tag anonymized.${tag}
108
+ remove_tag_prefix test.
109
+ </match>
96
110
 
97
- <match raw.**>
98
- type stdout
111
+ <match anonymized.message>
112
+ @type stdout
99
113
  </match>
100
- ```
114
+ `````
101
115
 
102
116
  #### result
103
117
 
104
- ```text
105
- $ fluentd -c fluent.conf
106
- ```
118
+ `````
119
+ $ echo '{"host":"10.102.3.80","member_id":"12345", "mail":"example@example.com"}' | fluent-cat test.message
120
+ $ echo '{"host":"2001:db8:0:8d3:0:8a2e:70:7344","member_id":"12345", "mail":"example@example.com"}' | fluent-cat test.message
121
+
122
+ $ tail -f /var/log/td-agent/td-agent.log
123
+ 2014-01-06 18:30:21 +0900 anonymized.message: {"host":"10.102.3.0","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
124
+ 2014-01-06 18:30:22 +0900 anonymized.message: {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
125
+ `````
107
126
 
108
127
  ## Parameters
109
128
 
@@ -132,6 +151,12 @@ Round number for following one or more keys. It makes easy to aggregate calculat
132
151
  * include_tag_key (default: false)
133
152
  * tag_key
134
153
 
154
+ set one or more option are required for editing tag name using HandleTagNameMixin.
155
+
156
+ * tag
157
+
158
+ In the case of using this option [like 'tag anonymized.${tag}' with tag placeholder](https://github.com/y-ken/fluent-plugin-anonymizer/blob/master/test/plugin/test_out_anonymizer.rb#L179), tag will be modified after these options affected. which are remove_tag_prefix, remove_tag_suffix, add_tag_prefix and add_tag_suffix.
159
+
135
160
  Add original tag name into filtered record using SetTagKeyMixin.
136
161
 
137
162
  * remove_tag_prefix
@@ -139,12 +164,6 @@ Add original tag name into filtered record using SetTagKeyMixin.
139
164
  * add_tag_prefix
140
165
  * add_tag_suffix
141
166
 
142
- set one or more option are required for editing tag name using HandleTagNameMixin.
143
-
144
- * tag
145
-
146
- On using this option [like 'tag anonymized.${tag}' with tag placeholder](https://github.com/y-ken/fluent-plugin-anonymizer/blob/master/test/plugin/test_out_anonymizer.rb#L153), it will be overwrite after these options affected. which are remove_tag_prefix, remove_tag_suffix, add_tag_prefix and add_tag_suffix.
147
-
148
167
  ## Notes
149
168
 
150
169
  * hashing nested value behavior is compatible with [LogStash::Filters::Anonymize](https://github.com/logstash/logstash/blob/master/lib/logstash/filters/anonymize.rb) does. For further details, please check it out the test code at [test_emit_nest_value](https://github.com/y-ken/fluent-plugin-anonymizer/blob/master/test/plugin/test_out_anonymizer.rb#L91).
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "fluent-plugin-anonymizer"
7
- spec.version = "0.4.1"
7
+ spec.version = "1.0.0"
8
8
  spec.authors = ["Kentaro Yoshida"]
9
9
  spec.email = ["y.ken.studio@gmail.com"]
10
10
  spec.summary = %q{Fluentd filter output plugin to anonymize records with HMAC of MD5/SHA1/SHA256/SHA384/SHA512 algorithms. This data masking plugin protects privacy data such as UserID, Email, Phone number, IPv4/IPv6 address and so on.}
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.add_development_dependency "bundler"
20
20
  spec.add_development_dependency "rake"
21
21
  spec.add_development_dependency "test-unit", "~> 3"
22
+ spec.add_development_dependency "appraisal"
22
23
  spec.add_runtime_dependency "fluentd"
23
24
  spec.add_runtime_dependency "fluent-mixin-rewrite-tag-name"
24
25
  end
@@ -1,3 +1,4 @@
1
+ require 'fluent/config/error'
1
2
  require 'openssl'
2
3
  require 'ipaddr'
3
4
 
@@ -79,7 +80,7 @@ module Fluent
79
80
  def anonymize_value(message, algorithm, salt)
80
81
  case algorithm
81
82
  when 'md5','sha1','sha256','sha384','sha512'
82
- OpenSSL::HMAC.hexdigest(DIGEST[algorithm].call, salt, message.to_s)
83
+ DIGEST[algorithm].call.update(salt).update(message.to_s).hexdigest
83
84
  when 'ipaddr_mask'
84
85
  address = IPAddr.new(message)
85
86
  subnet = address.ipv4? ? @ipv4_mask_subnet : @ipv6_mask_subnet
@@ -1,26 +1,290 @@
1
- module Fluent
1
+ require 'fluent/plugin/filter'
2
+ require 'openssl'
3
+ require 'uri'
4
+ require 'ipaddr'
5
+
6
+ # <filter **>
7
+ # @type mask
8
+ # # salts will be selected for field names in a deterministic way
9
+ # salt secret_salt # different salt for each fields?
10
+ # salt salt_brabra
11
+ # salts ["s1","s2","s3","s4"]
12
+ # <mask sha1>
13
+ # # key user_id
14
+ # keys ["user_id","session_id","source_ip"]
15
+ # key_pattern ^(source|src)_?ip_?(addr|address)?$
16
+ # value_pattern @mydomain\.example\.com$
17
+ # value_in_subnet 192.168.0.0/16 # naming?
18
+ # </mask>
19
+ # <mask uri_path>
20
+ # keys ["url","uri"]
21
+ # # or key_pattern
22
+ # </mask>
23
+ # <mask network>
24
+ # keys ["dest","destination","dest_ip"]
25
+ # # or key_pattern
26
+ # ipv4_mask_bits 24
27
+ # ipv6_mask_bits 104
28
+ # </mask>
29
+ # </filter>
30
+
31
+ module Fluent::Plugin
2
32
  class AnonymizerFilter < Filter
3
- Plugin.register_filter('anonymizer', self)
33
+ Fluent::Plugin.register_filter('anonymizer', self)
4
34
 
5
- config_param :tag, :string, :default => nil
6
- config_param :hash_salt, :string, :default => ''
7
- config_param :ipv4_mask_subnet, :integer, :default => 24
8
- config_param :ipv6_mask_subnet, :integer, :default => 104
35
+ MASK_METHODS = {
36
+ md5: ->(opts){ ->(v,salt){ OpenSSL::Digest.new("md5").update(salt).update(v.to_s).hexdigest } },
37
+ sha1: ->(opts){ ->(v,salt){ OpenSSL::Digest.new("sha1").update(salt).update(v.to_s).hexdigest } },
38
+ sha256: ->(opts){ ->(v,salt){ OpenSSL::Digest.new("sha256").update(salt).update(v.to_s).hexdigest } },
39
+ sha384: ->(opts){ ->(v,salt){ OpenSSL::Digest.new("sha384").update(salt).update(v.to_s).hexdigest } },
40
+ sha512: ->(opts){ ->(v,salt){ OpenSSL::Digest.new("sha512").update(salt).update(v.to_s).hexdigest } },
41
+ uri_path: ->(opts){ ->(v,salt){
42
+ begin
43
+ uri = URI.parse(v)
44
+ if uri.absolute?
45
+ uri.path = '/'
46
+ uri.user = uri.password = uri.query = uri.fragment = nil
47
+ end
48
+ uri.to_s
49
+ rescue
50
+ v
51
+ end
52
+ } },
53
+ network: ->(opts){ ->(v,salt){
54
+ begin
55
+ addr = IPAddr.new(v)
56
+ if addr.ipv4? && opts.ipv4_mask_bits
57
+ addr.mask(opts.ipv4_mask_bits).to_s
58
+ elsif addr.ipv6? && opts.ipv6_mask_bits
59
+ addr.mask(opts.ipv6_mask_bits).to_s
60
+ else
61
+ addr.to_s
62
+ end
63
+ rescue
64
+ v
65
+ end
66
+ } },
67
+ }
9
68
 
10
- config_set_default :include_tag_key, false
69
+ config_param :salt, :string, default: nil
70
+ config_param :salts, :array, default: nil
71
+ config_section :mask, param_name: :mask_config_list, required: false, multi: true do
72
+ config_argument :method, :enum, list: MASK_METHODS.keys
73
+ config_param :salt, :string, default: nil
74
+
75
+ config_param :key, :string, default: nil
76
+ config_param :keys, :array, default: []
77
+ config_param :key_chain, :string, default: nil # for.nested.key
78
+ config_param :key_chains, :array, default: [] # ["for.nested.key","can.be.specified","twice.or.more"]
79
+ config_param :key_pattern, :string, default: nil
80
+ config_param :value_pattern, :string, default: nil
81
+ config_param :value_in_subnet, :string, default: nil # 192.168.0.0/24
82
+
83
+ config_param :mask_array_elements, :bool, default: false
84
+ config_param :ipv4_mask_bits, :integer, default: nil
85
+ config_param :ipv6_mask_bits, :integer, default: nil
86
+ end
87
+
88
+ # obsolete configuration parameters
89
+ config_param :md5_keys, :string, default: nil
90
+ config_param :sha1_keys, :string, default: nil
91
+ config_param :sha256_keys, :string, default: nil
92
+ config_param :sha384_keys, :string, default: nil
93
+ config_param :sha512_keys, :string, default: nil
94
+ config_param :hash_salt, :string, default: nil
95
+ config_param :ipaddr_mask_keys, :string, default: nil
96
+ config_param :ipv4_mask_subnet, :integer, default: 24
97
+ config_param :ipv6_mask_subnet, :integer, default: 104
11
98
 
12
99
  def initialize
13
100
  super
14
- require 'fluent/plugin/anonymizer'
101
+ @salt_list = []
102
+ @salt_map = {}
103
+ @conversions = []
15
104
  end
16
105
 
17
106
  def configure(conf)
18
107
  super
19
- @anonymizer = Anonymizer.new(self, conf)
108
+
109
+ salt_missing = false
110
+
111
+ @salt_list << @salt
112
+ @salt_list += @salts if @salts
113
+
114
+ @masks = []
115
+ @mask_config_list.each do |c|
116
+ unless c.salt || @salt_list.size > 0
117
+ salt_missing = true
118
+ end
119
+
120
+ conv = MASK_METHODS[c.method].call(c)
121
+ [c.key || nil, *c.keys].compact.each do |key|
122
+ @masks << masker_for_key(conv, key, c)
123
+ end
124
+ [c.key_chain || nil, *c.key_chains].compact.each do |key_chain|
125
+ @masks << masker_for_key_chain(conv, key_chain.split('.'), c)
126
+ end
127
+ @masks << masker_for_key_pattern(conv, c.key_pattern, c) if c.key_pattern
128
+ @masks << masker_for_value_pattern(conv, c.value_pattern, c) if c.value_pattern
129
+ @masks << masker_for_value_in_subnet(conv, c.value_in_subnet, c) if c.value_in_subnet
130
+ end
131
+
132
+ # obsolete option handling
133
+ [[@md5_keys,:md5],[@sha1_keys,:sha1],[@sha256_keys,:sha256],[@sha384_keys,:sha384],[@sha512_keys,:sha512]].each do |param,m|
134
+ next unless param
135
+ @salt_list << (@hash_salt || '') if @salt_list.empty? # to suppress ConfigError for salt missing
136
+ conf = OpenStruct.new
137
+ conf.salt = @hash_salt || ''
138
+ conf.mask_array_elements = true
139
+ conv = MASK_METHODS[m].call(conf)
140
+ param.split(',').map(&:strip).each do |key|
141
+ if key.include?('.')
142
+ @masks << masker_for_key_chain(conv, key.split('.'), conf)
143
+ else
144
+ @masks << masker_for_key(conv, key, conf)
145
+ end
146
+ end
147
+ end
148
+ if @ipaddr_mask_keys
149
+ @salt_list << (@hash_salt || '') if @salt_list.empty? # to suppress ConfigError for salt missing
150
+ conf = OpenStruct.new
151
+ conf.salt = @hash_salt || ''
152
+ conf.mask_array_elements = true
153
+ conf.ipv4_mask_bits = @ipv4_mask_subnet
154
+ conf.ipv6_mask_bits = @ipv6_mask_subnet
155
+ conv = MASK_METHODS[:network].call(conf)
156
+ @ipaddr_mask_keys.split(',').map(&:strip).each do |key|
157
+ if key.include?('.')
158
+ @masks << masker_for_key_chain(conv, key.split('.'), conf)
159
+ else
160
+ @masks << masker_for_key(conv, key, conf)
161
+ end
162
+ end
163
+ end
164
+
165
+ if @masks.size < 1
166
+ raise Fluent::ConfigError, "no anonymizing operations configured"
167
+ end
168
+ if salt_missing
169
+ raise Fluent::ConfigError, "salt (or salts) required, but missing"
170
+ end
20
171
  end
21
172
 
22
173
  def filter(tag, time, record)
23
- record = @anonymizer.anonymize(record)
174
+ record.update(@masks.reduce(record){|r,mask| mask.call(r)})
175
+ end
176
+
177
+ def salt_determine(key)
178
+ return @salt_map[key] if @salt_map.has_key?(key)
179
+ keystr = key.to_s
180
+ if keystr.empty?
181
+ @salt_map[key] = @salt_list[0]
182
+ else
183
+ @salt_map[key] = @salt_list[(keystr[0].ord + keystr[-1].ord) % @salt_list.size]
184
+ end
185
+ @salt_map[key]
186
+ end
187
+
188
+ def mask_value(value, for_each)
189
+ if for_each && value.is_a?(Array)
190
+ value.map{|v|
191
+ yield v
192
+ }
193
+ else
194
+ yield value
195
+ end
196
+ end
197
+
198
+ def masker_for_key(conv, key, opts)
199
+ for_each = opts.mask_array_elements
200
+ salt = opts.salt || salt_determine(key)
201
+ if for_each
202
+ ->(record){
203
+ begin
204
+ if record.has_key?(key)
205
+ record[key] = mask_value(record[key], for_each){|v| conv.call(v, salt) }
206
+ end
207
+ rescue => e
208
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
209
+ end
210
+ record
211
+ }
212
+ else
213
+ ->(record){
214
+ begin
215
+ if record.has_key?(key)
216
+ record[key] = conv.call(record[key], salt)
217
+ end
218
+ rescue => e
219
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
220
+ end
221
+ record
222
+ }
223
+ end
224
+ end
225
+
226
+ def masker_for_key_chain(conv, key_chain, opts)
227
+ for_each = opts.mask_array_elements
228
+ heading = key_chain[0..-2]
229
+ container_fetcher = ->(record){ heading.reduce(record){|r,c| r && r.has_key?(c) ? r[c] : nil } }
230
+ tailing = key_chain[-1]
231
+ ->(record){
232
+ begin
233
+ container = container_fetcher.call(record)
234
+ if container && container.has_key?(tailing)
235
+ container[tailing] = mask_value(container[tailing], for_each){|v| conv.call(v, opts.salt || salt_determine(tailing)) }
236
+ end
237
+ rescue => e
238
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
239
+ end
240
+ record
241
+ }
242
+ end
243
+
244
+ def masker_for_key_pattern(conv, pattern, opts)
245
+ for_each = opts.mask_array_elements
246
+ regexp = Regexp.new(pattern)
247
+ ->(record){
248
+ begin
249
+ record.each_pair do |key, value|
250
+ next unless (regexp =~ key.to_s rescue nil)
251
+ record[key] = mask_value(record[key], for_each){|v| conv.call(v, opts.salt || salt_determine(key)) }
252
+ end
253
+ rescue => e
254
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
255
+ end
256
+ record
257
+ }
258
+ end
259
+
260
+ def masker_for_value_pattern(conv, pattern, opts)
261
+ regexp = Regexp.new(pattern)
262
+ ->(record){
263
+ begin
264
+ record.each_pair do |key, value|
265
+ next unless (regexp =~ value.to_s rescue nil)
266
+ record[key] = conv.call(value, opts.salt || salt_determine(key))
267
+ end
268
+ rescue => e
269
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
270
+ end
271
+ record
272
+ }
273
+ end
274
+
275
+ def masker_for_value_in_subnet(conv, network_str, opts)
276
+ network = IPAddr.new(network_str)
277
+ ->(record){
278
+ begin
279
+ record.each_pair do |key, value|
280
+ next unless (network.include?(value) rescue nil)
281
+ record[key] = conv.call(value, opts.salt || salt_determine(key))
282
+ end
283
+ rescue => e
284
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
285
+ end
286
+ record
287
+ }
24
288
  end
25
289
  end
26
290
  end
@@ -13,10 +13,17 @@ class Fluent::AnonymizerOutput < Fluent::Output
13
13
  define_method("router") { Fluent::Engine }
14
14
  end
15
15
 
16
- config_param :tag, :string, :default => nil
17
- config_param :hash_salt, :string, :default => ''
18
- config_param :ipv4_mask_subnet, :integer, :default => 24
19
- config_param :ipv6_mask_subnet, :integer, :default => 104
16
+ config_param :tag, :string, :default => nil,
17
+ :desc => 'The output tag.'
18
+ config_param :hash_salt, :string, :default => '',
19
+ :desc => <<-DESC
20
+ This salt affects for md5_keys sha1_keys sha256_keys sha384_keys sha512_keys settings.
21
+ It is recommend to set a hash salt to prevent rainbow table attacks.
22
+ DESC
23
+ config_param :ipv4_mask_subnet, :integer, :default => 24,
24
+ :desc => 'Anonymize ipv4 addresses by subnet mask.'
25
+ config_param :ipv6_mask_subnet, :integer, :default => 104,
26
+ :desc => 'Anonymize ipv6 addresses by subnet mask.'
20
27
 
21
28
  include Fluent::HandleTagNameMixin
22
29
  include Fluent::Mixin::RewriteTagName
@@ -29,6 +36,7 @@ class Fluent::AnonymizerOutput < Fluent::Output
29
36
  end
30
37
 
31
38
  def configure(conf)
39
+ log.warn "out_anonymizer is now deprecated. It will be removed in a future release. Please consider to use filter_anonymizer."
32
40
  super
33
41
  @anonymizer = Fluent::Anonymizer.new(self, conf)
34
42
  end
data/test/helper.rb CHANGED
@@ -23,7 +23,10 @@ unless ENV.has_key?('VERBOSE')
23
23
  end
24
24
 
25
25
  require 'fluent/plugin/out_anonymizer'
26
- require 'fluent/plugin/filter_anonymizer'
26
+ if Fluent.const_defined?(:Filter)
27
+ require 'fluent/plugin/filter_anonymizer'
28
+ end
29
+
27
30
 
28
31
  class Test::Unit::TestCase
29
32
  end
@@ -1,7 +1,9 @@
1
1
  require 'helper'
2
+ require 'fluent/test/driver/filter'
2
3
 
3
4
  class AnonymizerFilterTest < Test::Unit::TestCase
4
5
  def setup
6
+ omit_unless(Fluent.const_defined?(:Filter))
5
7
  Fluent::Test.setup
6
8
  @time = Fluent::Engine.now
7
9
  end
@@ -17,19 +19,66 @@ class AnonymizerFilterTest < Test::Unit::TestCase
17
19
  ipv4_mask_subnet 24
18
20
  ]
19
21
 
20
- def create_driver(conf=CONFIG, tag='test')
21
- Fluent::Test::FilterTestDriver.new(Fluent::AnonymizerFilter, tag).configure(conf)
22
+ def create_driver(conf=CONFIG)
23
+ Fluent::Test::Driver::Filter.new(Fluent::Plugin::AnonymizerFilter).configure(conf)
22
24
  end
23
25
 
24
26
  def filter(conf, messages)
25
27
  d = create_driver(conf)
26
- d.run {
28
+ d.run(default_tag: 'test') {
27
29
  messages.each {|message|
28
- d.filter(message, @time)
30
+ d.feed(@time, message)
29
31
  }
30
32
  }
31
- filtered = d.filtered_as_array
32
- filtered.map {|m| m[2] }
33
+ filtered = d.filtered
34
+ filtered.map {|m| m.last }
35
+ end
36
+
37
+ require 'ostruct'
38
+ test 'method md5 works correctly' do
39
+ conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:md5].call(OpenStruct.new)
40
+ digest = conv.call('value1', 'salt')
41
+ assert_equal 'd21fe9523421f12daad064fd082913fd', digest
42
+ end
43
+ test 'method sha1 works correctly' do
44
+ conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:sha1].call(OpenStruct.new)
45
+ digest = conv.call('value2', 'salt')
46
+ assert_equal 'd2ed8e797065322371012fd8c1a39682987ddb71', digest
47
+ end
48
+ test 'method sha256 works correctly' do
49
+ conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:sha256].call(OpenStruct.new)
50
+ digest = conv.call('value3', 'salt')
51
+ assert_equal 'd70daf9654b8a3ba335f8f9f9638a93e8eba6763a0012ac44a928857871abe82', digest
52
+ end
53
+ test 'method sha384 works correctly' do
54
+ conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:sha384].call(OpenStruct.new)
55
+ digest = conv.call('value4', 'salt')
56
+ assert_equal '646192f8b1ea905238df589a00a10598a53eb245df4ab14b7e9eccf80c37386c99abe5259ccb2ba950003423fa0790ee', digest
57
+ end
58
+ test 'method sha512 works correctly' do
59
+ conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:sha512].call(OpenStruct.new)
60
+ digest = conv.call('value5', 'salt')
61
+ expected = '47c82bfea3783c20e3ba3629f0f827bebf0fa65a9104ada5339e5776e5958f061fe7114bfbe1e9d410aff43c6bee8365adf4fdd072e54ab4fffad820f354f545'
62
+ assert_equal expected, digest
63
+ end
64
+ test 'method uri_path removes path, query parameters, fragment, user and password of uri strings' do
65
+ conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:uri_path].call(OpenStruct.new)
66
+ assert_equal '/my/path', conv.call('/my/path', '')
67
+ assert_equal 'yay/unknown/format', conv.call('yay/unknown/format', '')
68
+ assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret', '')
69
+ assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret?a=b', '')
70
+ assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret#xxx', '')
71
+ assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret?a=b#xxx', '')
72
+ assert_equal 'http://example.com/', conv.call('http://tagomoris:secret!@example.com/', '')
73
+ assert_equal 'http://example.com/', conv.call('http://tagomoris:secret!@example.com/?a=b#xxx', '')
74
+ assert_equal 'http://example.com/', conv.call('http://tagomoris:secret!@example.com/path/to/secret?a=b#xxx', '')
75
+ end
76
+ test 'method network masks ipaddresses with specified mask bit lengths' do
77
+ conf = OpenStruct.new(ipv4_mask_bits: 24, ipv6_mask_bits: 104)
78
+ conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:network].call(conf)
79
+ assert_equal '192.168.1.0', conv.call('192.168.1.1', '')
80
+ assert_equal '10.110.18.0', conv.call('10.110.18.9', '')
81
+ assert_equal '2001:db8:0:8d3:0:8a2e::', conv.call('2001:db8:0:8d3:0:8a2e:70:7344', '')
33
82
  end
34
83
 
35
84
  def test_configure
@@ -43,6 +92,91 @@ class AnonymizerFilterTest < Test::Unit::TestCase
43
92
  assert_equal 'test_salt_string', d.instance.config['hash_salt']
44
93
  end
45
94
 
95
+ test 'masker_for_key generates a lambda for conversion with exact key match' do
96
+ conf = OpenStruct.new(salt: 's')
97
+ plugin = create_driver.instance
98
+ conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
99
+ masker = plugin.masker_for_key(conv, 'kkk', conf)
100
+ r = masker.call({"k" => "x", "kk" => "xx", "kkk" => "xxx", "kkkk" => "xxxx"})
101
+ assert_equal "x", r["k"]
102
+ assert_equal "xx", r["kk"]
103
+ assert_equal "XXX:s", r["kkk"]
104
+ assert_equal "xxxx", r["kkkk"]
105
+ assert_equal 4, r.size
106
+ end
107
+
108
+ test 'masker_for_key_chain generates a lambda for conversion with recursive key fetching' do
109
+ conf = OpenStruct.new(salt: 's')
110
+ plugin = create_driver.instance
111
+ conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
112
+ masker = plugin.masker_for_key_chain(conv, 'a.b.c'.split('.'), conf)
113
+ event = {
114
+ 'a' => {
115
+ 'b' => { 'c' => 'v', 'd' => 'v' }
116
+ }
117
+ }
118
+ r = masker.call(event)
119
+ assert_equal 'V:s', r['a']['b']['c']
120
+ assert_equal 'v', r['a']['b']['d']
121
+ end
122
+
123
+ test 'masker_for_key_pattern generates a lambda for conversion with key pattern match' do
124
+ conf = OpenStruct.new(salt: 's')
125
+ plugin = create_driver.instance
126
+ conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
127
+ masker = plugin.masker_for_key_pattern(conv, '^k+$', conf)
128
+ r = masker.call({"k" => "x", "kk" => "xx", "kkk" => "xxx", "kkk0" => "xxxx", "f" => "x", "ff" => "xx"})
129
+ assert_equal "X:s", r["k"]
130
+ assert_equal "XX:s", r["kk"]
131
+ assert_equal "XXX:s", r["kkk"]
132
+ assert_equal "xxxx", r["kkk0"]
133
+ assert_equal "x", r["f"]
134
+ assert_equal "xx", r["ff"]
135
+ assert_equal 6, r.size
136
+ end
137
+
138
+ test 'masker_for_value_pattern' do
139
+ conf = OpenStruct.new(salt: 's')
140
+ plugin = create_driver.instance
141
+ conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
142
+ masker = plugin.masker_for_value_pattern(conv, '^x+$', conf)
143
+ r = masker.call({"k" => "x", "kk" => "x0", "kkk" => "xx0", "kkk0" => "xxxx", "f" => "x", "ff" => "xx"})
144
+ assert_equal "X:s", r["k"]
145
+ assert_equal "x0", r["kk"]
146
+ assert_equal "xx0", r["kkk"]
147
+ assert_equal "XXXX:s", r["kkk0"]
148
+ assert_equal "X:s", r["f"]
149
+ assert_equal "XX:s", r["ff"]
150
+ assert_equal 6, r.size
151
+ end
152
+
153
+ test 'masker_for_value_in_subnet' do
154
+ conf = OpenStruct.new(salt: 's')
155
+ plugin = create_driver.instance
156
+ conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
157
+ masker = plugin.masker_for_value_in_subnet(conv, '192.168.0.0/16', conf)
158
+ r = masker.call({"k1" => "x", "k2" => "192.169.1.1", "k3" => "192.168.128.13", "f1" => "x", "f2" => "10.0.12.1", "f3" => "192.168.1.1"})
159
+ assert_equal "x", r["k1"]
160
+ assert_equal "192.169.1.1", r["k2"]
161
+ assert_equal "192.168.128.13:s", r["k3"]
162
+ assert_equal "x", r["f1"]
163
+ assert_equal "10.0.12.1", r["f2"]
164
+ assert_equal "192.168.1.1:s", r["f3"]
165
+ end
166
+
167
+ test 'filter plugin can mask specified fields' do
168
+ plugin = create_driver(<<-CONF).instance
169
+ <mask md5>
170
+ salt testing
171
+ key test
172
+ </mask>
173
+ CONF
174
+ r = plugin.filter('tag', Time.now.to_i, {"test" => "value", "name" => "fluentd plugin"})
175
+ assert_equal 2, r.size
176
+ assert_equal "fluentd plugin", r["name"]
177
+ assert_equal "6255093f2e4204e24df48ddd7f4a8abe", r["test"]
178
+ end
179
+
46
180
  def test_filter
47
181
  messages = [
48
182
  {
@@ -56,11 +190,11 @@ class AnonymizerFilterTest < Test::Unit::TestCase
56
190
  ]
57
191
  expected = {
58
192
  'host' => '10.102.3.0',
59
- 'data_for_md5' => 'e738cbde82a514dc60582cd467c240ed',
60
- 'data_for_sha1' => '69cf099459c06b852ede96d39b710027727d13c6',
61
- 'data_for_sha256' => '804d83b8c6a3e01498d40677652b084333196d8e548ee5a8710fbd0e1e115527',
62
- 'data_for_sha384' => '6c90c389bbdfc210416b9318df3f526b4f218f8a8df3a67020353c35da22dc154460b18f22a8009a747b3ef2975acae7',
63
- 'data_for_sha512' => 'cdbb897e6f3a092161bdb51164eb2996b75b00555f568219628ff15cd2929865d217af5dff9c32ddc908b75a89baec96b3e9a0da120e919f5246de0f1bc54c58'
193
+ 'data_for_md5' => '9138bd41172f5485f7b6eee3afcd0d62',
194
+ 'data_for_sha1' => 'ee98db51658d38580b1cf788db19ad06e51a32f7',
195
+ 'data_for_sha256' => 'd53d15615b19597b0f95a984a132ed5164ba9676bf3cb28e018d28feaa2ea6fd',
196
+ 'data_for_sha384' => '6e9cd6d84ea371a72148b418f1a8cb2534da114bc2186d36ec6f14fd5c237b6f2e460f409dda89b7e42a14b7da8a8131',
197
+ 'data_for_sha512' => 'adcf4e5d1e52f57f67d8b0cd85051158d7362103d7ed4cb6302445c2708eff4b17cb309cf5d09fd5cf76615c75652bd29d1707ce689a28e8700afd7a7439ef20'
64
198
  }
65
199
  filtered = filter(CONFIG, messages)
66
200
  assert_equal(expected, filtered[0])
@@ -85,9 +219,9 @@ class AnonymizerFilterTest < Test::Unit::TestCase
85
219
  expected = {
86
220
  'host' => '10.102.0.0',
87
221
  'host2' => '10.102.0.0',
88
- 'member_id' => '774472f0dc892f0b3299cae8dadacd0a74ba59d7',
89
- 'mail' => 'd7b728209f5dd8df10cecbced30394c3c7fc2c82',
90
- 'telephone' => 'a67f73c395105a358a03a0f127bf64b5495e7841',
222
+ 'member_id' => '8cb2237d0679ca88db6464eac60da96345513964',
223
+ 'mail' => '914fec35ce8bfa1a067581032f26b053591ee38a',
224
+ 'telephone' => 'ce164718b94212332187eb8420903b46b334d609',
91
225
  'action' => 'signup'
92
226
  }
93
227
  filtered = filter(conf, messages)
@@ -118,9 +252,9 @@ class AnonymizerFilterTest < Test::Unit::TestCase
118
252
  'host1' => '10.102.0.0'
119
253
  },
120
254
  'nested' => {
121
- 'data' => '774472f0dc892f0b3299cae8dadacd0a74ba59d7',
255
+ 'data' => '8cb2237d0679ca88db6464eac60da96345513964',
122
256
  'nested' => {
123
- 'data' => '774472f0dc892f0b3299cae8dadacd0a74ba59d7'
257
+ 'data' => '8cb2237d0679ca88db6464eac60da96345513964'
124
258
  }
125
259
  }
126
260
  }
@@ -142,8 +276,8 @@ class AnonymizerFilterTest < Test::Unit::TestCase
142
276
  ]
143
277
  expected = {
144
278
  'host' => '10.102.3.0',
145
- 'array' => ["c1628fc0d473cb21b15607c10bdcad19d1a42e24", "ea87abc249f9f2d430edb816514bffeffd3e698e"],
146
- 'hash' => '28fe85deb0d1d39ee14c49c62bc4773b0338247b'
279
+ 'array' => ["e3cbba8883fe746c6e35783c9404b4bc0c7ee9eb", "a4ac914c09d7c097fe1f4f96b897e625b6922069"],
280
+ 'hash' => '1a1903d78aed9403649d61cb21ba6b489249761b'
147
281
  }
148
282
  filtered = filter(conf, messages)
149
283
  assert_equal(expected, filtered[0])
@@ -169,4 +303,3 @@ class AnonymizerFilterTest < Test::Unit::TestCase
169
303
  assert_equal(expected, filtered)
170
304
  end
171
305
  end
172
-
@@ -49,11 +49,11 @@ class AnonymizerOutputTest < Test::Unit::TestCase
49
49
  assert_equal 1, emits.length
50
50
  assert_equal 'anonymized.access', emits[0][0] # tag
51
51
  assert_equal '10.102.3.0', emits[0][2]['host']
52
- assert_equal 'e738cbde82a514dc60582cd467c240ed', emits[0][2]['data_for_md5']
53
- assert_equal '69cf099459c06b852ede96d39b710027727d13c6', emits[0][2]['data_for_sha1']
54
- assert_equal '804d83b8c6a3e01498d40677652b084333196d8e548ee5a8710fbd0e1e115527', emits[0][2]['data_for_sha256']
55
- assert_equal '6c90c389bbdfc210416b9318df3f526b4f218f8a8df3a67020353c35da22dc154460b18f22a8009a747b3ef2975acae7', emits[0][2]['data_for_sha384']
56
- assert_equal 'cdbb897e6f3a092161bdb51164eb2996b75b00555f568219628ff15cd2929865d217af5dff9c32ddc908b75a89baec96b3e9a0da120e919f5246de0f1bc54c58', emits[0][2]['data_for_sha512']
52
+ assert_equal '9138bd41172f5485f7b6eee3afcd0d62', emits[0][2]['data_for_md5']
53
+ assert_equal 'ee98db51658d38580b1cf788db19ad06e51a32f7', emits[0][2]['data_for_sha1']
54
+ assert_equal 'd53d15615b19597b0f95a984a132ed5164ba9676bf3cb28e018d28feaa2ea6fd', emits[0][2]['data_for_sha256']
55
+ assert_equal '6e9cd6d84ea371a72148b418f1a8cb2534da114bc2186d36ec6f14fd5c237b6f2e460f409dda89b7e42a14b7da8a8131', emits[0][2]['data_for_sha384']
56
+ assert_equal 'adcf4e5d1e52f57f67d8b0cd85051158d7362103d7ed4cb6302445c2708eff4b17cb309cf5d09fd5cf76615c75652bd29d1707ce689a28e8700afd7a7439ef20', emits[0][2]['data_for_sha512']
57
57
  end
58
58
 
59
59
  def test_emit_multi_keys
@@ -79,9 +79,9 @@ class AnonymizerOutputTest < Test::Unit::TestCase
79
79
  assert_equal 'anonymized.access', emits[0][0] # tag
80
80
  assert_equal '10.102.0.0', emits[0][2]['host']
81
81
  assert_equal '10.102.0.0', emits[0][2]['host2']
82
- assert_equal '774472f0dc892f0b3299cae8dadacd0a74ba59d7', emits[0][2]['member_id']
83
- assert_equal 'd7b728209f5dd8df10cecbced30394c3c7fc2c82', emits[0][2]['mail']
84
- assert_equal 'a67f73c395105a358a03a0f127bf64b5495e7841', emits[0][2]['telephone']
82
+ assert_equal '8cb2237d0679ca88db6464eac60da96345513964', emits[0][2]['member_id']
83
+ assert_equal '914fec35ce8bfa1a067581032f26b053591ee38a', emits[0][2]['mail']
84
+ assert_equal 'ce164718b94212332187eb8420903b46b334d609', emits[0][2]['telephone']
85
85
  assert_equal 'signup', emits[0][2]['action']
86
86
  end
87
87
 
@@ -110,8 +110,8 @@ class AnonymizerOutputTest < Test::Unit::TestCase
110
110
  assert_equal 1, emits.length
111
111
  assert_equal 'anonymized.access', emits[0][0] # tag
112
112
  assert_equal '10.102.0.0', emits[0][2]['hosts']['host1']
113
- assert_equal '774472f0dc892f0b3299cae8dadacd0a74ba59d7', emits[0][2]['nested']['data']
114
- assert_equal '774472f0dc892f0b3299cae8dadacd0a74ba59d7', emits[0][2]['nested']['nested']['data']
113
+ assert_equal '8cb2237d0679ca88db6464eac60da96345513964', emits[0][2]['nested']['data']
114
+ assert_equal '8cb2237d0679ca88db6464eac60da96345513964', emits[0][2]['nested']['nested']['data']
115
115
  end
116
116
 
117
117
  def test_emit_nest_value
@@ -132,8 +132,8 @@ class AnonymizerOutputTest < Test::Unit::TestCase
132
132
  assert_equal 1, emits.length
133
133
  assert_equal 'anonymized.access', emits[0][0] # tag
134
134
  assert_equal '10.102.3.0', emits[0][2]['host']
135
- assert_equal ["c1628fc0d473cb21b15607c10bdcad19d1a42e24", "ea87abc249f9f2d430edb816514bffeffd3e698e"], emits[0][2]['array']
136
- assert_equal '28fe85deb0d1d39ee14c49c62bc4773b0338247b', emits[0][2]['hash']
135
+ assert_equal ["e3cbba8883fe746c6e35783c9404b4bc0c7ee9eb", "a4ac914c09d7c097fe1f4f96b897e625b6922069"], emits[0][2]['array']
136
+ assert_equal '1a1903d78aed9403649d61cb21ba6b489249761b', emits[0][2]['hash']
137
137
  end
138
138
 
139
139
  def test_emit_ipv6
@@ -170,7 +170,7 @@ class AnonymizerOutputTest < Test::Unit::TestCase
170
170
  emits = d1.emits
171
171
  assert_equal 1, emits.length
172
172
  assert_equal 'anonymized.message', emits[0][0] # tag
173
- assert_equal '774472f0dc892f0b3299cae8dadacd0a74ba59d7', emits[0][2]['member_id']
173
+ assert_equal '8cb2237d0679ca88db6464eac60da96345513964', emits[0][2]['member_id']
174
174
  end
175
175
 
176
176
  def test_emit_tag_placeholder
@@ -187,7 +187,7 @@ class AnonymizerOutputTest < Test::Unit::TestCase
187
187
  emits = d1.emits
188
188
  assert_equal 1, emits.length
189
189
  assert_equal 'anonymized.access', emits[0][0] # tag
190
- assert_equal '774472f0dc892f0b3299cae8dadacd0a74ba59d7', emits[0][2]['member_id']
190
+ assert_equal '8cb2237d0679ca88db6464eac60da96345513964', emits[0][2]['member_id']
191
191
  end
192
192
  end
193
193
 
metadata CHANGED
@@ -1,94 +1,97 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-anonymizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
5
- prerelease:
4
+ version: 1.0.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Kentaro Yoshida
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2015-12-01 00:00:00.000000000 Z
11
+ date: 2017-12-17 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - ">="
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - ">="
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: test-unit
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ~>
45
+ - - "~>"
52
46
  - !ruby/object:Gem::Version
53
47
  version: '3'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ~>
52
+ - - "~>"
60
53
  - !ruby/object:Gem::Version
61
54
  version: '3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: appraisal
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
62
69
  - !ruby/object:Gem::Dependency
63
70
  name: fluentd
64
71
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
72
  requirements:
67
- - - ! '>='
73
+ - - ">="
68
74
  - !ruby/object:Gem::Version
69
75
  version: '0'
70
76
  type: :runtime
71
77
  prerelease: false
72
78
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
79
  requirements:
75
- - - ! '>='
80
+ - - ">="
76
81
  - !ruby/object:Gem::Version
77
82
  version: '0'
78
83
  - !ruby/object:Gem::Dependency
79
84
  name: fluent-mixin-rewrite-tag-name
80
85
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
86
  requirements:
83
- - - ! '>='
87
+ - - ">="
84
88
  - !ruby/object:Gem::Version
85
89
  version: '0'
86
90
  type: :runtime
87
91
  prerelease: false
88
92
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
93
  requirements:
91
- - - ! '>='
94
+ - - ">="
92
95
  - !ruby/object:Gem::Version
93
96
  version: '0'
94
97
  description:
@@ -98,8 +101,9 @@ executables: []
98
101
  extensions: []
99
102
  extra_rdoc_files: []
100
103
  files:
101
- - .gitignore
102
- - .travis.yml
104
+ - ".gitignore"
105
+ - ".travis.yml"
106
+ - Appraisals
103
107
  - Gemfile
104
108
  - LICENSE
105
109
  - README.md
@@ -114,27 +118,26 @@ files:
114
118
  homepage: https://github.com/y-ken/fluent-plugin-anonymizer
115
119
  licenses:
116
120
  - Apache License, Version 2.0
121
+ metadata: {}
117
122
  post_install_message:
118
123
  rdoc_options: []
119
124
  require_paths:
120
125
  - lib
121
126
  required_ruby_version: !ruby/object:Gem::Requirement
122
- none: false
123
127
  requirements:
124
- - - ! '>='
128
+ - - ">="
125
129
  - !ruby/object:Gem::Version
126
130
  version: '0'
127
131
  required_rubygems_version: !ruby/object:Gem::Requirement
128
- none: false
129
132
  requirements:
130
- - - ! '>='
133
+ - - ">="
131
134
  - !ruby/object:Gem::Version
132
135
  version: '0'
133
136
  requirements: []
134
137
  rubyforge_project:
135
- rubygems_version: 1.8.23
138
+ rubygems_version: 2.2.5
136
139
  signing_key:
137
- specification_version: 3
140
+ specification_version: 4
138
141
  summary: Fluentd filter output plugin to anonymize records with HMAC of MD5/SHA1/SHA256/SHA384/SHA512
139
142
  algorithms. This data masking plugin protects privacy data such as UserID, Email,
140
143
  Phone number, IPv4/IPv6 address and so on.