Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions detection_rules/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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."""
Expand Down
31 changes: 29 additions & 2 deletions tests/test_all_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = {}
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we never use soft-forking then, only hard-forking from the metadata field??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on our last discussions, I don't think it is supportable (or advisable)

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"""

Expand Down