diff --git a/detection_rules/rule.py b/detection_rules/rule.py index 0d1d844d8fb..307b7edbb42 100644 --- a/detection_rules/rule.py +++ b/detection_rules/rule.py @@ -38,6 +38,7 @@ MIN_FLEET_PACKAGE_VERSION = '7.13.0' BUILD_FIELD_VERSIONS = { + "related_integrations": (Version('8.3'), None), "required_fields": (Version('8.3'), None), "setup": (Version("8.3"), None) } @@ -250,6 +251,17 @@ def parsed_note(self) -> Optional[MarkoDocument]: def is_elastic_rule(self): return 'elastic' in [a.lower() for a in self.author] + def get_build_fields(self) -> {}: + """Get a list of build-time fields along with the stack versions which they will build within.""" + build_fields = {} + rule_fields = {f.name: f for f in dataclasses.fields(self)} + + for fld in BUILD_FIELD_VERSIONS: + if fld in rule_fields: + build_fields[fld] = BUILD_FIELD_VERSIONS[fld] + + return build_fields + class DataValidator: """Additional validation beyond base marshmallow schema validation.""" diff --git a/tests/test_all_rules.py b/tests/test_all_rules.py index 7f6996df462..d22ae481f6b 100644 --- a/tests/test_all_rules.py +++ b/tests/test_all_rules.py @@ -14,6 +14,7 @@ from detection_rules import attack from detection_rules.beats import parse_beats_from_index +from detection_rules.packaging import current_stack_version from detection_rules.rule import QueryRuleData from detection_rules.rule_loader import FILE_PATTERN from detection_rules.schemas import definitions @@ -382,8 +383,6 @@ def test_updated_date_newer_than_creation(self): 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 = {} @@ -698,6 +697,34 @@ def test_rule_backports_for_restricted_fields(self): self.fail(err_msg) +class TestBuildTimeFields(BaseRuleTest): + """Test validity of build-time fields.""" + + def test_build_fields_min_stack(self): + """Test that newly introduced build-time fields for a min_stack for applicable rules.""" + current_stack_ver = Version(current_stack_version()) + invalids = [] + + for rule in self.production_rules: + min_stack = rule.contents.metadata.min_stack_version + build_fields = rule.contents.data.get_build_fields() + + errors = [] + for build_field, field_versions in build_fields.items(): + start_ver, end_ver = field_versions + if start_ver is not None and current_stack_ver >= start_ver: + if min_stack is None or not Version(min_stack) >= start_ver: + errors.append(f'{build_field} >= {start_ver}') + + if errors: + err_str = ', '.join(errors) + invalids.append(f'{self.rule_str(rule)} uses a rule type with build fields requiring min_stack_versions' + f' to be set: {err_str}') + + if invalids: + self.fail(invalids) + + class TestRiskScoreMismatch(BaseRuleTest): """Test that severity and risk_score fields contain corresponding values"""