logstash-patterns-core 4.0.2 → 4.3.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.
Files changed (79) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +149 -8
  3. data/CONTRIBUTORS +1 -0
  4. data/Gemfile +11 -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/ecs-v1/maven +1 -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/legacy/aws +14 -0
  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} +7 -2
  37. data/patterns/{grok-patterns → legacy/grok-patterns} +5 -13
  38. data/patterns/{haproxy → legacy/haproxy} +1 -1
  39. data/patterns/legacy/httpd +15 -0
  40. data/patterns/{java → legacy/java} +1 -4
  41. data/patterns/{junos → legacy/junos} +0 -0
  42. data/patterns/{linux-syslog → legacy/linux-syslog} +1 -1
  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/legacy/redis +3 -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 -16
  58. data/spec/patterns/exim_spec.rb +201 -0
  59. data/spec/patterns/firewalls_spec.rb +683 -49
  60. data/spec/patterns/haproxy_spec.rb +253 -28
  61. data/spec/patterns/httpd_spec.rb +291 -10
  62. data/spec/patterns/java_spec.rb +375 -0
  63. data/spec/patterns/junos_spec.rb +101 -0
  64. data/spec/patterns/maven_spec.rb +61 -0
  65. data/spec/patterns/mcollective_spec.rb +35 -0
  66. data/spec/patterns/mongodb_spec.rb +170 -33
  67. data/spec/patterns/nagios_spec.rb +299 -78
  68. data/spec/patterns/netscreen_spec.rb +123 -0
  69. data/spec/patterns/rails3_spec.rb +87 -29
  70. data/spec/patterns/redis_spec.rb +207 -0
  71. data/spec/patterns/shorewall_spec.rb +85 -74
  72. data/spec/patterns/squid_spec.rb +139 -0
  73. data/spec/patterns/syslog_spec.rb +266 -8
  74. data/spec/spec_helper.rb +83 -5
  75. metadata +74 -26
  76. data/patterns/aws +0 -11
  77. data/patterns/redis +0 -3
  78. data/spec/patterns/bro.rb +0 -126
  79. data/spec/patterns/s3_spec.rb +0 -132
@@ -20,16 +20,6 @@ describe "SYSLOGLINE" do
20
20
 
21
21
  end
22
22
 
23
- describe "COMMONAPACHELOG" do
24
-
25
- let(:value) { '83.149.9.216 - - [24/Feb/2015:23:13:42 +0000] "GET /presentations/logstash-monitorama-2013/images/kibana-search.png HTTP/1.1" 200 203023 "http://semicomplete.com/presentations/logstash-monitorama-2013/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.77 Safari/537.36'}
26
-
27
- it "generates the clientip field" do
28
- expect(grok_match(subject, value)).to include("clientip" => "83.149.9.216")
29
- end
30
-
31
- end
32
-
33
23
  describe "HTTP DATE parsing" do
34
24
 
35
25
  context "HTTPDATE", "when having a German month" do
@@ -64,12 +54,16 @@ describe "HTTP DATE parsing" do
64
54
 
65
55
  end
66
56
 
67
- describe "TOMCATLOG" do
68
-
69
- 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
70
62
 
71
- it "generates the logmessage field" do
72
- 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
73
67
  end
74
68
  end
75
69
 
@@ -100,14 +94,224 @@ describe "UNIXPATH" do
100
94
  let(:value) { '/foo/bar' }
101
95
 
102
96
  it "should match the path" do
103
- expect(grok_match(pattern,value)).to pass
97
+ expect(grok_match(pattern, value, true)).to pass
104
98
  end
105
99
 
106
100
  context "when using comma separators and other regexp" do
107
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
+
108
110
  let(:value) { 'a=/some/path, b=/some/other/path' }
109
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
+
110
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
111
315
  expect(grok_match(pattern,value)).to pass
112
316
  end
113
317
  end
@@ -282,3 +486,54 @@ describe "URN" do
282
486
  end
283
487
  end
284
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