diff --git a/detection_rules/cli_utils.py b/detection_rules/cli_utils.py index ab0e64a84..e2701038d 100644 --- a/detection_rules/cli_utils.py +++ b/detection_rules/cli_utils.py @@ -22,7 +22,7 @@ from .rule_loader import (DEFAULT_PREBUILT_BBR_DIRS, DEFAULT_PREBUILT_RULES_DIRS, RuleCollection, dict_filter) from .schemas import definitions -from .utils import clear_caches +from .utils import clear_caches, rulename_to_filename def single_collection(f): @@ -95,6 +95,16 @@ def multi_collection(f): if len(rules) == 0: client_error("No rules found") + # Warn that if the path does not match the expected path, it will be saved to the expected path + for rule in rules: + threat = rule.contents.data.get("threat") + first_tactic = threat[0].tactic.name if threat else "" + rule_name = rulename_to_filename(rule.contents.data.name, tactic_name=first_tactic) + if rule.path.name != rule_name: + click.secho( + f"WARNING: Rule path does not match required path: {rule.path.name} != {rule_name}", fg="yellow" + ) + kwargs["rules"] = rules return f(*args, **kwargs) @@ -200,7 +210,19 @@ def rule_prompt(path=None, rule_type=None, required_only=True, save=True, verbos # DEFAULT_PREBUILT_RULES_DIRS[0] is a required directory just as a suggestion suggested_path = Path(DEFAULT_PREBUILT_RULES_DIRS[0]) / contents['name'] path = Path(path or input(f'File path for rule [{suggested_path}]: ') or suggested_path).resolve() - meta = {'creation_date': creation_date, 'updated_date': creation_date, 'maturity': 'development'} + # Inherit maturity from the rule already exists + maturity = "development" + if path.exists(): + rules = RuleCollection() + rules.load_file(path) + if rules: + maturity = rules.rules[0].contents.metadata.maturity + + meta = { + "creation_date": creation_date, + "updated_date": creation_date, + "maturity": maturity, + } try: rule = TOMLRule(path=Path(path), contents=TOMLRuleContents.from_dict({'rule': contents, 'metadata': meta})) diff --git a/detection_rules/kbwrap.py b/detection_rules/kbwrap.py index 527920357..8307f3a60 100644 --- a/detection_rules/kbwrap.py +++ b/detection_rules/kbwrap.py @@ -237,10 +237,21 @@ def kibana_export_rules(ctx: click.Context, directory: Path, action_connectors_d rule_resource["author"] = rule_resource.get("author") or default_author or [rule_resource.get("created_by")] if isinstance(rule_resource["author"], str): rule_resource["author"] = [rule_resource["author"]] - contents = TOMLRuleContents.from_rule_resource(rule_resource, maturity="production") - threat = contents.data.get("threat") - first_tactic = threat[0].tactic.name if threat else "" - rule_name = rulename_to_filename(contents.data.name, tactic_name=first_tactic) + # Inherit maturity from the rule already exists + maturity = "development" + threat = rule_resource.get("threat") + first_tactic = threat[0].get("tactic").get("name") if threat else "" + rule_name = rulename_to_filename(rule_resource.get("name"), tactic_name=first_tactic) + # check if directory / f"{rule_name}" exists + if (directory / f"{rule_name}").exists(): + rules = RuleCollection() + rules.load_file(directory / f"{rule_name}") + if rules: + maturity = rules.rules[0].contents.metadata.maturity + + contents = TOMLRuleContents.from_rule_resource( + rule_resource, maturity=maturity + ) rule = TOMLRule(contents=contents, path=directory / f"{rule_name}") except Exception as e: if skip_errors: diff --git a/pyproject.toml b/pyproject.toml index aceb817e6..933b63954 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "detection_rules" -version = "0.3.19" +version = "0.4.0" description = "Detection Rules is the home for rules used by Elastic Security. This repository is used for the development, maintenance, testing, validation, and release of rules for Elastic Security’s Detection Engine." readme = "README.md" requires-python = ">=3.12"