diff --git a/core/lib/rules.py b/core/lib/rules.py index 16bee98..7ad0300 100644 --- a/core/lib/rules.py +++ b/core/lib/rules.py @@ -10,6 +10,7 @@ except ImportError: import orjson from asgiref.sync import async_to_sync +from siphashc import siphash from core.lib.notify import sendmsg from core.lib.parsing import parse_index, parse_source @@ -205,8 +206,13 @@ class NotificationRuleData(object): Store a match result. Accepts None for the index to set all indices. :param index: the index to store the match for, can be None - :param match: True or False, indicating if the rule matched + :param match: the object that matched """ + if match is not False: + # Dump match to JSON while sorting the keys + match_normalised = orjson.dumps(match, option=orjson.OPT_SORT_KEYS) + match = siphash(self.db.hash_key, match_normalised) + if self.object.match is None: self.object.match = {} if not isinstance(self.object.match, dict): @@ -220,7 +226,7 @@ class NotificationRuleData(object): self.object.save() log.debug(f"Stored match: {index} - {match}") - def get_match(self, index=None): + def get_match(self, index=None, match=None): """ Get a match result for an index. If the index is None, it will return True if any index has a match. @@ -237,6 +243,13 @@ class NotificationRuleData(object): # Check if we have any matches on all indices return any(self.object.match.values()) + # Check if it's the same hash + if match is not None: + match_normalised = orjson.dumps(match, option=orjson.OPT_SORT_KEYS) + match = siphash(self.db.hash_key, match_normalised) + hash_matches = self.object.match.get(index) == match + return hash_matches + return self.object.match.get(index) def format_aggs(self, aggs): @@ -268,13 +281,13 @@ class NotificationRuleData(object): :param message: the message object that matched :param aggs: the aggregations that matched """ - current_match = self.get_match(index) + current_match = self.get_match(index, message) log.debug(f"Rule matched: {index} - current match: {current_match}") if current_match is False: # Matched now, but not before meta["matched"] = self.format_aggs(meta["aggs"]) rule_notify(self.object, index, message, meta) - self.store_match(index, True) + self.store_match(index, message) def rule_no_match(self, index=None): """ diff --git a/core/models.py b/core/models.py index 4bca3f0..a878437 100644 --- a/core/models.py +++ b/core/models.py @@ -203,7 +203,8 @@ class NotificationRule(models.Model): Get the total number of matches for this rule. """ if isinstance(self.match, dict): - return f"{sum(list(self.match.values()))}/{len(self.match)}" + truthy_values = [x for x in self.match.values() if x is not False] + return f"{len(truthy_values)}/{len(self.match)}" def get_notification_settings(self, check=True): """