fluent-plugin-anonymizer 0.4.1 → 0.5.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a438e2acef3632942d02792ba4fbe2b877ab90ad
4
+ data.tar.gz: 8979758d1e489f0bc103d881cacbbfc0b29a326b
5
+ SHA512:
6
+ metadata.gz: a4c53bc239db20b8ec60662476744adc1a28beddd7d300c6dde5a5ab68f63d3892ed61348377592b84ee310988f7136d74395981ead840ea7d6eee0f61256445
7
+ data.tar.gz: bb927b18908c72e6d0fcc0ea6d1fc9f9f7c1a395d1d8ea0867180dd744b7851d419e25568d88389a8274f672a20faa6a4194e4825b7e0c84334669c080c40093
@@ -1,7 +1,23 @@
1
+ sudo: false
1
2
  language: ruby
2
3
 
3
4
  rvm:
5
+ - 2.3.0
4
6
  - 2.2
5
7
  - 2.1
6
8
  - 2.0.0
7
9
  - 1.9.3
10
+
11
+ # to avoid travis-ci issue since 2015-12-25
12
+ before_install:
13
+ - gem update bundler
14
+
15
+ gemfile:
16
+ - Gemfile
17
+ - gemfiles/fluentd_v0.10.gemfile
18
+ - gemfiles/fluentd_v0.12.gemfile
19
+
20
+ matrix:
21
+ allow_failures:
22
+ - rvm: 1.9.3
23
+ - rvm: 2.0.0
@@ -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,42 @@
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
6
 
7
7
  ## Installation
8
8
 
9
9
  install with gem or fluent-gem command as:
10
10
 
11
11
  `````
12
- ### native gem
13
- gem install fluent-plugin-anonymizer
12
+ # for fluentd
13
+ $ gem install fluent-plugin-anonymizer
14
14
 
15
- ### td-agent gem
16
- /usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-anonymizer
15
+ # for td-agent
16
+ $ sudo /usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-anonymizer -v 0.5.0
17
+
18
+ # for td-agent2
19
+ $ sudo td-agent-gem install fluent-plugin-anonymizer -v 0.5.0
17
20
  `````
18
21
 
19
22
  ## Tutorial
20
23
 
21
- ### Output Plugin
24
+ ### Filter Plugin
22
25
 
23
26
  #### configuration
24
27
 
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
- `````
28
+ ```text
28
29
  <source>
