Skip to content

Commit d72cb92

Browse files
authored
Bringing back "fix: Cleaning up the hashable content for the rule" (#4621) (#4668)
1 parent 97e6d8b commit d72cb92

File tree

4 files changed

+35
-13
lines changed

4 files changed

+35
-13
lines changed

detection_rules/packaging.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ def create_bulk_index_body(self) -> Tuple[Ndjson, Ndjson]:
465465
for rule in self.rules:
466466
summary_doc['rule_ids'].append(rule.id)
467467
summary_doc['rule_names'].append(rule.name)
468-
summary_doc['rule_hashes'].append(rule.contents.sha256())
468+
summary_doc['rule_hashes'].append(rule.contents.get_hash())
469469

470470
if rule.id in self.new_ids:
471471
status = 'new'
@@ -481,7 +481,7 @@ def create_bulk_index_body(self) -> Tuple[Ndjson, Ndjson]:
481481
if relative_path is None:
482482
raise ValueError(f"Could not find a valid relative path for the rule: {rule.id}")
483483

484-
rule_doc = dict(hash=rule.contents.sha256(),
484+
rule_doc = dict(hash=rule.contents.get_hash(),
485485
source='repo',
486486
datetime_uploaded=now,
487487
status=status,

detection_rules/rule.py

+29-7
Original file line numberDiff line numberDiff line change
@@ -1010,18 +1010,25 @@ def type(self):
10101010

10111011
def lock_info(self, bump=True) -> dict:
10121012
version = self.autobumped_version if bump else (self.saved_version or 1)
1013-
contents = {"rule_name": self.name, "sha256": self.sha256(), "version": version, "type": self.type}
1013+
contents = {"rule_name": self.name, "sha256": self.get_hash(), "version": version, "type": self.type}
10141014

10151015
return contents
10161016

10171017
@property
1018-
def is_dirty(self) -> Optional[bool]:
1018+
def is_dirty(self) -> bool:
10191019
"""Determine if the rule has changed since its version was locked."""
10201020
min_stack = Version.parse(self.get_supported_version(), optional_minor_and_patch=True)
10211021
existing_sha256 = self.version_lock.get_locked_hash(self.id, f"{min_stack.major}.{min_stack.minor}")
10221022

1023-
if existing_sha256 is not None:
1024-
return existing_sha256 != self.sha256()
1023+
if not existing_sha256:
1024+
return False
1025+
1026+
rule_hash = self.get_hash()
1027+
rule_hash_with_integrations = self.get_hash(include_integrations=True)
1028+
1029+
# Checking against current and previous version of the hash to avoid mass version bump
1030+
is_dirty = existing_sha256 not in (rule_hash, rule_hash_with_integrations)
1031+
return is_dirty
10251032

10261033
@property
10271034
def lock_entry(self) -> Optional[dict]:
@@ -1123,10 +1130,25 @@ def _post_dict_conversion(self, obj: dict) -> dict:
11231130
def to_api_format(self, include_version: bool = True) -> dict:
11241131
"""Convert the rule to the API format."""
11251132

1133+
def get_hashable_content(self, include_version: bool = False, include_integrations: bool = False) -> dict:
1134+
"""Returns the rule content to be used for calculating the hash value for the rule"""
1135+
1136+
# get the API dict without the version by default, otherwise it'll always be dirty.
1137+
hashable_dict = self.to_api_format(include_version=include_version)
1138+
1139+
# drop related integrations if present
1140+
if not include_integrations:
1141+
hashable_dict.pop("related_integrations", None)
1142+
1143+
return hashable_dict
1144+
11261145
@cached
1127-
def sha256(self, include_version=False) -> str:
1128-
# get the hash of the API dict without the version by default, otherwise it'll always be dirty.
1129-
hashable_contents = self.to_api_format(include_version=include_version)
1146+
def get_hash(self, include_version: bool = False, include_integrations: bool = False) -> str:
1147+
"""Returns a sha256 hash of the rule contents"""
1148+
hashable_contents = self.get_hashable_content(
1149+
include_version=include_version,
1150+
include_integrations=include_integrations,
1151+
)
11301152
return utils.dict_hash(hashable_contents)
11311153

11321154

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "detection_rules"
3-
version = "1.1.2"
3+
version = "1.1.3"
44
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."
55
readme = "README.md"
66
requires-python = ">=3.12"

tests/test_packages.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def get_rule_contents():
4343
version_info = {
4444
rule.id: {
4545
'rule_name': rule.name,
46-
'sha256': rule.contents.sha256(),
46+
'sha256': rule.contents.get_hash(),
4747
'version': version
4848
} for rule in rules
4949
}
@@ -76,7 +76,7 @@ def test_rule_versioning(self):
7676
# test that no rules have versions defined
7777
for rule in rules:
7878
self.assertGreaterEqual(rule.contents.autobumped_version, 1, '{} - {}: version is not being set in package')
79-
original_hashes.append(rule.contents.sha256())
79+
original_hashes.append(rule.contents.get_hash())
8080

8181
package = Package(rules, 'test-package')
8282

@@ -87,7 +87,7 @@ def test_rule_versioning(self):
8787

8888
# test that rules validate with version
8989
for rule in package.rules:
90-
post_bump_hashes.append(rule.contents.sha256())
90+
post_bump_hashes.append(rule.contents.get_hash())
9191

9292
# test that no hashes changed as a result of the version bumps
9393
self.assertListEqual(original_hashes, post_bump_hashes, 'Version bumping modified the hash of a rule')

0 commit comments

Comments
 (0)