logstash-patterns-core 4.1.0 → 4.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +118 -0
  3. data/CONTRIBUTORS +1 -0
  4. data/Gemfile +8 -1
  5. data/LICENSE +199 -10
  6. data/README.md +12 -19
  7. data/lib/logstash/patterns/core.rb +11 -3
  8. data/logstash-patterns-core.gemspec +1 -1
  9. data/patterns/ecs-v1/aws +28 -0
  10. data/patterns/ecs-v1/bacula +53 -0
  11. data/patterns/ecs-v1/bind +13 -0
  12. data/patterns/ecs-v1/bro +30 -0
  13. data/patterns/ecs-v1/exim +26 -0
  14. data/patterns/ecs-v1/firewalls +111 -0
  15. data/patterns/ecs-v1/grok-patterns +95 -0
  16. data/patterns/ecs-v1/haproxy +40 -0
  17. data/patterns/ecs-v1/httpd +17 -0
  18. data/patterns/ecs-v1/java +34 -0
  19. data/patterns/ecs-v1/junos +13 -0
  20. data/patterns/ecs-v1/linux-syslog +16 -0
  21. data/patterns/{maven → ecs-v1/maven} +0 -0
  22. data/patterns/ecs-v1/mcollective +4 -0
  23. data/patterns/ecs-v1/mongodb +7 -0
  24. data/patterns/ecs-v1/nagios +124 -0
  25. data/patterns/ecs-v1/postgresql +2 -0
  26. data/patterns/ecs-v1/rails +13 -0
  27. data/patterns/ecs-v1/redis +3 -0
  28. data/patterns/ecs-v1/ruby +2 -0
  29. data/patterns/ecs-v1/squid +6 -0
  30. data/patterns/ecs-v1/zeek +33 -0
  31. data/patterns/{aws → legacy/aws} +1 -1
  32. data/patterns/{bacula → legacy/bacula} +5 -5
  33. data/patterns/legacy/bind +3 -0
  34. data/patterns/{bro → legacy/bro} +0 -0
  35. data/patterns/{exim → legacy/exim} +8 -2
  36. data/patterns/{firewalls → legacy/firewalls} +2 -2
  37. data/patterns/{grok-patterns → legacy/grok-patterns} +5 -5
  38. data/patterns/{haproxy → legacy/haproxy} +1 -1
  39. data/patterns/{httpd → legacy/httpd} +3 -3
  40. data/patterns/{java → legacy/java} +1 -3
  41. data/patterns/{junos → legacy/junos} +0 -0
  42. data/patterns/{linux-syslog → legacy/linux-syslog} +0 -0
  43. data/patterns/legacy/maven +1 -0
  44. data/patterns/{mcollective → legacy/mcollective} +0 -0
  45. data/patterns/{mcollective-patterns → legacy/mcollective-patterns} +0 -0
  46. data/patterns/{mongodb → legacy/mongodb} +0 -0
  47. data/patterns/{nagios → legacy/nagios} +1 -1
  48. data/patterns/{postgresql → legacy/postgresql} +0 -0
  49. data/patterns/{rails → legacy/rails} +0 -0
  50. data/patterns/{redis → legacy/redis} +0 -0
  51. data/patterns/{ruby → legacy/ruby} +0 -0
  52. data/patterns/legacy/squid +4 -0
  53. data/spec/patterns/aws_spec.rb +395 -0
  54. data/spec/patterns/bacula_spec.rb +367 -0
  55. data/spec/patterns/bind_spec.rb +78 -0
  56. data/spec/patterns/bro_spec.rb +613 -0
  57. data/spec/patterns/core_spec.rb +271 -6
  58. data/spec/patterns/exim_spec.rb +201 -0
  59. data/spec/patterns/firewalls_spec.rb +707 -66
  60. data/spec/patterns/haproxy_spec.rb +253 -28
  61. data/spec/patterns/httpd_spec.rb +255 -77
  62. data/spec/patterns/java_spec.rb +375 -0
  63. data/spec/patterns/junos_spec.rb +101 -0
  64. data/spec/patterns/mcollective_spec.rb +35 -0
  65. data/spec/patterns/mongodb_spec.rb +170 -33
  66. data/spec/patterns/nagios_spec.rb +299 -78
  67. data/spec/patterns/netscreen_spec.rb +123 -0
  68. data/spec/patterns/rails3_spec.rb +87 -29
  69. data/spec/patterns/redis_spec.rb +157 -121
  70. data/spec/patterns/shorewall_spec.rb +85 -74
  71. data/spec/patterns/squid_spec.rb +139 -0
  72. data/spec/patterns/syslog_spec.rb +266 -22
  73. data/spec/spec_helper.rb +83 -5
  74. metadata +70 -30
  75. data/patterns/bind +0 -3
  76. data/patterns/squid +0 -4
  77. data/spec/patterns/bro.rb +0 -126
  78. data/spec/patterns/s3_spec.rb +0 -173
@@ -54,12 +54,16 @@ describe "HTTP DATE parsing" do
54
54
 
55
55
  end
56
56
 
57
- describe "TOMCATLOG" do
58
-
59
- let(:value) { '2014-01-09 20:03:28,269 -0800 | ERROR | com.example.service.ExampleService - something compeletely unexpected happened...'}
57
+ describe 'LOGLEVEL' do
58
+ it 'matches info label' do
59
+ expect(grok_match(subject, 'INFO')).to pass
60
+ expect(grok_match(subject, 'info')).to pass
61
+ end
60
62
 
61
- it "generates the logmessage field" do
62
- expect(grok_match(subject, value)).to include("logmessage" => "something compeletely unexpected happened...")
63
+ it 'matches information label' do
64
+ expect(grok_match(subject, 'information')).to pass
65
+ expect(grok_match(subject, 'Information')).to pass
66
+ expect(grok_match(subject, 'INFORMATION')).to pass
63
67
  end
64
68
  end
65
69
 
@@ -90,14 +94,224 @@ describe "UNIXPATH" do
90
94
  let(:value) { '/foo/bar' }
91
95
 
92
96
  it "should match the path" do
93
- expect(grok_match(pattern,value)).to pass
97
+ expect(grok_match(pattern, value, true)).to pass
94
98
  end
95
99
 
96
100
  context "when using comma separators and other regexp" do
97
101
 
102
+ let(:pattern) { '((a=(?<a>%{UNIXPATH})?|b=(?<b>%{UNIXPATH})?)(,\s)?)+' }
103
+
104
+ let(:grok) do
105
+ grok = LogStash::Filters::Grok.new("match" => ["message", pattern])
106
+ grok.register
107
+ grok
108
+ end
109
+
98
110
  let(:value) { 'a=/some/path, b=/some/other/path' }
99
111
 
112
+ it "was expected to extract both but never really did" do # or maybe on JRuby 1.7
113
+ event = build_event(value)
114
+ grok.filter(event)
115
+ expect( event.to_hash['a'] ).to eql '/some/path,'
116
+ expect( event.to_hash['b'] ).to be nil
117
+ end
118
+
119
+ end
120
+
121
+ context 'relative path' do
122
+
123
+ let(:path_matcher) do # non-exact matcher
124
+ grok = LogStash::Filters::Grok.new("match" => ["message", '%{UNIXPATH:path}'])
125
+ grok.register
126
+ lambda { |msg| event = build_event(msg); grok.filter(event); event }
127
+ end
128
+
129
+ it "should not match (only partially)" do
130
+ expect(grok_match(pattern, 'a/./b/c', true)).to_not pass
131
+ event = path_matcher.('a/./b/c')
132
+ expect( event.to_hash['path'] ).to eql '/./b/c'
133
+
134
+ expect(grok_match(pattern, ',/.', true)).to_not pass
135
+ event = path_matcher.(',/.')
136
+ expect( event.to_hash['path'] ).to eql '/.'
137
+
138
+ expect(grok_match(pattern, '+/.../', true)).to_not pass
139
+ event = path_matcher.('+/.../')
140
+ expect( event.to_hash['path'] ).to eql '/.../'
141
+
142
+ expect(grok_match(pattern, '~/b/', true)).to_not pass
143
+ event = path_matcher.('~/b/')
144
+ expect( event.to_hash['path'] ).to eql '/b/'
145
+
146
+ expect(grok_match(pattern, './b//', true)).to_not pass
147
+ expect(grok_match(pattern, 'a//b', true)).to_not pass
148
+ end
149
+
150
+ it "should not match paths starting with ." do
151
+ expect(grok_match(pattern, '../0', true)).to_not pass
152
+ expect(grok_match(pattern, './~', true)).to_not pass
153
+ expect(grok_match(pattern, '.../-', true)).to_not pass
154
+ expect(grok_match(pattern, './', true)).to_not pass
155
+ expect(grok_match(pattern, './,', true)).to_not pass
156
+ expect(grok_match(pattern, '../', true)).to_not pass
157
+ expect(grok_match(pattern, '.a/', true)).to_not pass
158
+ expect(grok_match(pattern, '.~/', true)).to_not pass
159
+ end
160
+
161
+ it "should not match expression wout separator" do
162
+ expect(grok_match(pattern, '.')).to_not pass
163
+ expect(grok_match(pattern, '..')).to_not pass
164
+ expect(grok_match(pattern, '...')).to_not pass
165
+ expect(grok_match(pattern, '.,')).to_not pass
166
+ expect(grok_match(pattern, '.-')).to_not pass
167
+ end
168
+
169
+ end
170
+
171
+ context "dotted path" do
172
+
173
+ it "should match path containing ." do
174
+ expect(grok_match(pattern, '/some/./path/', true)).to pass
175
+ expect(grok_match(pattern, '/some/../path', true)).to pass
176
+ expect(grok_match(pattern, '/../.', true)).to pass
177
+ expect(grok_match(pattern, '/.', true)).to pass
178
+ expect(grok_match(pattern, '/..', true)).to pass
179
+ expect(grok_match(pattern, '/...', true)).to pass
180
+ end
181
+
182
+ end
183
+
184
+ context "separators" do
185
+
186
+ it "should match root" do
187
+ expect(grok_match(pattern, '/', true)).to pass
188
+ end
189
+
190
+ it "should match" do
191
+ expect(grok_match(pattern, '//', true)).to pass
192
+ expect(grok_match(pattern, '//00', true)).to pass
193
+ expect(grok_match(pattern, '///a', true)).to pass
194
+ expect(grok_match(pattern, '/a//', true)).to pass
195
+ expect(grok_match(pattern, '///a//b/c///', true)).to pass
196
+ end
197
+
198
+ it "should not match windows separator" do
199
+ expect(grok_match(pattern, "\\a", true)).to_not pass
200
+ expect(grok_match(pattern, '/0\\', true)).to_not pass
201
+ expect(grok_match(pattern, "/a\\b", true)).to_not pass
202
+ end
203
+
204
+ end
205
+
206
+ context "long path" do
207
+
208
+ let(:grok) do
209
+ grok = LogStash::Filters::Grok.new("match" => ["message", '%{UNIXPATH:path} '], 'timeout_millis' => 1500)
210
+ grok.register
211
+ grok
212
+ end
213
+
214
+ let(:value) { '/opt/abcdef/1/.22/3:3+3/foo@BAR/X-Y+Z/~Sample_l_SUBc b' }
215
+
100
216
  it "should match the path" do
