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)
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")
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")
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")
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' )
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
def test_versioned_downgrade_8_x(self): """Downgrade a KQL rule with version information""" if Version(self.current_version) > (8, ): return
def test_query_downgrade_8_x(self): """Downgrade a standard KQL rule.""" if Version(self.current_version) > (8, ): return