From: Vagisha Gupta Date: Thu, 17 Oct 2019 06:46:50 +0000 (+0530) Subject: Separate out matchers X-Git-Tag: 1.2.0rc1~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5679add13c62f653879959869ca282bf79dfebd3;p=thirdparty%2Fsuricata-update.git Separate out matchers Currently, all the code for matchers happens to be in main.py which makes it quite cluttered. A separate `matchers.py` module is created which contains all the code for matching rules and integrated with main.py. Also the modules `test_main.py` and `test_matchers.py` are modified accordingly. Redmine issue: https://redmine.openinfosecfoundation.org/issues/2873 --- diff --git a/suricata/update/main.py b/suricata/update/main.py index e7e2174..ff9d2dd 100644 --- a/suricata/update/main.py +++ b/suricata/update/main.py @@ -22,7 +22,6 @@ import re import os.path import logging import argparse -import shlex import time import hashlib import fnmatch @@ -60,6 +59,7 @@ from suricata.update import ( rule as rule_mod, sources, util, + matchers as matchers_mod ) from suricata.update.version import version @@ -92,215 +92,6 @@ DEFAULT_OUTPUT_RULE_FILENAME = "suricata.rules" INDEX_EXPIRATION_TIME = 60 * 60 * 24 * 14 -class AllRuleMatcher(object): - """Matcher object to match all rules. """ - - def match(self, rule): - return True - - @classmethod - def parse(cls, buf): - if buf.strip() == "*": - return cls() - return None - -class ProtoRuleMatcher: - """A rule matcher that matches on the protocol of a rule.""" - - def __init__(self, proto): - self.proto = proto - - def match(self, rule): - return rule.proto == self.proto - -class IdRuleMatcher(object): - """Matcher object to match an idstools rule object by its signature - ID.""" - - def __init__(self, generatorId=None, signatureId=None): - self.signatureIds = [] - if generatorId and signatureId: - self.signatureIds.append((generatorId, signatureId)) - - def match(self, rule): - for (generatorId, signatureId) in self.signatureIds: - if generatorId == rule.gid and signatureId == rule.sid: - return True - return False - - @classmethod - def parse(cls, buf): - matcher = cls() - - for entry in buf.split(","): - entry = entry.strip() - - parts = entry.split(":", 1) - if not parts: - return None - if len(parts) == 1: - try: - signatureId = int(parts[0]) - matcher.signatureIds.append((1, signatureId)) - except: - return None - else: - try: - generatorId = int(parts[0]) - signatureId = int(parts[1]) - matcher.signatureIds.append((generatorId, signatureId)) - except: - return None - - return matcher - -class FilenameMatcher(object): - """Matcher object to match a rule by its filename. This is similar to - a group but has no specifier prefix. - """ - - def __init__(self, pattern): - self.pattern = pattern - - def match(self, rule): - if hasattr(rule, "group") and rule.group is not None: - return fnmatch.fnmatch(rule.group, self.pattern) - return False - - @classmethod - def parse(cls, buf): - if buf.startswith("filename:"): - try: - group = buf.split(":", 1)[1] - return cls(group.strip()) - except: - pass - return None - -class GroupMatcher(object): - """Matcher object to match an idstools rule object by its group (ie: - filename). - - The group is just the basename of the rule file with or without - extension. - - Examples: - - emerging-shellcode - - emerging-trojan.rules - - """ - - def __init__(self, pattern): - self.pattern = pattern - - def match(self, rule): - if hasattr(rule, "group") and rule.group is not None: - if fnmatch.fnmatch(os.path.basename(rule.group), self.pattern): - return True - # Try matching against the rule group without the file - # extension. - if fnmatch.fnmatch( - os.path.splitext( - os.path.basename(rule.group))[0], self.pattern): - return True - return False - - @classmethod - def parse(cls, buf): - if buf.startswith("group:"): - try: - logger.debug("Parsing group matcher: %s" % (buf)) - group = buf.split(":", 1)[1] - return cls(group.strip()) - except: - pass - if buf.endswith(".rules"): - return cls(buf.strip()) - return None - -class ReRuleMatcher(object): - """Matcher object to match an idstools rule object by regular - expression.""" - - def __init__(self, pattern): - self.pattern = pattern - - def match(self, rule): - if self.pattern.search(rule.raw): - return True - return False - - @classmethod - def parse(cls, buf): - if buf.startswith("re:"): - try: - logger.debug("Parsing regex matcher: %s" % (buf)) - patternstr = buf.split(":", 1)[1].strip() - pattern = re.compile(patternstr, re.I) - return cls(pattern) - except: - pass - return None - -class ModifyRuleFilter(object): - """Filter to modify an idstools rule object. - - Important note: This filter does not modify the rule inplace, but - instead returns a new rule object with the modification. - """ - - def __init__(self, matcher, pattern, repl): - self.matcher = matcher - self.pattern = pattern - self.repl = repl - - def match(self, rule): - return self.matcher.match(rule) - - def run(self, rule): - modified_rule = self.pattern.sub(self.repl, rule.format()) - parsed = rule_mod.parse(modified_rule, rule.group) - if parsed is None: - logger.error("Modification of rule %s results in invalid rule: %s", - rule.idstr, modified_rule) - return rule - return parsed - - @classmethod - def parse(cls, buf): - tokens = shlex.split(buf) - if len(tokens) == 3: - matchstring, a, b = tokens - elif len(tokens) > 3 and tokens[0] == "modifysid": - matchstring, a, b = tokens[1], tokens[2], tokens[4] - else: - raise Exception("Bad number of arguments.") - matcher = parse_rule_match(matchstring) - if not matcher: - raise Exception("Bad match string: %s" % (matchstring)) - pattern = re.compile(a) - - # Convert Oinkmaster backticks to Python. - b = re.sub("\$\{(\d+)\}", "\\\\\\1", b) - - return cls(matcher, pattern, b) - -class DropRuleFilter(object): - """ Filter to modify an idstools rule object to a drop rule. """ - - def __init__(self, matcher): - self.matcher = matcher - - def match(self, rule): - if rule["noalert"]: - return False - return self.matcher.match(rule) - - def run(self, rule): - drop_rule = rule_mod.parse(re.sub("^\w+", "drop", rule.raw)) - drop_rule.enabled = rule.enabled - return drop_rule - class Fetch: def __init__(self): @@ -434,29 +225,6 @@ class Fetch: files[basename] = open(filename, "rb").read() return files -def parse_rule_match(match): - matcher = AllRuleMatcher.parse(match) - if matcher: - return matcher - - matcher = IdRuleMatcher.parse(match) - if matcher: - return matcher - - matcher = ReRuleMatcher.parse(match) - if matcher: - return matcher - - matcher = FilenameMatcher.parse(match) - if matcher: - return matcher - - matcher = GroupMatcher.parse(match) - if matcher: - return matcher - - return None - def load_filters(filename): filters = [] @@ -469,7 +237,7 @@ def load_filters(filename): line = line.rsplit(" #")[0] line = re.sub(r'\\\$', '$', line) # needed to escape $ in pp - rule_filter = ModifyRuleFilter.parse(line) + rule_filter = matchers_mod.ModifyRuleFilter.parse(line) if rule_filter: filters.append(rule_filter) else: @@ -483,7 +251,7 @@ def load_drop_filters(filename): filters = [] for matcher in matchers: - filters.append(DropRuleFilter(matcher)) + filters.append(matchers_mod.DropRuleFilter(matcher)) return filters @@ -495,7 +263,7 @@ def parse_matchers(fileobj): if not line or line.startswith("#"): continue line = line.rsplit(" #")[0] - matcher = parse_rule_match(line) + matcher = matchers_mod.parse_rule_match(line) if not matcher: logger.warn("Failed to parse: \"%s\"" % (line)) else: @@ -1227,7 +995,7 @@ def _main(): proto = m.group(1) if not suriconf.is_true(key, ["detection-only"]): logger.info("Disabling rules for protocol %s", proto) - disable_matchers.append(ProtoRuleMatcher(proto)) + disable_matchers.append(matchers_mod.ProtoRuleMatcher(proto)) elif proto == "smb" and suriconf.build_info: # Special case for SMB rules. For versions less # than 5, disable smb rules if Rust is not @@ -1235,7 +1003,7 @@ def _main(): if suriconf.build_info["version"].major < 5: if not "RUST" in suriconf.build_info["features"]: logger.info("Disabling rules for protocol {}".format(proto)) - disable_matchers.append(ProtoRuleMatcher(proto)) + disable_matchers.append(matchers_mod.ProtoRuleMatcher(proto)) # Check that the cache directory exists and is writable. if not os.path.exists(config.get_cache_dir()): diff --git a/suricata/update/matchers.py b/suricata/update/matchers.py new file mode 100644 index 0000000..ca492be --- /dev/null +++ b/suricata/update/matchers.py @@ -0,0 +1,270 @@ +# Copyright (C) 2017 Open Information Security Foundation +# +# You can copy, redistribute or modify this Program under the terms of +# the GNU General Public License version 2 as published by the Free +# Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# version 2 along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# This module contains functions for matching rules for disabling, +# enabling, converting to drop or modification. + +import re +import os.path +import logging +import shlex +import fnmatch +import suricata.update.rule + + +logger = logging.getLogger() + + +class AllRuleMatcher(object): + """Matcher object to match all rules. """ + + def match(self, rule): + return True + + @classmethod + def parse(cls, buf): + if buf.strip() == "*": + return cls() + return None + + +class ProtoRuleMatcher: + """A rule matcher that matches on the protocol of a rule.""" + + def __init__(self, proto): + self.proto = proto + + def match(self, rule): + return rule.proto == self.proto + + +class IdRuleMatcher(object): + """Matcher object to match an idstools rule object by its signature + ID.""" + + def __init__(self, generatorId=None, signatureId=None): + self.signatureIds = [] + if generatorId and signatureId: + self.signatureIds.append((generatorId, signatureId)) + + def match(self, rule): + for (generatorId, signatureId) in self.signatureIds: + if generatorId == rule.gid and signatureId == rule.sid: + return True + return False + + @classmethod + def parse(cls, buf): + matcher = cls() + + for entry in buf.split(","): + entry = entry.strip() + + parts = entry.split(":", 1) + if not parts: + return None + if len(parts) == 1: + try: + signatureId = int(parts[0]) + matcher.signatureIds.append((1, signatureId)) + except: + return None + else: + try: + generatorId = int(parts[0]) + signatureId = int(parts[1]) + matcher.signatureIds.append((generatorId, signatureId)) + except: + return None + + return matcher + + +class FilenameMatcher(object): + """Matcher object to match a rule by its filename. This is similar to + a group but has no specifier prefix. + """ + + def __init__(self, pattern): + self.pattern = pattern + + def match(self, rule): + if hasattr(rule, "group") and rule.group is not None: + return fnmatch.fnmatch(rule.group, self.pattern) + return False + + @classmethod + def parse(cls, buf): + if buf.startswith("filename:"): + try: + group = buf.split(":", 1)[1] + return cls(group.strip()) + except: + pass + return None + + +class GroupMatcher(object): + """Matcher object to match an idstools rule object by its group (ie: + filename). + + The group is just the basename of the rule file with or without + extension. + + Examples: + - emerging-shellcode + - emerging-trojan.rules + + """ + + def __init__(self, pattern): + self.pattern = pattern + + def match(self, rule): + if hasattr(rule, "group") and rule.group is not None: + if fnmatch.fnmatch(os.path.basename(rule.group), self.pattern): + return True + # Try matching against the rule group without the file + # extension. + if fnmatch.fnmatch( + os.path.splitext( + os.path.basename(rule.group))[0], self.pattern): + return True + return False + + @classmethod + def parse(cls, buf): + if buf.startswith("group:"): + try: + logger.debug("Parsing group matcher: %s" % (buf)) + group = buf.split(":", 1)[1] + return cls(group.strip()) + except: + pass + if buf.endswith(".rules"): + return cls(buf.strip()) + return None + + +class ReRuleMatcher(object): + """Matcher object to match an idstools rule object by regular + expression.""" + + def __init__(self, pattern): + self.pattern = pattern + + def match(self, rule): + if self.pattern.search(rule.raw): + return True + return False + + @classmethod + def parse(cls, buf): + if buf.startswith("re:"): + try: + logger.debug("Parsing regex matcher: %s" % (buf)) + patternstr = buf.split(":", 1)[1].strip() + pattern = re.compile(patternstr, re.I) + return cls(pattern) + except: + pass + return None + + +class ModifyRuleFilter(object): + """Filter to modify an idstools rule object. + + Important note: This filter does not modify the rule inplace, but + instead returns a new rule object with the modification. + """ + + def __init__(self, matcher, pattern, repl): + self.matcher = matcher + self.pattern = pattern + self.repl = repl + + def match(self, rule): + return self.matcher.match(rule) + + def run(self, rule): + modified_rule = self.pattern.sub(self.repl, rule.format()) + parsed = suricata.update.rule.parse(modified_rule, rule.group) + if parsed is None: + logger.error("Modification of rule %s results in invalid rule: %s", + rule.idstr, modified_rule) + return rule + return parsed + + @classmethod + def parse(cls, buf): + tokens = shlex.split(buf) + if len(tokens) == 3: + matchstring, a, b = tokens + elif len(tokens) > 3 and tokens[0] == "modifysid": + matchstring, a, b = tokens[1], tokens[2], tokens[4] + else: + raise Exception("Bad number of arguments.") + matcher = parse_rule_match(matchstring) + if not matcher: + raise Exception("Bad match string: %s" % (matchstring)) + pattern = re.compile(a) + + # Convert Oinkmaster backticks to Python. + b = re.sub("\$\{(\d+)\}", "\\\\\\1", b) + + return cls(matcher, pattern, b) + + +class DropRuleFilter(object): + """ Filter to modify an idstools rule object to a drop rule. """ + + def __init__(self, matcher): + self.matcher = matcher + + def match(self, rule): + if rule["noalert"]: + return False + return self.matcher.match(rule) + + def run(self, rule): + drop_rule = suricata.update.rule.parse(re.sub( + "^\w+", "drop", rule.raw)) + drop_rule.enabled = rule.enabled + return drop_rule + + +def parse_rule_match(match): + matcher = AllRuleMatcher.parse(match) + if matcher: + return matcher + + matcher = IdRuleMatcher.parse(match) + if matcher: + return matcher + + matcher = ReRuleMatcher.parse(match) + if matcher: + return matcher + + matcher = FilenameMatcher.parse(match) + if matcher: + return matcher + + matcher = GroupMatcher.parse(match) + if matcher: + return matcher + + return None diff --git a/tests/test_main.py b/tests/test_main.py index f71c0ec..1723c82 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -24,6 +24,7 @@ import unittest import suricata.update.rule from suricata.update import main import suricata.update.extract +from suricata.update import matchers as matchers_mod class TestRulecat(unittest.TestCase): @@ -116,7 +117,7 @@ class ModifyRuleFilterTestCase(unittest.TestCase): def test_id_match(self): rule0 = suricata.update.rule.parse(self.rule_string) line = '2020757 "\|0d 0a\|" "|ff ff|"' - rule_filter = main.ModifyRuleFilter.parse(line) + rule_filter = matchers_mod.ModifyRuleFilter.parse(line) self.assertTrue(rule_filter != None) self.assertTrue(rule_filter.match(rule0)) rule1 = rule_filter.run(rule0) @@ -127,7 +128,7 @@ class ModifyRuleFilterTestCase(unittest.TestCase): def test_re_match(self): rule0 = suricata.update.rule.parse(self.rule_string) line = 're:classtype:trojan-activity "\|0d 0a\|" "|ff ff|"' - rule_filter = main.ModifyRuleFilter.parse(line) + rule_filter = matchers_mod.ModifyRuleFilter.parse(line) self.assertTrue(rule_filter != None) self.assertTrue(rule_filter.match(rule0)) rule1 = rule_filter.run(rule0) @@ -138,7 +139,7 @@ class ModifyRuleFilterTestCase(unittest.TestCase): def test_re_backref_one(self): rule0 = suricata.update.rule.parse(self.rule_string) line = 're:classtype:trojan-activity "(alert)(.*)" "drop\\2"' - rule_filter = main.ModifyRuleFilter.parse(line) + rule_filter = matchers_mod.ModifyRuleFilter.parse(line) self.assertTrue(rule_filter != None) self.assertTrue(rule_filter.match(rule0)) rule1 = rule_filter.run(rule0) @@ -148,7 +149,7 @@ class ModifyRuleFilterTestCase(unittest.TestCase): def test_re_backref_two(self): rule0 = suricata.update.rule.parse(self.rule_string) line = 're:classtype:trojan-activity "(alert)(.*)(from_server)(.*)" "drop\\2to_client\\4"' - rule_filter = main.ModifyRuleFilter.parse(line) + rule_filter = matchers_mod.ModifyRuleFilter.parse(line) self.assertTrue(rule_filter != None) self.assertTrue(rule_filter.match(rule0)) rule1 = rule_filter.run(rule0) @@ -159,7 +160,7 @@ class ModifyRuleFilterTestCase(unittest.TestCase): rule_in = suricata.update.rule.parse(self.rule_string) self.assertIsNotNone(rule_in) - f = main.ModifyRuleFilter.parse( + f = matchers_mod.ModifyRuleFilter.parse( 'group:emerging-trojan.rules "^alert" "drop"') self.assertIsNotNone(f) @@ -167,14 +168,14 @@ class ModifyRuleFilterTestCase(unittest.TestCase): self.assertTrue(rule_out.format().startswith("drop")) def test_oinkmaster_backticks(self): - f = main.ModifyRuleFilter.parse( + f = matchers_mod.ModifyRuleFilter.parse( '* "^drop(.*)noalert(.*)" "alert${1}noalert${2}"') rule_in ="""drop http $EXTERNAL_NET any -> $HOME_NET any (msg:"ET MALWARE Windows executable sent when remote host claims to send an image 2"; flow: established,to_client; content:"|0d 0a|Content-Type|3a| image/jpeg|0d 0a 0d 0a|MZ"; fast_pattern:12,20; noalert; classtype:trojan-activity; sid:2020757; rev:2;)""" rule_out = f.run(suricata.update.rule.parse(rule_in)) self.assertEqual("""alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"ET MALWARE Windows executable sent when remote host claims to send an image 2"; flow: established,to_client; content:"|0d 0a|Content-Type|3a| image/jpeg|0d 0a 0d 0a|MZ"; fast_pattern:12,20; noalert; classtype:trojan-activity; sid:2020757; rev:2;)""", rule_out.format()) def test_oinkmaster_backticks_not_noalert(self): - f = main.ModifyRuleFilter.parse( + f = matchers_mod.ModifyRuleFilter.parse( 'modifysid * "^drop(.*)noalert(.*)" | "alert${1}noalert${2}"') rule_in ="""drop http $EXTERNAL_NET any -> $HOME_NET any (msg:"ET MALWARE Windows executable sent when remote host claims to send an image 2"; flow: established,to_client; content:"|0d 0a|Content-Type|3a| image/jpeg|0d 0a 0d 0a|MZ"; fast_pattern:12,20; classtype:trojan-activity; sid:2020757; rev:2;)""" rule_out = f.run(suricata.update.rule.parse(rule_in)) @@ -182,7 +183,7 @@ class ModifyRuleFilterTestCase(unittest.TestCase): def test_oinkmaster_modify_group_name(self): """Test an Oinkmaster style modification line using a group name.""" - f = main.ModifyRuleFilter.parse( + f = matchers_mod.ModifyRuleFilter.parse( 'modifysid botcc.rules "^alert" | "drop"') rule_in ="""alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"ET MALWARE Windows executable sent when remote host claims to send an image 2"; flow: established,to_client; content:"|0d 0a|Content-Type|3a| image/jpeg|0d 0a 0d 0a|MZ"; fast_pattern:12,20; classtype:trojan-activity; sid:2020757; rev:2;)""" rule = suricata.update.rule.parse(rule_in, "rules/botcc.rules") @@ -195,10 +196,10 @@ class DropRuleFilterTestCase(unittest.TestCase): def test_enabled_rule(self): rule0 = suricata.update.rule.parse(self.rule_string, "rules/malware.rules") - id_matcher = main.IdRuleMatcher.parse("2020757") + id_matcher = matchers_mod.IdRuleMatcher.parse("2020757") self.assertTrue(id_matcher.match(rule0)) - drop_filter = main.DropRuleFilter(id_matcher) + drop_filter = matchers_mod.DropRuleFilter(id_matcher) rule1 = drop_filter.run(rule0) self.assertEqual("drop", rule1.action) self.assertTrue(rule1.enabled) @@ -207,10 +208,10 @@ class DropRuleFilterTestCase(unittest.TestCase): def test_disabled_rule(self): rule0 = suricata.update.rule.parse( "# " + self.rule_string, "rules/malware.rules") - id_matcher = main.IdRuleMatcher.parse("2020757") + id_matcher = matchers_mod.IdRuleMatcher.parse("2020757") self.assertTrue(id_matcher.match(rule0)) - drop_filter = main.DropRuleFilter(id_matcher) + drop_filter = matchers_mod.DropRuleFilter(id_matcher) rule1 = drop_filter.run(rule0) self.assertEqual("drop", rule1.action) self.assertFalse(rule1.enabled) @@ -224,11 +225,11 @@ class DropRuleFilterTestCase(unittest.TestCase): rule_with_noalert = """alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"ET TROJAN [CrowdStrike] ANCHOR PANDA Torn RAT Beacon Message Header Local"; flow:established, to_server; dsize:16; content:"|00 00 00 11 c8 00 00 00 00 00 00 00 00 00 00 00|"; depth:16; flowbits:set,ET.Torn.toread_header; flowbits: noalert; reference:url,blog.crowdstrike.com/whois-anchor-panda/index.html; classtype:trojan-activity; sid:2016659; rev:2; metadata:created_at 2013_03_22, updated_at 2013_03_22;)""" rule = suricata.update.rule.parse(rule_without_noalert) - matcher = main.IdRuleMatcher.parse("2016659") - rule_filter = main.DropRuleFilter(matcher) + matcher = matchers_mod.IdRuleMatcher.parse("2016659") + rule_filter = matchers_mod.DropRuleFilter(matcher) self.assertTrue(rule_filter.match(rule)) rule = suricata.update.rule.parse(rule_with_noalert) - matcher = main.IdRuleMatcher.parse("2016659") - rule_filter = main.DropRuleFilter(matcher) + matcher = matchers_mod.IdRuleMatcher.parse("2016659") + rule_filter = matchers_mod.DropRuleFilter(matcher) self.assertFalse(rule_filter.match(rule)) diff --git a/tests/test_matchers.py b/tests/test_matchers.py index 3f937b9..8113c53 100644 --- a/tests/test_matchers.py +++ b/tests/test_matchers.py @@ -23,6 +23,7 @@ import unittest import suricata.update.rule from suricata.update import main import suricata.update.extract +from suricata.update import matchers as matchers_mod class GroupMatcherTestCase(unittest.TestCase): @@ -30,15 +31,15 @@ class GroupMatcherTestCase(unittest.TestCase): def test_match(self): rule = suricata.update.rule.parse(self.rule_string, "rules/malware.rules") - matcher = main.parse_rule_match("group: malware.rules") + matcher = matchers_mod.parse_rule_match("group: malware.rules") self.assertEqual( - matcher.__class__, suricata.update.main.GroupMatcher) + matcher.__class__, matchers_mod.GroupMatcher) self.assertTrue(matcher.match(rule)) # Test match of just the group basename. - matcher = main.parse_rule_match("group: malware") + matcher = matchers_mod.parse_rule_match("group: malware") self.assertEqual( - matcher.__class__, suricata.update.main.GroupMatcher) + matcher.__class__, matchers_mod.GroupMatcher) self.assertTrue(matcher.match(rule)) class FilenameMatcherTestCase(unittest.TestCase): @@ -47,9 +48,9 @@ class FilenameMatcherTestCase(unittest.TestCase): def test_match(self): rule = suricata.update.rule.parse(self.rule_string, "rules/trojan.rules") - matcher = main.parse_rule_match("filename: */trojan.rules") + matcher = matchers_mod.parse_rule_match("filename: */trojan.rules") self.assertEqual( - matcher.__class__, suricata.update.main.FilenameMatcher) + matcher.__class__, matchers_mod.FilenameMatcher) self.assertTrue(matcher.match(rule)) class LoadMatchersTestCase(unittest.TestCase): @@ -61,48 +62,48 @@ re:.# This is a comment* 1:100 # Trailing comment. """)) self.assertEqual( - matchers[0].__class__, suricata.update.main.FilenameMatcher) + matchers[0].__class__, matchers_mod.FilenameMatcher) self.assertEqual( - matchers[1].__class__, suricata.update.main.ReRuleMatcher) + matchers[1].__class__, matchers_mod.ReRuleMatcher) self.assertEqual( - matchers[2].__class__, suricata.update.main.IdRuleMatcher) + matchers[2].__class__, matchers_mod.IdRuleMatcher) class IdRuleMatcherTestCase(unittest.TestCase): def test_parse_single_sid(self): - matcher = main.IdRuleMatcher.parse("123") + matcher = matchers_mod.IdRuleMatcher.parse("123") self.assertIsNotNone(matcher) self.assertEqual(1, len(matcher.signatureIds)) def test_parse_single_gidsid(self): - matcher = main.IdRuleMatcher.parse("1:123") + matcher = matchers_mod.IdRuleMatcher.parse("1:123") self.assertIsNotNone(matcher) self.assertEqual(1, len(matcher.signatureIds)) def test_parse_multi_sid(self): - matcher = main.IdRuleMatcher.parse("1,2,3") + matcher = matchers_mod.IdRuleMatcher.parse("1,2,3") self.assertIsNotNone(matcher) self.assertEqual(3, len(matcher.signatureIds)) def test_parse_multi_gidsid(self): - matcher = main.IdRuleMatcher.parse("1:1000,2:2000, 3:3000, 4:4000") + matcher = matchers_mod.IdRuleMatcher.parse("1:1000,2:2000, 3:3000, 4:4000") self.assertIsNotNone(matcher) self.assertEqual(4, len(matcher.signatureIds)) def test_parse_multi_mixed(self): - matcher = main.IdRuleMatcher.parse("1:1000, 2000, 3:3000, 4000") + matcher = matchers_mod.IdRuleMatcher.parse("1:1000, 2000, 3:3000, 4000") self.assertIsNotNone(matcher) self.assertEqual(4, len(matcher.signatureIds)) def test_parse_invalid(self): - matcher = main.IdRuleMatcher.parse("a") + matcher = matchers_mod.IdRuleMatcher.parse("a") self.assertIsNone(matcher) - matcher = main.IdRuleMatcher.parse("1, a") + matcher = matchers_mod.IdRuleMatcher.parse("1, a") self.assertIsNone(matcher) - matcher = main.IdRuleMatcher.parse("1a") + matcher = matchers_mod.IdRuleMatcher.parse("1a") self.assertIsNone(matcher) - matcher = main.IdRuleMatcher.parse("1:a") + matcher = matchers_mod.IdRuleMatcher.parse("1:a") self.assertIsNone(matcher)