Skip to content

Commit f4b5088

Browse files
Allow field to be over written with original field
1 parent 434a0cb commit f4b5088

File tree

1 file changed

+16
-20
lines changed

1 file changed

+16
-20
lines changed

detection_rules/rule_validators.py

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -373,9 +373,13 @@ def text_fields(self, eql_schema: ecs.KqlSchema2Eql | endgame.EndgameSchema) ->
373373
def unique_fields(self) -> list[str]: # type: ignore[reportIncompatibleMethodOverride]
374374
return list({str(f) for f in self.ast if isinstance(f, eql.ast.Field)}) # type: ignore[reportUnknownVariableType]
375375

376-
def auto_add_field(self, validation_checks_error: eql.EqlParseError, index_or_dataview: str) -> None:
376+
def auto_add_field(
377+
self, validation_checks_error: eql.EqlParseError, index_or_dataview: str, field: str | None = None
378+
) -> None:
377379
"""Auto add a missing field to the schema."""
378-
field_name = extract_error_field(self.query, validation_checks_error)
380+
field_name = field
381+
if not field:
382+
field_name = extract_error_field(self.query, validation_checks_error)
379383
if not field_name:
380384
raise ValueError("No field name found")
381385
field_type = ecs.get_all_flattened_schema().get(field_name)
@@ -606,7 +610,7 @@ def validate(self, data: "QueryRuleData", meta: RuleMeta, max_attempts: int = 10
606610
)
607611
first_error: EQL_ERROR_TYPES | ValueError | None = None
608612
for t in ordered_targets:
609-
exc = self.validate_query_text_with_schema(
613+
exc, field = self.validate_query_text_with_schema(
610614
t.query_text,
611615
t.schema,
612616
err_trailer=t.err_trailer,
@@ -629,7 +633,7 @@ def validate(self, data: "QueryRuleData", meta: RuleMeta, max_attempts: int = 10
629633
and RULES_CONFIG.auto_gen_schema_file
630634
and data.index_or_dataview
631635
):
632-
self.auto_add_field(first_error, data.index_or_dataview[0]) # type: ignore[reportArgumentType]
636+
self.auto_add_field(first_error, data.index_or_dataview[0], field=field) # type: ignore[reportArgumentType]
633637
continue
634638

635639
# Raise the enriched parse error (includes target trailer + metadata)
@@ -645,7 +649,7 @@ def validate_query_text_with_schema( # noqa: PLR0913
645649
min_stack_version: str,
646650
beat_types: list[str] | None = None,
647651
integration_types: list[str] | None = None,
648-
) -> EQL_ERROR_TYPES | ValueError | None:
652+
) -> tuple[EQL_ERROR_TYPES | ValueError | None, str | None]:
649653
"""Validate the provided EQL query text against the schema (variant of validate_query_with_schema)."""
650654
try:
651655
config = set_eql_config(min_stack_version)
@@ -663,7 +667,7 @@ def validate_query_text_with_schema( # noqa: PLR0913
663667
and ("Unknown field" in message or "Field not recognized" in message)
664668
and f"?{field}" in self.query
665669
):
666-
return None
670+
return None, field
667671
if "Unknown field" in message and beat_types:
668672
trailer_parts.insert(0, "Try adding event.module or event.dataset to specify beats module")
669673
elif "Field not recognized" in message and isinstance(schema, ecs.KqlSchema2Eql):
@@ -691,10 +695,11 @@ def validate_query_text_with_schema( # noqa: PLR0913
691695
exc.source, # type: ignore[reportUnknownArgumentType]
692696
len(exc.caret.lstrip()),
693697
trailer=trailer,
694-
)
698+
), field
695699
except Exception as exc: # noqa: BLE001
696700
print(err_trailer)
697-
return exc # type: ignore[reportReturnType]
701+
return exc, None # type: ignore[reportReturnType]
702+
return None, None
698703

699704
def validate_rule_type_configurations(self, data: EQLRuleData, meta: RuleMeta) -> tuple[list[str], bool]:
700705
"""Validate EQL rule type configurations (timestamp_field, event_category_override, tiebreaker_field).
@@ -929,20 +934,11 @@ def remote_validate_rule( # noqa: PLR0913
929934
return response
930935

931936

932-
def extract_error_field(source: str, exc: eql.EqlParseError | kql.KqlParseError, max_attempts: int = 10) -> str | None:
937+
def extract_error_field(source: str, exc: eql.EqlParseError | kql.KqlParseError) -> str | None:
933938
"""Extract the field name from an EQL or KQL parse error."""
934-
# If error reported in subquery and exc references exc.source rather than source, adjust source accordingly
935-
if exc.source != source and len(exc.source.splitlines()) > exc.line: # type: ignore[reportUnknownMemberType]
936-
source = exc.source # type: ignore[reportUnknownMemberType]
937-
lines = source.splitlines() # type: ignore[reportUnknownMemberType]
939+
lines = source.splitlines()
938940
mod = -1 if exc.line == len(lines) else 0 # type: ignore[reportUnknownMemberType]
939941
line = lines[exc.line + mod] # type: ignore[reportUnknownMemberType]
940-
start: int = exc.column # type: ignore[reportUnknownMemberType]
941-
# Handle cases where subqueries cause column alignment to be off
942-
for _ in range(max_attempts):
943-
if line[start - 1].isalnum() or line[start - 1] == ".": # type: ignore[reportUnknownMemberType]
944-
start -= 1 # type: ignore[reportUnknownMemberType]
945-
else:
946-
break
942+
start = exc.column # type: ignore[reportUnknownMemberType]
947943
stop = start + len(exc.caret.strip()) # type: ignore[reportUnknownVariableType]
948944
return re.sub(r"^\W+|\W+$", "", line[start:stop]) # type: ignore[reportUnknownArgumentType]

0 commit comments

Comments
 (0)