Skip to content

Commit 559c288

Browse files
authored
Merge pull request #390 from splunk/requirement_test/modinput
feat: requirement test modinput TA's
2 parents 5fc0e97 + c4b0334 commit 559c288

27 files changed

+913
-59
lines changed

pytest_splunk_addon/standard_lib/event_ingestors/ingestor_helper.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,21 @@ def get_event_ingestor(cls, input_type, ingest_meta_data):
3838
LOGGER.debug("Using the following HEC ingestor: {}".format(str(ingestor)))
3939
return ingestor
4040

41+
@classmethod
42+
def get_consolidated_events(cls, events):
43+
ingestor_dict = dict()
44+
for event in events:
45+
input_type = event.metadata.get("input_type")
46+
if input_type in ["modinput", "windows_input", "syslog_tcp", "syslog_udp", "uf_file_monitor"]:
47+
event.event = event.event.encode("utf-8").decode()
48+
else:
49+
event.event = event.event.encode("utf-8")
50+
if input_type in ingestor_dict:
51+
ingestor_dict[input_type].append(event)
52+
else:
53+
ingestor_dict[input_type] = [event]
54+
return ingestor_dict
55+
4156
@classmethod
4257
def ingest_events(
4358
cls,
@@ -60,23 +75,7 @@ def ingest_events(
6075
sample_generator = SampleXdistGenerator(addon_path, config_path)
6176
store_sample = sample_generator.get_samples(store_events)
6277
tokenized_events = store_sample.get("tokenized_events")
63-
ingestor_dict = dict()
64-
for event in tokenized_events:
65-
input_type = event.metadata.get("input_type")
66-
if input_type in [
67-
"modinput",
68-
"windows_input",
69-
"syslog_tcp",
70-
"syslog_udp",
71-
"uf_file_monitor",
72-
]:
73-
event.event = event.event.encode("utf-8").decode()
74-
else:
75-
event.event = event.event.encode("utf-8")
76-
if input_type in ingestor_dict:
77-
ingestor_dict[input_type].append(event)
78-
else:
79-
ingestor_dict[input_type] = [event]
78+
ingestor_dict = cls.get_consolidated_events(tokenized_events)
8079
for input_type, events in ingestor_dict.items():
8180
LOGGER.debug(
8281
"Received the following input type for HEC event: {}".format(input_type)
@@ -85,9 +84,12 @@ def ingest_events(
8584
event_ingestor.ingest(events, thread_count)
8685

8786
if run_requirement_test != "None":
88-
requirement_event = RequirementEventIngestor(run_requirement_test)
89-
events = requirement_event.get_events()
90-
input_type = "syslog_tcp"
91-
event_ingestor = cls.get_event_ingestor(input_type, ingest_meta_data)
92-
event_ingestor.ingest(events, thread_count)
87+
requirement_events = RequirementEventIngestor(run_requirement_test)
88+
requirement_events_get = requirement_events.get_events()
89+
requirement_events_dict = cls.get_consolidated_events(requirement_events_get)
90+
for input_type, events in requirement_events_dict.items():
91+
LOGGER.debug(
92+
"Received the following input type for HEC event: {}".format(input_type))
93+
event_ingestor = cls.get_event_ingestor(input_type, ingest_meta_data)
94+
event_ingestor.ingest(events, thread_count)
9395
LOGGER.info("Ingestion Done")

pytest_splunk_addon/standard_lib/event_ingestors/requirement_event_ingester.py

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,22 @@ def get_models(self, root):
7373
model_list.append(str(model.text))
7474
return model_list
7575

76+
#extract_params_transport
77+
def extract_params(self, event):
78+
host, source, source_type = "", "", ""
79+
for transport in event.iter('transport'):
80+
if transport.get('host'):
81+
host = transport.get('host')
82+
if transport.get('source'):
83+
source = transport.get('source')
84+
if transport.get('sourcetype'):
85+
source_type = transport.get('sourcetype')
86+
return host, source, source_type
87+
7688
def get_events(self):
7789
req_file_path = self.requirement_file_path
7890
events = []
91+
host, source, sourcetype = "", "",""
7992
if os.path.isdir(req_file_path):
8093
for file1 in os.listdir(req_file_path):
8194
filename = os.path.join(req_file_path, file1)
@@ -87,24 +100,30 @@ def get_events(self):
87100
if len(model_list) != 0:
88101
transport_type = self.extract_transport_tag(event_tag)
89102
if transport_type == "syslog":
90-
LOGGER.info(
91-
"sending data using sc4s {}".format(filename)
92-
)
103+
transport_type = "syslog_tcp"
104+
LOGGER.info("sending data using sc4s {}".format(filename))
105+
elif transport_type in ("modinput","Modinput", "Mod input","Modular Input", "Modular input", "modular input","modular_input", "Mod Input","hec_event"):
106+
transport_type = "modinput"
107+
LOGGER.info("sending data via HEC {}".format(filename))
108+
host, source, sourcetype = self.extract_params(event_tag)
109+
LOGGER.info(f"sending data via HEC {host}, {source} {sourcetype}")
110+
elif transport_type == "dbx":
111+
transport_type = "modinput"
112+
elif transport_type == "windows_input":
113+
transport_type = "windows_input"
93114
else:
94115
transport_type = "default"
95116
unescaped_event = self.extract_raw_events(event_tag)
96-
escaped_ingest = self.escape_before_ingest(
97-
unescaped_event
98-
)
99-
metadata = {
100-
"input_type": transport_type,
101-
"index": "main",
102-
}
103-
events.append(
104-
SampleEvent(
105-
escaped_ingest, metadata, "requirement_test"
106-
)
107-
)
117+
escaped_ingest = self.escape_before_ingest(unescaped_event)
118+
metadata = {'input_type': transport_type,
119+
'index': 'main',
120+
"source": source,
121+
"host": host,
122+
"sourcetype": sourcetype,
123+
"timestamp_type": "event",
124+
}
125+
events.append(SampleEvent(escaped_ingest, metadata, "requirement_test"))
126+
108127
else:
109128
# if there is no model in event do not ingest that event
110129
continue

pytest_splunk_addon/standard_lib/requirement_tests/requirement_test_datamodel_tag_constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,5 @@
8181
"Updates_Update_Errors": ["update", "error"],
8282
"Vulnerabilities": ["report", "vulnerability"],
8383
"Web": ["web"],
84-
"Web_proxy": ["web", "proxy"],
84+
"Web_Proxy": ["web", "proxy"],
8585
}

pytest_splunk_addon/standard_lib/requirement_tests/test_generator.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ def generate_cim_req_params(self):
9797
"""
9898
req_file_path = self.folder_path
9999
req_test_id = 0
100+
modinput_params = None
100101
if os.path.isdir(req_file_path):
101102
for file1 in os.listdir(req_file_path):
102103
filename = os.path.join(req_file_path, file1)
@@ -113,17 +114,24 @@ def generate_cim_req_params(self):
113114
event_no += 1
114115
unescaped_event = self.get_event(event_tag)
115116
transport_type = self.extract_transport_tag(event_tag)
116-
if transport_type == "syslog":
117+
if transport_type.lower() == "syslog":
117118
stripped_event = self.strip_syslog_header(unescaped_event)
119+
unescaped_event = stripped_event
120+
if stripped_event is None:
121+
LOGGER.error("Syslog event do not match CEF, RFC_3164, RFC_5424 format")
122+
continue
123+
elif transport_type in ("modinput","Modinput", "Mod input","Modular Input", "Modular input", "modular input","modular_input", "Mod Input", "dbx", "windows_input","hec_event"):
124+
host, source, sourcetype = self.extract_params(event_tag)
125+
modinput_params = {
126+
"host": host,
127+
"source": source,
128+
"sourcetype": sourcetype
129+
}
118130
else:
119-
# todo: non syslog events are skipped currently until we support it
131+
# todo: non syslog/modinput events are skipped currently until we support it
120132
continue
121-
if stripped_event is None:
122-
LOGGER.error(
123-
"Syslog event do not match CEF, RFC_3164, RFC_5424 format"
124-
)
125-
continue
126-
escaped_event = self.escape_char_event(stripped_event)
133+
134+
escaped_event = self.escape_char_event(unescaped_event)
127135
model_list = self.get_models(event_tag)
128136
# Fetching kay value pair from XML
129137
key_value_dict = self.extract_key_value_xml(event_tag)
@@ -144,6 +152,8 @@ def generate_cim_req_params(self):
144152
"model_list": list_model_dataset_subdataset,
145153
"escaped_event": escaped_event,
146154
"Key_value_dict": key_value_dict,
155+
"modinput_params": modinput_params,
156+
"transport_type": transport_type,
147157
},
148158
id=f"{model_list}::{filename}::event_no::{event_no}::req_test_id::{req_test_id}",
149159
)
@@ -206,14 +216,26 @@ def check_xml_format(self, file_name):
206216
else:
207217
return False
208218

219+
# extract_params_transport
220+
221+
def extract_params(self, event):
222+
host, source, source_type = "", "", ""
223+
for transport in event.iter('transport'):
224+
if transport.get('host'):
225+
host = transport.get('host')
226+
if transport.get('source'):
227+
source = transport.get('source')
228+
if transport.get('sourcetype'):
229+
source_type = transport.get('sourcetype')
230+
return host, source, source_type
231+
209232
def escape_char_event(self, event):
210233
"""
211234
Input: Event getting parsed
212235
Function to escape special characters in Splunk
213236
https://docs.splunk.com/Documentation/StyleGuide/current/StyleGuide/Specialcharacters
214237
"""
215238
escape_splunk_chars = [
216-
"\\",
217239
"`",
218240
"~",
219241
"!",
@@ -249,6 +271,8 @@ def escape_char_event(self, event):
249271
"WHERE",
250272
"LIKE",
251273
]
274+
event = event.replace("\\", "\\\\")
252275
for character in escape_splunk_chars:
253-
event = event.replace(character, "\\" + character)
276+
event = event.replace(character, '\\' + character)
277+
event = event.replace("*", " ")
254278
return event

pytest_splunk_addon/standard_lib/requirement_tests/test_templates.py

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
import pytest
3+
import re
34
from .requirement_test_datamodel_tag_constants import dict_datamodel_tag
45

56
INTERVAL = 3
@@ -107,20 +108,50 @@ def datamodel_check_test(self, keyValue_dict_SPL, requrement_file_model_list):
107108
) = self.compare_datamodel(requrement_file_model_list, datamodel_based_on_tag)
108109
return list_extra_datamodel_requirement_file, lis_extra_extracted_splunkside
109110

111+
112+
def remove_empty_keys(self, event):
113+
event = re.sub(
114+
r"(\s[a-zA-Z0-9_]*(\\=|\\:)(\\\"\\\"|\\'\\'|\\-))",
115+
'',
116+
str(event)
117+
)
118+
return event
119+
120+
110121
@pytest.mark.splunk_searchtime_requirements
111122
def test_requirement_params(
112123
self, splunk_searchtime_requirement_param, splunk_search_util
113124
):
114125
model_datalist = splunk_searchtime_requirement_param["model_list"]
115126
escaped_event = splunk_searchtime_requirement_param["escaped_event"]
116127
key_values_xml = splunk_searchtime_requirement_param["Key_value_dict"]
128+
modinput_params = splunk_searchtime_requirement_param["modinput_params"]
129+
transport_type = splunk_searchtime_requirement_param["transport_type"]
117130
# search = f" search source= pytest_splunk_addon:hec:raw sourcetype={sourcetype} {escaped_event} |fields * "
118131
# removed source and sourcetype as sc4s assigns it based on event
119-
search = f"search index=* {escaped_event} |fields * "
132+
if transport_type in (
133+
"modinput", "Modinput", "Mod input", "Modular Input", "Modular input", "modular input", "modular_input",
134+
"Mod Input"):
135+
host = modinput_params["host"]
136+
source = modinput_params["source"]
137+
sourcetype = modinput_params["sourcetype"]
138+
search = f"search index=* host={host} source={source} sourcetype={sourcetype} {escaped_event}|fields * "
139+
else:
140+
search = f"search index=* {escaped_event} |fields * "
120141
ingestion_check = splunk_search_util.checkQueryCountIsGreaterThanZero(
121142
search, interval=INTERVAL, retries=RETRIES
122143
)
123-
assert ingestion_check, f"ingestion failure \nsearch={search}\n"
144+
145+
if not ingestion_check and transport_type.lower() == "syslog":
146+
empty_field_removed = self.remove_empty_keys(escaped_event)
147+
search = f"search index=* {empty_field_removed} |fields * "
148+
ingestion_check = splunk_search_util.checkQueryCountIsGreaterThanZero(
149+
search, interval=INTERVAL, retries=RETRIES
150+
)
151+
152+
assert ingestion_check, (
153+
f"ingestion failure \nsearch={search}\n"
154+
)
124155
self.logger.info(f"ingestion_check: {ingestion_check}")
125156
keyValue_dict_SPL = splunk_search_util.getFieldValuesDict(
126157
search, interval=INTERVAL, retries=RETRIES
@@ -141,9 +172,9 @@ def test_requirement_params(
141172
sourcetype = keyValue_dict_SPL["_sourcetype"]
142173
assert datamodel_check, (
143174
f"data model check: {datamodel_check} \n"
144-
f"data model in requirement file not in Splunk ingested event/ missing dataset {list_unmatched_datamodel_splunkside}\n "
145-
f"data model extracted on Splunk side not in requirement file {list_unmatched_datamodel_requirement_file}\n"
146-
f"source type of ingested event: {sourcetype} \n"
175+
f"data model in requirement file {list_unmatched_datamodel_splunkside}\n "
176+
f"data model extracted by TA {list_unmatched_datamodel_requirement_file}\n"
177+
f"sourcetype of ingested event: {sourcetype} \n"
147178
)
148179
field_extraction_check, missing_key_value = self.compare(
149180
keyValue_dict_SPL, key_values_xml
@@ -158,7 +189,7 @@ def test_requirement_params(
158189
assert field_extraction_check, (
159190
f"Issue with the field extraction.\nsearch={search}\n"
160191
f" Field_extraction_check: {field_extraction_check} \n"
161-
f" Key value not extracted in splunk event: {missing_key_value} \n"
192+
f" Key value not extracted by TA: {missing_key_value} \n"
162193
f" Mismatched key value: {mismapped_key_value_pair}\n"
163-
f" source type of ingested event: {sourcetype} \n"
194+
f" sourcetype of ingested event: {sourcetype} \n"
164195
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Splunk Add-on for Microsoft Sysmon version 11.0.0
2+
Copyright (C) 2021 Splunk Inc. All Rights Reserved.
3+
4+
For documentation, see: https://docs.splunk.com/Documentation/AddOns/latest/MSSysmon
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"schemaVersion": "2.0.0",
3+
"info": {
4+
"title": "Splunk Add-on for Microsoft Sysmon",
5+
"id": {
6+
"group": null,
7+
"name": "Splunk_TA_microsoft_sysmon",
8+
"version": "11.0.0"
9+
},
10+
"author": [
11+
{
12+
"name": "Splunk, Inc.",
13+
"email": "[email protected]",
14+
"company": "Splunk, Inc."
15+
}
16+
],
17+
"releaseDate": null,
18+
"description": "Splunk Add-on for Microsoft Sysmon",
19+
"classification": {
20+
"intendedAudience": "IT Professionals",
21+
"categories": [
22+
"Security, Fraud & Compliance"
23+
],
24+
"developmentStatus": "Production/Stable"
25+
},
26+
"commonInformationModels": null,
27+
"license": {
28+
"name": "Splunk General Terms",
29+
"text": "LICENSES/LicenseRef-Splunk-8-2021.txt",
30+
"uri": "https://www.splunk.com/en_us/legal/splunk-general-terms.html"
31+
},
32+
"privacyPolicy": {
33+
"name": null,
34+
"text": null,
35+
"uri": null
36+
},
37+
"releaseNotes": {
38+
"name": "README",
39+
"text": "README.txt",
40+
"uri": "https://docs.splunk.com/Documentation/AddOns/MSSysmon/About"
41+
}
42+
},
43+
"dependencies": null,
44+
"tasks": null,
45+
"inputGroups": null,
46+
"incompatibleApps": null,
47+
"platformRequirements": null,
48+
"supportedDeployments": [
49+
"_standalone",
50+
"_distributed",
51+
"_search_head_clustering"
52+
],
53+
"targetWorkloads": [
54+
"_search_heads",
55+
"_forwarders",
56+
"_indexers"
57+
]
58+
}

0 commit comments

Comments
 (0)