10
10
import eql
11
11
12
12
import kql
13
- from detection_rules import beats , ecs
14
- from detection_rules .rule import QueryValidator , QueryRuleData , RuleMeta
13
+ from . import ecs , beats
14
+ from .rule import QueryValidator , QueryRuleData , RuleMeta
15
15
16
16
17
17
class KQLValidator (QueryValidator ):
@@ -36,35 +36,34 @@ def validate(self, data: QueryRuleData, meta: RuleMeta) -> None:
36
36
# syntax only, which is done via self.ast
37
37
return
38
38
39
- indexes = data .index or []
40
- beats_version = meta .beats_version or beats .get_max_version ()
41
- ecs_versions = meta .ecs_versions or [ecs .get_max_version ()]
39
+ for stack_version , mapping in meta .get_validation_stack_versions ().items ():
40
+ beats_version = mapping ['beats' ]
41
+ ecs_version = mapping ['ecs' ]
42
+ err_trailer = f'stack: { stack_version } , beats: { beats_version } , ecs: { ecs_version } '
42
43
43
- beat_types = [index .split ("-" )[0 ] for index in indexes if "beat-*" in index ]
44
- beat_schema = beats .get_schema_from_kql (ast , beat_types , version = beats_version ) if beat_types else None
44
+ beat_types = beats .parse_beats_from_index (data .index )
45
+ beat_schema = beats .get_schema_from_kql (ast , beat_types , version = beats_version ) if beat_types else None
46
+ schema = ecs .get_kql_schema (version = ecs_version , indexes = data .index or [], beat_schema = beat_schema )
45
47
46
- if not ecs_versions :
47
- kql .parse (self .query , schema = ecs .get_kql_schema (indexes = indexes , beat_schema = beat_schema ))
48
- else :
49
- for version in ecs_versions :
50
- schema = ecs .get_kql_schema (version = version , indexes = indexes , beat_schema = beat_schema )
51
-
52
- try :
53
- kql .parse (self .query , schema = schema )
54
- except kql .KqlParseError as exc :
55
- message = exc .error_msg
56
- trailer = None
57
- if "Unknown field" in message and beat_types :
58
- trailer = "\n Try adding event.module or event.dataset to specify beats module"
48
+ try :
49
+ kql .parse (self .query , schema = schema )
50
+ except kql .KqlParseError as exc :
51
+ message = exc .error_msg
52
+ trailer = err_trailer
53
+ if "Unknown field" in message and beat_types :
54
+ trailer = f"\n Try adding event.module or event.dataset to specify beats module\n \n { trailer } "
59
55
60
- raise kql .KqlParseError (exc .error_msg , exc .line , exc .column , exc .source ,
61
- len (exc .caret .lstrip ()), trailer = trailer ) from None
56
+ raise kql .KqlParseError (exc .error_msg , exc .line , exc .column , exc .source ,
57
+ len (exc .caret .lstrip ()), trailer = trailer ) from None
58
+ except Exception :
59
+ print (err_trailer )
60
+ raise
62
61
63
62
64
63
class EQLValidator (QueryValidator ):
65
64
66
65
@cached_property
67
- def ast (self ) -> kql .ast .Expression :
66
+ def ast (self ) -> eql .ast .Expression :
68
67
with eql .parser .elasticsearch_syntax , eql .parser .ignore_missing_functions :
69
68
return eql .parse_query (self .query )
70
69
@@ -74,41 +73,34 @@ def unique_fields(self) -> List[str]:
74
73
75
74
def validate (self , data : 'QueryRuleData' , meta : RuleMeta ) -> None :
76
75
"""Validate an EQL query while checking TOMLRule."""
77
- _ = self .ast
76
+ ast = self .ast
78
77
79
78
if meta .query_schema_validation is False or meta .maturity == "deprecated" :
80
79
# syntax only, which is done via self.ast
81
80
return
82
81
83
- indexes = data .index or []
84
- beats_version = meta .beats_version or beats .get_max_version ()
85
- ecs_versions = meta .ecs_versions or [ecs .get_max_version ()]
82
+ for stack_version , mapping in meta .get_validation_stack_versions ().items ():
83
+ beats_version = mapping ['beats' ]
84
+ ecs_version = mapping ['ecs' ]
85
+ err_trailer = f'stack: { stack_version } , beats: { beats_version } , ecs: { ecs_version } '
86
86
87
- # TODO: remove once py-eql supports ipv6 for cidrmatch
88
- # Or, unregister the cidrMatch function and replace it with one that doesn't validate against strict IPv4
89
- with eql .parser .elasticsearch_syntax , eql .parser .ignore_missing_functions :
90
- parsed = eql .parse_query (self .query )
91
-
92
- beat_types = [index .split ("-" )[0 ] for index in indexes if "beat-*" in index ]
93
- beat_schema = beats .get_schema_from_eql (parsed , beat_types , version = beats_version ) if beat_types else None
94
-
95
- for version in ecs_versions :
96
- schema = ecs .get_kql_schema (indexes = indexes , beat_schema = beat_schema , version = version )
87
+ beat_types = beats .parse_beats_from_index (data .index )
88
+ beat_schema = beats .get_schema_from_kql (ast , beat_types , version = beats_version ) if beat_types else None
89
+ schema = ecs .get_kql_schema (version = ecs_version , indexes = data .index or [], beat_schema = beat_schema )
97
90
eql_schema = ecs .KqlSchema2Eql (schema )
98
91
99
92
try :
100
93
# TODO: switch to custom cidrmatch that allows ipv6
101
94
with eql_schema , eql .parser .elasticsearch_syntax , eql .parser .ignore_missing_functions :
102
95
eql .parse_query (self .query )
103
-
104
- except eql .EqlTypeMismatchError :
105
- raise
106
-
107
96
except eql .EqlParseError as exc :
108
97
message = exc .error_msg
109
- trailer = None
98
+ trailer = err_trailer
110
99
if "Unknown field" in message and beat_types :
111
- trailer = "\n Try adding event.module or event.dataset to specify beats module"
100
+ trailer = f "\n Try adding event.module or event.dataset to specify beats module\n \n { trailer } "
112
101
113
102
raise exc .__class__ (exc .error_msg , exc .line , exc .column , exc .source ,
114
103
len (exc .caret .lstrip ()), trailer = trailer ) from None
104
+ except Exception :
105
+ print (err_trailer )
106
+ raise
0 commit comments