logstash-filter-fingerprint 3.2.1 → 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a80216d6dfbd2b4724fe593ce35e2c57ea7501f3c9fb78efbed7122c953b875
4
- data.tar.gz: 3ad6b211481f4b7eb7233055e2665b13d5329a2c41af220b77bad02566581e0e
3
+ metadata.gz: 13e031f1122fff7d28e213e702e7d4ffbae573c76daf65445bb8e1059187f7db
4
+ data.tar.gz: 20046dd2014ef538c96de60c2c7ac7c5ec62542f14f3dbb65f5987ad79e4bf81
5
5
  SHA512:
6
- metadata.gz: 4315fa59ed1276927c5af00e033652eed383e375956a632ddd572866d8e702daef3406040ccbd649f15885bd74e1ad68be70e93b812d1af9a0a9b15d3198d18d
7
- data.tar.gz: e9137e8dd254c15b6a27ed4eddd2dd785c09410e84978148adf424449aefeadfc6f7af48dd2431c1032bab4d3dc0500d2ecfd4411f00009d578f529c020b6c26
6
+ metadata.gz: 4213b3572ebbae2559d5873fc0b2e2322c0c2b5d7df1dd2e8361c55b5d684560862b1b26f3d0ad54a2e0e827a51d31c393e466ed1d0a979694567aff7e7a9289
7
+ data.tar.gz: 4c4acdab141a7159c8eabd411f23e2d66925c3baecda1f2b75b0a57e000e18883c55edf81a1e1601d2ced5b3ecbda9d9884c12fc0f129ecab7987d35d72627a2
@@ -1,3 +1,6 @@
1
+ ## 3.2.2
2
+ - Fixed lack of consistent fingerprints on Hash/Map objects [#55](https://github.com/logstash-plugins/logstash-filter-fingerprint/pull/55)
3
+
1
4
  ## 3.2.1
2
5
  - Fixed concurrent SHA fingerprinting by making the instances thread local
3
6
 
data/LICENSE CHANGED
@@ -1,13 +1,202 @@
1
- Copyright (c) 2012-2018 Elasticsearch <http://www.elastic.co>
2
1
 
3
- Licensed under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License.
5
- You may obtain a copy of the License at
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
6
5
 
7
- http://www.apache.org/licenses/LICENSE-2.0
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
7
 
9
- Unless required by applicable law or agreed to in writing, software
10
- distributed under the License is distributed on an "AS IS" BASIS,
11
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- See the License for the specific language governing permissions and
13
- limitations under the License.
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2020 Elastic and contributors
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
@@ -119,12 +119,12 @@ class LogStash::Filters::Fingerprint < LogStash::Filters::Base
119
119
  if @concatenate_sources || @concatenate_all_fields
120
120
  to_string = ""
121
121
  if @concatenate_all_fields
122
- event.to_hash.sort.map do |k,v|
122
+ deep_sort_hashes(event.to_hash).each do |k,v|
123
123
  to_string << "|#{k}|#{v}"
124
124
  end
125
125
  else
126
126
  @source.sort.each do |k|
127
- to_string << "|#{k}|#{event.get(k)}"
127
+ to_string << "|#{k}|#{deep_sort_hashes(event.get(k))}"
128
128
  end
129
129
  end
130
130
  to_string << "|"
@@ -134,9 +134,9 @@ class LogStash::Filters::Fingerprint < LogStash::Filters::Base
134
134
  @source.each do |field|
135
135
  next unless event.include?(field)
136
136
  if event.get(field).is_a?(Array)
137
- event.set(@target, event.get(field).collect { |v| fingerprint(v) })
137
+ event.set(@target, event.get(field).collect { |v| fingerprint(deep_sort_hashes(v)) })
138
138
  else
139
- event.set(@target, fingerprint(event.get(field)))
139
+ event.set(@target, fingerprint(deep_sort_hashes(event.get(field))))
140
140
  end
141
141
  end
142
142
  end
@@ -145,6 +145,20 @@ class LogStash::Filters::Fingerprint < LogStash::Filters::Base
145
145
  end
146
146
 
147
147
  private
148
+ def deep_sort_hashes(object)
149
+ case object
150
+ when Hash
151
+ sorted_hash = Hash.new
152
+ object.sort.each do |sorted_key, value|
153
+ sorted_hash[sorted_key] = deep_sort_hashes(value)
154
+ end
155
+ sorted_hash
156
+ when Array
157
+ object.map {|element| deep_sort_hashes(element) }
158
+ else
159
+ object
160
+ end
161
+ end
148
162
 
149
163
  def fingerprint_ipv4_network(ip_string)
150
164
  # in JRuby 1.7.11 outputs as US-ASCII
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-filter-fingerprint'
4
- s.version = '3.2.1'
4
+ s.version = '3.2.2'
5
5
  s.licenses = ['Apache-2.0']
6
6
  s.summary = "Fingerprints fields by replacing values with a consistent hash"
7
7
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -4,412 +4,284 @@ require "logstash/filters/fingerprint"
4
4
 
5
5
  describe LogStash::Filters::Fingerprint do
6
6
 
7
- describe "fingerprint ipaddress with IPV4_NETWORK method" do
8
- config <<-CONFIG
9
- filter {
10
- fingerprint {
11
- source => ["clientip"]
12
- method => "IPV4_NETWORK"
13
- key => 24
14
- }
15
- }
16
- CONFIG
17
-
18
- sample("clientip" => "233.255.13.44") do
19
- insist { subject.get("fingerprint") } == "233.255.13.0"
20
- end
7
+ let(:plugin) { described_class.new(config) }
8
+ let(:config) { { "method" => fingerprint_method } }
9
+ let(:fingerprint_method) { "SHA1" } # default
10
+ let(:data) { {} }
11
+ let(:event) { LogStash::Event.new(data) }
12
+ let(:fingerprint) { event.get("fingerprint") }
13
+
14
+ before(:each) do
15
+ plugin.register
16
+ plugin.filter(event)
21
17
  end
22
18
 
23
- describe "fingerprint string with MURMUR3 method" do
24
- config <<-CONFIG
25
- filter {
26
- fingerprint {
27
- source => ["clientip"]
28
- method => "MURMUR3"
29
- }
30
- }
31
- CONFIG
32
-
33
- sample("clientip" => "123.52.122.33") do
34
- insist { subject.get("fingerprint") } == 1541804874
35
- end
36
- end
19
+ context "with a string field" do
20
+ let(:data) { {"clientip" => "123.123.123.123" } }
21
+ let(:config) { super.merge("source" => ["clientip" ]) }
37
22
 
38
- describe "fingerprint string with SHA1 algorithm" do
39
- config <<-CONFIG
40
- filter {
41
- fingerprint {
42
- source => ["clientip"]
43
- method => 'SHA1'
44
- }
45
- }
46
- CONFIG
23
+ describe "the IPV4_NETWORK method" do
24
+ let(:fingerprint_method) { "IPV4_NETWORK" }
25
+ let(:config) { super.merge("key" => 24) }
47
26
 
48
- sample("clientip" => "123.123.123.123") do
49
- insist { subject.get("fingerprint") } == "3a5076c520b4b463f43806896ea0b3978d09dcae"
27
+ it "fingerprints the ip as the network" do
28
+ expect(fingerprint).to eq("123.123.123.0")
29
+ end
50
30
  end
51
- end
52
31
 
53
- describe "fingerprint string with SHA1 HMAC algorithm" do
54
- config <<-CONFIG
55
- filter {
56
- fingerprint {
57
- source => ["clientip"]
58
- key => "longencryptionkey"
59
- method => 'SHA1'
60
- }
61
- }
62
- CONFIG
32
+ describe "the MURMUR3 method" do
33
+ let(:fingerprint_method) { "MURMUR3" }
63
34
 
64
- sample("clientip" => "123.123.123.123") do
65
- insist { subject.get("fingerprint") } == "fdc60acc4773dc5ac569ffb78fcb93c9630797f4"
35
+ it "fingerprints the value" do
36
+ expect(fingerprint).to eq(4013733395)
37
+ end
66
38
  end
67
- end
68
-
69
- describe "fingerprint string with SHA1 HMAC algorithm on all event fields" do
70
- config <<-CONFIG
71
- filter {
72
- fingerprint {
73
- concatenate_all_fields => true
74
- key => "longencryptionkey"
75
- method => 'SHA1'
76
- }
77
- }
78
- CONFIG
79
39
 
80
- # The @timestamp field is specified in this sample event as we need the event contents to be constant for the tests
81
- sample("@timestamp" => "2017-07-26T14:44:27.064Z", "clientip" => "123.123.123.123", "message" => "This is a test message", "log_level" => "INFO", "offset" => 123456789, "type" => "test") do
82
- insist { subject.get("fingerprint") } == "d7c617f4d40b2cb677a7003af68a41c415f58031"
83
- end
84
- end
40
+ describe "the SHA1 method" do
41
+ let(:fingerprint_method) { "SHA1" }
85
42
 
86
- describe "fingerprint string with SHA1 algorithm and base64 encoding" do
87
- config <<-CONFIG
88
- filter {
89
- fingerprint {
90
- source => ["clientip"]
91
- method => 'SHA1'
92
- base64encode => true
93
- }
94
- }
95
- CONFIG
43
+ it "fingerprints the value" do
44
+ expect(fingerprint).to eq("3a5076c520b4b463f43806896ea0b3978d09dcae")
45
+ end
96
46
 
97
- sample("clientip" => "123.123.123.123") do
98
- insist { subject.get("fingerprint") } == "OlB2xSC0tGP0OAaJbqCzl40J3K4="
47
+ context "with HMAC" do
48
+ let(:config) { super.merge("key" => "longencryptionkey") }
49
+
50
+ it "fingerprints the value" do
51
+ expect(fingerprint).to eq("fdc60acc4773dc5ac569ffb78fcb93c9630797f4")
52
+ end
53
+ context "with HMAC and base64 encoding" do
54
+ let(:config) { super.merge("base64encode" => true) }
55
+ it "fingerprints the value" do
56
+ expect(fingerprint).to eq("/cYKzEdz3FrFaf+3j8uTyWMHl/Q=")
57
+ end
58
+ end
59
+ end
60
+ context "and base64 encoding" do
61
+ let(:config) { super.merge("base64encode" => true) }
62
+ it "fingerprints the value" do
63
+ expect(fingerprint).to eq("OlB2xSC0tGP0OAaJbqCzl40J3K4=")
64
+ end
65
+ end
99
66
  end
100
- end
101
-
102
- describe "fingerprint string with SHA1 HMAC algorithm and base64 encoding" do
103
- config <<-CONFIG
104
- filter {
105
- fingerprint {
106
- source => ["clientip"]
107
- key => "longencryptionkey"
108
- method => 'SHA1'
109
- base64encode => true
110
- }
111
- }
112
- CONFIG
113
67
 
114
- sample("clientip" => "123.123.123.123") do
115
- insist { subject.get("fingerprint") } == "/cYKzEdz3FrFaf+3j8uTyWMHl/Q="
68
+ context "the SHA256 algorithm" do
69
+ let(:fingerprint_method) { "SHA256" }
70
+ it "fingerprints the value" do
71
+ expect(fingerprint).to eq("4dabcab210766e35f03e77120e6986d6e6d4752b2a9ff22980b9253d026080d8")
72
+ end
73
+ context "with HMAC" do
74
+ let(:config) { super.merge("key" => "longencryptionkey") }
75
+ it "fingerprints the value" do
76
+ expect(fingerprint).to eq("345bec3eff242d53b568916c2610b3e393d885d6b96d643f38494fd74bf4a9ca")
77
+ end
78
+ context "and base64 encoding" do
79
+ let(:config) { super.merge("base64encode" => true) }
80
+ it "fingerprints the value" do
81
+ expect(fingerprint).to eq("NFvsPv8kLVO1aJFsJhCz45PYhda5bWQ/OElP10v0qco=")
82
+ end
83
+ end
84
+ end
116
85
  end
117
- end
118
-
119
- describe "fingerprint string with SHA256 algorithm" do
120
- config <<-CONFIG
121
- filter {
122
- fingerprint {
123
- source => ["clientip"]
124
- method => 'SHA256'
125
- }
126
- }
127
- CONFIG
128
86
 
129
- sample("clientip" => "123.123.123.123") do
130
- insist { subject.get("fingerprint") } == "4dabcab210766e35f03e77120e6986d6e6d4752b2a9ff22980b9253d026080d8"
87
+ context "the SHA384 algorithm" do
88
+ let(:fingerprint_method) { "SHA384" }
89
+ it "fingerprints the value" do
90
+ expect(fingerprint).to eq("fd605b0a3af3e04ce0d7a0b0d9c48d67a12dab811f60072e6eae84e35d567793ffb68a1807536f11c90874065c2a4392")
91
+ end
92
+ context "with HMAC" do
93
+ let(:config) { super.merge("key" => "longencryptionkey") }
94
+ it "fingerprints the value" do
95
+ expect(fingerprint).to eq("22d4c0e8c4fbcdc4887d2038fca7650f0e2e0e2457ff41c06eb2a980dded6749561c814fe182aff93e2538d18593947a")
96
+ end
97
+ context "and base64 encoding" do
98
+ let(:config) { super.merge("base64encode" => true) }
99
+ it "fingerprints the value" do
100
+ expect(fingerprint).to eq("ItTA6MT7zcSIfSA4/KdlDw4uDiRX/0HAbrKpgN3tZ0lWHIFP4YKv+T4lONGFk5R6")
101
+ end
102
+ end
103
+ end
131
104
  end
132
- end
133
-
134
- describe "fingerprint string with SHA256 HMAC algorithm" do
135
- config <<-CONFIG
136
- filter {
137
- fingerprint {
138
- source => ["clientip"]
139
- key => "longencryptionkey"
140
- method => 'SHA256'
141
- }
142
- }
143
- CONFIG
144
-
145
- sample("clientip" => "123.123.123.123") do
146
- insist { subject.get("fingerprint") } == "345bec3eff242d53b568916c2610b3e393d885d6b96d643f38494fd74bf4a9ca"
105
+ context "the SHA512 algorithm" do
106
+ let(:fingerprint_method) { "SHA512" }
107
+ it "fingerprints the value" do
108
+ expect(fingerprint).to eq("5468e2dc64ea92b617782aae884b35af60041ac9e168a283615b6a462c54c13d42fa9542cce9b7d76a8124ac6616818905e3e5dd35d6e519f77c3b517558639a")
109
+ end
110
+ context "with HMAC" do
111
+ let(:config) { super.merge("key" => "longencryptionkey") }
112
+ it "fingerprints the value" do
113
+ expect(fingerprint).to eq("11c19b326936c08d6c50a3c847d883e5a1362e6a64dd55201a25f2c1ac1b673f7d8bf15b8f112a4978276d573275e3b14166e17246f670c2a539401c5bfdace8")
114
+ end
115
+ context "and base64 encoding" do
116
+ let(:config) { super.merge("base64encode" => true) }
117
+ it "fingerprints the value" do
118
+ expect(fingerprint).to eq("EcGbMmk2wI1sUKPIR9iD5aE2Lmpk3VUgGiXywawbZz99i/FbjxEqSXgnbVcydeOxQWbhckb2cMKlOUAcW/2s6A==")
119
+ end
120
+ end
121
+ end
147
122
  end
148
- end
149
-
150
- describe "fingerprint string with SHA256 HMAC algorithm and base64 encoding" do
151
- config <<-CONFIG
152
- filter {
153
- fingerprint {
154
- source => ["clientip"]
155
- key => "longencryptionkey"
156
- method => 'SHA256'
157
- base64encode => true
158
- }
159
- }
160
- CONFIG
161
-
162
- sample("clientip" => "123.123.123.123") do
163
- insist { subject.get("fingerprint") } == "NFvsPv8kLVO1aJFsJhCz45PYhda5bWQ/OElP10v0qco="
123
+ context "the MD5 algorithm" do
124
+ let(:fingerprint_method) { "MD5" }
125
+ it "fingerprints the value" do
126
+ expect(fingerprint).to eq("ccdd8d3d940a01b2fb3258c059924c0d")
127
+ end
128
+ context "with HMAC" do
129
+ let(:config) { super.merge("key" => "longencryptionkey") }
130
+ it "fingerprints the value" do
131
+ expect(fingerprint).to eq("9336c879e305c9604a3843fc3e75948f")
132
+ end
133
+ context "and base64 encoding" do
134
+ let(:config) { super.merge("base64encode" => true) }
135
+ it "fingerprints the value" do
136
+ expect(fingerprint).to eq("kzbIeeMFyWBKOEP8PnWUjw==")
137
+ end
138
+ end
139
+ end
164
140
  end
165
141
  end
166
142
 
167
- describe "fingerprint string with SHA384 algorithm" do
168
- config <<-CONFIG
169
- filter {
170
- fingerprint {
171
- source => ["clientip"]
172
- method => 'SHA384'
173
- }
174
- }
175
- CONFIG
143
+ context "multiple values in the source field" do
144
+ let(:config) { super.merge("source" => ["clientip" ]) }
145
+ let(:data) { { "clientip" => [ "123.123.123.123", "223.223.223.223" ] } }
176
146
 
177
- sample("clientip" => "123.123.123.123") do
178
- insist { subject.get("fingerprint") } == "fd605b0a3af3e04ce0d7a0b0d9c48d67a12dab811f60072e6eae84e35d567793ffb68a1807536f11c90874065c2a4392"
147
+ it "produces a fingerprint array" do
148
+ expect(fingerprint).to eq(["3a5076c520b4b463f43806896ea0b3978d09dcae", "47bbc4e06edebbace047fed35abeceec64968b81"])
179
149
  end
180
150
  end
181
151
 
182
- describe "fingerprint string with SHA384 HMAC algorithm" do
183
- config <<-CONFIG
184
- filter {
185
- fingerprint {
186
- source => ["clientip"]
187
- key => "longencryptionkey"
188
- method => 'SHA384'
189
- }
190
- }
191
- CONFIG
192
-
193
- sample("clientip" => "123.123.123.123") do
194
- insist { subject.get("fingerprint") } == "22d4c0e8c4fbcdc4887d2038fca7650f0e2e0e2457ff41c06eb2a980dded6749561c814fe182aff93e2538d18593947a"
152
+ describe "concatenate_all_fields" do
153
+ let(:config) { { "concatenate_all_fields" => true } }
154
+ # The @timestamp field is specified in this sample event as we need the event contents to be constant for the tests
155
+ let(:data) do
156
+ { "@timestamp" => "2017-07-26T14:44:27.064Z", "clientip" => "123.123.123.123", "message" => "This is a test message", "log_level" => "INFO", "offset" => 123456789, "type" => "test" }
195
157
  end
196
- end
197
-
198
- describe "fingerprint string with SHA384 HMAC algorithm and base64 encoding" do
199
- config <<-CONFIG
200
- filter {
201
- fingerprint {
202
- source => ["clientip"]
203
- key => "longencryptionkey"
204
- method => 'SHA384'
205
- base64encode => true
206
- }
207
- }
208
- CONFIG
209
158
 
210
- sample("clientip" => "123.123.123.123") do
211
- insist { subject.get("fingerprint") } == "ItTA6MT7zcSIfSA4/KdlDw4uDiRX/0HAbrKpgN3tZ0lWHIFP4YKv+T4lONGFk5R6"
159
+ it "fingerprints the concatenated values" do
160
+ expect(fingerprint).to eq("cbf022518e97860403160ed8a41847c0db104e63")
212
161
  end
213
162
  end
214
163
 
215
- describe "fingerprint string with SHA512 algorithm" do
216
- config <<-CONFIG
217
- filter {
218
- fingerprint {
219
- source => ["clientip"]
220
- method => 'SHA512'
221
- }
222
- }
223
- CONFIG
164
+ context "when multiple fields are used" do
165
+ let(:config) { super.merge("source" => ['field1', 'field2']) }
166
+ let(:data) { { "field1" => "test1", "field2" => "test2" } }
224
167
 
225
- sample("clientip" => "123.123.123.123") do
226
- insist { subject.get("fingerprint") } == "5468e2dc64ea92b617782aae884b35af60041ac9e168a283615b6a462c54c13d42fa9542cce9b7d76a8124ac6616818905e3e5dd35d6e519f77c3b517558639a"
168
+ it "fingerprints the value of the last value" do
169
+ # SHA1 of "test2"
170
+ expect(fingerprint).to eq("109f4b3c50d7b0df729d299bc6f8e9ef9066971f")
227
171
  end
228
- end
229
-
230
- describe "fingerprint string with SHA512 HMAC algorithm" do
231
- config <<-CONFIG
232
- filter {
233
- fingerprint {
234
- source => ["clientip"]
235
- key => "longencryptionkey"
236
- method => 'SHA512'
237
- }
238
- }
239
- CONFIG
240
172
 
241
- sample("clientip" => "123.123.123.123") do
242
- insist { subject.get("fingerprint") } == "11c19b326936c08d6c50a3c847d883e5a1362e6a64dd55201a25f2c1ac1b673f7d8bf15b8f112a4978276d573275e3b14166e17246f670c2a539401c5bfdace8"
173
+ describe "with concatenate_sources" do
174
+ let(:config) { super.merge("concatenate_sources" => true) }
175
+ it "fingerprints the value of concatenated key/pairs" do
176
+ # SHA1 of "|field1|test1|field2|test2|"
177
+ expect(fingerprint).to eq("e3b6b71eedc656f1d29408264e8a75535db985cb")
178
+ end
243
179
  end
244
180
  end
245
181
 
246
- describe "fingerprint string with SHA512 HMAC algorithm and base64 encoding" do
247
- config <<-CONFIG
248
- filter {
249
- fingerprint {
250
- source => ["clientip"]
251
- key => "longencryptionkey"
252
- method => 'SHA512'
253
- base64encode => true
254
- }
255
- }
256
- CONFIG
182
+ describe "PUNCTUATION method" do
183
+ let(:fingerprint_method) { 'PUNCTUATION' }
184
+ let(:config) { super.merge("source" => 'field1') }
185
+ let(:data) { { "field1" => "PHP Warning: json_encode() [<a href='function.json-encode'>function.json-encode</a>]: Invalid UTF-8 sequence in argument in /var/www/htdocs/test.php on line 233" } }
257
186
 
258
- sample("clientip" => "123.123.123.123") do
259
- insist { subject.get("fingerprint") } == "EcGbMmk2wI1sUKPIR9iD5aE2Lmpk3VUgGiXywawbZz99i/FbjxEqSXgnbVcydeOxQWbhckb2cMKlOUAcW/2s6A=="
187
+ it "extracts punctiation as the fingerprint" do
188
+ expect(fingerprint).to eq(":_()[<='.-'>.-</>]:-////.")
260
189
  end
261
190
  end
262
191
 
263
- describe "fingerprint string with MD5 algorithm" do
264
- config <<-CONFIG
265
- filter {
266
- fingerprint {
267
- source => ["clientip"]
268
- method => 'MD5'
269
- }
270
- }
271
- CONFIG
192
+ context 'Timestamps' do
193
+ epoch_time = Time.at(0).gmtime
194
+ let(:config) { super.merge("source" => ['@timestamp']) }
272
195
 
273
- sample("clientip" => "123.123.123.123") do
274
- insist { subject.get("fingerprint") } == "ccdd8d3d940a01b2fb3258c059924c0d"
196
+ describe 'OpenSSL Fingerprinting' do
197
+ let(:config) { super.merge("key" => '0123') }
198
+ let(:fingerprint_method) { "SHA1" }
199
+ let(:data) { { "@timestamp" => epoch_time } }
200
+ it "fingerprints the timestamp correctly" do
201
+ expect(fingerprint).to eq('1d5379ec92d86a67cfc642d55aa050ca312d3b9a')
202
+ end
275
203
  end
276
- end
277
-
278
- describe "fingerprint string with MD5 HMAC algorithm" do
279
- config <<-CONFIG
280
- filter {
281
- fingerprint {
282
- source => ["clientip"]
283
- key => "longencryptionkey"
284
- method => 'MD5'
285
- }
286
- }
287
- CONFIG
288
204
 
289
- sample("clientip" => "123.123.123.123") do
290
- insist { subject.get("fingerprint") } == "9336c879e305c9604a3843fc3e75948f"
205
+ describe 'MURMUR3 Fingerprinting' do
206
+ let(:fingerprint_method) { "MURMUR3" }
207
+ let(:data) { { "@timestamp" => epoch_time } }
208
+ it "fingerprints the timestamp correctly" do
209
+ expect(fingerprint).to eq(743372282)
210
+ end
291
211
  end
292
212
  end
293
213
 
294
- describe "fingerprint string with MD5 HMAC algorithm and base64 encoding" do
295
- config <<-CONFIG
296
- filter {
297
- fingerprint {
298
- source => ["clientip"]
299
- key => "longencryptionkey"
300
- method => 'MD5'
301
- base64encode => true
302
- }
214
+ describe "post fingerprint execution triggers" do
215
+ let(:fingerprint_method) { "PUNCTUATION" }
216
+ let(:config) do
217
+ {
218
+ "source" => 'field1',
219
+ "add_field" => { 'myfield' => 'myvalue' },
220
+ "add_tag" => ['mytag']
303
221
  }
304
- CONFIG
305
-
306
- sample("clientip" => "123.123.123.123") do
307
- insist { subject.get("fingerprint") } == "kzbIeeMFyWBKOEP8PnWUjw=="
308
222
  end
309
- end
310
-
311
- describe "Test field with multiple values" do
312
- config <<-CONFIG
313
- filter {
314
- fingerprint {
315
- source => ["clientip"]
316
- key => "longencryptionkey"
317
- method => 'MD5'
318
- }
319
- }
320
- CONFIG
223
+ let(:data) { { "field1" => "Hello, World!" } }
321
224
 
322
- sample("clientip" => [ "123.123.123.123", "223.223.223.223" ]) do
323
- insist { subject.get("fingerprint")} == [ "9336c879e305c9604a3843fc3e75948f", "7a6c66b8d3f42a7d650e3354af508df3" ]
225
+ it "adds the new field" do
226
+ expect(event.get("myfield")).to eq("myvalue")
324
227
  end
325
- end
326
-
327
- describe "Concatenate multiple values into 1" do
328
- config <<-CONFIG
329
- filter {
330
- fingerprint {
331
- source => ['field1', 'field2']
332
- key => "longencryptionkey"
333
- method => 'MD5'
334
- }
335
- }
336
- CONFIG
337
-
338
- sample("field1" => "test1", "field2" => "test2") do
339
- insist { subject.get("fingerprint")} == "872da745e45192c2a1d4bf7c1ff8a370"
228
+ it "adds the new tag" do
229
+ expect(event.get("tags")).to include("mytag")
340
230
  end
341
231
  end
342
232
 
343
- describe "PUNCTUATION method" do
344
- config <<-CONFIG
345
- filter {
346
- fingerprint {
347
- source => 'field1'
348
- method => 'PUNCTUATION'
349
- }
350
- }
351
- CONFIG
352
-
353
- sample("field1" => "PHP Warning: json_encode() [<a href='function.json-encode'>function.json-encode</a>]: Invalid UTF-8 sequence in argument in /var/www/htdocs/test.php on line 233") do
354
- insist { subject.get("fingerprint") } == ":_()[<='.-'>.-</>]:-////."
233
+ describe "tolerance to hash order" do
234
+ # insertion order can influence the result of to_hash's keys
235
+ let(:data1) { {
236
+ "a" => {"a0" => 0, "a1" => 1},
237
+ "b" => {"b0" => 0, "b1" => 1},
238
+ } }
239
+ let(:event1) { LogStash::Event.new(data1) }
240
+ let(:data2) { {
241
+ "b" => {"b1" => 1, "b0" => 0},
242
+ "a" => {"a1" => 1, "a0" => 0},
243
+ } }
244
+ let(:event2) { LogStash::Event.new(data2) }
245
+ let(:config) { { "source" => [ "a" ] } }
246
+
247
+ before(:each) do
248
+ # for testing purposes we want to ensure the hash order is different.
249
+ # since we can't easily control the order on the underlying Map,
250
+ # we're mocking the order here:
251
+ allow(event1).to receive(:to_hash).and_return(data1)
252
+ allow(event2).to receive(:to_hash).and_return(data2)
253
+ # by default event.get(key) fetches data from the event.
254
+ # mocking the default value has to be done first, and only
255
+ # then we can mock the getting "a" and "b"
256
+ allow(event1).to receive(:get).and_call_original
257
+ allow(event2).to receive(:get).and_call_original
258
+ # mock event.get("a") and event.get("b") for both events
259
+ # so we can inject an inconsistent order for the tests
260
+ allow(event1).to receive(:get).with("a") {|arg| data1["a"] }
261
+ allow(event1).to receive(:get).with("b") {|arg| data1["b"] }
262
+ allow(event2).to receive(:get).with("a") {|arg| data2["a"] }
263
+ allow(event2).to receive(:get).with("b") {|arg| data2["b"] }
264
+ plugin.filter(event1)
265
+ plugin.filter(event2)
355
266
  end
356
-
357
- sample("field1" => "Warning: Ruby(ルビ) is an awesome language.") do
358
- insist { subject.get("fingerprint") } == ":()."
267
+ it "computes the same hash" do
268
+ # confirm the order of the keys in the nested hash is different
269
+ # (of course it is, we just mocked the to_hash return)
270
+ expect(event1.to_hash["a"].keys).to_not eq(event2.to_hash["a"].keys)
271
+ # let's check that the fingerprint doesn't care about the insertion order
272
+ expect(event1.get("fingerprint")).to eq(event2.get("fingerprint"))
359
273
  end
360
- end
361
-
362
- context 'Timestamps' do
363
- epoch_time = Time.at(0).gmtime
364
-
365
- describe 'OpenSSL Fingerprinting' do
366
- config <<-CONFIG
367
- filter {
368
- fingerprint {
369
- source => ['@timestamp']
370
- key => '0123'
371
- method => 'SHA1'
372
- }
373
- }
374
- CONFIG
375
-
376
- sample("@timestamp" => epoch_time) do
377
- insist { subject.get("fingerprint") } == '1d5379ec92d86a67cfc642d55aa050ca312d3b9a'
274
+ context "concatenate_sources" do
275
+ let("config") { { "source" => [ "a", "b"], "concatenate_sources" => true } }
276
+ it "computes the same hash" do
277
+ expect(event1.get("fingerprint")).to eq(event2.get("fingerprint"))
378
278
  end
379
279
  end
380
-
381
- describe 'MURMUR3 Fingerprinting' do
382
- config <<-CONFIG
383
- filter {
384
- fingerprint {
385
- source => ['@timestamp']
386
- method => 'MURMUR3'
387
- }
388
- }
389
- CONFIG
390
-
391
- sample("@timestamp" => epoch_time) do
392
- insist { subject.get("fingerprint") } == 743372282
280
+ context "concatenate_all_fields => true" do
281
+ let(:config) { { "concatenate_all_fields" => true } }
282
+ it "computes the same hash" do
283
+ expect(event1.get("fingerprint")).to eq(event2.get("fingerprint"))
393
284
  end
394
285
  end
395
286
  end
396
-
397
- describe "execution triggers addition of fields and tags" do
398
- config <<-CONFIG
399
- filter {
400
- fingerprint {
401
- source => 'field1'
402
- method => 'PUNCTUATION'
403
- add_field => { 'myfield' => 'myvalue' }
404
- add_tag => ['mytag']
405
- }
406
- }
407
- CONFIG
408
-
409
- sample("field1" => "Hello, World!") do
410
- insist { subject.get("myfield") } == "myvalue"
411
- insist { subject.get("tags") } == ["mytag"]
412
- end
413
- end
414
-
415
287
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-filter-fingerprint
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-23 00:00:00.000000000 Z
11
+ date: 2020-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement