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
28 changes: 27 additions & 1 deletion api/howler/odm/models/ecs/related.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ class Related(odm.Model):
)

# Extra fields not defined in ECS but added for outline purposes
id = odm.Optional(odm.Keyword(description="The id related to the event."))
id = odm.Optional(
odm.Keyword(
description="The id related to the event.",
deprecated=True,
deprecated_description="related.ids should be used instead of related.id.",
)
)

uri = odm.Optional(odm.List(odm.URI(), description="All of the URIs related to the event."))

Expand All @@ -43,3 +49,23 @@ class Related(odm.Model):
description="All the signatures/rules that were triggered by the event.",
)
)

def __init__(self, data: dict = None, *args, **kwargs):
if data is not None and data.get("id") is not None:
# Avoid mutating the caller-provided dict
data = dict(data)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since you re-construct the only potential list that you modify on line 60, making a shallow copy here works in the current __init__, but it might be safer to use deepcopy so that future changes don't end up modifying the provided dict by accident.


existing_ids = data.get("ids")
if isinstance(existing_ids, (list, tuple)):
merged_ids = list(existing_ids)
elif existing_ids is None:
merged_ids = []
else:
merged_ids = [existing_ids]

if data["id"] not in merged_ids:
merged_ids.append(data["id"])

data["ids"] = merged_ids

super().__init__(data, *args, **kwargs)
Comment thread
Copilot marked this conversation as resolved.
29 changes: 29 additions & 0 deletions api/test/unit/odm/test_related.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from howler.odm.models.ecs.related import Related
from howler.odm.models.hit import Hit


class TestRelated:
def test_related_id_is_copied_to_ids(self):
related = Related({"id": "indicator-1"})

assert related.id == "indicator-1"
assert related.ids == ["indicator-1"]

def test_related_id_merges_into_existing_ids_without_duplicates(self):
related = Related({"id": "indicator-2", "ids": ["indicator-1", "indicator-2"]})

assert related.id == "indicator-2"
assert related.ids == ["indicator-1", "indicator-2"]

def test_related_id_merges_into_existing_ids_tuple(self):
related = Related({"id": "indicator-2", "ids": ("indicator-1", "indicator-2")})

assert related.id == "indicator-2"
assert related.ids == ["indicator-1", "indicator-2"]

def test_parent_odm_related_id_key_is_copied_to_related_ids(self):
hit = Hit({"howler.analytic": "Test Analytic", "howler.hash": "a", "related.id": "indicator-3"})

assert hit.related is not None
assert hit.related.id == "indicator-3"
assert hit.related.ids == ["indicator-3"]
Comment thread
Copilot marked this conversation as resolved.
Loading