@@ -935,8 +935,15 @@ def validates_esql_data(self, data: dict[str, Any], **_: Any) -> None:
935
935
query_lower = data ["query" ].lower ()
936
936
937
937
# Combine both patterns using an OR operator and compile the regex
938
+ # The first part matches the metadata fields in the from clause by allowing one or
939
+ # multiple indices and any order of the metadata fields
940
+ # The second part matches the stats command with the by clause
938
941
combined_pattern = re .compile (
939
- r"(from\s+\S+\s+metadata\s+_id,\s*_version,\s*_index)|(\bstats\b.*?\bby\b)" , re .DOTALL
942
+ r"(from\s+(?:\S+\s*,\s*)*\S+\s+metadata\s+"
943
+ r"(_id,\s*_version,\s*_index|_id,\s*_index,\s*_version|_version,\s*_id,\s*_index|"
944
+ r"_version,\s*_index,\s*_id|_index,\s*_id,\s*_version|_index,\s*_version,\s*_id))"
945
+ r"|(\bstats\b.*?\bby\b)" ,
946
+ re .DOTALL
940
947
)
941
948
942
949
# Ensure that non-aggregate queries have metadata
@@ -948,9 +955,12 @@ def validates_esql_data(self, data: dict[str, Any], **_: Any) -> None:
948
955
)
949
956
950
957
# Enforce KEEP command for ESQL rules
951
- if "| keep" not in query_lower :
958
+ # Match | followed by optional whitespace/newlines and then 'keep'
959
+ keep_pattern = re .compile (r"\|\s*keep\b" , re .IGNORECASE | re .DOTALL )
960
+ if not keep_pattern .search (query_lower ):
952
961
raise ValidationError (
953
- f"Rule: { data ['name' ]} does not contain a 'keep' command -> Add a 'keep' command to the query."
962
+ f"Rule: { data ['name' ]} does not contain a 'keep' command ->"
963
+ f" Add a 'keep' command to the query."
954
964
)
955
965
956
966
0 commit comments