Skip to content

Commit 0c38662

Browse files
[FR] [DAC] Add Support for Known Types to Auto-generated Schemas (#3985)
* Add support for autogen known type * Add support for ML packages * rename known_type to field_type
1 parent f7b7a04 commit 0c38662

File tree

3 files changed

+34
-8
lines changed

3 files changed

+34
-8
lines changed

detection_rules/custom_schemas.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,9 @@ def resolve_schema_path(path: str) -> Path:
4747
return path_obj if path_obj.is_absolute() else RULES_CONFIG.stack_schema_map_file.parent.joinpath(path)
4848

4949

50-
def update_data(index: str, field: str, data: dict) -> dict:
50+
def update_data(index: str, field: str, data: dict, field_type: str = None) -> dict:
5151
"""Update the schema entry with the appropriate index and field."""
52-
if index not in data:
53-
data[index] = {}
54-
data[index][field] = "keyword"
52+
data.setdefault(index, {})[field] = field_type if field_type else "keyword"
5553
return data
5654

5755

@@ -82,14 +80,14 @@ def clean_stack_schema_map(stack_schema_map: dict, auto_generated_id: str, rando
8280
return stack_schema_map
8381

8482

85-
def update_auto_generated_schema(index: str, field: str):
83+
def update_auto_generated_schema(index: str, field: str, field_type: str = None):
8684
"""Load custom schemas if present."""
8785
auto_gen_schema_file = str(RULES_CONFIG.auto_gen_schema_file)
8886
stack_schema_map_file = str(RULES_CONFIG.stack_schema_map_file)
8987

9088
# Update autogen schema file
9189
data = load_dump(auto_gen_schema_file)
92-
data = update_data(index, field, data)
90+
data = update_data(index, field, data, field_type)
9391
save_dump(data, auto_gen_schema_file)
9492

9593
# Update the stack-schema-map.yaml file with the appropriate auto_gen_schema_file location

detection_rules/ecs.py

+26
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from .config import CUSTOM_RULES_DIR, parse_rules_config
2020
from .custom_schemas import get_custom_schemas
21+
from .integrations import load_integrations_schemas
2122
from .utils import (DateTimeEncoder, cached, get_etc_path, gzip_compress,
2223
load_etc_dump, read_gzip, unzip)
2324

@@ -150,6 +151,31 @@ def flatten(schema):
150151
return flattened
151152

152153

154+
@cached
155+
def get_all_flattened_schema() -> dict:
156+
"""Load all schemas into a flattened dictionary."""
157+
all_flattened_schema = {}
158+
for _, schema in get_non_ecs_schema().items():
159+
all_flattened_schema.update(flatten(schema))
160+
161+
ecs_schemas = get_schemas()
162+
for version in ecs_schemas:
163+
for index, info in ecs_schemas[version]["ecs_flat"].items():
164+
all_flattened_schema.update({index: info["type"]})
165+
166+
for _, integration_schema in load_integrations_schemas().items():
167+
for index, index_schema in integration_schema.items():
168+
# Detect if ML integration
169+
if "jobs" in index_schema:
170+
ml_schemas = {k: v for k, v in index_schema.items() if k != "jobs"}
171+
for _, ml_schema in ml_schemas.items():
172+
all_flattened_schema.update(flatten(ml_schema))
173+
else:
174+
all_flattened_schema.update(flatten(index_schema))
175+
176+
return all_flattened_schema
177+
178+
153179
@cached
154180
def get_non_ecs_schema():
155181
"""Load non-ecs schema."""

detection_rules/rule_validators.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ def unique_fields(self) -> List[str]:
119119
def auto_add_field(self, validation_checks_error: kql.errors.KqlParseError, index_or_dataview: str) -> None:
120120
"""Auto add a missing field to the schema."""
121121
field_name = extract_error_field(self.query, validation_checks_error)
122-
update_auto_generated_schema(index_or_dataview, field_name)
122+
field_type = ecs.get_all_flattened_schema().get(field_name)
123+
update_auto_generated_schema(index_or_dataview, field_name, field_type)
123124

124125
def to_eql(self) -> eql.ast.Expression:
125126
return kql.to_eql(self.query)
@@ -328,7 +329,8 @@ def unique_fields(self) -> List[str]:
328329
def auto_add_field(self, validation_checks_error: eql.errors.EqlParseError, index_or_dataview: str) -> None:
329330
"""Auto add a missing field to the schema."""
330331
field_name = extract_error_field(self.query, validation_checks_error)
331-
update_auto_generated_schema(index_or_dataview, field_name)
332+
field_type = ecs.get_all_flattened_schema().get(field_name)
333+
update_auto_generated_schema(index_or_dataview, field_name, field_type)
332334

333335
def validate(self, data: "QueryRuleData", meta: RuleMeta, max_attempts: int = 10) -> None:
334336
"""Validate an EQL query while checking TOMLRule."""

0 commit comments

Comments
 (0)