diff --git a/docs/configuration.md b/docs/configuration.md index b48fc23..7544efb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -261,6 +261,11 @@ In some cases rogue or port-probing data can be sent to SC4S from misconfigured the `vendor_product_by_source.conf` filter `f_null_queue` with one or more ip/subnet masks to drop events without logging. Note that drop metrics will be recorded. +## Fixing (overriding) the host field + +In some cases the host value is not present or an IP address in the syslog even analysts and users prefer host names. SC4S +will first check `host.csv` and replace the value of `host` with the value specified. If a value is not found in `dns.csv` +reverse dns lookup will be attempted. IP will only be used as the host value as a last result. ## Splunk Connect for Syslog output templates (syslog-ng templates) diff --git a/package/etc/conf.d/conflib/_splunk/fix_dns.conf b/package/etc/conf.d/conflib/_splunk/fix_dns.conf new file mode 100644 index 0000000..5dd0a94 --- /dev/null +++ b/package/etc/conf.d/conflib/_splunk/fix_dns.conf @@ -0,0 +1,46 @@ +python { + +""" +simple syslog-ng Python parser example +resolves IP to hostname +value pair names are hard-coded +""" +import re +import socket + +class FixHostResolver(object): + + def parse(self, log_message): + """ + Resolves IP to hostname + """ + + + # try to resolve the IP address + try: + ipaddr = log_message['HOST'].decode('utf-8') + + resolved = socket.gethostbyaddr(ipaddr) + hostname = resolved[0] + log_message['HOST'] = hostname + except: + pass + + # return True, other way message is dropped + return True + +}; + + +parser p_fix_host_resolver { + python( + class("FixHostResolver") + ); +}; + +parser p_add_context_host { + add-contextual-data( + selector("${HOST}"), + database("conf.d/local/context/host.csv"), + ); +}; diff --git a/package/etc/context_templates/host.csv.example b/package/etc/context_templates/host.csv.example new file mode 100644 index 0000000..3263644 --- /dev/null +++ b/package/etc/context_templates/host.csv.example @@ -0,0 +1 @@ +169.254.0.2,HOST,foo.example \ No newline at end of file diff --git a/package/etc/go_templates/source_network.t b/package/etc/go_templates/source_network.t index 9aeb850..f849a26 100644 --- a/package/etc/go_templates/source_network.t +++ b/package/etc/go_templates/source_network.t @@ -207,8 +207,20 @@ source s_{{ .port_id }} { }; {{ end }} rewrite(r_set_splunk_default); - parser { - vendor_product_by_source(); + if { + filter { + host('((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))') + }; + parser(p_add_context_host); + }; + if { + filter { + host('((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))') + }; + parser(p_fix_host_resolver); + }; + parser { + vendor_product_by_source(); }; if { diff --git a/package/etc/syslog-ng.conf.tmpl b/package/etc/syslog-ng.conf.tmpl index 1a06e5f..1cd2a87 100644 --- a/package/etc/syslog-ng.conf.tmpl +++ b/package/etc/syslog-ng.conf.tmpl @@ -18,11 +18,11 @@ options { time_reopen (10); log_fifo_size (10000); chain_hostnames (yes); - use_dns ({{getenv "SC4S_GLOBAL_DNS_USE" "no"}}); + use_dns (no); use_fqdn (no); - dns-cache({{getenv "SC4S_GLOBAL_DNS_CACHE" "yes"}}); + dns-cache(no); create_dirs (no); - keep-hostname (no); + keep-hostname (yes); create_dirs(yes); dir_perm(0750); stats-freq(30); diff --git a/tests/test_common.py b/tests/test_common.py index d2b54f2..625d374 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -18,8 +18,7 @@ def test_defaultroute(record_property, setup_wordlist, setup_splunk, setup_sc4s): - host = "{}-{}".format(random.choice(setup_wordlist), - random.choice(setup_wordlist)) + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) dt = datetime.datetime.now() iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) @@ -27,14 +26,14 @@ def test_defaultroute(record_property, setup_wordlist, setup_splunk, setup_sc4s) # Tune time functions epoch = epoch[:-7] - mt = env.from_string( - "{{ mark }} {{ bsd }} {{ host }} test something else\n") + mt = env.from_string("{{ mark }} {{ bsd }} {{ host }} test something else\n") message = mt.render(mark="<111>", bsd=bsd, host=host) sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) st = env.from_string( - "search _time={{ epoch }} index=main host=\"{{ host }}\" sourcetype=\"sc4s:fallback\" PROGRAM=\"test\"") + 'search _time={{ epoch }} index=main host="{{ host }}" sourcetype="sc4s:fallback" PROGRAM="test"' + ) search = st.render(epoch=epoch, host=host) resultCount, eventCount = splunk_single(setup_splunk, search) @@ -48,8 +47,7 @@ def test_defaultroute(record_property, setup_wordlist, setup_splunk, setup_sc4s) @mark.skip() def test_internal(record_property, setup_wordlist, setup_splunk, setup_sc4s): - host = "{}-{}".format(random.choice(setup_wordlist), - random.choice(setup_wordlist)) + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) dt = datetime.datetime.now() iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) @@ -57,14 +55,14 @@ def test_internal(record_property, setup_wordlist, setup_splunk, setup_sc4s): # Tune time functions epoch = epoch[:-7] - mt = env.from_string( - "{{ mark }} {{ bsd }} {{ host }} sc4sdefault[0]: test\n") + mt = env.from_string("{{ mark }} {{ bsd }} {{ host }} sc4sdefault[0]: test\n") message = mt.render(mark="<111>", bsd=bsd, host=host) sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) st = env.from_string( - "search _time={{ epoch }} index=main NOT host=\"{{ host }}\" sourcetype=\"sc4s:events\"") + 'search _time={{ epoch }} index=main NOT host="{{ host }}" sourcetype="sc4s:events"' + ) search = st.render(epoch=epoch, host=host) resultCount, eventCount = splunk_single(setup_splunk, search) @@ -77,8 +75,7 @@ def test_internal(record_property, setup_wordlist, setup_splunk, setup_sc4s): def test_fallback(record_property, setup_wordlist, setup_splunk, setup_sc4s): - host = "{}-{}".format(random.choice(setup_wordlist), - random.choice(setup_wordlist)) + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) dt = datetime.datetime.now() iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) @@ -92,7 +89,8 @@ def test_fallback(record_property, setup_wordlist, setup_splunk, setup_sc4s): sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) st = env.from_string( - "search _time={{ epoch }} index=main host=\"testvp-{{ host }}\" sourcetype=\"sc4s:fallback\"") + 'search _time={{ epoch }} index=main host="testvp-{{ host }}" sourcetype="sc4s:fallback"' + ) search = st.render(epoch=epoch, host=host) resultCount, eventCount = splunk_single(setup_splunk, search) @@ -103,26 +101,103 @@ def test_fallback(record_property, setup_wordlist, setup_splunk, setup_sc4s): assert resultCount == 1 + # +def test_fix_dns_context(record_property, setup_wordlist, setup_splunk, setup_sc4s): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + pid = random.randint(1000, 32000) + + dt = datetime.datetime.now() + iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) + + # Tune time functions + epoch = epoch[:-7] + + mt = env.from_string("{{ mark }} {{ bsd }} 169.254.0.2 dnstest[{{ pid }}]: {{ host }}\n") + message = mt.render(mark="<111>", bsd=bsd, host=host, pid=pid) + + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string("search _time={{ epoch }} host=foo.example index=osnix \"[{{ pid }}]\" {{ host }} sourcetype=\"nix:syslog\"") + search = st.render(epoch=epoch, pid=pid, host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 +def test_fix_dns(record_property, setup_wordlist, setup_splunk, setup_sc4s): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + pid = random.randint(1000, 32000) + + dt = datetime.datetime.now() + iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) + + # Tune time functions + epoch = epoch[:-7] + + mt = env.from_string("{{ mark }} {{ bsd }} 8.8.4.4 dnstest[{{ pid }}]: {{ host }}\n") + message = mt.render(mark="<111>", bsd=bsd, host=host, pid=pid) + + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string("search _time={{ epoch }} host=dns.google index=osnix \"[{{ pid }}]\" {{ host }} sourcetype=\"nix:syslog\"") + search = st.render(epoch=epoch, pid=pid, host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 + +def test_fix_dns_notfound(record_property, setup_wordlist, setup_splunk, setup_sc4s): + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) + pid = random.randint(1000, 32000) + + dt = datetime.datetime.now() + iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) + + # Tune time functions + epoch = epoch[:-7] + + mt = env.from_string("{{ mark }} {{ bsd }} 169.254.0.1 dnstest[{{ pid }}]: {{ host }}\n") + message = mt.render(mark="<111>", bsd=bsd, host=host, pid=pid) + + sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) + + st = env.from_string("search _time={{ epoch }} host=169.254.0.1 index=osnix \"[{{ pid }}]\" {{ host }} sourcetype=\"nix:syslog\"") + search = st.render(epoch=epoch, pid=pid, host=host) + + resultCount, eventCount = splunk_single(setup_splunk, search) + + record_property("host", host) + record_property("resultCount", resultCount) + record_property("message", message) + + assert resultCount == 1 def test_metrics(record_property, setup_wordlist, setup_splunk, setup_sc4s): st = env.from_string( - 'mcatalog values(metric_name) WHERE metric_name="syslogng.*" AND ("index"="*" OR "index"="_*") BY metric_name | fields metric_name') + 'mcatalog values(metric_name) WHERE metric_name="syslogng.*" AND ("index"="*" OR "index"="_*") BY metric_name | fields metric_name' + ) search = st.render() resultCount, eventCount = splunk_single(setup_splunk, search) record_property("resultCount", resultCount) - assert resultCount != 0 + assert resultCount != 0 def test_tz_guess(record_property, setup_wordlist, setup_splunk, setup_sc4s): - host = "{}-{}".format(random.choice(setup_wordlist), - random.choice(setup_wordlist)) + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) dt = datetime.datetime.now() iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) @@ -131,14 +206,17 @@ def test_tz_guess(record_property, setup_wordlist, setup_splunk, setup_sc4s): epoch = epoch[:-7] mt = env.from_string( - "{{ mark }} {{ bsd }} {{ host }} : %ASA-3-003164: TCP access denied by ACL from 179.236.133.160/3624 to outside:72.142.18.38/23\n") - message = mt.render(mark="<111>", bsd=bsd, host=host, - date=date, time=time, tzoffset=tzoffset) + "{{ mark }} {{ bsd }} {{ host }} : %ASA-3-003164: TCP access denied by ACL from 179.236.133.160/3624 to outside:72.142.18.38/23\n" + ) + message = mt.render( + mark="<111>", bsd=bsd, host=host, date=date, time=time, tzoffset=tzoffset + ) sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) st = env.from_string( - "search _time={{ epoch }} index=netfw host=\"{{ host }}\" sourcetype=\"cisco:asa\" \"%ASA-3-003164\"") + 'search _time={{ epoch }} index=netfw host="{{ host }}" sourcetype="cisco:asa" "%ASA-3-003164"' + ) search = st.render(epoch=epoch, host=host) resultCount, eventCount = splunk_single(setup_splunk, search) @@ -152,33 +230,33 @@ def test_tz_guess(record_property, setup_wordlist, setup_splunk, setup_sc4s): def test_tz_fix_hst(record_property, setup_wordlist, setup_splunk, setup_sc4s): - host = "{}-{}".format(random.choice(setup_wordlist), - random.choice(setup_wordlist)) + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) -# 10 minute offset (reserved for future use) -# dt = datetime.datetime.utcnow() - datetime.timedelta(hours=10, minutes=10) + # 10 minute offset (reserved for future use) + # dt = datetime.datetime.utcnow() - datetime.timedelta(hours=10, minutes=10) -# dt = datetime.datetime.utcnow() - datetime.timedelta(hours=10) + # dt = datetime.datetime.utcnow() - datetime.timedelta(hours=10) -# Set the date to Hawaii time - dt = datetime.datetime.now(datetime.timezone.utc) - \ - datetime.timedelta(hours=10) + # Set the date to Hawaii time + dt = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=10) iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) mt = env.from_string( - "{{ mark }} {{ bsd }} tzfhst-{{ host }} : %ASA-3-003164: TCP access denied by ACL from 179.236.133.160/3624 to outside:72.142.18.38/23\n") + "{{ mark }} {{ bsd }} tzfhst-{{ host }} : %ASA-3-003164: TCP access denied by ACL from 179.236.133.160/3624 to outside:72.142.18.38/23\n" + ) message = mt.render(mark="<111>", bsd=bsd, host=host) sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) -# Add the 10 hours back to search for current time + # Add the 10 hours back to search for current time dt = dt + datetime.timedelta(hours=10) iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) epoch = epoch[:-7] st = env.from_string( - "search _time={{ epoch }} index=netfw host=\"tzfhst-{{ host }}\" sourcetype=\"cisco:asa\"") + 'search _time={{ epoch }} index=netfw host="tzfhst-{{ host }}" sourcetype="cisco:asa"' + ) search = st.render(epoch=epoch, host=host) resultCount, eventCount = splunk_single(setup_splunk, search) @@ -192,26 +270,27 @@ def test_tz_fix_hst(record_property, setup_wordlist, setup_splunk, setup_sc4s): def test_tz_fix_ny(record_property, setup_wordlist, setup_splunk, setup_sc4s): - host = "{}-{}".format(random.choice(setup_wordlist), - random.choice(setup_wordlist)) + host = "{}-{}".format(random.choice(setup_wordlist), random.choice(setup_wordlist)) -# 10 minute offset (reserved for future use) -# dt = datetime.datetime.now(pytz.timezone('America/New_York')) - datetime.timedelta(minutes=10) + # 10 minute offset (reserved for future use) + # dt = datetime.datetime.now(pytz.timezone('America/New_York')) - datetime.timedelta(minutes=10) - dt = datetime.datetime.now(pytz.timezone('America/New_York')) + dt = datetime.datetime.now(pytz.timezone("America/New_York")) iso, bsd, time, date, tzoffset, tzname, epoch = time_operations(dt) # Tune time functions epoch = epoch[:-7] mt = env.from_string( - "{{ mark }} {{ bsd }} tzfny-{{ host }} : %ASA-3-003164: TCP access denied by ACL from 179.236.133.160/3624 to outside:72.142.18.38/23\n") + "{{ mark }} {{ bsd }} tzfny-{{ host }} : %ASA-3-003164: TCP access denied by ACL from 179.236.133.160/3624 to outside:72.142.18.38/23\n" + ) message = mt.render(mark="<111>", bsd=bsd, host=host) sendsingle(message, setup_sc4s[0], setup_sc4s[1][514]) st = env.from_string( - "search _time={{ epoch }} index=netfw host=\"tzfny-{{ host }}\" sourcetype=\"cisco:asa\"") + 'search _time={{ epoch }} index=netfw host="tzfny-{{ host }}" sourcetype="cisco:asa"' + ) search = st.render(epoch=epoch, host=host) resultCount, eventCount = splunk_single(setup_splunk, search) @@ -223,10 +302,13 @@ def test_tz_fix_ny(record_property, setup_wordlist, setup_splunk, setup_sc4s): assert resultCount == 1 -def test_check_config_version(record_property, setup_wordlist, setup_splunk, setup_sc4s): - +def test_check_config_version( + record_property, setup_wordlist, setup_splunk, setup_sc4s +): + st = env.from_string( - "search earliest=-50m@m latest=+1m@m index=main sourcetype=\"sc4s:events:startup:err\" \"Configuration file format is too old\" ") + 'search earliest=-50m@m latest=+1m@m index=main sourcetype="sc4s:events:startup:err" "Configuration file format is too old" ' + ) search = st.render() resultCount, eventCount = splunk_single(setup_splunk, search) @@ -236,10 +318,13 @@ def test_check_config_version(record_property, setup_wordlist, setup_splunk, set assert resultCount == 0 -def test_check_config_version_multiple(record_property, setup_wordlist, setup_splunk, setup_sc4s): +def test_check_config_version_multiple( + record_property, setup_wordlist, setup_splunk, setup_sc4s +): st = env.from_string( - "search earliest=-50m@m latest=+1m@m index=main sourcetype=\"sc4s:events:startup:err\" \"you have multiple @version directives\" ") + 'search earliest=-50m@m latest=+1m@m index=main sourcetype="sc4s:events:startup:err" "you have multiple @version directives" ' + ) search = st.render() resultCount, eventCount = splunk_single(setup_splunk, search) @@ -248,11 +333,13 @@ def test_check_config_version_multiple(record_property, setup_wordlist, setup_sp assert resultCount == 0 + # This test fails on circle; Cisco ACS single test seems to trigger a utf8 error. @mark.skip() def test_check_utf8(record_property, setup_wordlist, setup_splunk, setup_sc4s): st = env.from_string( - "search earliest=-50m@m latest=+1m@m index=main sourcetype=\"sc4s:events\" \"Input is valid utf8\"") + 'search earliest=-50m@m latest=+1m@m index=main sourcetype="sc4s:events" "Input is valid utf8"' + ) search = st.render() resultCount, eventCount = splunk_single(setup_splunk, search) @@ -261,10 +348,12 @@ def test_check_utf8(record_property, setup_wordlist, setup_splunk, setup_sc4s): assert resultCount == 0 + def test_check_sc4s_version(record_property, setup_wordlist, setup_splunk, setup_sc4s): st = env.from_string( - "search earliest=-50m@m latest=+1m@m index=main sourcetype=\"sc4s:events:startup:out\" \"sc4s version=\" NOT \"UNKNOWN\"") + 'search earliest=-50m@m latest=+1m@m index=main sourcetype="sc4s:events:startup:out" "sc4s version=" NOT "UNKNOWN"' + ) search = st.render() resultCount, eventCount = splunk_single(setup_splunk, search)