diff --git a/pytest_splunk_addon/fields_tests/test_generator.py b/pytest_splunk_addon/fields_tests/test_generator.py
index 85374d4b2..46b4ef4bb 100644
--- a/pytest_splunk_addon/fields_tests/test_generator.py
+++ b/pytest_splunk_addon/fields_tests/test_generator.py
@@ -250,17 +250,19 @@ def generate_requirements_tests(self):
}
cim_fields = event.requirement_test_data.get("cim_fields", {})
+ other_fields = event.requirement_test_data.get("other_fields", {})
+ requirement_fields = {**cim_fields, **other_fields}
- if cim_fields:
- cim_fields = {
+ if requirement_fields:
+ requirement_fields = {
field: value
- for field, value in cim_fields.items()
+ for field, value in requirement_fields.items()
if field not in exceptions
}
yield pytest.param(
{
"escaped_event": escaped_event,
- "fields": cim_fields,
+ "fields": requirement_fields,
"modinput_params": modinput_params,
},
id=f"sample_name::{event.sample_name}::host::{event.metadata.get('host')}",
diff --git a/pytest_splunk_addon/sample_generation/sample_stanza.py b/pytest_splunk_addon/sample_generation/sample_stanza.py
index 55d7cfce9..941d24614 100644
--- a/pytest_splunk_addon/sample_generation/sample_stanza.py
+++ b/pytest_splunk_addon/sample_generation/sample_stanza.py
@@ -398,6 +398,16 @@ def populate_requirement_test_data(event):
"""
requirement_test_data = {}
cim = event.get("cim")
+ other_mappings = event.get("other_mappings")
+ if other_mappings:
+ other_fields = {}
+ fields = other_mappings["field"]
+ if type(fields) == list:
+ for field in fields:
+ other_fields[field["@name"]] = field["@value"]
+ elif type(fields) == dict:
+ other_fields[fields["@name"]] = fields["@value"]
+ requirement_test_data["other_fields"] = other_fields
if cim:
requirement_test_data["cim_version"] = cim.get("@version", "latest")
requirement_test_data["datamodels"] = cim.get("models") or {}
diff --git a/tests/e2e/addons/TA_req_broken/samples/sample_modinput.xml b/tests/e2e/addons/TA_req_broken/samples/sample_modinput.xml
index ccd341d48..20911af1f 100644
--- a/tests/e2e/addons/TA_req_broken/samples/sample_modinput.xml
+++ b/tests/e2e/addons/TA_req_broken/samples/sample_modinput.xml
@@ -102,4 +102,32 @@
+
+
+
+
+ lab
+
+
+
+
+
+ Authentication
+
+
+
+
+
+
+
+
+
+
+ src_user
+
+
+
+
+
+
diff --git a/tests/e2e/addons/TA_transition_from_req/default/props.conf b/tests/e2e/addons/TA_transition_from_req/default/props.conf
index 61c9b087a..ee1bf418e 100644
--- a/tests/e2e/addons/TA_transition_from_req/default/props.conf
+++ b/tests/e2e/addons/TA_transition_from_req/default/props.conf
@@ -10,4 +10,5 @@ FIELDALIAS-action = result AS action
EVAL-app = "psa"
FIELDALIAS-user = tester AS user
FIELDALIAS-src = ip AS src
-EVAL-status = case(action=="success", "PASS", action=="failure", "FAIL", 0==0, "OTHER")
\ No newline at end of file
+EVAL-status = case(action=="success", "PASS", action=="failure", "FAIL", 0==0, "OTHER")
+EVAL-vendor_product = "Pytest Splunk Addon"
\ No newline at end of file
diff --git a/tests/e2e/addons/TA_transition_from_req/samples/sample_modinput.xml b/tests/e2e/addons/TA_transition_from_req/samples/sample_modinput.xml
index 0f4a83286..1a1b2369a 100644
--- a/tests/e2e/addons/TA_transition_from_req/samples/sample_modinput.xml
+++ b/tests/e2e/addons/TA_transition_from_req/samples/sample_modinput.xml
@@ -26,5 +26,8 @@
src_user
+
+
+
\ No newline at end of file
diff --git a/tests/e2e/constants.py b/tests/e2e/constants.py
index 3e6be5345..403c1910b 100644
--- a/tests/e2e/constants.py
+++ b/tests/e2e/constants.py
@@ -798,6 +798,7 @@
"*test_splunk_app_req.py::Test_App::test_props_fields[test:data:1::field::status* PASSED*",
"*test_splunk_app_req.py::Test_App::test_props_fields[test:data:1::field::tester* PASSED*",
"*test_splunk_app_req.py::Test_App::test_props_fields[test:data:1::field::user* PASSED*",
+ "*test_splunk_app_req.py::Test_App::test_props_fields[test:data:1::field::vendor_product* PASSED*",
"*test_splunk_app_req.py::Test_App::test_requirements_fields[sample_name::sample_modinput.xml::host::so1-4* PASSED*",
"*test_splunk_app_req.py::Test_App::test_requirements_fields[sample_name::sample_modinput.xml::host::so1-5* PASSED*",
"*test_splunk_app_req.py::Test_App::test_requirements_fields[sample_name::sample_modinput.xml::host::so1-6* PASSED*",
@@ -811,6 +812,7 @@
"*test_splunk_app_req.py::Test_App::test_props_fields_no_dash_not_empty[test:data:1::field::status* PASSED*",
"*test_splunk_app_req.py::Test_App::test_props_fields_no_dash_not_empty[test:data:1::field::tester* PASSED*",
"*test_splunk_app_req.py::Test_App::test_props_fields_no_dash_not_empty[test:data:1::field::user* PASSED*",
+ "*test_splunk_app_req.py::Test_App::test_props_fields_no_dash_not_empty[test:data:1::field::vendor_product* PASSED*",
"*test_splunk_app_req.py::Test_App::test_datamodels[Authentication::sample_name::sample_modinput.xml::host::so1-4* PASSED*",
"*test_splunk_app_req.py::Test_App::test_datamodels[Authentication::sample_name::sample_modinput.xml::host::so1-5* PASSED*",
"*test_splunk_app_req.py::Test_App::test_datamodels[Authentication::sample_name::sample_modinput.xml::host::so1-6* PASSED*",
@@ -836,6 +838,7 @@
"*test_splunk_app_req_broken.py::Test_App::test_indextime_time[req:test:broken::so11* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_indextime_time[req:test:broken::so12* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_indextime_time[req:test:broken::so13* PASSED*",
+ "*test_splunk_app_req_broken.py::Test_App::test_indextime_time[req:test:broken::so14* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_indextime_line_breaker[juniper:junos:firewall::syslog.xml* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_indextime_line_breaker[req:test:broken::sample_modinput.xml* PASSED*",
'*test_splunk_app_req_broken.py::Test_App::test_cim_required_fields[eventtype="net"::All_Traffic* PASSED*',
@@ -882,6 +885,7 @@
"*test_splunk_app_req_broken.py::Test_App::test_cim_fields_recommended[Authentication-::sample_name::sample_modinput.xml::host::so10* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_cim_fields_recommended[Authentication-::sample_name::sample_modinput.xml::host::so12* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_cim_fields_recommended[Authentication-::sample_name::sample_modinput.xml::host::so13* PASSED*",
+ "*test_splunk_app_req_broken.py::Test_App::test_cim_fields_recommended[Authentication-::sample_name::sample_modinput.xml::host::so14* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_cim_fields_recommended[Network_Traffic-::sample_name::syslog.xml::host::10.0.0.30* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_cim_fields_recommended[Network_Traffic-::sample_name::syslog.xml::host::10.0.0.31* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_splunk_internal_errors PASSED*",
@@ -897,6 +901,7 @@
"*test_splunk_app_req_broken.py::Test_App::test_datamodels[Authentication::sample_name::sample_modinput.xml::host::so11* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_datamodels[Authentication::sample_name::sample_modinput.xml::host::so12* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_datamodels[Authentication::sample_name::sample_modinput.xml::host::so13* PASSED*",
+ "*test_splunk_app_req_broken.py::Test_App::test_datamodels[Authentication::sample_name::sample_modinput.xml::host::so14* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_datamodels[Network_Traffic::sample_name::syslog.xml::host::10.0.0.30* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_eventtype[eventtype::net* PASSED*",
"*test_splunk_app_req_broken.py::Test_App::test_eventtype[eventtype::test_auth* PASSED*",
@@ -913,6 +918,7 @@
'*test_splunk_app_req_broken.py::Test_App::test_cim_required_fields[eventtype="net"::All_Traffic::src_zone* FAILED*',
'*test_splunk_app_req_broken.py::Test_App::test_cim_required_fields[eventtype="net"::Blocked_Traffic* FAILED*',
"*test_splunk_app_req_broken.py::Test_App::test_requirements_fields[sample_name::sample_modinput.xml::host::so13* FAILED*",
+ "*test_splunk_app_req_broken.py::Test_App::test_requirements_fields[sample_name::sample_modinput.xml::host::so14* FAILED*",
"*test_splunk_app_req_broken.py::Test_App::test_cim_fields_recommended[Authentication-::sample_name::sample_modinput.xml::host::so11* FAILED*",
"*test_splunk_app_req_broken.py::Test_App::test_datamodels[Network_Traffic::sample_name::syslog.xml::host::10.0.0.31* FAILED*",
]
diff --git a/tests/unit/tests_standard_lib/test_fields_tests/test_test_generator.py b/tests/unit/tests_standard_lib/test_fields_tests/test_test_generator.py
index c0508166c..e57daefa5 100644
--- a/tests/unit/tests_standard_lib/test_fields_tests/test_test_generator.py
+++ b/tests/unit/tests_standard_lib/test_fields_tests/test_test_generator.py
@@ -3,6 +3,8 @@
from pytest_splunk_addon.fields_tests.test_generator import (
FieldTestGenerator,
)
+from pytest_splunk_addon.sample_generation.sample_event import SampleEvent
+from pytest_splunk_addon.utilities import xml_event_parser
def field_1():
@@ -71,6 +73,14 @@ def test_field_test_generator_instantiation(addon_parser_mock):
"splunk_searchtime_fields_savedsearches",
"GENERATE_SAVEDSEARCHES_TESTS_RETURN_VALUE",
),
+ (
+ "splunk_searchtime_fields_requirements",
+ "GENERATE_REQUIREMENT_TESTS_RETURN_VALUE",
+ ),
+ (
+ "splunk_searchtime_fields_datamodels",
+ "GENERATE_REQUIREMENT_DATAMODEL_TESTS_RETURN_VALUE",
+ ),
],
)
def test_generate_tests(addon_parser_mock, fixture_name, expected_ouptput):
@@ -90,6 +100,14 @@ def test_generate_tests(addon_parser_mock, fixture_name, expected_ouptput):
FieldTestGenerator,
"generate_savedsearches_tests",
return_value=(["GENERATE_SAVEDSEARCHES_TESTS_RETURN_VALUE"]),
+ ), patch.object(
+ FieldTestGenerator,
+ "generate_requirements_tests",
+ return_value=(["GENERATE_REQUIREMENT_TESTS_RETURN_VALUE"]),
+ ), patch.object(
+ FieldTestGenerator,
+ "generate_requirements_datamodels_tests",
+ return_value=(["GENERATE_REQUIREMENT_DATAMODEL_TESTS_RETURN_VALUE"]),
):
assert list(
FieldTestGenerator(
@@ -391,3 +409,190 @@ def test_generate_field_tests(
)
assert out == expected_output
assert param_mock.call_count == len(expected_output)
+
+
+@pytest.mark.parametrize(
+ "tokenised_events, expected_output",
+ [
+ (
+ [
+ SampleEvent(
+ event_string="escaped_event",
+ metadata={
+ "input_type": "modinput",
+ "sourcetype_to_search": "dummy_sourcetype",
+ "host": "dummy_host",
+ },
+ sample_name="file1.xml",
+ requirement_test_data={
+ "cim_fields": {
+ "dest": "192.168.0.1",
+ "severity": "low",
+ "signature_id": "405001",
+ "src": "192.168.0.1",
+ "type": "event",
+ },
+ "exceptions": {"mane_1": "value_1", "dest": "192.168.0.1"},
+ "other_fields": {
+ "vendor_product": "Pytest Splunk Addon",
+ "target_users": "dummy.user@splunk.com",
+ },
+ },
+ ),
+ SampleEvent(
+ event_string="escaped_event",
+ metadata={
+ "input_type": "syslog_tcp",
+ "sourcetype_to_search": "dummy_sourcetype",
+ "host": "dummy_host_syslog",
+ },
+ sample_name="file1.xml",
+ requirement_test_data={},
+ ),
+ SampleEvent(
+ event_string="escaped_event",
+ metadata={
+ "input_type": "syslog_tcp",
+ "sourcetype_to_search": "dummy_sourcetype",
+ "host": "dummy_host_syslog",
+ },
+ sample_name="file1.xml",
+ requirement_test_data={
+ "cim_fields": {
+ "src": "192.168.0.1",
+ "type": "event",
+ },
+ "exceptions": {},
+ "other_fields": {
+ "vendor_product": "Pytest Splunk Addon",
+ "target_users": "dummy.user@splunk.com",
+ },
+ },
+ ),
+ ],
+ [
+ (
+ {
+ "escaped_event": "escaped_event",
+ "fields": {
+ "severity": "low",
+ "signature_id": "405001",
+ "src": "192.168.0.1",
+ "type": "event",
+ "vendor_product": "Pytest Splunk Addon",
+ "target_users": "dummy.user@splunk.com",
+ },
+ "modinput_params": {"sourcetype": "dummy_sourcetype"},
+ },
+ "sample_name::file1.xml::host::dummy_host",
+ ),
+ (
+ {
+ "escaped_event": "escaped_event",
+ "fields": {
+ "src": "192.168.0.1",
+ "type": "event",
+ "vendor_product": "Pytest Splunk Addon",
+ "target_users": "dummy.user@splunk.com",
+ },
+ "modinput_params": {"sourcetype": "dummy_sourcetype"},
+ },
+ "sample_name::file1.xml::host::dummy_host_syslog",
+ ),
+ ],
+ ),
+ ],
+)
+def test_generate_requirement_tests(tokenised_events, expected_output):
+ with patch.object(
+ xml_event_parser, "strip_syslog_header", return_value="escaped_event"
+ ), patch.object(
+ xml_event_parser, "escape_char_event", return_value="escaped_event"
+ ), patch.object(
+ pytest, "param", side_effect=lambda x, id: (x, id)
+ ) as param_mock:
+ out = list(
+ FieldTestGenerator(
+ "app_path",
+ tokenised_events,
+ "field_bank",
+ ).generate_requirements_tests()
+ )
+ assert out == expected_output
+ assert param_mock.call_count == len(expected_output)
+
+
+@pytest.mark.parametrize(
+ "tokenised_events, expected_output",
+ [
+ (
+ [
+ SampleEvent(
+ event_string="escaped_event",
+ metadata={
+ "input_type": "modinput",
+ "sourcetype_to_search": "dummy_sourcetype",
+ "host": "dummy_host",
+ },
+ sample_name="file1.xml",
+ requirement_test_data={"datamodels": {"model": "Alerts"}},
+ ),
+ SampleEvent(
+ event_string="escaped_event",
+ metadata={
+ "input_type": "syslog_tcp",
+ "sourcetype_to_search": "dummy_sourcetype",
+ "host": "dummy_host_syslog",
+ },
+ sample_name="file1.xml",
+ requirement_test_data={},
+ ),
+ SampleEvent(
+ event_string="escaped_event",
+ metadata={
+ "input_type": "syslog_tcp",
+ "sourcetype_to_search": "dummy_sourcetype",
+ "host": "dummy_host_syslog",
+ },
+ sample_name="file1.xml",
+ requirement_test_data={
+ "datamodels": {"model": ["Change", "Account Management"]}
+ },
+ ),
+ ],
+ [
+ (
+ {
+ "datamodels": ["Alerts"],
+ "stanza": "escaped_event",
+ },
+ "Alerts::sample_name::file1.xml::host::dummy_host",
+ ),
+ (
+ {
+ "datamodels": ["Change", "Account_Management"],
+ "stanza": "escaped_event",
+ },
+ "Change-Account_Management::sample_name::file1.xml::host::dummy_host_syslog",
+ ),
+ ],
+ ),
+ ],
+)
+def test_generate_requirement_datamodel_tests(tokenised_events, expected_output):
+ with patch.object(
+ xml_event_parser, "strip_syslog_header", return_value="escaped_event"
+ ), patch.object(
+ xml_event_parser, "escape_char_event", return_value="escaped_event"
+ ), patch.object(
+ pytest, "param", side_effect=lambda x, id: (x, id)
+ ) as param_mock:
+ out = list(
+ FieldTestGenerator(
+ "app_path",
+ tokenised_events,
+ "field_bank",
+ ).generate_requirements_datamodels_tests()
+ )
+ assert out == expected_output
+ assert param_mock.call_count == len(expected_output)
diff --git a/tests/unit/tests_standard_lib/tests_sample_generation/test_sample_stanza.py b/tests/unit/tests_standard_lib/tests_sample_generation/test_sample_stanza.py
index e8429f7b2..92da36e37 100644
--- a/tests/unit/tests_standard_lib/tests_sample_generation/test_sample_stanza.py
+++ b/tests/unit/tests_standard_lib/tests_sample_generation/test_sample_stanza.py
@@ -358,12 +358,63 @@ def test_break_events_exception(self, sample_stanza, caplog):
"missing_recommended_fields": [],
},
),
+ (
+ {
+ "cim": {
+ "@version": "4.20.2",
+ "models": {"model": "Alerts"},
+ "cim_fields": {
+ "field": [
+ {"@name": "dest", "@value": "192.168.0.1"},
+ {"@name": "signature_id", "@value": "405001"},
+ {"@name": "severity", "@value": "low"},
+ {"@name": "src", "@value": "192.168.0.1"},
+ {"@name": "type", "@value": "event"},
+ ]
+ },
+ "missing_recommended_fields": {
+ "field": ["app", "id", "user", "user_name"]
+ },
+ "exceptions": {},
+ },
+ "other_mappings": {
+ "field": [
+ {
+ "@name": "vendor_product",
+ "@value": "Pytest Splunk Addon",
+ },
+ {
+ "@name": "target_users",
+ "@value": "dummy.user@splunk.com",
+ },
+ ]
+ },
+ },
+ {
+ "cim_version": "4.20.2",
+ "cim_fields": {
+ "dest": "192.168.0.1",
+ "severity": "low",
+ "signature_id": "405001",
+ "src": "192.168.0.1",
+ "type": "event",
+ },
+ "datamodels": {"model": "Alerts"},
+ "exceptions": {},
+ "missing_recommended_fields": ["app", "id", "user", "user_name"],
+ "other_fields": {
+ "vendor_product": "Pytest Splunk Addon",
+ "target_users": "dummy.user@splunk.com",
+ },
+ },
+ ),
],
ids=[
"event-empty-directory",
"event-no-cim",
"event-full-cim",
"event-with-exceptions",
+ "event-with-other-mappings",
],
)
def test_populate_requirement_test_data(self, sample_stanza, event, expected):