Skip to content

Commit 289bbef

Browse files
Move functions out of utils for clarity
1 parent 0fa8c0f commit 289bbef

File tree

2 files changed

+52
-52
lines changed

2 files changed

+52
-52
lines changed

detection_rules/index_mappings.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,55 @@
2828
from .utils import combine_dicts
2929

3030

31+
def delete_nested_key_from_dict(d: dict[str, Any], compound_key: str) -> None:
32+
"""Delete a nested key from a dictionary."""
33+
keys = compound_key.split(".")
34+
for key in keys[:-1]:
35+
if key in d and isinstance(d[key], dict):
36+
d = d[key] # type: ignore[reportUnknownVariableType]
37+
else:
38+
return
39+
d.pop(keys[-1], None)
40+
41+
42+
def flat_schema_to_index_mapping(flat_schema: dict[str, str]) -> dict[str, Any]:
43+
"""
44+
Convert dicts with flat JSON paths and values into a nested mapping with
45+
intermediary `properties`, `fields` and `type` fields.
46+
"""
47+
48+
# Sorting here ensures that 'a.b' processed before 'a.b.c', allowing us to correctly
49+
# detect and handle multi-fields.
50+
sorted_items = sorted(flat_schema.items())
51+
result = {}
52+
53+
for field_path, field_type in sorted_items:
54+
parts = field_path.split(".")
55+
current_level = result
56+
57+
for part in parts[:-1]:
58+
node = current_level.setdefault(part, {}) # type: ignore[reportUnknownVariableType]
59+
60+
if "type" in node and node["type"] not in ("nested", "object"):
61+
current_level = node.setdefault("fields", {}) # type: ignore[reportUnknownVariableType]
62+
else:
63+
current_level = node.setdefault("properties", {}) # type: ignore[reportUnknownVariableType]
64+
65+
leaf_key = parts[-1]
66+
current_level[leaf_key] = {"type": field_type}
67+
68+
# add `scaling_factor` field missing in the schema
69+
# https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/number#scaled-float-params
70+
if field_type == "scaled_float":
71+
current_level[leaf_key]["scaling_factor"] = 1000
72+
73+
# add `path` field for `alias` fields, set to a dummy value
74+
if field_type == "alias":
75+
current_level[leaf_key]["path"] = "@timestamp"
76+
77+
return result # type: ignore[reportUnknownVariableType]
78+
79+
3180
def get_rule_integrations(metadata: RuleMeta) -> list[str]:
3281
"""Retrieve rule integrations from metadata."""
3382
if metadata.integration:
@@ -139,23 +188,23 @@ def prepare_integration_mappings( # noqa: PLR0913
139188

140189
for stream in package_schema:
141190
flat_schema = package_schema[stream]
142-
stream_mappings = utils.flat_schema_to_index_mapping(flat_schema)
191+
stream_mappings = flat_schema_to_index_mapping(flat_schema)
143192
nested_multifields = find_nested_multifields(stream_mappings)
144193
for field in nested_multifields:
145194
field_name = str(field).split(".fields.")[0].replace(".", ".properties.") + ".fields"
146195
log(
147196
f"Warning: Nested multi-field `{field}` found in `{integration}-{stream}`. "
148197
f"Removing parent field from schema for ES|QL validation."
149198
)
150-
utils.delete_nested_key_from_dict(stream_mappings, field_name)
199+
delete_nested_key_from_dict(stream_mappings, field_name)
151200
nested_flattened_fields = find_flattened_fields_with_subfields(stream_mappings)
152201
for field in nested_flattened_fields:
153202
field_name = str(field).split(".fields.")[0].replace(".", ".properties.") + ".fields"
154203
log(
155204
f"Warning: flattened field `{field}` found in `{integration}-{stream}` with sub fields. "
156205
f"Removing parent field from schema for ES|QL validation."
157206
)
158-
utils.delete_nested_key_from_dict(stream_mappings, field_name)
207+
delete_nested_key_from_dict(stream_mappings, field_name)
159208
utils.combine_dicts(integration_mappings, stream_mappings)
160209
index_lookup[f"{integration}-{stream}"] = stream_mappings
161210

detection_rules/utils.py

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -556,44 +556,6 @@ def combine_dicts(dest: dict[Any, Any], src: dict[Any, Any]) -> None:
556556
dest[k] = v
557557

558558

559-
def flat_schema_to_index_mapping(flat_schema: dict[str, str]) -> dict[str, Any]:
560-
"""
561-
Convert dicts with flat JSON paths and values into a nested mapping with
562-
intermediary `properties`, `fields` and `type` fields.
563-
"""
564-
565-
# Sorting here ensures that 'a.b' processed before 'a.b.c', allowing us to correctly
566-
# detect and handle multi-fields.
567-
sorted_items = sorted(flat_schema.items())
568-
result = {}
569-
570-
for field_path, field_type in sorted_items:
571-
parts = field_path.split(".")
572-
current_level = result
573-
574-
for part in parts[:-1]:
575-
node = current_level.setdefault(part, {}) # type: ignore[reportUnknownVariableType]
576-
577-
if "type" in node and node["type"] not in ("nested", "object"):
578-
current_level = node.setdefault("fields", {}) # type: ignore[reportUnknownVariableType]
579-
else:
580-
current_level = node.setdefault("properties", {}) # type: ignore[reportUnknownVariableType]
581-
582-
leaf_key = parts[-1]
583-
current_level[leaf_key] = {"type": field_type}
584-
585-
# add `scaling_factor` field missing in the schema
586-
# https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/number#scaled-float-params
587-
if field_type == "scaled_float":
588-
current_level[leaf_key]["scaling_factor"] = 1000
589-
590-
# add `path` field for `alias` fields, set to a dummy value
591-
if field_type == "alias":
592-
current_level[leaf_key]["path"] = "@timestamp"
593-
594-
return result # type: ignore[reportUnknownVariableType]
595-
596-
597559
def get_column_from_index_mapping_schema(keys: list[str], current_schema: dict[str, Any] | None) -> str | None:
598560
"""Recursively traverse the schema to find the type of the column."""
599561
key = keys[0]
@@ -604,14 +566,3 @@ def get_column_from_index_mapping_schema(keys: list[str], current_schema: dict[s
604566
if not column_type and len(keys) > 1:
605567
return get_column_from_index_mapping_schema(keys[1:], current_schema=column.get("properties")) # type: ignore[reportUnknownVariableType]
606568
return column_type # type: ignore[reportUnknownVariableType]
607-
608-
609-
def delete_nested_key_from_dict(d: dict[str, Any], compound_key: str) -> None:
610-
"""Delete a nested key from a dictionary."""
611-
keys = compound_key.split(".")
612-
for key in keys[:-1]:
613-
if key in d and isinstance(d[key], dict):
614-
d = d[key] # type: ignore[reportUnknownVariableType]
615-
else:
616-
return
617-
d.pop(keys[-1], None)

0 commit comments

Comments
 (0)