fluent-plugin-windows-eventlog 0.8.0 → 0.8.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.
@@ -1,572 +1,619 @@
1
- require 'helper'
2
- require 'fileutils'
3
- require 'generate-windows-event'
4
-
5
- # Monkey patch for testing
6
- class Winevt::EventLog::Session
7
- def ==(obj)
8
- self.server == obj.server &&
9
- self.domain == obj.domain &&
10
- self.username == obj.username &&
11
- self.password == obj.password &&
12
- self.flags == obj.flags
13
- end
14
- end
15
-
16
- class WindowsEventLog2InputTest < Test::Unit::TestCase
17
-
18
- def setup
19
- Fluent::Test.setup
20
- end
21
-
22
- CONFIG = config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
23
- config_element("storage", "", {
24
- '@type' => 'local',
25
- 'persistent' => false
26
- })
27
- ])
28
-
29
- XML_RENDERING_CONFIG = config_element("ROOT", "", {"tag" => "fluent.eventlog",
30
- "render_as_xml" => true}, [
31
- config_element("storage", "", {
32
- '@type' => 'local',
33
- 'persistent' => false
34
- })
35
- ])
36
-
37
- def create_driver(conf = CONFIG)
38
- Fluent::Test::Driver::Input.new(Fluent::Plugin::WindowsEventLog2Input).configure(conf)
39
- end
40
-
41
- def test_configure
42
- d = create_driver CONFIG
43
- assert_equal 'fluent.eventlog', d.instance.tag
44
- assert_equal 2, d.instance.read_interval
45
- assert_equal [], d.instance.channels
46
- assert_false d.instance.read_existing_events
47
- assert_false d.instance.render_as_xml
48
- assert_nil d.instance.refresh_subscription_interval
49
- end
50
-
51
- sub_test_case "configure" do
52
- test "refresh subscription interval" do
53
- d = create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog",
54
- "refresh_subscription_interval" => "2m"}, [
55
- config_element("storage", "", {
56
- '@type' => 'local',
57
- 'persistent' => false
58
- })
59
- ])
60
- assert_equal 120, d.instance.refresh_subscription_interval
61
- end
62
-
63
- test "subscribe directive" do
64
- d = create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
65
- config_element("storage", "", {
66
- '@type' => 'local',
67
- 'persistent' => false
68
- }),
69
- config_element("subscribe", "", {
70
- 'channels' => ['System', 'Windows PowerShell'],
71
- }),
72
- config_element("subscribe", "", {
73
- 'channels' => ['Security'],
74
- 'read_existing_events' => true
75
- }),
76
- ])
77
- expected = [["system", false, nil], ["windows powershell", false, nil], ["security", true, nil]]
78
- assert_equal expected, d.instance.instance_variable_get(:@chs)
79
- end
80
-
81
- test "subscribe directive with remote server session" do
82
- d = create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
83
- config_element("storage", "", {
84
- '@type' => 'local',
85
- 'persistent' => false
86
- }),
87
- config_element("subscribe", "", {
88
- 'channels' => ['System', 'Windows PowerShell'],
89
- 'remote_server' => '127.0.0.1',
90
- }),
91
- config_element("subscribe", "", {
92
- 'channels' => ['Security'],
93
- 'read_existing_events' => true,
94
- 'remote_server' => '192.168.0.1',
95
- 'remote_username' => 'fluentd',
96
- 'remote_password' => 'changeme!'
97
- }),
98
- ])
99
- localhost_session = Winevt::EventLog::Session.new("127.0.0.1")
100
- remote_session = Winevt::EventLog::Session.new("192.168.0.1",
101
- nil,
102
- "fluentd",
103
- "changeme!")
104
- expected = [["system", false, localhost_session],
105
- ["windows powershell", false, localhost_session],
106
- ["security", true, remote_session]]
107
- assert_equal expected, d.instance.instance_variable_get(:@chs)
108
- end
109
-
110
- test "duplicated subscribe" do
111
- d = create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog",
112
- "channels" => ["System", "Windows PowerShell"]
113
- }, [
114
- config_element("storage", "", {
115
- '@type' => 'local',
116
- 'persistent' => false
117
- }),
118
- config_element("subscribe", "", {
119
- 'channels' => ['System', 'Windows PowerShell'],
120
- }),
121
- config_element("subscribe", "", {
122
- 'channels' => ['Security'],
123
- 'read_existing_events' => true
124
- }),
125
- ])
126
- expected = [["system", false, nil], ["windows powershell", false, nil], ["security", true, nil]]
127
- assert_equal 1, d.instance.instance_variable_get(:@chs).select {|ch, flag| ch == "system"}.size
128
- assert_equal expected, d.instance.instance_variable_get(:@chs)
129
- end
130
-
131
- test "non duplicated subscribe" do
132
- d = create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog",
133
- "channels" => ["System", "Windows PowerShell"]
134
- }, [
135
- config_element("storage", "", {
136
- '@type' => 'local',
137
- 'persistent' => false
138
- }),
139
- config_element("subscribe", "", {
140
- 'channels' => ['System', 'Windows PowerShell'],
141
- 'read_existing_events' => true
142
- }),
143
- config_element("subscribe", "", {
144
- 'channels' => ['Security'],
145
- 'read_existing_events' => true
146
- }),
147
- ])
148
- expected = [["system", false, nil], ["windows powershell", false, nil], ["system", true, nil], ["windows powershell", true, nil], ["security", true, nil]]
149
- assert_equal 2, d.instance.instance_variable_get(:@chs).select {|ch, flag| ch == "system"}.size
150
- assert_equal expected, d.instance.instance_variable_get(:@chs)
151
- end
152
-
153
- test "invalid combination for preserving qualifiers" do
154
- assert_raise(Fluent::ConfigError) do
155
- create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog",
156
- "render_as_xml" => true,
157
- "preserve_qualifiers_on_hash" => true,
158
- }, [
159
- config_element("storage", "", {
160
- '@type' => 'local',
161
- 'persistent' => false
162
- }),
163
- ])
164
- end
165
- end
166
-
167
- test "invalid description locale" do
168
- assert_raise(Fluent::ConfigError) do
169
- create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog",
170
- "description_locale" => "ex_EX"
171
- }, [
172
- config_element("storage", "", {
173
- '@type' => 'local',
174
- 'persistent' => false
175
- })
176
- ])
177
- end
178
- end
179
- end
180
-
181
- data("Japanese" => ["ja_JP", false],
182
- "English (United States)" => ["en_US", false],
183
- "English (UK)" => ["en_GB", false],
184
- "Dutch" => ["nl_NL", false],
185
- "French" => ["fr_FR", false],
186
- "German" => ["de_DE", false],
187
- "Russian" => ["ru_RU", false],
188
- "Spanish" => ["es_ES", false],
189
- "Invalid" => ["ex_EX", true],
190
- )
191
- def test_unsupported_locale_p(data)
192
- description_locale, expected = data
193
- d = create_driver CONFIG
194
- locale = Winevt::EventLog::Locale.new
195
- result = d.instance.unsupported_locale?(locale, description_locale)
196
- assert_equal expected, result
197
- end
198
-
199
- data("application" => ["Application", "Application"],
200
- "windows powershell" => ["Windows PowerShell", "Windows PowerShell"],
201
- "escaped" => ["Should_Be_Escaped_", "Should+Be;Escaped/"]
202
- )
203
- def test_escape_channel(data)
204
- expected, actual = data
205
- d = create_driver CONFIG
206
- assert_equal expected, d.instance.escape_channel(actual)
207
- end
208
-
209
- def test_parse_desc
210
- d = create_driver
211
- desc =<<-DESC
212
- A user's local group membership was enumerated.\r\n\r\nSubject:\r\n\tSecurity ID:\t\tS-X-Y-XX-WWWWWW-VVVV\r\n\tAccount Name:\t\tAdministrator\r\n\tAccount Domain:\t\tDESKTOP-FLUENTTEST\r\n\tLogon ID:\t\t0x3185B1\r\n\r\nUser:\r\n\tSecurity ID:\t\tS-X-Y-XX-WWWWWW-VVVV\r\n\tAccount Name:\t\tAdministrator\r\n\tAccount Domain:\t\tDESKTOP-FLUENTTEST\r\n\r\nProcess Information:\r\n\tProcess ID:\t\t0x50b8\r\n\tProcess Name:\t\tC:\\msys64\\usr\\bin\\make.exe
213
- DESC
214
- h = {"Description" => desc}
215
- expected = {"DescriptionTitle" => "A user's local group membership was enumerated.",
216
- "subject.security_id" => "S-X-Y-XX-WWWWWW-VVVV",
217
- "subject.account_name" => "Administrator",
218
- "subject.account_domain" => "DESKTOP-FLUENTTEST",
219
- "subject.logon_id" => "0x3185B1",
220
- "user.security_id" => "S-X-Y-XX-WWWWWW-VVVV",
221
- "user.account_name" => "Administrator",
222
- "user.account_domain" => "DESKTOP-FLUENTTEST",
223
- "process_information.process_id" => "0x50b8",
224
- "process_information.process_name" => "C:\\msys64\\usr\\bin\\make.exe"}
225
- d.instance.parse_desc(h)
226
- assert_equal(expected, h)
227
- end
228
-
229
- def test_parse_privileges_description
230
- d = create_driver
231
- desc = ["Special privileges assigned to new logon.\r\n\r\nSubject:\r\n\tSecurity ID:\t\tS-X-Y-ZZ\r\n\t",
232
- "AccountName:\t\tSYSTEM\r\n\tAccount Domain:\t\tNT AUTHORITY\r\n\tLogon ID:\t\t0x3E7\r\n\r\n",
233
- "Privileges:\t\tSeAssignPrimaryTokenPrivilege\r\n\t\t\tSeTcbPrivilege\r\n\t\t\t",
234
- "SeSecurityPrivilege\r\n\t\t\tSeTakeOwnershipPrivilege\r\n\t\t\tSeLoadDriverPrivilege\r\n\t\t\t",
235
- "SeBackupPrivilege\r\n\t\t\tSeRestorePrivilege\r\n\t\t\tSeDebugPrivilege\r\n\t\t\t",
236
- "SeAuditPrivilege\r\n\t\t\tSeSystemEnvironmentPrivilege\r\n\t\t\tSeImpersonatePrivilege\r\n\t\t\t",
237
- "SeDelegateSessionUserImpersonatePrivilege"].join("")
238
-
239
- h = {"Description" => desc}
240
- expected = {"DescriptionTitle" => "Special privileges assigned to new logon.",
241
- "subject.security_id" => "S-X-Y-ZZ",
242
- "subject.accountname" => "SYSTEM",
243
- "subject.account_domain" => "NT AUTHORITY",
244
- "subject.logon_id" => "0x3E7",
245
- "privileges" => ["SeAssignPrimaryTokenPrivilege",
246
- "SeTcbPrivilege",
247
- "SeSecurityPrivilege",
248
- "SeTakeOwnershipPrivilege",
249
- "SeLoadDriverPrivilege",
250
- "SeBackupPrivilege",
251
- "SeRestorePrivilege",
252
- "SeDebugPrivilege",
253
- "SeAuditPrivilege",
254
- "SeSystemEnvironmentPrivilege",
255
- "SeImpersonatePrivilege",
256
- "SeDelegateSessionUserImpersonatePrivilege"]}
257
- d.instance.parse_desc(h)
258
- assert_equal(expected, h)
259
- end
260
-
261
- test "A new external device was recognized by the system." do
262
- # using the event log example: eventopedia.cloudapp.net/EventDetails.aspx?id=17ef124e-eb89-4c01-9ba2-d761e06b2b68
263
- d = create_driver
264
- desc = nil
265
- File.open('./test/data/eventid_6416', 'r') do |f|
266
- desc = f.read.gsub(/\R/, "\r\n")
267
- end
268
- h = {"Description" => desc}
269
- expected = {"DescriptionTitle" => "A new external device was recognized by the system.",
270
- "class_id" => "{1ed2bbf9-11f0-4084-b21f-ad83a8e6dcdc}",
271
- "class_name" => "PrintQueue",
272
- "compatible_ids" => ["GenPrintQueue", "SWD\\GenericRaw", "SWD\\Generic"],
273
- "device_id" => "SWD\\PRINTENUM\\{60FA1C6A-1AB2-440A-AEE1-62ABFB9A4650}",
274
- "device_name" => "Microsoft Print to PDF",
275
- "subject.account_domain" => "ITSS",
276
- "subject.account_name" => "IIZHU2016$",
277
- "subject.logon_id" => "0x3E7",
278
- "subject.security_id" => "SYSTEM",
279
- "vendor_ids" => ["PRINTENUM\\{084f01fa-e634-4d77-83ee-074817c03581}",
280
- "PRINTENUM\\LocalPrintQueue",
281
- "{084f01fa-e634-4d77-83ee-074817c03581}"]}
282
- d.instance.parse_desc(h)
283
- assert_equal(expected, h)
284
- end
285
-
286
- def test_write
287
- d = create_driver
288
-
289
- service = Fluent::Plugin::EventService.new
290
-
291
- d.run(expect_emits: 1) do
292
- service.run
293
- end
294
-
295
- assert(d.events.length >= 1)
296
- event = d.events.select {|e| e.last["EventID"] == "65500" }.last
297
- record = event.last
298
-
299
- assert_equal("Application", record["Channel"])
300
- assert_equal("65500", record["EventID"])
301
- assert_equal("4", record["Level"])
302
- assert_equal("fluent-plugins", record["ProviderName"])
303
- end
304
-
305
- CONFIG_KEYS = config_element("ROOT", "", {
306
- "tag" => "fluent.eventlog",
307
- "keys" => ["EventID", "Level", "Channel", "ProviderName"]
308
- }, [
309
- config_element("storage", "", {
310
- '@type' => 'local',
311
- 'persistent' => false
312
- })
313
- ])
314
- def test_write_with_keys
315
- d = create_driver(CONFIG_KEYS)
316
-
317
- service = Fluent::Plugin::EventService.new
318
-
319
- d.run(expect_emits: 1) do
320
- service.run
321
- end
322
-
323
- assert(d.events.length >= 1)
324
- event = d.events.select {|e| e.last["EventID"] == "65500" }.last
325
- record = event.last
326
-
327
- expected = {"EventID" => "65500",
328
- "Level" => "4",
329
- "Channel" => "Application",
330
- "ProviderName" => "fluent-plugins"}
331
-
332
- assert_equal(expected, record)
333
- end
334
-
335
- REMOTING_ACCESS_CONFIG = config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
336
- config_element("storage", "", {
337
- '@type' => 'local',
338
- 'persistent' => false
339
- }),
340
- config_element("subscribe", "", {
341
- 'channels' => ['Application'],
342
- 'remote_server' => '127.0.0.1',
343
- }),
344
- ])
345
-
346
- def test_write_with_remoting_access
347
- d = create_driver(REMOTING_ACCESS_CONFIG)
348
-
349
- service = Fluent::Plugin::EventService.new
350
-
351
- d.run(expect_emits: 1) do
352
- service.run
353
- end
354
-
355
- assert(d.events.length >= 1)
356
- event = d.events.select {|e| e.last["EventID"] == "65500" }.last
357
- record = event.last
358
-
359
- assert_equal("Application", record["Channel"])
360
- assert_equal("65500", record["EventID"])
361
- assert_equal("4", record["Level"])
362
- assert_equal("fluent-plugins", record["ProviderName"])
363
- end
364
-
365
- class HashRendered < self
366
- def test_write
367
- d = create_driver(config_element("ROOT", "", {"tag" => "fluent.eventlog",
368
- "render_as_xml" => false}, [
369
- config_element("storage", "", {
370
- '@type' => 'local',
371
- 'persistent' => false
372
- })
373
- ]))
374
-
375
- service = Fluent::Plugin::EventService.new
376
-
377
- d.run(expect_emits: 1) do
378
- service.run
379
- end
380
-
381
- assert(d.events.length >= 1)
382
- event = d.events.select {|e| e.last["EventID"] == "65500" }.last
383
- record = event.last
384
-
385
- assert_false(d.instance.render_as_xml)
386
- assert_equal("Application", record["Channel"])
387
- assert_equal("65500", record["EventID"])
388
- assert_equal("4", record["Level"])
389
- assert_equal("fluent-plugins", record["ProviderName"])
390
- end
391
-
392
- def test_write_with_preserving_qualifiers
393
- require 'winevt'
394
-
395
- d = create_driver(config_element("ROOT", "", {"tag" => "fluent.eventlog",
396
- "render_as_xml" => false,
397
- 'preserve_qualifiers_on_hash' => true
398
- }, [
399
- config_element("storage", "", {
400
- '@type' => 'local',
401
- 'persistent' => false
402
- }),
403
- ]))
404
-
405
- service = Fluent::Plugin::EventService.new
406
- subscribe = Winevt::EventLog::Subscribe.new
407
-
408
- omit "@parser.preserve_qualifiers does not respond" unless subscribe.respond_to?(:preserve_qualifiers?)
409
-
410
- d.run(expect_emits: 1) do
411
- service.run
412
- end
413
-
414
- assert(d.events.length >= 1)
415
- event = d.events.last
416
- record = event.last
417
-
418
- assert_true(record.has_key?("Description"))
419
- assert_true(record.has_key?("EventData"))
420
- assert_true(record.has_key?("Qualifiers"))
421
- end
422
- end
423
-
424
- class PersistBookMark < self
425
- TEST_PLUGIN_STORAGE_PATH = File.join( File.dirname(File.dirname(__FILE__)), 'tmp', 'in_windows_eventlog2', 'store' )
426
- CONFIG2 = config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
427
- config_element("storage", "", {
428
- '@type' => 'local',
429
- '@id' => 'test-02',
430
- '@log_level' => "info",
431
- 'path' => File.join(TEST_PLUGIN_STORAGE_PATH,
432
- 'json', 'test-02.json'),
433
- 'persistent' => true,
434
- })
435
- ])
436
-
437
- def setup
438
- FileUtils.rm_rf(TEST_PLUGIN_STORAGE_PATH)
439
- FileUtils.mkdir_p(File.join(TEST_PLUGIN_STORAGE_PATH, 'json'))
440
- FileUtils.chmod_R(0755, File.join(TEST_PLUGIN_STORAGE_PATH, 'json'))
441
- end
442
-
443
- def test_write
444
- d = create_driver(CONFIG2)
445
-
446
- assert !File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
447
-
448
- service = Fluent::Plugin::EventService.new
449
-
450
- d.run(expect_emits: 1) do
451
- service.run
452
- end
453
-
454
- assert(d.events.length >= 1)
455
- event = d.events.select {|e| e.last["EventID"] == "65500" }.last
456
- record = event.last
457
-
458
- prev_id = record["EventRecordID"].to_i
459
- assert_equal("Application", record["Channel"])
460
- assert_equal("65500", record["EventID"])
461
- assert_equal("4", record["Level"])
462
- assert_equal("fluent-plugins", record["ProviderName"])
463
-
464
- assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
465
-
466
- d2 = create_driver(CONFIG2)
467
- d2.run(expect_emits: 1) do
468
- service.run
469
- end
470
-
471
- assert(d2.events.length == 1) # should be tailing after previous context.
472
- event2 = d2.events.last
473
- record2 = event2.last
474
-
475
- curr_id = record2["EventRecordID"].to_i
476
- assert(curr_id > prev_id)
477
-
478
- assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
479
- end
480
-
481
- def test_start_with_invalid_bookmark
482
- invalid_storage_contents = <<-EOS
483
- <BookmarkList>\r\n <Bookmark Channel='Application' RecordId='20063' IsCurrent='true'/>\r\n
484
- EOS
485
- d = create_driver(CONFIG2)
486
- storage = d.instance.instance_variable_get(:@bookmarks_storage)
487
- storage.put('application', invalid_storage_contents)
488
- assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
489
-
490
- d2 = create_driver(CONFIG2)
491
- assert_raise(Fluent::ConfigError) do
492
- d2.instance.start
493
- end
494
- assert_equal 0, d2.logs.grep(/This stored bookmark is incomplete for using. Referring `read_existing_events` parameter to subscribe:/).length
495
- end
496
-
497
- def test_start_with_empty_bookmark
498
- invalid_storage_contents = <<-EOS
499
- <BookmarkList>\r\n</BookmarkList>
500
- EOS
501
- d = create_driver(CONFIG2)
502
- storage = d.instance.instance_variable_get(:@bookmarks_storage)
503
- storage.put('application', invalid_storage_contents)
504
- assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
505
-
506
- d2 = create_driver(CONFIG2)
507
- d2.instance.start
508
- assert_equal 1, d2.logs.grep(/This stored bookmark is incomplete for using. Referring `read_existing_events` parameter to subscribe:/).length
509
- end
510
- end
511
-
512
- def test_write_with_none_parser
513
- d = create_driver(config_element("ROOT", "", {"tag" => "fluent.eventlog",
514
- "render_as_xml" => true}, [
515
- config_element("storage", "", {
516
- '@type' => 'local',
517
- 'persistent' => false
518
- }),
519
- config_element("parse", "", {
520
- '@type' => 'none',
521
- }),
522
- ]))
523
-
524
- service = Fluent::Plugin::EventService.new
525
-
526
- d.run(expect_emits: 1) do
527
- service.run
528
- end
529
-
530
- assert(d.events.length >= 1)
531
- event = d.events.last
532
- record = event.last
533
-
534
- assert do
535
- # record should be {message: <RAW XML EventLog>}.
536
- record["message"]
537
- end
538
-
539
- assert_true(record.has_key?("Description"))
540
- assert_true(record.has_key?("EventData"))
541
- end
542
-
543
- def test_write_with_winevt_xml_parser_without_qualifiers
544
- d = create_driver(config_element("ROOT", "", {"tag" => "fluent.eventlog",
545
- "render_as_xml" => true}, [
546
- config_element("storage", "", {
547
- '@type' => 'local',
548
- 'persistent' => false
549
- }),
550
- config_element("parse", "", {
551
- '@type' => 'winevt_xml',
552
- 'preserve_qualifiers' => false
553
- }),
554
- ]))
555
-
556
- service = Fluent::Plugin::EventService.new
557
-
558
- omit "@parser.preserve_qualifiers does not respond" unless d.instance.instance_variable_get(:@parser).respond_to?(:preserve_qualifiers?)
559
-
560
- d.run(expect_emits: 1) do
561
- service.run
562
- end
563
-
564
- assert(d.events.length >= 1)
565
- event = d.events.last
566
- record = event.last
567
-
568
- assert_true(record.has_key?("Description"))
569
- assert_true(record.has_key?("EventData"))
570
- assert_false(record.has_key?("Qualifiers"))
571
- end
572
- end
1
+ require 'helper'
2
+ require 'fileutils'
3
+ require 'generate-windows-event'
4
+
5
+ # Monkey patch for testing
6
+ class Winevt::EventLog::Session
7
+ def ==(obj)
8
+ self.server == obj.server &&
9
+ self.domain == obj.domain &&
10
+ self.username == obj.username &&
11
+ self.password == obj.password &&
12
+ self.flags == obj.flags
13
+ end
14
+ end
15
+
16
+ class WindowsEventLog2InputTest < Test::Unit::TestCase
17
+
18
+ def setup
19
+ Fluent::Test.setup
20
+ end
21
+
22
+ CONFIG = config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
23
+ config_element("storage", "", {
24
+ '@type' => 'local',
25
+ 'persistent' => false
26
+ })
27
+ ])
28
+
29
+ XML_RENDERING_CONFIG = config_element("ROOT", "", {"tag" => "fluent.eventlog",
30
+ "render_as_xml" => true}, [
31
+ config_element("storage", "", {
32
+ '@type' => 'local',
33
+ 'persistent' => false
34
+ })
35
+ ])
36
+
37
+ def create_driver(conf = CONFIG)
38
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::WindowsEventLog2Input).configure(conf)
39
+ end
40
+
41
+ def test_configure
42
+ d = create_driver CONFIG
43
+ assert_equal 'fluent.eventlog', d.instance.tag
44
+ assert_equal 2, d.instance.read_interval
45
+ assert_equal [], d.instance.channels
46
+ assert_false d.instance.read_existing_events
47
+ assert_false d.instance.render_as_xml
48
+ assert_nil d.instance.refresh_subscription_interval
49
+ end
50
+
51
+ sub_test_case "configure" do
52
+ test "refresh subscription interval" do
53
+ d = create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog",
54
+ "refresh_subscription_interval" => "2m"}, [
55
+ config_element("storage", "", {
56
+ '@type' => 'local',
57
+ 'persistent' => false
58
+ })
59
+ ])
60
+ assert_equal 120, d.instance.refresh_subscription_interval
61
+ end
62
+
63
+ test "subscribe directive" do
64
+ d = create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
65
+ config_element("storage", "", {
66
+ '@type' => 'local',
67
+ 'persistent' => false
68
+ }),
69
+ config_element("subscribe", "", {
70
+ 'channels' => ['System', 'Windows PowerShell'],
71
+ }),
72
+ config_element("subscribe", "", {
73
+ 'channels' => ['Security'],
74
+ 'read_existing_events' => true
75
+ }),
76
+ ])
77
+ expected = [["system", false, nil], ["windows powershell", false, nil], ["security", true, nil]]
78
+ assert_equal expected, d.instance.instance_variable_get(:@chs)
79
+ end
80
+
81
+ test "subscribe directive with remote server session" do
82
+ d = create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
83
+ config_element("storage", "", {
84
+ '@type' => 'local',
85
+ 'persistent' => false
86
+ }),
87
+ config_element("subscribe", "", {
88
+ 'channels' => ['System', 'Windows PowerShell'],
89
+ 'remote_server' => '127.0.0.1',
90
+ }),
91
+ config_element("subscribe", "", {
92
+ 'channels' => ['Security'],
93
+ 'read_existing_events' => true,
94
+ 'remote_server' => '192.168.0.1',
95
+ 'remote_username' => 'fluentd',
96
+ 'remote_password' => 'changeme!'
97
+ }),
98
+ ])
99
+ localhost_session = Winevt::EventLog::Session.new("127.0.0.1")
100
+ remote_session = Winevt::EventLog::Session.new("192.168.0.1",
101
+ nil,
102
+ "fluentd",
103
+ "changeme!")
104
+ expected = [["system", false, localhost_session],
105
+ ["windows powershell", false, localhost_session],
106
+ ["security", true, remote_session]]
107
+ assert_equal expected, d.instance.instance_variable_get(:@chs)
108
+ end
109
+
110
+ test "duplicated subscribe" do
111
+ d = create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog",
112
+ "channels" => ["System", "Windows PowerShell"]
113
+ }, [
114
+ config_element("storage", "", {
115
+ '@type' => 'local',
116
+ 'persistent' => false
117
+ }),
118
+ config_element("subscribe", "", {
119
+ 'channels' => ['System', 'Windows PowerShell'],
120
+ }),
121
+ config_element("subscribe", "", {
122
+ 'channels' => ['Security'],
123
+ 'read_existing_events' => true
124
+ }),
125
+ ])
126
+ expected = [["system", false, nil], ["windows powershell", false, nil], ["security", true, nil]]
127
+ assert_equal 1, d.instance.instance_variable_get(:@chs).select {|ch, flag| ch == "system"}.size
128
+ assert_equal expected, d.instance.instance_variable_get(:@chs)
129
+ end
130
+
131
+ test "non duplicated subscribe" do
132
+ d = create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog",
133
+ "channels" => ["System", "Windows PowerShell"]
134
+ }, [
135
+ config_element("storage", "", {
136
+ '@type' => 'local',
137
+ 'persistent' => false
138
+ }),
139
+ config_element("subscribe", "", {
140
+ 'channels' => ['System', 'Windows PowerShell'],
141
+ 'read_existing_events' => true
142
+ }),
143
+ config_element("subscribe", "", {
144
+ 'channels' => ['Security'],
145
+ 'read_existing_events' => true
146
+ }),
147
+ ])
148
+ expected = [["system", false, nil], ["windows powershell", false, nil], ["system", true, nil], ["windows powershell", true, nil], ["security", true, nil]]
149
+ assert_equal 2, d.instance.instance_variable_get(:@chs).select {|ch, flag| ch == "system"}.size
150
+ assert_equal expected, d.instance.instance_variable_get(:@chs)
151
+ end
152
+
153
+ test "invalid combination for preserving qualifiers" do
154
+ assert_raise(Fluent::ConfigError) do
155
+ create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog",
156
+ "render_as_xml" => true,
157
+ "preserve_qualifiers_on_hash" => true,
158
+ }, [
159
+ config_element("storage", "", {
160
+ '@type' => 'local',
161
+ 'persistent' => false
162
+ }),
163
+ ])
164
+ end
165
+ end
166
+
167
+ test "invalid description locale" do
168
+ assert_raise(Fluent::ConfigError) do
169
+ create_driver config_element("ROOT", "", {"tag" => "fluent.eventlog",
170
+ "description_locale" => "ex_EX"
171
+ }, [
172
+ config_element("storage", "", {
173
+ '@type' => 'local',
174
+ 'persistent' => false
175
+ })
176
+ ])
177
+ end
178
+ end
179
+ end
180
+
181
+ data("Japanese" => ["ja_JP", false],
182
+ "English (United States)" => ["en_US", false],
183
+ "English (UK)" => ["en_GB", false],
184
+ "Dutch" => ["nl_NL", false],
185
+ "French" => ["fr_FR", false],
186
+ "German" => ["de_DE", false],
187
+ "Russian" => ["ru_RU", false],
188
+ "Spanish" => ["es_ES", false],
189
+ "Invalid" => ["ex_EX", true],
190
+ )
191
+ def test_unsupported_locale_p(data)
192
+ description_locale, expected = data
193
+ d = create_driver CONFIG
194
+ locale = Winevt::EventLog::Locale.new
195
+ result = d.instance.unsupported_locale?(locale, description_locale)
196
+ assert_equal expected, result
197
+ end
198
+
199
+ data("application" => ["Application", "Application"],
200
+ "windows powershell" => ["Windows PowerShell", "Windows PowerShell"],
201
+ "escaped" => ["Should_Be_Escaped_", "Should+Be;Escaped/"]
202
+ )
203
+ def test_escape_channel(data)
204
+ expected, actual = data
205
+ d = create_driver CONFIG
206
+ assert_equal expected, d.instance.escape_channel(actual)
207
+ end
208
+
209
+ def test_parse_desc
210
+ d = create_driver
211
+ desc =<<-DESC
212
+ A user's local group membership was enumerated.\r\n\r\nSubject:\r\n\tSecurity ID:\t\tS-X-Y-XX-WWWWWW-VVVV\r\n\tAccount Name:\t\tAdministrator\r\n\tAccount Domain:\t\tDESKTOP-FLUENTTEST\r\n\tLogon ID:\t\t0x3185B1\r\n\r\nUser:\r\n\tSecurity ID:\t\tS-X-Y-XX-WWWWWW-VVVV\r\n\tAccount Name:\t\tAdministrator\r\n\tAccount Domain:\t\tDESKTOP-FLUENTTEST\r\n\r\nProcess Information:\r\n\tProcess ID:\t\t0x50b8\r\n\tProcess Name:\t\tC:\\msys64\\usr\\bin\\make.exe
213
+ DESC
214
+ h = {"Description" => desc}
215
+ expected = {"DescriptionTitle" => "A user's local group membership was enumerated.",
216
+ "subject.security_id" => "S-X-Y-XX-WWWWWW-VVVV",
217
+ "subject.account_name" => "Administrator",
218
+ "subject.account_domain" => "DESKTOP-FLUENTTEST",
219
+ "subject.logon_id" => "0x3185B1",
220
+ "user.security_id" => "S-X-Y-XX-WWWWWW-VVVV",
221
+ "user.account_name" => "Administrator",
222
+ "user.account_domain" => "DESKTOP-FLUENTTEST",
223
+ "process_information.process_id" => "0x50b8",
224
+ "process_information.process_name" => "C:\\msys64\\usr\\bin\\make.exe"}
225
+ d.instance.parse_desc(h)
226
+ assert_equal(expected, h)
227
+ end
228
+
229
+ def test_parse_privileges_description
230
+ d = create_driver
231
+ desc = ["Special privileges assigned to new logon.\r\n\r\nSubject:\r\n\tSecurity ID:\t\tS-X-Y-ZZ\r\n\t",
232
+ "AccountName:\t\tSYSTEM\r\n\tAccount Domain:\t\tNT AUTHORITY\r\n\tLogon ID:\t\t0x3E7\r\n\r\n",
233
+ "Privileges:\t\tSeAssignPrimaryTokenPrivilege\r\n\t\t\tSeTcbPrivilege\r\n\t\t\t",
234
+ "SeSecurityPrivilege\r\n\t\t\tSeTakeOwnershipPrivilege\r\n\t\t\tSeLoadDriverPrivilege\r\n\t\t\t",
235
+ "SeBackupPrivilege\r\n\t\t\tSeRestorePrivilege\r\n\t\t\tSeDebugPrivilege\r\n\t\t\t",
236
+ "SeAuditPrivilege\r\n\t\t\tSeSystemEnvironmentPrivilege\r\n\t\t\tSeImpersonatePrivilege\r\n\t\t\t",
237
+ "SeDelegateSessionUserImpersonatePrivilege"].join("")
238
+
239
+ h = {"Description" => desc}
240
+ expected = {"DescriptionTitle" => "Special privileges assigned to new logon.",
241
+ "subject.security_id" => "S-X-Y-ZZ",
242
+ "subject.accountname" => "SYSTEM",
243
+ "subject.account_domain" => "NT AUTHORITY",
244
+ "subject.logon_id" => "0x3E7",
245
+ "privileges" => ["SeAssignPrimaryTokenPrivilege",
246
+ "SeTcbPrivilege",
247
+ "SeSecurityPrivilege",
248
+ "SeTakeOwnershipPrivilege",
249
+ "SeLoadDriverPrivilege",
250
+ "SeBackupPrivilege",
251
+ "SeRestorePrivilege",
252
+ "SeDebugPrivilege",
253
+ "SeAuditPrivilege",
254
+ "SeSystemEnvironmentPrivilege",
255
+ "SeImpersonatePrivilege",
256
+ "SeDelegateSessionUserImpersonatePrivilege"]}
257
+ d.instance.parse_desc(h)
258
+ assert_equal(expected, h)
259
+ end
260
+
261
+ test "A new external device was recognized by the system." do
262
+ # using the event log example: eventopedia.cloudapp.net/EventDetails.aspx?id=17ef124e-eb89-4c01-9ba2-d761e06b2b68
263
+ d = create_driver
264
+ desc = nil
265
+ File.open('./test/data/eventid_6416', 'r') do |f|
266
+ desc = f.read.gsub(/\R/, "\r\n")
267
+ end
268
+ h = {"Description" => desc}
269
+ expected = {"DescriptionTitle" => "A new external device was recognized by the system.",
270
+ "class_id" => "{1ed2bbf9-11f0-4084-b21f-ad83a8e6dcdc}",
271
+ "class_name" => "PrintQueue",
272
+ "compatible_ids" => ["GenPrintQueue", "SWD\\GenericRaw", "SWD\\Generic"],
273
+ "device_id" => "SWD\\PRINTENUM\\{60FA1C6A-1AB2-440A-AEE1-62ABFB9A4650}",
274
+ "device_name" => "Microsoft Print to PDF",
275
+ "subject.account_domain" => "ITSS",
276
+ "subject.account_name" => "IIZHU2016$",
277
+ "subject.logon_id" => "0x3E7",
278
+ "subject.security_id" => "SYSTEM",
279
+ "vendor_ids" => ["PRINTENUM\\{084f01fa-e634-4d77-83ee-074817c03581}",
280
+ "PRINTENUM\\LocalPrintQueue",
281
+ "{084f01fa-e634-4d77-83ee-074817c03581}"]}
282
+ d.instance.parse_desc(h)
283
+ assert_equal(expected, h)
284
+ end
285
+
286
+ def test_write
287
+ d = create_driver
288
+
289
+ service = Fluent::Plugin::EventService.new
290
+
291
+ d.run(expect_emits: 1) do
292
+ service.run
293
+ end
294
+
295
+ assert(d.events.length >= 1)
296
+ event = d.events.select {|e| e.last["EventID"] == "65500" }.last
297
+ record = event.last
298
+
299
+ assert_equal("Application", record["Channel"])
300
+ assert_equal("65500", record["EventID"])
301
+ assert_equal("4", record["Level"])
302
+ assert_equal("fluent-plugins", record["ProviderName"])
303
+ end
304
+
305
+ CONFIG_WITH_NON_EXISTENT_CHANNEL = config_element("ROOT", "", {
306
+ "channels" => ["application", "NonExistentChannel"]
307
+ })
308
+ def test_skip_non_existent_channel
309
+ d = create_driver(CONFIG + CONFIG_WITH_NON_EXISTENT_CHANNEL)
310
+
311
+ service = Fluent::Plugin::EventService.new
312
+
313
+ assert_nothing_raised do
314
+ d.run(expect_emits: 1) do
315
+ service.run
316
+ end
317
+ end
318
+
319
+ assert(d.events.length >= 1)
320
+ assert(d.logs.any?{|log| log.include?("[warn]: Channel Not Found: nonexistentchannel") },
321
+ d.logs.join("\n"))
322
+ end
323
+
324
+ CONFIG_WITH_QUERY = config_element("ROOT", "", {"tag" => "fluent.eventlog",
325
+ "event_query" => "Event/System[EventID=65500]"}, [
326
+ config_element("storage", "", {
327
+ '@type' => 'local',
328
+ 'persistent' => false
329
+ })
330
+ ])
331
+ def test_write_with_event_query
332
+ d = create_driver(CONFIG_WITH_QUERY)
333
+
334
+ service = Fluent::Plugin::EventService.new
335
+
336
+ d.run(expect_emits: 1) do
337
+ service.run
338
+ end
339
+
340
+ assert(d.events.length >= 1)
341
+ event = d.events.last
342
+ record = event.last
343
+
344
+ assert_equal("Application", record["Channel"])
345
+ assert_equal("65500", record["EventID"])
346
+ assert_equal("4", record["Level"])
347
+ assert_equal("fluent-plugins", record["ProviderName"])
348
+ end
349
+
350
+
351
+ CONFIG_KEYS = config_element("ROOT", "", {
352
+ "tag" => "fluent.eventlog",
353
+ "keys" => ["EventID", "Level", "Channel", "ProviderName"]
354
+ }, [
355
+ config_element("storage", "", {
356
+ '@type' => 'local',
357
+ 'persistent' => false
358
+ })
359
+ ])
360
+ def test_write_with_keys
361
+ d = create_driver(CONFIG_KEYS)
362
+
363
+ service = Fluent::Plugin::EventService.new
364
+
365
+ d.run(expect_emits: 1) do
366
+ service.run
367
+ end
368
+
369
+ assert(d.events.length >= 1)
370
+ event = d.events.select {|e| e.last["EventID"] == "65500" }.last
371
+ record = event.last
372
+
373
+ expected = {"EventID" => "65500",
374
+ "Level" => "4",
375
+ "Channel" => "Application",
376
+ "ProviderName" => "fluent-plugins"}
377
+
378
+ assert_equal(expected, record)
379
+ end
380
+
381
+ REMOTING_ACCESS_CONFIG = config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
382
+ config_element("storage", "", {
383
+ '@type' => 'local',
384
+ 'persistent' => false
385
+ }),
386
+ config_element("subscribe", "", {
387
+ 'channels' => ['Application'],
388
+ 'remote_server' => '127.0.0.1',
389
+ }),
390
+ ])
391
+
392
+ def test_write_with_remoting_access
393
+ d = create_driver(REMOTING_ACCESS_CONFIG)
394
+
395
+ service = Fluent::Plugin::EventService.new
396
+
397
+ d.run(expect_emits: 1) do
398
+ service.run
399
+ end
400
+
401
+ assert(d.events.length >= 1)
402
+ event = d.events.select {|e| e.last["EventID"] == "65500" }.last
403
+ record = event.last
404
+
405
+ assert_equal("Application", record["Channel"])
406
+ assert_equal("65500", record["EventID"])
407
+ assert_equal("4", record["Level"])
408
+ assert_equal("fluent-plugins", record["ProviderName"])
409
+ end
410
+
411
+ class HashRendered < self
412
+ def test_write
413
+ d = create_driver(config_element("ROOT", "", {"tag" => "fluent.eventlog",
414
+ "render_as_xml" => false}, [
415
+ config_element("storage", "", {
416
+ '@type' => 'local',
417
+ 'persistent' => false
418
+ })
419
+ ]))
420
+
421
+ service = Fluent::Plugin::EventService.new
422
+
423
+ d.run(expect_emits: 1) do
424
+ service.run
425
+ end
426
+
427
+ assert(d.events.length >= 1)
428
+ event = d.events.select {|e| e.last["EventID"] == "65500" }.last
429
+ record = event.last
430
+
431
+ assert_false(d.instance.render_as_xml)
432
+ assert_equal("Application", record["Channel"])
433
+ assert_equal("65500", record["EventID"])
434
+ assert_equal("4", record["Level"])
435
+ assert_equal("fluent-plugins", record["ProviderName"])
436
+ end
437
+
438
+ def test_write_with_preserving_qualifiers
439
+ require 'winevt'
440
+
441
+ d = create_driver(config_element("ROOT", "", {"tag" => "fluent.eventlog",
442
+ "render_as_xml" => false,
443
+ 'preserve_qualifiers_on_hash' => true
444
+ }, [
445
+ config_element("storage", "", {
446
+ '@type' => 'local',
447
+ 'persistent' => false
448
+ }),
449
+ ]))
450
+
451
+ service = Fluent::Plugin::EventService.new
452
+ subscribe = Winevt::EventLog::Subscribe.new
453
+
454
+ omit "@parser.preserve_qualifiers does not respond" unless subscribe.respond_to?(:preserve_qualifiers?)
455
+
456
+ d.run(expect_emits: 1) do
457
+ service.run
458
+ end
459
+
460
+ assert(d.events.length >= 1)
461
+ event = d.events.last
462
+ record = event.last
463
+
464
+ assert_true(record.has_key?("Description"))
465
+ assert_true(record.has_key?("EventData"))
466
+ assert_true(record.has_key?("Qualifiers"))
467
+ end
468
+ end
469
+
470
+ class PersistBookMark < self
471
+ TEST_PLUGIN_STORAGE_PATH = File.join( File.dirname(File.dirname(__FILE__)), 'tmp', 'in_windows_eventlog2', 'store' )
472
+ CONFIG2 = config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
473
+ config_element("storage", "", {
474
+ '@type' => 'local',
475
+ '@id' => 'test-02',
476
+ '@log_level' => "info",
477
+ 'path' => File.join(TEST_PLUGIN_STORAGE_PATH,
478
+ 'json', 'test-02.json'),
479
+ 'persistent' => true,
480
+ })
481
+ ])
482
+
483
+ def setup
484
+ FileUtils.rm_rf(TEST_PLUGIN_STORAGE_PATH)
485
+ FileUtils.mkdir_p(File.join(TEST_PLUGIN_STORAGE_PATH, 'json'))
486
+ FileUtils.chmod_R(0755, File.join(TEST_PLUGIN_STORAGE_PATH, 'json'))
487
+ end
488
+
489
+ def test_write
490
+ d = create_driver(CONFIG2)
491
+
492
+ assert !File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
493
+
494
+ service = Fluent::Plugin::EventService.new
495
+
496
+ d.run(expect_emits: 1) do
497
+ service.run
498
+ end
499
+
500
+ assert(d.events.length >= 1)
501
+ event = d.events.select {|e| e.last["EventID"] == "65500" }.last
502
+ record = event.last
503
+
504
+ prev_id = record["EventRecordID"].to_i
505
+ assert_equal("Application", record["Channel"])
506
+ assert_equal("65500", record["EventID"])
507
+ assert_equal("4", record["Level"])
508
+ assert_equal("fluent-plugins", record["ProviderName"])
509
+
510
+ assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
511
+
512
+ d2 = create_driver(CONFIG2)
513
+ d2.run(expect_emits: 1) do
514
+ service.run
515
+ end
516
+
517
+ events = d2.events.select {|e| e.last["EventID"] == "65500" }
518
+ assert(events.length == 1) # should be tailing after previous context.
519
+ event2 = events.last
520
+ record2 = event2.last
521
+
522
+ curr_id = record2["EventRecordID"].to_i
523
+ assert(curr_id > prev_id)
524
+
525
+ assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
526
+ end
527
+
528
+ def test_start_with_invalid_bookmark
529
+ invalid_storage_contents = <<-EOS
530
+ <BookmarkList>\r\n <Bookmark Channel='Application' RecordId='20063' IsCurrent='true'/>\r\n
531
+ EOS
532
+ d = create_driver(CONFIG2)
533
+ storage = d.instance.instance_variable_get(:@bookmarks_storage)
534
+ storage.put('application', invalid_storage_contents)
535
+ assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
536
+
537
+ d2 = create_driver(CONFIG2)
538
+ assert_raise(Fluent::ConfigError) do
539
+ d2.instance.start
540
+ end
541
+ assert_equal 0, d2.logs.grep(/This stored bookmark is incomplete for using. Referring `read_existing_events` parameter to subscribe:/).length
542
+ end
543
+
544
+ def test_start_with_empty_bookmark
545
+ invalid_storage_contents = <<-EOS
546
+ <BookmarkList>\r\n</BookmarkList>
547
+ EOS
548
+ d = create_driver(CONFIG2)
549
+ storage = d.instance.instance_variable_get(:@bookmarks_storage)
550
+ storage.put('application', invalid_storage_contents)
551
+ assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
552
+
553
+ d2 = create_driver(CONFIG2)
554
+ d2.instance.start
555
+ assert_equal 1, d2.logs.grep(/This stored bookmark is incomplete for using. Referring `read_existing_events` parameter to subscribe:/).length
556
+ end
557
+ end
558
+
559
+ def test_write_with_none_parser
560
+ d = create_driver(config_element("ROOT", "", {"tag" => "fluent.eventlog",
561
+ "render_as_xml" => true}, [
562
+ config_element("storage", "", {
563
+ '@type' => 'local',
564
+ 'persistent' => false
565
+ }),
566
+ config_element("parse", "", {
567
+ '@type' => 'none',
568
+ }),
569
+ ]))
570
+
571
+ service = Fluent::Plugin::EventService.new
572
+
573
+ d.run(expect_emits: 1) do
574
+ service.run
575
+ end
576
+
577
+ assert(d.events.length >= 1)
578
+ event = d.events.last
579
+ record = event.last
580
+
581
+ assert do
582
+ # record should be {message: <RAW XML EventLog>}.
583
+ record["message"]
584
+ end
585
+
586
+ assert_true(record.has_key?("Description"))
587
+ assert_true(record.has_key?("EventData"))
588
+ end
589
+
590
+ def test_write_with_winevt_xml_parser_without_qualifiers
591
+ d = create_driver(config_element("ROOT", "", {"tag" => "fluent.eventlog",
592
+ "render_as_xml" => true}, [
593
+ config_element("storage", "", {
594
+ '@type' => 'local',
595
+ 'persistent' => false
596
+ }),
597
+ config_element("parse", "", {
598
+ '@type' => 'winevt_xml',
599
+ 'preserve_qualifiers' => false
600
+ }),
601
+ ]))
602
+
603
+ service = Fluent::Plugin::EventService.new
604
+
605
+ omit "@parser.preserve_qualifiers does not respond" unless d.instance.instance_variable_get(:@parser).respond_to?(:preserve_qualifiers?)
606
+
607
+ d.run(expect_emits: 1) do
608
+ service.run
609
+ end
610
+
611
+ assert(d.events.length >= 1)
612
+ event = d.events.last
613
+ record = event.last
614
+
615
+ assert_true(record.has_key?("Description"))
616
+ assert_true(record.has_key?("EventData"))
617
+ assert_false(record.has_key?("Qualifiers"))
618
+ end
619
+ end