Esempio n. 1
0
 def test_stack_schema_map(self):
     """Test to ensure that an entry exists in the stack-schema-map for the current package version."""
     package_version = Version(load_current_package_version())
     stack_map = utils.load_etc_dump('stack-schema-map.yaml')
     err_msg = f'There is no entry defined for the current package ({package_version}) in the stack-schema-map'
     self.assertIn(package_version, [Version(v)[:2] for v in stack_map],
                   err_msg)
Esempio n. 2
0
    def test_threshold_downgrade_7_x(self):
        """Downgrade a threshold rule that was first introduced in 7.9."""
        if Version(self.current_version) > (7, ):
            return

        api_contents = self.v712_threshold_rule
        self.assertDictEqual(downgrade(api_contents, '7.13'), api_contents)
        self.assertDictEqual(downgrade(api_contents, '7.13.1'), api_contents)

        exc_msg = 'Cannot downgrade a threshold rule that has multiple threshold fields defined'
        with self.assertRaisesRegex(ValueError, exc_msg):
            downgrade(api_contents, '7.9')

        v712_threshold_contents_single_field = copy.deepcopy(api_contents)
        v712_threshold_contents_single_field['threshold']['field'].pop()

        with self.assertRaisesRegex(
                ValueError,
                "Cannot downgrade a threshold rule that has a defined cardinality"
        ):
            downgrade(v712_threshold_contents_single_field, "7.9")

        v712_no_cardinality = copy.deepcopy(
            v712_threshold_contents_single_field)
        v712_no_cardinality['threshold'].pop('cardinality')
        self.assertEqual(downgrade(v712_no_cardinality, "7.9"),
                         self.v79_threshold_contents)

        with self.assertRaises(ValueError):
            downgrade(v712_no_cardinality, "7.7")

        with self.assertRaisesRegex(ValueError, "Unsupported rule type"):
            downgrade(v712_no_cardinality, "7.8")
Esempio n. 3
0
    def test_versioned_downgrade_7_x(self):
        """Downgrade a KQL rule with version information"""
        if Version(self.current_version) > (7, ):
            return

        api_contents = self.v79_kql
        self.assertDictEqual(downgrade(api_contents, "7.9"), api_contents)
        self.assertDictEqual(downgrade(api_contents, "7.9.2"), api_contents)

        api_contents78 = api_contents.copy()
        api_contents78.pop("author")
        api_contents78.pop("license")

        self.assertDictEqual(downgrade(api_contents, "7.8"), api_contents78)

        with self.assertRaises(ValueError):
            downgrade(api_contents, "7.7")
Esempio n. 4
0
    def test_query_downgrade_7_x(self):
        """Downgrade a standard KQL rule."""
        if Version(self.current_version) > (7, ):
            return

        self.assertDictEqual(downgrade(self.v711_kql, "7.11"), self.v711_kql)
        self.assertDictEqual(downgrade(self.v711_kql, "7.9"), self.v79_kql)
        self.assertDictEqual(downgrade(self.v711_kql, "7.9.2"), self.v79_kql)
        self.assertDictEqual(downgrade(self.v711_kql, "7.8.1"), self.v78_kql)
        self.assertDictEqual(downgrade(self.v79_kql, "7.8"), self.v78_kql)
        self.assertDictEqual(downgrade(self.v79_kql, "7.8"), self.v78_kql)

        with self.assertRaises(ValueError):
            downgrade(self.v711_kql, "7.7")

        with self.assertRaises(ValueError):
            downgrade(self.v79_kql, "7.7")

        with self.assertRaises(ValueError):
            downgrade(self.v78_kql, "7.7", current_version="7.8")
Esempio n. 5
0
    def test_deprecated_rules(self):
        """Test that deprecated rules are properly handled."""
        from detection_rules.packaging import current_stack_version

        versions = default_version_lock.version_lock
        deprecations = load_etc_dump('deprecated_rules.json')
        deprecated_rules = {}
        rules_path = get_path('rules')
        deprecated_path = get_path("rules", "_deprecated")

        misplaced_rules = [
            r for r in self.all_rules
            if r.path.relative_to(rules_path).parts[-2] == '_deprecated'
            and  # noqa: W504
            r.contents.metadata.maturity != 'deprecated'
        ]
        misplaced = '\n'.join(
            f'{self.rule_str(r)} {r.contents.metadata.maturity}'
            for r in misplaced_rules)
        err_str = f'The following rules are stored in {deprecated_path} but are not marked as deprecated:\n{misplaced}'
        self.assertListEqual(misplaced_rules, [], err_str)

        for rule in self.deprecated_rules:
            meta = rule.contents.metadata

            deprecated_rules[rule.id] = rule
            err_msg = f'{self.rule_str(rule)} cannot be deprecated if it has not been version locked. ' \
                      f'Convert to `development` or delete the rule file instead'
            self.assertIn(rule.id, versions, err_msg)

            rule_path = rule.path.relative_to(rules_path)
            err_msg = f'{self.rule_str(rule)} deprecated rules should be stored in ' \
                      f'"{deprecated_path}" folder'
            self.assertEqual('_deprecated', rule_path.parts[-2], err_msg)

            err_msg = f'{self.rule_str(rule)} missing deprecation date'
            self.assertIsNotNone(meta['deprecation_date'], err_msg)

            err_msg = f'{self.rule_str(rule)} deprecation_date and updated_date should match'
            self.assertEqual(meta['deprecation_date'], meta['updated_date'],
                             err_msg)

        # skip this so the lock file can be shared across branches
        #
        # missing_rules = sorted(set(versions).difference(set(self.rule_lookup)))
        # missing_rule_strings = '\n '.join(f'{r} - {versions[r]["rule_name"]}' for r in missing_rules)
        # err_msg = f'Deprecated rules should not be removed, but moved to the rules/_deprecated folder instead. ' \
        #           f'The following rules have been version locked and are missing. ' \
        #           f'Re-add to the deprecated folder and update maturity to "deprecated": \n {missing_rule_strings}'
        # self.assertEqual([], missing_rules, err_msg)

        stack_version = Version(current_stack_version())
        for rule_id, entry in deprecations.items():
            # if a rule is deprecated and not backported in order to keep the rule active in older branches, then it
            # will exist in the deprecated_rules.json file and not be in the _deprecated folder - this is expected.
            # However, that should not occur except by exception - the proper way to handle this situation is to
            # "fork" the existing rule by adding a new min_stack_version.
            if stack_version < Version(entry['stack_version']):
                continue

            rule_str = f'{rule_id} - {entry["rule_name"]} ->'
            self.assertIn(
                rule_id, deprecated_rules,
                f'{rule_str} is logged in "deprecated_rules.json" but is missing'
            )
Esempio n. 6
0
 def test_threshold_downgrade_8_x(self):
     """Downgrade a threshold rule that was first introduced in 7.9."""
     if Version(self.current_version) > (8, ):
         return
Esempio n. 7
0
 def test_versioned_downgrade_8_x(self):
     """Downgrade a KQL rule with version information"""
     if Version(self.current_version) > (8, ):
         return
Esempio n. 8
0
 def test_query_downgrade_8_x(self):
     """Downgrade a standard KQL rule."""
     if Version(self.current_version) > (8, ):
         return