def setUpClass(cls):
     cls.compatible_rule = Rule(
         "test.toml", {
             "author": ["Elastic"],
             "description": "test description",
             "index": ["filebeat-*"],
             "language": "kuery",
             "license": "Elastic License",
             "name": "test rule",
             "query": "process.name:test.query",
             "risk_score": 21,
             "rule_id": str(uuid.uuid4()),
             "severity": "low",
             "type": "query"
         })
     cls.versioned_rule = cls.compatible_rule.copy()
     cls.versioned_rule.contents["version"] = 10
     cls.threshold_rule = Rule(
         "test.toml", {
             "author": ["Elastic"],
             "description": "test description",
             "language": "kuery",
             "license": "Elastic License",
             "name": "test rule",
             "query": "process.name:test.query",
             "risk_score": 21,
             "rule_id": str(uuid.uuid4()),
             "severity": "low",
             "threshold": {
                 "field": "destination.bytes",
                 "value": 75,
             },
             "type": "threshold",
         })
Exemple #2
0
    def test_eql_validation(self):
        base_fields = {
            "author": ["Elastic"],
            "description": "test description",
            "index": ["filebeat-*"],
            "language": "eql",
            "license": "Elastic License",
            "name": "test rule",
            "risk_score": 21,
            "rule_id": str(uuid.uuid4()),
            "severity": "low",
            "type": "eql"
        }

        Rule(
            "test.toml",
            dict(base_fields,
                 query="""
            process where process.name == "cmd.exe"
        """))

        with self.assertRaises(eql.EqlSyntaxError):
            Rule(
                "test.toml",
                dict(base_fields,
                     query="""
                    process where process.name == this!is$not#v@lid
            """))

        with self.assertRaises(eql.EqlSemanticError):
            Rule(
                "test.toml",
                dict(base_fields,
                     query="""
                    process where process.invalid_field == "hello world"
            """))

        with self.assertRaises(eql.EqlTypeMismatchError):
            Rule(
                "test.toml",
                dict(base_fields,
                     query="""
                    process where process.pid == "some string field"
            """))
Exemple #3
0
    def test_all_rule_queries_optimized(self):
        """Ensure that every rule query is in optimized form."""
        for file_name, contents in rule_loader.load_rule_files().items():
            rule = Rule(file_name, contents)

            if rule.query and rule.contents['language'] == 'kuery':
                tree = kql.parse(rule.query, optimize=False)
                optimized = tree.optimize(recursive=True)
                err_message = '\nQuery not optimized for rule: {} - {}\nExpected: {}\nActual:   {}'.format(
                    rule.name, rule.id, optimized, rule.query)
                self.assertEqual(tree, optimized, err_message)
Exemple #4
0
    def test_no_unrequired_defaults(self):
        """Test that values that are not required in the schema are not set with default values."""
        rules_with_hits = {}

        for file_name, contents in rule_loader.load_rule_files().items():
            rule = Rule(file_name, contents)
            default_matches = rule_loader.find_unneeded_defaults(rule)

            if default_matches:
                rules_with_hits['{} - {}'.format(rule.name, rule.id)] = default_matches

        error_msg = 'The following rules have unnecessary default values set: \n{}'.format(
            json.dumps(rules_with_hits, indent=2))
        self.assertDictEqual(rules_with_hits, {}, error_msg)
Exemple #5
0
    def test_all_rule_files(self):
        """Ensure that every rule file can be loaded and validate against schema."""
        rules = []

        for file_name, contents in rule_loader.load_rule_files().items():
            try:
                rule = Rule(file_name, contents)
                rules.append(rule)

            except (pytoml.TomlError, toml.TomlDecodeError) as e:
                print("TOML error when parsing rule file \"{}\"".format(os.path.basename(file_name)), file=sys.stderr)
                raise e

            except jsonschema.ValidationError as e:
                print("Schema error when parsing rule file \"{}\"".format(os.path.basename(file_name)), file=sys.stderr)
                raise e
Exemple #6
0
 def test_all_rules_as_rule_schema(self):
     """Ensure that every rule file validates against the rule schema."""
     for file_name, contents in rule_loader.load_rule_files().items():
         rule = Rule(file_name, contents)
         rule.validate(as_rule=True)
Exemple #7
0
    def setUpClass(cls):
        # expected contents for a downgraded rule
        cls.v78_kql = {
            "description":
            "test description",
            "index": ["filebeat-*"],
            "language":
            "kuery",
            "name":
            "test rule",
            "query":
            "process.name:test.query",
            "risk_score":
            21,
            "rule_id":
            str(uuid.uuid4()),
            "severity":
            "low",
            "type":
            "query",
            "threat": [{
                "framework":
                "MITRE ATT&CK",
                "tactic": {
                    "id": "TA0001",
                    "name": "Execution",
                    "reference": "https://attack.mitre.org/tactics/TA0001/"
                },
                "technique": [{
                    "id":
                    "T1059",
                    "name":
                    "Command and Scripting Interpreter",
                    "reference":
                    "https://attack.mitre.org/techniques/T1059/",
                }],
            }]
        }
        cls.v79_kql = dict(cls.v78_kql,
                           author=["Elastic"],
                           license="Elastic License")
        cls.v711_kql = copy.deepcopy(cls.v79_kql)
        cls.v711_kql["threat"][0]["technique"][0]["subtechnique"] = [{
            "id":
            "T1059.001",
            "name":
            "PowerShell",
            "reference":
            "https://attack.mitre.org/techniques/T1059/001/"
        }]
        cls.v711_kql["threat"].append({
            "framework": "MITRE ATT&CK",
            "tactic": {
                "id": "TA0008",
                "name": "Lateral Movement",
                "reference": "https://attack.mitre.org/tactics/TA0008/"
            },
        })

        cls.versioned_rule = Rule("test.toml", copy.deepcopy(cls.v79_kql))
        cls.versioned_rule.contents["version"] = 10

        cls.threshold_rule = Rule(
            "test.toml", {
                "author": ["Elastic"],
                "description": "test description",
                "language": "kuery",
                "license": "Elastic License",
                "name": "test rule",
                "query": "process.name:test.query",
                "risk_score": 21,
                "rule_id": str(uuid.uuid4()),
                "severity": "low",
                "threshold": {
                    "field": "destination.bytes",
                    "value": 75,
                },
                "type": "threshold",
            })