diff --git a/oa/rules/meta.py b/oa/rules/meta.py index d9e85ba2..34ee8fea 100644 --- a/oa/rules/meta.py +++ b/oa/rules/meta.py @@ -12,13 +12,13 @@ CONVERT = ( ("&&", " and "), ("||", " or "), - ("!", " not "), + ("!", " not ") ) # Simple protection against recursion, or infinite loops with the meta rules. MAX_RECURSION = 10 -_SUBRULE_P = Regex(r"([_a-zA-Z]\w*)(?=\W|$)") +_SUBRULE_P = Regex(r"((?:!\s*)?[_a-zA-Z]\w*)(?=\W|$)") class MetaRule(oa.rules.base.BaseRule): @@ -46,7 +46,7 @@ def postparsing(self, ruleset, _depth=0): return subrules = set(_SUBRULE_P.findall(self.rule)) - rule = _SUBRULE_P.sub(r"\1(msg)", self.rule) + rule = _SUBRULE_P.sub(r"(\1(msg))", self.rule) for operator, repl in CONVERT: rule = rule.replace(operator, repl) rule_match = "match = lambda msg: %s" % rule @@ -56,8 +56,9 @@ def postparsing(self, ruleset, _depth=0): oa.rules.base.BaseRule.postparsing(self, ruleset) for subrule_name in subrules: + clean_subrule_name = subrule_name.strip("! \t") try: - subrule = ruleset.get_rule(subrule_name) + subrule = ruleset.get_rule(clean_subrule_name) # Call any postparsing for this subrule to ensure that the rule # is usable. (For example when the meta rule references other # meta rules). @@ -66,7 +67,7 @@ def postparsing(self, ruleset, _depth=0): raise oa.errors.InvalidRule(self.name, "Undefined subrule " "referenced %r" % subrule_name) - self._location[subrule_name] = subrule.match + self._location[clean_subrule_name] = subrule.match exec(_code_obj, self._location) assert "match" in self._location diff --git a/tests/functional/test_rules/test_meta.py b/tests/functional/test_rules/test_meta.py index 28fad6f6..3d6aaa6c 100644 --- a/tests/functional/test_rules/test_meta.py +++ b/tests/functional/test_rules/test_meta.py @@ -86,3 +86,14 @@ def test_header_meta_rule_combined_match(self): "Please click this link: https://example.com and follow the instructions", config=config, score=9.0, symbols=["TEST_DKIM_AND_FROM", "TEST_FROM_AND_URL", "TEST_ALL_RULE"]) + + def test_header_meta_rule_combined_match_not_operator(self): + config = ("body __TEST_NOT_DIFFERENT /instructions/\n" + "body __TEST_NOT_DIFFERENT2 /follow/\n" + "meta TEST_ALL_RULE (((__TEST_NOT_DIFFERENT*5) +! (1*__TEST_NOT_DIFFERENT2))>1)\n" + "score TEST_ALL_RULE 5") + self.check_symbols("DKIM-Signature: TestDkim \n" + "From: test \n\n" + "Please click this link: https://example.com and follow the instructions", + config=config, + score=5.0, symbols=["TEST_ALL_RULE"])