217
+ event = build_event(value)
218
+ grok.filter(event)
219
+ expect( event.to_hash['path'] ).to eql '/opt/abcdef/1/.22/3:3+3/foo@BAR/X-Y+Z/~Sample_l_SUBc'
220
+ end
221
+
222
+ it "should not match with invalid chars (or cause DoS)" do
223
+ event = build_event(value.sub('SUB', '&^_'))
224
+ grok.filter(event) # used to call a looong looop (DoS) despite the timeout guard
225
+ expect( event.to_hash['tags'] ).to include '_grokparsefailure'
226
+ end
227
+ end
228
+
229
+ it "matches paths with non-ascii characters" do
230
+ event = build_event path = '/opt/Čierný_Peter/.中'
231
+ build_grok('UNIXPATH:path').filter event
232
+ expect( event.get('path') ).to eql path
233
+ end
234
+
235
+ end
236
+
237
+ describe "WINPATH" do
238
+
239
+ let(:pattern) { 'WINPATH' }
240
+ let(:value) { 'C:\\foo\\bar' }
241
+
242
+ it "should match the path" do
243
+ expect(grok_match(pattern, value, true)).to pass
244
+ end
245
+
246
+ it "should match root path" do
247
+ expect(grok_match(pattern, 'C:\\', true)).to pass
248
+ expect(grok_match(pattern, 'C:\\\\', true)).to pass
249
+ expect(grok_match(pattern, 'a:\\', true)).to pass
250
+ expect(grok_match(pattern, 'x:\\\\', true)).to pass
251
+ end
252
+
253
+ it "should match paths with spaces" do
254
+ expect(grok_match(pattern, 'C:\\Documents and Settings\\Public', true)).to pass
255
+ expect(grok_match(pattern, 'C:\\\\Users\\\\Public\\\\.Mozilla Firefox', true)).to pass
256
+ end
257
+
258
+ it "should not match unix-style paths" do
259
+ expect(grok_match(pattern, '/foo', true)).to_not pass
260
+ expect(grok_match(pattern, '//C/path', true)).to_not pass
261
+ expect(grok_match(pattern, '/', true)).to_not pass
262
+ expect(grok_match(pattern, '/foo/bar', true)).to_not pass
263
+ expect(grok_match(pattern, '/..', true)).to_not pass
264
+ expect(grok_match(pattern, 'C://', true)).to_not pass
265
+ end
266
+
267
+ it "matches paths with non-ascii characters" do
268
+ expect(grok_match(pattern, 'C:\\Čierný Peter\\.中.exe', true)).to pass
269
+ end
270
+
271
+ context 'relative paths' do
272
+
273
+ it "should not match" do
274
+ expect(grok_match(pattern, 'a\\bar', true)).to_not pass
275
+ expect(grok_match(pattern, 'foo\\bar', true)).to_not pass
276
+ expect(grok_match(pattern, 'C\\A\\B', true)).to_not pass
277
+ expect(grok_match(pattern, 'C\\\\0', true)).to_not pass
278
+ expect(grok_match(pattern, '.\\0', true)).to_not pass
279
+ expect(grok_match(pattern, '..\\', true)).to_not pass
280
+ expect(grok_match(pattern, '...\\-', true)).to_not pass
281
+ expect(grok_match(pattern, '.\\', true)).to_not pass
282
+ expect(grok_match(pattern, '.\\,', true)).to_not pass
283
+ expect(grok_match(pattern, '..\\', true)).to_not pass
284
+ expect(grok_match(pattern, '.a\\', true)).to_not pass
285
+ end
286
+
287
+ it "should not match expression wout separator" do
288
+ expect(grok_match(pattern, '.')).to_not pass
289
+ expect(grok_match(pattern, '..')).to_not pass
290
+ expect(grok_match(pattern, '...')).to_not pass
291
+ expect(grok_match(pattern, 'C:')).to_not pass
292
+ expect(grok_match(pattern, 'C')).to_not pass
293
+ end
294
+
295
+ end
296
+
297
+ end
298
+
299
+
300
+ describe "URIPROTO" do
301
+ let(:pattern) { 'URIPROTO' }
302
+
303
+ context "http is a valid URIPROTO" do
304
+ let(:value) { 'http' }
305
+
306
+ it "should match" do
307
+ expect(grok_match(pattern,value)).to pass
308
+ end
309
+ end
310
+
311
+ context "android-app is a valid URIPROTO" do
312
+ let(:value) { 'android-app' }
313
+
314
+ it "should match" do
101
315
  expect(grok_match(pattern,value)).to pass
102
316
  end
103
317
  end
@@ -272,3 +486,54 @@ describe "URN" do
272
486
  end
273
487
  end
274
488
  end
