]> git.ipfire.org Git - thirdparty/suricata-update.git/commitdiff
Separate out matchers
authorVagisha Gupta <vagishagupta23@gmail.com>
Thu, 17 Oct 2019 06:46:50 +0000 (12:16 +0530)
committerShivani Bhardwaj <shivanib134@gmail.com>
Sun, 22 Dec 2019 17:25:38 +0000 (22:55 +0530)
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

suricata/update/main.py
suricata/update/matchers.py [new file with mode: 0644]
tests/test_main.py
tests/test_matchers.py

index e7e21742cd7dd941b2d0408c7bf07c326369450d..ff9d2dd3aa10406e20d3287f28c28d2098cf806a 100644 (file)
@@ -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 (file)
index 0000000..ca492be
--- /dev/null
@@ -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
index f71c0ec6c053a4c6be33571c887136801e1a1d15..1723c82484826f662c008cdd6bb572d8e76e40cd 100644 (file)
@@ -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))
index 3f937b92fb1ff7111363433289cf63cb8b9bcbf1..8113c5343b1a889f49c81349d8b3bafbc8a76278 100644 (file)
@@ -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)