29
- type forward
30
- port 24224
30
+ @type dummy
31
+ tag raw.dummy
32
+ dummy [
33
+ {"host":"10.102.3.80","member_id":"12345", "mail":"example@example.com"},
34
+ {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
35
+ ]
31
36
  </source>
32
37
 
33
- <match test.message>
34
- type anonymizer
35
-
38
+ <filter raw.**>
39
+ @type anonymizer
40
+
36
41
  # Specify hashing keys with comma
37
42
  sha1_keys user_id, member_id, mail
38
43
 
@@ -43,45 +48,40 @@ It is a sample to hash record with sha1 for `user_id`, `member_id` and `mail`. F
43
48
  ipaddr_mask_keys host
44
49
  ipv4_mask_subnet 24
45
50
  ipv6_mask_subnet 104
46
-
47
- # Set tag rename pattern
48
- remove_tag_prefix test.
49
- add_tag_prefix anonymized.
50
- </match>
51
+ </filter>
51
52
 
52
- <match anonymized.message>
53
- type stdout
53
+ <match raw.**>
54
+ @type stdout
54
55
  </match>
55
- `````
56
+ ```
56
57
 
57
58
  #### result
58
59
 
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
60
+ This sample result has made with the above configuration into "fluent.conf".
62
61
 
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
- `````
62
+ ```text
63
+ $ fluentd -c fluent.conf
64
+ 2017-02-27 22:59:18.070132000 +0900 raw.dummy: {"host":"10.102.3.0","member_id":"5ab2cebb0537866c4a0cd2e2f3502c0976b788da","mail":"7e9d6dbefa72d56056c8c740b34b5c0bbfec8d87"}
65
+ 2017-02-27 22:59:19.079251000 +0900 raw.dummy: {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"445514dfcd82b2a8b94ec6763afa6e349e78c5f8","mail":"54608576c8d815a4ffd595a3c1fe72751ed04424"}
66
+ 2017-02-27 22:59:20.086747000 +0900 raw.dummy: {"host":"10.102.3.0","member_id":"b14a8f98019ec84c6fe329d5af62c46bb45348f8","mail":"723da8084da3438d9287b44e5a714b70e10a9755"}
67
+ 2017-02-27 22:59:21.094767000 +0900 raw.dummy: {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"d38ebb9b96c0cbffd4136935c7f6fe9dd05980cd","mail":"b6f9d777831cbecfd2ea806f5f62f79a275bbb82"}
68
+ ```
67
69
 
68
- ### Filter Plugin
70
+ ### Output Plugin
69
71
 
70
72
  #### configuration
71
73
 
72
- ```text
74
+ 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.
75
+
76
+ `````
73
77
  <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
- ]
78
+ @type forward
79
+ port 24224
80
80
  </source>
81
81
 
82
- <filter raw.**>
83
- type anonymizer
84
-
82
+ <match test.message>
83
+ @type anonymizer
84
+
85
85
  # Specify hashing keys with comma
86
86
  sha1_keys user_id, member_id, mail
87
87
 
@@ -92,18 +92,27 @@ $ tail -f /var/log/td-agent/td-agent.log
92
92
  ipaddr_mask_keys host
93
93
  ipv4_mask_subnet 24
94
94
  ipv6_mask_subnet 104
95
- </filter>
95
+
96
+ # Set tag rename pattern
97
+ remove_tag_prefix test.
98
+ add_tag_prefix anonymized.
99
+ </match>
96
100
 
97
- <match raw.**>
98
- type stdout
101
+ <match anonymized.message>
102
+ @type stdout
99
103
  </match>
100
- ```
104
+ `````
101
105
 
102
106
  #### result
103
107
 
104
- ```text
105
- $ fluentd -c fluent.conf
106
- ```
108
+ `````
109
+ $ echo '{"host":"10.102.3.80","member_id":"12345", "mail":"example@example.com"}' | fluent-cat test.message
110
+ $ echo '{"host":"2001:db8:0:8d3:0:8a2e:70:7344","member_id":"12345", "mail":"example@example.com"}' | fluent-cat test.message
111
+
112
+ $ tail -f /var/log/td-agent/td-agent.log
113
+ 2014-01-06 18:30:21 +0900 anonymized.message: {"host":"10.102.3.0","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
114
+ 2014-01-06 18:30:22 +0900 anonymized.message: {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
115
+ `````
107
116
 
108
117
  ## Parameters
109
118
 
@@ -143,7 +152,7 @@ set one or more option are required for editing tag name using HandleTagNameMixi
143
152
 
144
153
  * tag
145
154
 
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.
155
+ 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#L179), it will be overwrite after these options affected. which are remove_tag_prefix, remove_tag_suffix, add_tag_prefix and add_tag_suffix.
147
156
 
148
157
  ## Notes
149
158
 
@@ -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 = "0.5.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
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "fluentd", "~> 0.10.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "fluentd", "~> 0.12.0"
6
+
7
+ gemspec :path => "../"
@@ -79,7 +79,7 @@ module Fluent
79
79
  def anonymize_value(message, algorithm, salt)
80
80
  case algorithm
81
81
  when 'md5','sha1','sha256','sha384','sha512'
82
- OpenSSL::HMAC.hexdigest(DIGEST[algorithm].call, salt, message.to_s)
82
+ DIGEST[algorithm].call.update(salt).update(message.to_s).hexdigest
83
83
  when 'ipaddr_mask'
84
84
  address = IPAddr.new(message)
85
85
  subnet = address.ipv4? ? @ipv4_mask_subnet : @ipv6_mask_subnet
@@ -1,26 +1,298 @@
1
+ require 'fluent/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
+
1
31
  module Fluent
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 filter_stream(tag, es)
178
+ new_es = MultiEventStream.new
179
+ es.each do |time, record|
180
+ new_es.add(time, @masks.reduce(record){|r,mask| mask.call(r) })
181
+ end
182
+ new_es
183
+ end
184
+
185
+ def salt_determine(key)
186
+ return @salt_map[key] if @salt_map.has_key?(key)
187
+ keystr = key.to_s
188
+ if keystr.empty?
189
+ @salt_map[key] = @salt_list[0]
190
+ else
191
+ @salt_map[key] = @salt_list[(keystr[0].ord + keystr[-1].ord) % @salt_list.size]
192
+ end
193
+ @salt_map[key]
194
+ end
195
+
196
+ def mask_value(value, for_each)
197
+ if for_each && value.is_a?(Array)
198
+ value.map{|v|
199
+ yield v
200
+ }
201
+ else
202
+ yield value
203
+ end
204
+ end
205
+
206
+ def masker_for_key(conv, key, opts)
207
+ for_each = opts.mask_array_elements
208
+ salt = opts.salt || salt_determine(key)
209
+ if for_each
210
+ ->(record){
211
+ begin
212
+ if record.has_key?(key)
213
+ record[key] = mask_value(record[key], for_each){|v| conv.call(v, salt) }
214
+ end
215
+ rescue => e
216
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
217
+ end
218
+ record
219
+ }
220
+ else
221
+ ->(record){
222
+ begin
223
+ if record.has_key?(key)
224
+ record[key] = conv.call(record[key], salt)
225
+ end
226
+ rescue => e
227
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
228
+ end
229
+ record
230
+ }
231
+ end
232
+ end
233
+
234
+ def masker_for_key_chain(conv, key_chain, opts)
235
+ for_each = opts.mask_array_elements
236
+ heading = key_chain[0..-2]
237
+ container_fetcher = ->(record){ heading.reduce(record){|r,c| r && r.has_key?(c) ? r[c] : nil } }
238
+ tailing = key_chain[-1]
239
+ ->(record){
240
+ begin
241
+ container = container_fetcher.call(record)
242
+ if container && container.has_key?(tailing)
243
+ container[tailing] = mask_value(container[tailing], for_each){|v| conv.call(v, opts.salt || salt_determine(tailing)) }
244
+ end
245
+ rescue => e
246
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
247
+ end
248
+ record
249
+ }
250
+ end
251
+
252
+ def masker_for_key_pattern(conv, pattern, opts)
253
+ for_each = opts.mask_array_elements
254
+ regexp = Regexp.new(pattern)
255
+ ->(record){
256
+ begin
257
+ record.each_pair do |key, value|
258
+ next unless (regexp =~ key.to_s rescue nil)
259
+ record[key] = mask_value(record[key], for_each){|v| conv.call(v, opts.salt || salt_determine(key)) }
260
+ end
261
+ rescue => e
262
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
263
+ end
264
+ record
265
+ }
266
+ end
267
+
268
+ def masker_for_value_pattern(conv, pattern, opts)
269
+ regexp = Regexp.new(pattern)
270
+ ->(record){
271
+ begin
272
+ record.each_pair do |key, value|
273
+ next unless (regexp =~ value.to_s rescue nil)
274
+ record[key] = conv.call(value, opts.salt || salt_determine(key))
275
+ end
276
+ rescue => e
277
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
278
+ end
279
+ record
280
+ }
281
+ end
282
+
283
+ def masker_for_value_in_subnet(conv, network_str, opts)
284
+ network = IPAddr.new(network_str)
285
+ ->(record){
286
+ begin
287
+ record.each_pair do |key, value|
288
+ next unless (network.include?(value) rescue nil)
289
+ record[key] = conv.call(value, opts.salt || salt_determine(key))
290
+ end
291
+ rescue => e
292
+ log.error "unexpected error while masking value", error_class: e.class, error: e.message
293
+ end
294
+ record
295
+ }
24
296
  end
25
297
  end
26
298
  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
@@ -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
@@ -2,6 +2,7 @@ require 'helper'
2
2
 
3
3
  class AnonymizerFilterTest < Test::Unit::TestCase
4
4
  def setup
5
+ omit_unless(Fluent.const_defined?(:Filter))
5
6
  Fluent::Test.setup
6
7
  @time = Fluent::Engine.now
7
8
  end
@@ -25,13 +26,60 @@ class AnonymizerFilterTest < Test::Unit::TestCase
25
26
  d = create_driver(conf)
26
27
  d.run {
27
28
  messages.each {|message|
28
- d.filter(message, @time)
29
+ d.emit(message, @time)
29
30
  }
30
31
  }
31
32
  filtered = d.filtered_as_array
32
33
  filtered.map {|m| m[2] }
33
34
  end
34
35
 
36
+ require 'ostruct'
37
+ test 'method md5 works correctly' do
38
+ conv = Fluent::AnonymizerFilter::MASK_METHODS[:md5].call(OpenStruct.new)
39
+ digest = conv.call('value1', 'salt')
40
+ assert_equal 'd21fe9523421f12daad064fd082913fd', digest
41
+ end
42
+ test 'method sha1 works correctly' do
43
+ conv = Fluent::AnonymizerFilter::MASK_METHODS[:sha1].call(OpenStruct.new)
44
+ digest = conv.call('value2', 'salt')
45
+ assert_equal 'd2ed8e797065322371012fd8c1a39682987ddb71', digest
46
+ end
47
+ test 'method sha256 works correctly' do
48
+ conv = Fluent::AnonymizerFilter::MASK_METHODS[:sha256].call(OpenStruct.new)
49
+ digest = conv.call('value3', 'salt')
50
+ assert_equal 'd70daf9654b8a3ba335f8f9f9638a93e8eba6763a0012ac44a928857871abe82', digest
51
+ end
52
+ test 'method sha384 works correctly' do
53
+ conv = Fluent::AnonymizerFilter::MASK_METHODS[:sha384].call(OpenStruct.new)
54
+ digest = conv.call('value4', 'salt')
55
+ assert_equal '646192f8b1ea905238df589a00a10598a53eb245df4ab14b7e9eccf80c37386c99abe5259ccb2ba950003423fa0790ee', digest
56
+ end
57
+ test 'method sha512 works correctly' do
58
+ conv = Fluent::AnonymizerFilter::MASK_METHODS[:sha512].call(OpenStruct.new)
59
+ digest = conv.call('value5', 'salt')
60
+ expected = '47c82bfea3783c20e3ba3629f0f827bebf0fa65a9104ada5339e5776e5958f061fe7114bfbe1e9d410aff43c6bee8365adf4fdd072e54ab4fffad820f354f545'
61
+ assert_equal expected, digest
62
+ end
63
+ test 'method uri_path removes path, query parameters, fragment, user and password of uri strings' do
64
+ conv = Fluent::AnonymizerFilter::MASK_METHODS[:uri_path].call(OpenStruct.new)
65
+ assert_equal '/my/path', conv.call('/my/path', '')
66
+ assert_equal 'yay/unknown/format', conv.call('yay/unknown/format', '')
67
+ assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret', '')
68
+ assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret?a=b', '')
69
+ assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret#xxx', '')
70
+ assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret?a=b#xxx', '')
71
+ assert_equal 'http://example.com/', conv.call('http://tagomoris:secret!@example.com/', '')
72
+ assert_equal 'http://example.com/', conv.call('http://tagomoris:secret!@example.com/?a=b#xxx', '')
73
+ assert_equal 'http://example.com/', conv.call('http://tagomoris:secret!@example.com/path/to/secret?a=b#xxx', '')
74
+ end
75
+ test 'method network masks ipaddresses with specified mask bit lengths' do
76
+ conf = OpenStruct.new(ipv4_mask_bits: 24, ipv6_mask_bits: 104)
77
+ conv = Fluent::AnonymizerFilter::MASK_METHODS[:network].call(conf)
78
+ assert_equal '192.168.1.0', conv.call('192.168.1.1', '')
79
+ assert_equal '10.110.18.0', conv.call('10.110.18.9', '')
80
+ assert_equal '2001:db8:0:8d3:0:8a2e::', conv.call('2001:db8:0:8d3:0:8a2e:70:7344', '')
81
+ end
82
+
35
83
  def test_configure
36
84
  assert_raise(Fluent::ConfigError) {
37
85
  d = create_driver('')
@@ -43,6 +91,91 @@ class AnonymizerFilterTest < Test::Unit::TestCase
43
91
  assert_equal 'test_salt_string', d.instance.config['hash_salt']
44
92
  end
45
93
 
94
+ test 'masker_for_key generates a lambda for conversion with exact key match' do
95
+ conf = OpenStruct.new(salt: 's')
96
+ plugin = create_driver.instance
97
+ conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
98
+ masker = plugin.masker_for_key(conv, 'kkk', conf)
99
+ r = masker.call({"k" => "x", "kk" => "xx", "kkk" => "xxx", "kkkk" => "xxxx"})
100
+ assert_equal "x", r["k"]
101
+ assert_equal "xx", r["kk"]
102
+ assert_equal "XXX:s", r["kkk"]
103
+ assert_equal "xxxx", r["kkkk"]
104
+ assert_equal 4, r.size
105
+ end
106
+
107
+ test 'masker_for_key_chain generates a lambda for conversion with recursive key fetching' do
108
+ conf = OpenStruct.new(salt: 's')
109
+ plugin = create_driver.instance
110
+ conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
111
+ masker = plugin.masker_for_key_chain(conv, 'a.b.c'.split('.'), conf)
112
+ event = {
113
+ 'a' => {
114
+ 'b' => { 'c' => 'v', 'd' => 'v' }
115
+ }
116
+ }
117
+ r = masker.call(event)
118
+ assert_equal 'V:s', r['a']['b']['c']
119
+ assert_equal 'v', r['a']['b']['d']
120
+ end
121
+
122
+ test 'masker_for_key_pattern generates a lambda for conversion with key pattern match' do
123
+ conf = OpenStruct.new(salt: 's')
124
+ plugin = create_driver.instance
125
+ conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
126
+ masker = plugin.masker_for_key_pattern(conv, '^k+$', conf)
127
+ r = masker.call({"k" => "x", "kk" => "xx", "kkk" => "xxx", "kkk0" => "xxxx", "f" => "x", "ff" => "xx"})
128
+ assert_equal "X:s", r["k"]
129
+ assert_equal "XX:s", r["kk"]
130
+ assert_equal "XXX:s", r["kkk"]
131
+ assert_equal "xxxx", r["kkk0"]
132
+ assert_equal "x", r["f"]
133
+ assert_equal "xx", r["ff"]
134
+ assert_equal 6, r.size
135
+ end
136
+
137
+ test 'masker_for_value_pattern' do
138
+ conf = OpenStruct.new(salt: 's')
139
+ plugin = create_driver.instance
140
+ conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
141
+ masker = plugin.masker_for_value_pattern(conv, '^x+$', conf)
142
+ r = masker.call({"k" => "x", "kk" => "x0", "kkk" => "xx0", "kkk0" => "xxxx", "f" => "x", "ff" => "xx"})
143
+ assert_equal "X:s", r["k"]
144
+ assert_equal "x0", r["kk"]
145
+ assert_equal "xx0", r["kkk"]
146
+ assert_equal "XXXX:s", r["kkk0"]
147
+ assert_equal "X:s", r["f"]
148
+ assert_equal "XX:s", r["ff"]
149
+ assert_equal 6, r.size
150
+ end
151
+
152
+ test 'masker_for_value_in_subnet' do
153
+ conf = OpenStruct.new(salt: 's')
154
+ plugin = create_driver.instance
155
+ conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
156
+ masker = plugin.masker_for_value_in_subnet(conv, '192.168.0.0/16', conf)
157
+ 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"})
158
+ assert_equal "x", r["k1"]
159
+ assert_equal "192.169.1.1", r["k2"]
160
+ assert_equal "192.168.128.13:s", r["k3"]
161
+ assert_equal "x", r["f1"]
162
+ assert_equal "10.0.12.1", r["f2"]
163
+ assert_equal "192.168.1.1:s", r["f3"]
164
+ end
165
+
166
+ test 'filter plugin can mask specified fields' do
167
+ plugin = create_driver(<<-CONF).instance
168
+ <mask md5>
169
+ salt testing
170
+ key test
171
+ </mask>
172
+ CONF
173
+ r = plugin.filter('tag', Time.now.to_i, {"test" => "value", "name" => "fluentd plugin"})
174
+ assert_equal 2, r.size
175
+ assert_equal "fluentd plugin", r["name"]
176
+ assert_equal "6255093f2e4204e24df48ddd7f4a8abe", r["test"]
177
+ end
178
+
46
179
  def test_filter
47
180
  messages = [
48
181
  {
@@ -56,11 +189,11 @@ class AnonymizerFilterTest < Test::Unit::TestCase
56
189
  ]
57
190
  expected = {
58
191
  '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'
192
+ 'data_for_md5' => '9138bd41172f5485f7b6eee3afcd0d62',
193
+ 'data_for_sha1' => 'ee98db51658d38580b1cf788db19ad06e51a32f7',
194
+ 'data_for_sha256' => 'd53d15615b19597b0f95a984a132ed5164ba9676bf3cb28e018d28feaa2ea6fd',
195
+ 'data_for_sha384' => '6e9cd6d84ea371a72148b418f1a8cb2534da114bc2186d36ec6f14fd5c237b6f2e460f409dda89b7e42a14b7da8a8131',
196
+ 'data_for_sha512' => 'adcf4e5d1e52f57f67d8b0cd85051158d7362103d7ed4cb6302445c2708eff4b17cb309cf5d09fd5cf76615c75652bd29d1707ce689a28e8700afd7a7439ef20'
64
197
  }
65
198
  filtered = filter(CONFIG, messages)
66
199
  assert_equal(expected, filtered[0])
@@ -85,9 +218,9 @@ class AnonymizerFilterTest < Test::Unit::TestCase
85
218
  expected = {
86
219
  'host' => '10.102.0.0',
87
220
  'host2' => '10.102.0.0',
88
- 'member_id' => '774472f0dc892f0b3299cae8dadacd0a74ba59d7',
89
- 'mail' => 'd7b728209f5dd8df10cecbced30394c3c7fc2c82',
90
- 'telephone' => 'a67f73c395105a358a03a0f127bf64b5495e7841',
221
+ 'member_id' => '8cb2237d0679ca88db6464eac60da96345513964',
222
+ 'mail' => '914fec35ce8bfa1a067581032f26b053591ee38a',
223
+ 'telephone' => 'ce164718b94212332187eb8420903b46b334d609',
91
224
  'action' => 'signup'
92
225
  }
93
226
  filtered = filter(conf, messages)
@@ -118,9 +251,9 @@ class AnonymizerFilterTest < Test::Unit::TestCase
118
251
  'host1' => '10.102.0.0'
119
252
  },
120
253
  'nested' => {
121
- 'data' => '774472f0dc892f0b3299cae8dadacd0a74ba59d7',
254
+ 'data' => '8cb2237d0679ca88db6464eac60da96345513964',
122
255
  'nested' => {
123
- 'data' => '774472f0dc892f0b3299cae8dadacd0a74ba59d7'
256
+ 'data' => '8cb2237d0679ca88db6464eac60da96345513964'
124
257
  }
125
258
  }
126
259
  }
@@ -142,8 +275,8 @@ class AnonymizerFilterTest < Test::Unit::TestCase
142
275
  ]
143
276
  expected = {
144
277
  'host' => '10.102.3.0',
145
- 'array' => ["c1628fc0d473cb21b15607c10bdcad19d1a42e24", "ea87abc249f9f2d430edb816514bffeffd3e698e"],
146
- 'hash' => '28fe85deb0d1d39ee14c49c62bc4773b0338247b'
278
+ 'array' => ["e3cbba8883fe746c6e35783c9404b4bc0c7ee9eb", "a4ac914c09d7c097fe1f4f96b897e625b6922069"],
279
+ 'hash' => '1a1903d78aed9403649d61cb21ba6b489249761b'
147
280
  }
148
281
  filtered = filter(conf, messages)
149
282
  assert_equal(expected, filtered[0])
@@ -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: 0.5.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-02-27 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,13 +101,16 @@ 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
106
110
  - Rakefile
107
111
  - fluent-plugin-anonymizer.gemspec
112
+ - gemfiles/fluentd_v0.10.gemfile
113
+ - gemfiles/fluentd_v0.12.gemfile
108
114
  - lib/fluent/plugin/anonymizer.rb
109
115
  - lib/fluent/plugin/filter_anonymizer.rb
110
116
  - lib/fluent/plugin/out_anonymizer.rb
@@ -114,27 +120,26 @@ files:
114
120
  homepage: https://github.com/y-ken/fluent-plugin-anonymizer
115
121
  licenses:
116
122
  - Apache License, Version 2.0
123
+ metadata: {}
117
124
  post_install_message:
118
125
  rdoc_options: []
119
126
  require_paths:
120
127
  - lib
121
128
  required_ruby_version: !ruby/object:Gem::Requirement
122
- none: false
123
129
  requirements:
124
- - - ! '>='
130
+ - - ">="
125
131
  - !ruby/object:Gem::Version
126
132
  version: '0'
127
133
  required_rubygems_version: !ruby/object:Gem::Requirement
128
- none: false
129
134
  requirements:
130
- - - ! '>='
135
+ - - ">="
131
136
  - !ruby/object:Gem::Version
132
137
  version: '0'
133
138
  requirements: []
134
139
  rubyforge_project:
135
- rubygems_version: 1.8.23
140
+ rubygems_version: 2.2.5
136
141
  signing_key:
137
- specification_version: 3
142
+ specification_version: 4
138
143
  summary: Fluentd filter output plugin to anonymize records with HMAC of MD5/SHA1/SHA256/SHA384/SHA512
139
144
  algorithms. This data masking plugin protects privacy data such as UserID, Email,
140
145
  Phone number, IPv4/IPv6 address and so on.