489
+
490
+ describe_pattern "EMAILADDRESS", ['legacy', 'ecs-v1'] do
491
+
492
+ # NOTE: EMAILLOCALPART was only updated in ECS mode, as following the RFC is
493
+ # actually a breaking change -> legacy does match incorrect e-mail addresses.
494
+
495
+ it "matches 'hello.world@123.net' address" do
496
+ expect(grok_exact_match(pattern, 'hello.world@123.net')).to pass
497
+ end
498
+
499
+ [
500
+ 'a@example.com',
501
+ 'a{b}@example.com',
502
+ 'Foo+Bar@x.exposed',
503
+ ].each do |valid_email|
504
+ it "matches #{valid_email.inspect} address" do
505
+ expect(grok_exact_match(pattern, valid_email)).to pass if ecs_compatibility?
506
+ end
507
+ end
508
+
509
+ it "does not match 'x y@example.com' address" do
510
+ expect(grok_exact_match(pattern, 'hello.world@123.net')).to pass
511
+ end
512
+
513
+ [
514
+ 'a:b@example.com',
515
+ 'a..b@example.com',
516
+ ].each do |invalid_email|
517
+ it "does not match #{invalid_email.inspect} address" do
518
+ expect(grok_exact_match(pattern, invalid_email)).to_not pass if ecs_compatibility?
519
+ end
520
+ end
521
+
522
+ it "matches e-mail with digits only local-part" do
523
+ expect(grok_exact_match(pattern, '00@q.ro')).to pass if ecs_compatibility?
524
+ end
525
+
526
+ it "matches e-mail with 64 chars in local-part" do
527
+ expect(grok_exact_match(pattern, ('ab' * 32) + '@root.cz')).to pass
528
+ expect(grok_exact_match(pattern, ('a.bc' * 16) + '@00.cz')).to pass
529
+ end
530
+
531
+ it "does not match e-mail with more than 64 char length local-part" do
532
+ if ecs_compatibility?
533
+ expect(grok_exact_match(pattern, ('ab' * 32) + 'a' + '@root.cz')).to_not pass
534
+ # NOTE: we allow longer with '.' but at least we limit "too long" :
535
+ expect(grok_exact_match(pattern, ('a.bc' * 64) + '@00.cz')).to_not pass
536
+ end
537
+ end
538
+
539
+ end
@@ -0,0 +1,201 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+ require "logstash/patterns/core"
4
+
5
+ describe_pattern 'EXIM', ['legacy', 'ecs-v1'] do
6
+
7
+ context 'message arrival (old)' do
8
+
9
+ let(:message) do
10
+ "1995-10-31 08:57:53 0tACW1-0005MB-00 <= kryten@dwarf.fict.example H=mailer.fict.example [192.168.123.123] " +
11
+ "U=exim P=smtp S=5678 id=f828ca60127d8646a0fa75cbf8db9ba3@dwarf.fict.example"
12
+ end
13
+
14
+ it "matches" do
15
+ expect(grok).to include("timestamp" => "1995-10-31 08:57:53")
16
+
17
+ if ecs_compatibility?
18
+ expect(grok).to include("exim"=>{"log"=>{
19
+ "flags"=>"<=",
20
+ "sender"=>{"email"=>"kryten@dwarf.fict.example"},
21
+ "message"=>{"id"=>"0tACW1-0005MB-00", "size"=>5678},
22
+ "header_id"=>"f828ca60127d8646a0fa75cbf8db9ba3@dwarf.fict.example"
23
+ }})
24
+ expect(grok).to include("source"=>{"address"=>"mailer.fict.example", "ip"=>"192.168.123.123"})
25
+ expect(grok).to include("network"=>{"protocol"=>"smtp"})
26
+ else
27
+ expect(grok).to include("exim_year" => "1995", "exim_month" => "10", "exim_day" => "31", "@version" => "1", "exim_time" => "08:57:53")
28
+ expect(grok.keys).to_not include("pid")
29
+ expect(grok).to include("exim_sender_email" => "kryten@dwarf.fict.example")
30
+ expect(grok).to include("exim_flags" => "<=")
31
+ expect(grok).to include("exim_msg_size" => "5678")
32
+ expect(grok).to include("exim_msgid" => "0tACW1-0005MB-00")
33
+ expect(grok).to include("remote_hostname" => "mailer.fict.example", "remote_host" => "192.168.123.123")
34
+ expect(grok).to include("protocol" => "smtp")
35
+ expect(grok).to include("exim_header_id" => "f828ca60127d8646a0fa75cbf8db9ba3@dwarf.fict.example")
36
+ end
37
+
38
+ expect(grok).to include("message" => message)
39
+ end
40
+
41
+ end
42
+
43
+ context 'message arrival (new)' do
44
+ let(:message) do
45
+ '2010-09-13 05:00:13 [1487] 1Ov4tU-0000Nz-Rm <= mailling.list@domain.com ' +
46
+ 'H=mailhost.domain.com [208.42.54.2]:51792 I=[67.215.162.175]:25 P=esmtps X=TLSv1:AES256-SHA:256 CV=no S=21778 ' +
47
+ 'id=384a86a39e83be0d9b3a94d1feb3119f@domain.com T="Daily List: Chameleon" for user@example.com'
48
+ end
49
+
50
+ it "matches" do
51
+ expect(grok).to include("timestamp" => "2010-09-13 05:00:13") # new in legacy mode
52
+
53
+ if ecs_compatibility?
54
+ expect(grok).to include("process"=>{"pid"=>1487})
55
+ expect(grok).to include("exim"=>{"log"=>hash_including(
56
+ "message"=>{"id"=>"1Ov4tU-0000Nz-Rm", "size"=>21778, "subject"=>"Daily List: Chameleon"},
57
+ "flags"=>"<=",
58
+ "header_id"=>"384a86a39e83be0d9b3a94d1feb3119f@domain.com",
59
+ "sender"=>{"email"=>"mailling.list@domain.com"},
60
+ "recipient"=>{"email"=>"user@example.com"},
61
+ )})
62
+ expect(grok).to include("source"=>{"address"=>"mailhost.domain.com", "ip"=>"208.42.54.2", "port"=>51792})
63
+ expect(grok).to include("destination"=>{"ip"=>"67.215.162.175", "port"=>25})
64
+ else
65
+
66
+ expect(grok).to include("exim_year" => "2010", "exim_month" => "09", "exim_day" => "13", "exim_time" => "05:00:13")
67
+ expect(grok).to include("pid" => "1487") # new
68
+ expect(grok).to include("exim_sender_email" => "mailling.list@domain.com") # new
69
+ expect(grok).to include("remote_hostname" => "mailhost.domain.com", "remote_host" => "208.42.54.2", "remote_port" => "51792") # (remote_port) new
70
+ expect(grok).to include("exim_interface" => "67.215.162.175", "exim_interface_port" => "25")
71
+ expect(grok).to include("protocol" => "esmtps")
72
+ expect(grok).to include("exim_msg_size" => "21778")
73
+ expect(grok).to include("exim_header_id" => "384a86a39e83be0d9b3a94d1feb3119f@domain.com")
74
+ expect(grok).to include("exim_subject" => '"Daily List: Chameleon"')
75
+ expect(grok).to include("exim_recipient_email" => "user@example.com") # new
76
+ end
77
+
78
+ expect(grok).to include("message" => message)
79
+ end
80
+
81
+ end
82
+
83
+ context 'message arrival (simple)' do
84
+
85
+ let(:message) do
86
+ '2020-02-11 17:09:46 1j1Z2g-00Faoy-Uh <= example@strawberry.active-ns.com U=example P=local ' +
87
+ 'T="[Examples Galore] Please moderate: \"Hello world!\"" for admin@example.net'
88
+ end
89
+
90
+ it "matches" do
91
+ expect(grok).to include("timestamp"=>"2020-02-11 17:09:46")
92
+ if ecs_compatibility?
93
+ expect(grok).to include("exim"=>{"log"=>{
94
+ "flags"=>"<=",
95
+ "message"=>{"id"=>"1j1Z2g-00Faoy-Uh", "subject"=>'[Examples Galore] Please moderate: \\"Hello world!\\"'},
96
+ "sender"=>{"email"=>"example@strawberry.active-ns.com"},
97
+ "recipient"=>{"email"=>"admin@example.net"}
98
+ }})
99
+ expect(grok).to include("network"=>{"protocol"=>"local"})
100
+ else
101
+ expect(grok).to include(
102
+ "exim_msgid"=>"1j1Z2g-00Faoy-Uh",
103
+ "exim_sender_email"=>"example@strawberry.active-ns.com",
104
+ "exim_flags"=>"<=",
105
+ "protocol"=>"local",
106
+ "exim_subject"=>"\"[Examples Galore] Please moderate: \\\"Hello world!\\\"\""
107
+ )
108
+ end
109
+ end
110
+
111
+ end
112
+
113
+ context 'message arrival with quoted hostname' do
114
+
115
+ let(:message) do
116
+ '2013-03-20 12:44:02 1UIIN7-0004t9-8R <= root@example.com ' +
117
+ 'H=localhost (hostname.example.com) [127.0.0.1] ' +
118
+ 'P=esmtps X=TLSv1:DHE-RSA-AES256-SHA:256 S=811 id=201303201244.r2KCi11V018784@hostname.example.com'
119
+ end
120
+
121
+ it "matches" do
122
+ expect(grok).to include("timestamp"=>"2013-03-20 12:44:02")
123
+
124
+ if ecs_compatibility?
125
+ expect(grok).to include("exim"=>{"log"=>hash_including(
126
+ "sender"=>{ "email"=>"root@example.com" },
127
+ "message"=>{ "id"=>"1UIIN7-0004t9-8R", "size"=>811 },
128
+ "header_id"=>"201303201244.r2KCi11V018784@hostname.example.com",
129
+ "remote_address"=>"hostname.example.com")})
130
+ expect(grok).to include("source"=>{"address"=>"localhost", "ip"=>"127.0.0.1"})
131
+ expect(grok).to include("network"=>{"protocol"=>"esmtps"})
132
+ else
133
+ expect(grok).to include(
134
+ "exim_msgid"=>"1UIIN7-0004t9-8R",
135
+ "exim_sender_email"=>"root@example.com",
136
+ "exim_flags"=>"<=",
137
+ "protocol"=>"esmtps",
138
+ "exim_header_id"=>"201303201244.r2KCi11V018784@hostname.example.com"
139
+ )
140
+ expect(grok).to include(
141
+ "remote_host"=>"127.0.0.1",
142
+ "remote_heloname"=>"hostname.example.com",
143
+ "remote_hostname"=>"localhost"
144
+ )
145
+ end
146
+ end
147
+
148
+ end
149
+
150
+
151
+ context 'message arrival with quoted hostname and port' do
152
+
153
+ let(:message) do
154
+ '2014-08-10 11:18:35 [28107] 1gsu1C-003dCu-Hb <= aaron@domain.com ' +
155
+ 'H=localhost (10.5.40.204) [127.0.0.1]:39753 I=[127.0.0.1]:25 ' +
156
+ 'P=esmtpa A=dovecot_plain:aaron@domain.com S=4315 M8S=0 id=d2b648f00f1a1b0813c483d552778dc6@domain.com ' +
157
+ "T=\"what's up?!? ;-)\" from <aaron@domain.com> for aaron+forward@domain.com"
158
+ end
159
+
160
+ it "matches" do
161
+ if ecs_compatibility?
162
+ expect(grok).to include("exim"=>{"log"=>hash_including(
163
+ "message"=>hash_including("id"=>"1gsu1C-003dCu-Hb", "subject"=>"what's up?!? ;-)"),
164
+ )})
165
+ expect(grok).to include("destination"=>{"ip"=>"127.0.0.1", "port"=>25})
166
+ expect(grok).to include("exim"=>{"log"=>hash_including(
167
+ "sender"=>{"email"=>"aaron@domain.com", 'original'=>'aaron@domain.com'}
168
+ )})
169
+ expect(grok).to include("exim"=>{"log"=>hash_including("recipient"=>{"email"=>"aaron+forward@domain.com"})})
170
+ expect(grok).to include("source"=>{"address"=>"localhost", "ip"=>"127.0.0.1", "port"=>39753})
171
+ expect(grok).to include("exim"=>{"log" => hash_including("remote_address"=>'10.5.40.204')})
172
+ else
173
+ expect(grok).to include(
174
+ "exim_msgid"=>"1gsu1C-003dCu-Hb",
175
+ "exim_sender_email"=>"aaron@domain.com",
176
+ "exim_flags"=>"<=",
177
+ "protocol"=>"esmtpa"
178
+ )
179
+ expect(grok).to include(
180
+ "remote_host"=>"127.0.0.1", "remote_port"=>"39753",
181
+ "remote_heloname"=>"10.5.40.204",
182
+ "remote_hostname"=>"localhost"
183
+ )
184
+ end
185
+ end
186
+
187
+ end
188
+
189
+ context 'delivery failed' do
190
+
191
+ let(:message) do
192
+ '2020-02-11 17:09:47 1j1Z2g-00Faoy-Uh ** admin@example.net R=virtual_aliases: No such person at this address.'
193
+ end
194
+
195
+ it "does not parse" do # matching not implemented
196
+ expect(grok['tags']).to include("_grokparsefailure")
197
+ end
198
+
199
+ end
200
+
201
+ end