logstash-filter-fingerprint 3.2.3 → 3.3.2
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 +4 -4
- data/CHANGELOG.md +12 -0
- data/docs/index.asciidoc +36 -8
- data/lib/logstash/filters/fingerprint.rb +15 -5
- data/logstash-filter-fingerprint.gemspec +2 -1
- data/spec/filters/fingerprint_spec.rb +231 -199
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6c1e99ae34e1681eba14af022fa9fc79909bfaf306542326f0ae88881810370
|
4
|
+
data.tar.gz: ffe46fdd11dc94d0fdcdb35d889ec41f3560f205c78e872985ea426fdd776cef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c343f48b3f479e5a38c4de0510fc9a51b6785c4437441206baf491c1373df9abae24d30b5efc4f761295857e7d376c7c276e69378ed305e53432fe55e6e5477a
|
7
|
+
data.tar.gz: 964ad01b6482e60069421ec1c26c1ad422c44e88372ff63305d752210fd31755752409af9dd2bee96adf8fafa9b9065ca1ce40b53f18da05b5a76b86a54fffb0
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
## 3.3.2
|
2
|
+
- [DOC] Clarify behavior when key is set [#65](https://github.com/logstash-plugins/logstash-filter-fingerprint/pull/65).
|
3
|
+
|
4
|
+
## 3.3.1
|
5
|
+
- Force encoding to UTF-8 when concatenating sources to generate fingerprint [#64](https://github.com/logstash-plugins/logstash-filter-fingerprint/pull/64)
|
6
|
+
|
7
|
+
## 3.3.0
|
8
|
+
- Add ECS compatibility [#62](https://github.com/logstash-plugins/logstash-filter-fingerprint/pull/62)
|
9
|
+
|
10
|
+
## 3.2.4
|
11
|
+
- Fixed the error in Murmur3 with Integer [#61](https://github.com/logstash-plugins/logstash-filter-fingerprint/pull/61)
|
12
|
+
|
1
13
|
## 3.2.3
|
2
14
|
- [DOC] Expanded description for concatenate_sources behavior and provided examples [#60](https://github.com/logstash-plugins/logstash-filter-fingerprint/pull/60)
|
3
15
|
|
data/docs/index.asciidoc
CHANGED
@@ -23,15 +23,30 @@ include::{include_path}/plugin_header.asciidoc[]
|
|
23
23
|
Create consistent hashes (fingerprints) of one or more fields and store
|
24
24
|
the result in a new field.
|
25
25
|
|
26
|
-
You can use this plugin to create consistent document ids when
|
26
|
+
You can use this plugin to create consistent document ids when events are
|
27
27
|
inserted into Elasticsearch. This approach means that existing documents can be
|
28
28
|
updated instead of creating new documents.
|
29
29
|
|
30
|
-
NOTE: When the `
|
30
|
+
NOTE: When the `method` option is set to `UUID` the result won't be
|
31
31
|
a consistent hash but a random
|
32
32
|
https://en.wikipedia.org/wiki/Universally_unique_identifier[UUID].
|
33
33
|
To generate UUIDs, prefer the {logstash-ref}/plugins-filters-uuid.html[uuid filter].
|
34
34
|
|
35
|
+
[id="plugins-{type}s-{plugin}-ecs_metadata"]
|
36
|
+
==== Event Metadata and the Elastic Common Schema (ECS)
|
37
|
+
This plugin adds a hash value to event as an identifier. You can configure the `target` option to change the output field.
|
38
|
+
|
39
|
+
When ECS compatibility is disabled, the hash value is stored in the `fingerprint` field.
|
40
|
+
When ECS is enabled, the value is stored in the `[event][hash]` field.
|
41
|
+
|
42
|
+
Here’s how ECS compatibility mode affects output.
|
43
|
+
[cols="<l,<l,e,<e"]
|
44
|
+
|=======================================================================
|
45
|
+
| ECS disabled | ECS v1 | Availability | Description
|
46
|
+
|
47
|
+
| fingerprint | [event][hash] | Always | a hash value of event
|
48
|
+
|=======================================================================
|
49
|
+
|
35
50
|
[id="plugins-{type}s-{plugin}-options"]
|
36
51
|
==== Fingerprint Filter Configuration Options
|
37
52
|
|
@@ -43,6 +58,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
|
|
43
58
|
| <<plugins-{type}s-{plugin}-base64encode>> |<<boolean,boolean>>|No
|
44
59
|
| <<plugins-{type}s-{plugin}-concatenate_sources>> |<<boolean,boolean>>|No
|
45
60
|
| <<plugins-{type}s-{plugin}-concatenate_all_fields>> |<<boolean,boolean>>|No
|
61
|
+
| <<plugins-{type}s-{plugin}-ecs_compatibility>> | <<string,string>>|No
|
46
62
|
| <<plugins-{type}s-{plugin}-key>> |<<string,string>>|No
|
47
63
|
| <<plugins-{type}s-{plugin}-method>> |<<string,string>>, one of `["SHA1", "SHA256", "SHA384", "SHA512", "MD5", "MURMUR3", "IPV4_NETWORK", "UUID", "PUNCTUATION"]`|Yes
|
48
64
|
| <<plugins-{type}s-{plugin}-source>> |<<array,array>>|No
|
@@ -133,6 +149,18 @@ fingerprint computation. If `false` and at least one source field is
|
|
133
149
|
given, the target field will be an array with fingerprints of the
|
134
150
|
source fields given.
|
135
151
|
|
152
|
+
[id="plugins-{type}s-{plugin}-ecs_compatibility"]
|
153
|
+
===== `ecs_compatibility`
|
154
|
+
|
155
|
+
* Value type is <<string,string>>
|
156
|
+
* Supported values are:
|
157
|
+
** `disabled`: unstructured data added at root level
|
158
|
+
** `v1`: uses `[event][hash]` fields that are compatible with Elastic Common Schema
|
159
|
+
|
160
|
+
Controls this plugin's compatibility with the
|
161
|
+
{ecs-ref}[Elastic Common Schema (ECS)].
|
162
|
+
See <<plugins-{type}s-{plugin}-ecs_metadata>> for detailed information.
|
163
|
+
|
136
164
|
[id="plugins-{type}s-{plugin}-key"]
|
137
165
|
===== `key`
|
138
166
|
|
@@ -151,12 +179,11 @@ With other methods, optionally fill in the HMAC key.
|
|
151
179
|
|
152
180
|
The fingerprint method to use.
|
153
181
|
|
154
|
-
If set to `SHA1`, `SHA256`, `SHA384`, `SHA512`, or `MD5` and a key is set,
|
155
|
-
|
156
|
-
|
157
|
-
be used.
|
182
|
+
If set to `SHA1`, `SHA256`, `SHA384`, `SHA512`, or `MD5` and a key is set, the
|
183
|
+
corresponding cryptographic hash function and the keyed-hash (HMAC) digest function
|
184
|
+
are used to generate the fingerprint.
|
158
185
|
|
159
|
-
If set to `MURMUR3` the non-cryptographic MurmurHash function will be used.
|
186
|
+
If set to `MURMUR3` the non-cryptographic 64 bit MurmurHash function will be used.
|
160
187
|
|
161
188
|
If set to `IPV4_NETWORK` the input data needs to be a IPv4 address and
|
162
189
|
the hash value will be the masked-out address using the number of bits
|
@@ -184,7 +211,8 @@ to create the fingerprint. If an array is given, see the
|
|
184
211
|
===== `target`
|
185
212
|
|
186
213
|
* Value type is <<string,string>>
|
187
|
-
* Default value is `"fingerprint"`
|
214
|
+
* Default value is `"fingerprint"` when ECS is disabled
|
215
|
+
* Default value is `"[event][hash]"` when ECS is enabled
|
188
216
|
|
189
217
|
The name of the field where the generated fingerprint will be stored.
|
190
218
|
Any current contents of that field will be overwritten.
|
@@ -6,6 +6,7 @@ require "openssl"
|
|
6
6
|
require "ipaddr"
|
7
7
|
require "murmurhash3"
|
8
8
|
require "securerandom"
|
9
|
+
require "logstash/plugin_mixins/ecs_compatibility_support"
|
9
10
|
|
10
11
|
# Create consistent hashes (fingerprints) of one or more fields and store
|
11
12
|
# the result in a new field.
|
@@ -22,6 +23,8 @@ require "securerandom"
|
|
22
23
|
# https://en.wikipedia.org/wiki/Universally_unique_identifier[UUID].
|
23
24
|
# To generate UUIDs, prefer the <<plugins-filters-uuid,uuid filter>>.
|
24
25
|
class LogStash::Filters::Fingerprint < LogStash::Filters::Base
|
26
|
+
include LogStash::PluginMixins::ECSCompatibilitySupport(:disabled, :v1, :v8 => :v1)
|
27
|
+
|
25
28
|
config_name "fingerprint"
|
26
29
|
|
27
30
|
# The name(s) of the source field(s) whose contents will be used
|
@@ -31,7 +34,7 @@ class LogStash::Filters::Fingerprint < LogStash::Filters::Base
|
|
31
34
|
|
32
35
|
# The name of the field where the generated fingerprint will be stored.
|
33
36
|
# Any current contents of that field will be overwritten.
|
34
|
-
config :target, :validate => :string
|
37
|
+
config :target, :validate => :string
|
35
38
|
|
36
39
|
# When used with the `IPV4_NETWORK` method fill in the subnet prefix length.
|
37
40
|
# With other methods, optionally fill in the HMAC key.
|
@@ -76,6 +79,11 @@ class LogStash::Filters::Fingerprint < LogStash::Filters::Base
|
|
76
79
|
# without having to proide the field names in the `source` attribute
|
77
80
|
config :concatenate_all_fields, :validate => :boolean, :default => false
|
78
81
|
|
82
|
+
def initialize(*params)
|
83
|
+
super
|
84
|
+
@target ||= ecs_select[disabled: 'fingerprint', v1: '[event][hash]']
|
85
|
+
end
|
86
|
+
|
79
87
|
def register
|
80
88
|
# convert to symbol for faster comparisons
|
81
89
|
@method = @method.to_sym
|
@@ -120,11 +128,13 @@ class LogStash::Filters::Fingerprint < LogStash::Filters::Base
|
|
120
128
|
to_string = ""
|
121
129
|
if @concatenate_all_fields
|
122
130
|
deep_sort_hashes(event.to_hash).each do |k,v|
|
123
|
-
|
131
|
+
# Force encoding to UTF-8 to get around https://github.com/jruby/jruby/issues/6748
|
132
|
+
to_string << "|#{k}|#{v}".force_encoding("UTF-8")
|
124
133
|
end
|
125
134
|
else
|
126
135
|
@source.sort.each do |k|
|
127
|
-
|
136
|
+
# Force encoding to UTF-8 to get around https://github.com/jruby/jruby/issues/6748
|
137
|
+
to_string << "|#{k}|#{deep_sort_hashes(event.get(k))}".force_encoding("UTF-8")
|
128
138
|
end
|
129
139
|
end
|
130
140
|
to_string << "|"
|
@@ -193,8 +203,8 @@ class LogStash::Filters::Fingerprint < LogStash::Filters::Base
|
|
193
203
|
|
194
204
|
def fingerprint_murmur3(value)
|
195
205
|
case value
|
196
|
-
when
|
197
|
-
MurmurHash3::V32.
|
206
|
+
when Integer
|
207
|
+
MurmurHash3::V32.int64_hash(value)
|
198
208
|
else
|
199
209
|
MurmurHash3::V32.str_hash(value.to_s)
|
200
210
|
end
|
@@ -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
|
4
|
+
s.version = '3.3.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"
|
@@ -23,5 +23,6 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
|
24
24
|
s.add_runtime_dependency "murmurhash3" #(MIT license)
|
25
25
|
s.add_development_dependency 'logstash-devutils'
|
26
|
+
s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~>1.2'
|
26
27
|
end
|
27
28
|
|
@@ -1,287 +1,319 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "logstash/devutils/rspec/spec_helper"
|
3
3
|
require "logstash/filters/fingerprint"
|
4
|
+
require 'logstash/plugin_mixins/ecs_compatibility_support/spec_helper'
|
4
5
|
|
5
|
-
describe LogStash::Filters::Fingerprint do
|
6
|
+
describe LogStash::Filters::Fingerprint, :ecs_compatibility_support, :aggregate_failures do
|
7
|
+
ecs_compatibility_matrix(:disabled, :v1, :v8 => :v1) do |ecs_select|
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
let(:plugin) { described_class.new(config) }
|
10
|
+
let(:config) { { "method" => fingerprint_method } }
|
11
|
+
let(:fingerprint_method) { "SHA1" } # default
|
12
|
+
let(:data) { {} }
|
13
|
+
let(:event) { LogStash::Event.new(data) }
|
14
|
+
let(:fingerprint) { event.get(ecs_select[disabled: "fingerprint", v1: "[event][hash]"]) }
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
before(:each) do
|
17
|
+
allow_any_instance_of(described_class).to receive(:ecs_compatibility).and_return(ecs_compatibility)
|
18
|
+
plugin.register
|
19
|
+
plugin.filter(event)
|
20
|
+
end
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
context "with a string field" do
|
23
|
+
let(:data) { {"clientip" => "123.123.123.123" } }
|
24
|
+
let(:config) { super().merge("source" => ["clientip" ]) }
|
22
25
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
describe "the IPV4_NETWORK method" do
|
27
|
+
let(:fingerprint_method) { "IPV4_NETWORK" }
|
28
|
+
let(:config) { super().merge("key" => 24) }
|
26
29
|
|
27
|
-
|
28
|
-
|
30
|
+
it "fingerprints the ip as the network" do
|
31
|
+
expect(fingerprint).to eq("123.123.123.0")
|
32
|
+
end
|
29
33
|
end
|
30
|
-
end
|
31
34
|
|
32
|
-
|
33
|
-
|
35
|
+
describe "the MURMUR3 method" do
|
36
|
+
let(:fingerprint_method) { "MURMUR3" }
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
38
|
+
context "string" do
|
39
|
+
it "fingerprints the value" do
|
40
|
+
expect(fingerprint).to eq(4013733395)
|
41
|
+
end
|
42
|
+
end
|
39
43
|
|
40
|
-
|
41
|
-
|
44
|
+
context "number" do
|
45
|
+
let(:data) { {"clientip" => 123 } }
|
42
46
|
|
43
|
-
|
44
|
-
|
47
|
+
it "fingerprints the value" do
|
48
|
+
expect(fingerprint).to eq(823512154)
|
49
|
+
end
|
50
|
+
end
|
45
51
|
end
|
46
52
|
|
47
|
-
|
48
|
-
let(:
|
53
|
+
describe "the SHA1 method" do
|
54
|
+
let(:fingerprint_method) { "SHA1" }
|
49
55
|
|
50
56
|
it "fingerprints the value" do
|
51
|
-
expect(fingerprint).to eq("
|
57
|
+
expect(fingerprint).to eq("3a5076c520b4b463f43806896ea0b3978d09dcae")
|
52
58
|
end
|
53
|
-
|
54
|
-
|
59
|
+
|
60
|
+
context "with HMAC" do
|
61
|
+
let(:config) { super().merge("key" => "longencryptionkey") }
|
62
|
+
|
55
63
|
it "fingerprints the value" do
|
56
|
-
expect(fingerprint).to eq("
|
64
|
+
expect(fingerprint).to eq("fdc60acc4773dc5ac569ffb78fcb93c9630797f4")
|
65
|
+
end
|
66
|
+
context "with HMAC and base64 encoding" do
|
67
|
+
let(:config) { super().merge("base64encode" => true) }
|
68
|
+
it "fingerprints the value" do
|
69
|
+
expect(fingerprint).to eq("/cYKzEdz3FrFaf+3j8uTyWMHl/Q=")
|
70
|
+
end
|
57
71
|
end
|
58
72
|
end
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
73
|
+
context "and base64 encoding" do
|
74
|
+
let(:config) { super().merge("base64encode" => true) }
|
75
|
+
it "fingerprints the value" do
|
76
|
+
expect(fingerprint).to eq("OlB2xSC0tGP0OAaJbqCzl40J3K4=")
|
77
|
+
end
|
64
78
|
end
|
65
79
|
end
|
66
|
-
end
|
67
80
|
|
68
|
-
|
69
|
-
|
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") }
|
81
|
+
context "the SHA256 algorithm" do
|
82
|
+
let(:fingerprint_method) { "SHA256" }
|
75
83
|
it "fingerprints the value" do
|
76
|
-
expect(fingerprint).to eq("
|
84
|
+
expect(fingerprint).to eq("4dabcab210766e35f03e77120e6986d6e6d4752b2a9ff22980b9253d026080d8")
|
77
85
|
end
|
78
|
-
context "
|
79
|
-
let(:config) { super().merge("
|
86
|
+
context "with HMAC" do
|
87
|
+
let(:config) { super().merge("key" => "longencryptionkey") }
|
80
88
|
it "fingerprints the value" do
|
81
|
-
expect(fingerprint).to eq("
|
89
|
+
expect(fingerprint).to eq("345bec3eff242d53b568916c2610b3e393d885d6b96d643f38494fd74bf4a9ca")
|
90
|
+
end
|
91
|
+
context "and base64 encoding" do
|
92
|
+
let(:config) { super().merge("base64encode" => true) }
|
93
|
+
it "fingerprints the value" do
|
94
|
+
expect(fingerprint).to eq("NFvsPv8kLVO1aJFsJhCz45PYhda5bWQ/OElP10v0qco=")
|
95
|
+
end
|
82
96
|
end
|
83
97
|
end
|
84
98
|
end
|
85
|
-
end
|
86
99
|
|
87
|
-
|
88
|
-
|
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") }
|
100
|
+
context "the SHA384 algorithm" do
|
101
|
+
let(:fingerprint_method) { "SHA384" }
|
94
102
|
it "fingerprints the value" do
|
95
|
-
expect(fingerprint).to eq("
|
103
|
+
expect(fingerprint).to eq("fd605b0a3af3e04ce0d7a0b0d9c48d67a12dab811f60072e6eae84e35d567793ffb68a1807536f11c90874065c2a4392")
|
96
104
|
end
|
97
|
-
context "
|
98
|
-
let(:config) { super().merge("
|
105
|
+
context "with HMAC" do
|
106
|
+
let(:config) { super().merge("key" => "longencryptionkey") }
|
99
107
|
it "fingerprints the value" do
|
100
|
-
expect(fingerprint).to eq("
|
108
|
+
expect(fingerprint).to eq("22d4c0e8c4fbcdc4887d2038fca7650f0e2e0e2457ff41c06eb2a980dded6749561c814fe182aff93e2538d18593947a")
|
109
|
+
end
|
110
|
+
context "and base64 encoding" do
|
111
|
+
let(:config) { super().merge("base64encode" => true) }
|
112
|
+
it "fingerprints the value" do
|
113
|
+
expect(fingerprint).to eq("ItTA6MT7zcSIfSA4/KdlDw4uDiRX/0HAbrKpgN3tZ0lWHIFP4YKv+T4lONGFk5R6")
|
114
|
+
end
|
101
115
|
end
|
102
116
|
end
|
103
117
|
end
|
104
|
-
|
105
|
-
|
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") }
|
118
|
+
context "the SHA512 algorithm" do
|
119
|
+
let(:fingerprint_method) { "SHA512" }
|
112
120
|
it "fingerprints the value" do
|
113
|
-
expect(fingerprint).to eq("
|
121
|
+
expect(fingerprint).to eq("5468e2dc64ea92b617782aae884b35af60041ac9e168a283615b6a462c54c13d42fa9542cce9b7d76a8124ac6616818905e3e5dd35d6e519f77c3b517558639a")
|
114
122
|
end
|
115
|
-
context "
|
116
|
-
let(:config) { super().merge("
|
123
|
+
context "with HMAC" do
|
124
|
+
let(:config) { super().merge("key" => "longencryptionkey") }
|
117
125
|
it "fingerprints the value" do
|
118
|
-
expect(fingerprint).to eq("
|
126
|
+
expect(fingerprint).to eq("11c19b326936c08d6c50a3c847d883e5a1362e6a64dd55201a25f2c1ac1b673f7d8bf15b8f112a4978276d573275e3b14166e17246f670c2a539401c5bfdace8")
|
127
|
+
end
|
128
|
+
context "and base64 encoding" do
|
129
|
+
let(:config) { super().merge("base64encode" => true) }
|
130
|
+
it "fingerprints the value" do
|
131
|
+
expect(fingerprint).to eq("EcGbMmk2wI1sUKPIR9iD5aE2Lmpk3VUgGiXywawbZz99i/FbjxEqSXgnbVcydeOxQWbhckb2cMKlOUAcW/2s6A==")
|
132
|
+
end
|
119
133
|
end
|
120
134
|
end
|
121
135
|
end
|
122
|
-
|
123
|
-
|
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") }
|
136
|
+
context "the MD5 algorithm" do
|
137
|
+
let(:fingerprint_method) { "MD5" }
|
130
138
|
it "fingerprints the value" do
|
131
|
-
expect(fingerprint).to eq("
|
139
|
+
expect(fingerprint).to eq("ccdd8d3d940a01b2fb3258c059924c0d")
|
132
140
|
end
|
133
|
-
context "
|
134
|
-
let(:config) { super().merge("
|
141
|
+
context "with HMAC" do
|
142
|
+
let(:config) { super().merge("key" => "longencryptionkey") }
|
135
143
|
it "fingerprints the value" do
|
136
|
-
expect(fingerprint).to eq("
|
144
|
+
expect(fingerprint).to eq("9336c879e305c9604a3843fc3e75948f")
|
145
|
+
end
|
146
|
+
context "and base64 encoding" do
|
147
|
+
let(:config) { super().merge("base64encode" => true) }
|
148
|
+
it "fingerprints the value" do
|
149
|
+
expect(fingerprint).to eq("kzbIeeMFyWBKOEP8PnWUjw==")
|
150
|
+
end
|
137
151
|
end
|
138
152
|
end
|
139
153
|
end
|
140
154
|
end
|
141
|
-
end
|
142
155
|
|
143
|
-
|
144
|
-
|
145
|
-
|
156
|
+
context "multiple values in the source field" do
|
157
|
+
let(:config) { super().merge("source" => ["clientip" ]) }
|
158
|
+
let(:data) { { "clientip" => [ "123.123.123.123", "223.223.223.223" ] } }
|
146
159
|
|
147
|
-
|
148
|
-
|
160
|
+
it "produces a fingerprint array" do
|
161
|
+
expect(fingerprint).to eq(["3a5076c520b4b463f43806896ea0b3978d09dcae", "47bbc4e06edebbace047fed35abeceec64968b81"])
|
162
|
+
end
|
149
163
|
end
|
150
|
-
end
|
151
164
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
165
|
+
describe "concatenate_all_fields" do
|
166
|
+
let(:config) { { "concatenate_all_fields" => true } }
|
167
|
+
# The @timestamp field is specified in this sample event as we need the event contents to be constant for the tests
|
168
|
+
let(:data) do
|
169
|
+
{ "@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" }
|
170
|
+
end
|
158
171
|
|
159
|
-
|
160
|
-
|
172
|
+
it "fingerprints the concatenated values" do
|
173
|
+
expect(fingerprint).to eq("cbf022518e97860403160ed8a41847c0db104e63")
|
174
|
+
end
|
161
175
|
end
|
162
|
-
end
|
163
176
|
|
164
|
-
|
165
|
-
|
166
|
-
|
177
|
+
context "when multiple fields are used" do
|
178
|
+
let(:config) { super().merge("source" => ['field1', 'field2']) }
|
179
|
+
let(:data) { { "field1" => "test1", "field2" => "test2" } }
|
167
180
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
181
|
+
it "fingerprints the value of the last value" do
|
182
|
+
# SHA1 of "test2"
|
183
|
+
expect(fingerprint).to eq("109f4b3c50d7b0df729d299bc6f8e9ef9066971f")
|
184
|
+
end
|
172
185
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
186
|
+
describe "with concatenate_sources" do
|
187
|
+
let(:config) { super().merge("concatenate_sources" => true) }
|
188
|
+
it "fingerprints the value of concatenated key/pairs" do
|
189
|
+
# SHA1 of "|field1|test1|field2|test2|"
|
190
|
+
expect(fingerprint).to eq("e3b6b71eedc656f1d29408264e8a75535db985cb")
|
191
|
+
end
|
178
192
|
end
|
179
193
|
end
|
180
|
-
end
|
181
194
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
195
|
+
context "when utf-8 chars used" do
|
196
|
+
let(:config) { super().merge("source" => ['field1', 'field2']) }
|
197
|
+
let(:data) { {"field1"=>[{"inner_key"=>"🂡"}, {"1"=>"2"}], "field2"=>"🂡"} }
|
198
|
+
it "fingerprints the value of the last value" do
|
199
|
+
# SHA1 of "|field1|inner_key|🂡|1|2|field2|🂡|"
|
200
|
+
expect(fingerprint).to eq("58fa9e0e60c9f0d24b51d84cddb26732a39eeb3d")
|
201
|
+
end
|
186
202
|
|
187
|
-
|
188
|
-
|
203
|
+
describe "with concatenate_sources" do
|
204
|
+
let(:config) { super().merge("concatenate_sources" => true) }
|
205
|
+
it "fingerprints the value of concatenated key/pairs" do
|
206
|
+
# SHA1 of "|field1|inner_key|🂡|1|2|field2|🂡|"
|
207
|
+
expect(fingerprint).to eq("d74f41841c7cdc793a97c218d2ff18064a5f1950")
|
208
|
+
end
|
209
|
+
end
|
189
210
|
end
|
190
|
-
end
|
191
211
|
|
192
|
-
|
193
|
-
|
194
|
-
|
212
|
+
describe "PUNCTUATION method" do
|
213
|
+
let(:fingerprint_method) { 'PUNCTUATION' }
|
214
|
+
let(:config) { super().merge("source" => 'field1') }
|
215
|
+
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" } }
|
195
216
|
|
196
|
-
|
197
|
-
|
198
|
-
let(:fingerprint_method) { "SHA1" }
|
199
|
-
let(:data) { { "@timestamp" => epoch_time } }
|
200
|
-
it "fingerprints the timestamp correctly" do
|
201
|
-
expect(fingerprint).to eq('1d5379ec92d86a67cfc642d55aa050ca312d3b9a')
|
217
|
+
it "extracts punctiation as the fingerprint" do
|
218
|
+
expect(fingerprint).to eq(":_()[<='.-'>.-</>]:-////.")
|
202
219
|
end
|
203
220
|
end
|
204
221
|
|
205
|
-
|
206
|
-
|
207
|
-
let(:
|
208
|
-
|
209
|
-
|
222
|
+
context 'Timestamps' do
|
223
|
+
epoch_time = Time.at(0).gmtime
|
224
|
+
let(:config) { super().merge("source" => ['@timestamp']) }
|
225
|
+
|
226
|
+
describe 'OpenSSL Fingerprinting' do
|
227
|
+
let(:config) { super().merge("key" => '0123') }
|
228
|
+
let(:fingerprint_method) { "SHA1" }
|
229
|
+
let(:data) { { "@timestamp" => epoch_time } }
|
230
|
+
it "fingerprints the timestamp correctly" do
|
231
|
+
expect(fingerprint).to eq('1d5379ec92d86a67cfc642d55aa050ca312d3b9a')
|
232
|
+
end
|
210
233
|
end
|
211
|
-
end
|
212
|
-
end
|
213
234
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
}
|
235
|
+
describe 'MURMUR3 Fingerprinting' do
|
236
|
+
let(:fingerprint_method) { "MURMUR3" }
|
237
|
+
let(:data) { { "@timestamp" => epoch_time } }
|
238
|
+
it "fingerprints the timestamp correctly" do
|
239
|
+
expect(fingerprint).to eq(743372282)
|
240
|
+
end
|
241
|
+
end
|
222
242
|
end
|
223
|
-
let(:data) { { "field1" => "Hello, World!" } }
|
224
243
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
244
|
+
describe "post fingerprint execution triggers" do
|
245
|
+
let(:fingerprint_method) { "PUNCTUATION" }
|
246
|
+
let(:config) do
|
247
|
+
{
|
248
|
+
"source" => 'field1',
|
249
|
+
"add_field" => { 'myfield' => 'myvalue' },
|
250
|
+
"add_tag" => ['mytag']
|
251
|
+
}
|
252
|
+
end
|
253
|
+
let(:data) { { "field1" => "Hello, World!" } }
|
254
|
+
|
255
|
+
it "adds the new field" do
|
256
|
+
expect(event.get("myfield")).to eq("myvalue")
|
257
|
+
end
|
258
|
+
it "adds the new tag" do
|
259
|
+
expect(event.get("tags")).to include("mytag")
|
260
|
+
end
|
230
261
|
end
|
231
|
-
end
|
232
262
|
|
233
|
-
|
234
|
-
|
235
|
-
|
263
|
+
describe "tolerance to hash order" do
|
264
|
+
# insertion order can influence the result of to_hash's keys
|
265
|
+
let(:data1) { {
|
236
266
|
"a" => {"a0" => 0, "a1" => 1},
|
237
267
|
"b" => {"b0" => 0, "b1" => 1},
|
238
|
-
|
239
|
-
|
240
|
-
|
268
|
+
} }
|
269
|
+
let(:event1) { LogStash::Event.new(data1) }
|
270
|
+
let(:data2) { {
|
241
271
|
"b" => {"b1" => 1, "b0" => 0},
|
242
272
|
"a" => {"a1" => 1, "a0" => 0},
|
243
|
-
|
244
|
-
|
245
|
-
|
273
|
+
} }
|
274
|
+
let(:event2) { LogStash::Event.new(data2) }
|
275
|
+
let(:config) { { "source" => [ "a" ] } }
|
246
276
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
end
|
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"))
|
273
|
-
end
|
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"))
|
277
|
+
before(:each) do
|
278
|
+
# for testing purposes we want to ensure the hash order is different.
|
279
|
+
# since we can't easily control the order on the underlying Map,
|
280
|
+
# we're mocking the order here:
|
281
|
+
allow(event1).to receive(:to_hash).and_return(data1)
|
282
|
+
allow(event2).to receive(:to_hash).and_return(data2)
|
283
|
+
# by default event.get(key) fetches data from the event.
|
284
|
+
# mocking the default value has to be done first, and only
|
285
|
+
# then we can mock the getting "a" and "b"
|
286
|
+
allow(event1).to receive(:get).and_call_original
|
287
|
+
allow(event2).to receive(:get).and_call_original
|
288
|
+
# mock event.get("a") and event.get("b") for both events
|
289
|
+
# so we can inject an inconsistent order for the tests
|
290
|
+
allow(event1).to receive(:get).with("a") {|arg| data1["a"] }
|
291
|
+
allow(event1).to receive(:get).with("b") {|arg| data1["b"] }
|
292
|
+
allow(event2).to receive(:get).with("a") {|arg| data2["a"] }
|
293
|
+
allow(event2).to receive(:get).with("b") {|arg| data2["b"] }
|
294
|
+
plugin.filter(event1)
|
295
|
+
plugin.filter(event2)
|
278
296
|
end
|
279
|
-
end
|
280
|
-
context "concatenate_all_fields => true" do
|
281
|
-
let(:config) { { "concatenate_all_fields" => true } }
|
282
297
|
it "computes the same hash" do
|
298
|
+
# confirm the order of the keys in the nested hash is different
|
299
|
+
# (of course it is, we just mocked the to_hash return)
|
300
|
+
expect(event1.to_hash["a"].keys).to_not eq(event2.to_hash["a"].keys)
|
301
|
+
# let's check that the fingerprint doesn't care about the insertion order
|
283
302
|
expect(event1.get("fingerprint")).to eq(event2.get("fingerprint"))
|
284
303
|
end
|
304
|
+
context "concatenate_sources" do
|
305
|
+
let("config") { { "source" => [ "a", "b"], "concatenate_sources" => true } }
|
306
|
+
it "computes the same hash" do
|
307
|
+
expect(event1.get("fingerprint")).to eq(event2.get("fingerprint"))
|
308
|
+
end
|
309
|
+
end
|
310
|
+
context "concatenate_all_fields => true" do
|
311
|
+
let(:config) { { "concatenate_all_fields" => true } }
|
312
|
+
it "computes the same hash" do
|
313
|
+
expect(event1.get("fingerprint")).to eq(event2.get("fingerprint"))
|
314
|
+
end
|
315
|
+
end
|
285
316
|
end
|
317
|
+
|
286
318
|
end
|
287
319
|
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
|
4
|
+
version: 3.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,6 +58,20 @@ dependencies:
|
|
58
58
|
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - "~>"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '1.2'
|
67
|
+
name: logstash-mixin-ecs_compatibility_support
|
68
|
+
prerelease: false
|
69
|
+
type: :runtime
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.2'
|
61
75
|
description: This gem is a Logstash plugin required to be installed on top of the
|
62
76
|
Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This
|
63
77
|
gem is not a stand-alone program
|