@@ -101,6 +101,17 @@ def get_field_match(cls, field):
101101 def get_field_condition (cls , field ):
102102 return cls .construct_field_variable (field , "Condition" )
103103
104+ @classmethod
105+ def get_all_condition_fields (cls ):
106+ """Return the set of all condition field names created by deferred OR regex expressions."""
107+ result = set ()
108+ for field , count in cls .field_counts .items ():
109+ cleaned = cls .clean_field (field )
110+ for i in range (1 , count + 1 ):
111+ suffix = "" if i == 1 else str (i )
112+ result .add (f"{ cleaned } Condition{ suffix } " )
113+ return result
114+
104115 @classmethod
105116 def reset (cls ):
106117 cls .field_counts = {}
@@ -188,6 +199,11 @@ class SplunkBackend(TextQueryBackend):
188199 deferred_separator : ClassVar [str ] = "\n | "
189200 deferred_only_query : ClassVar [str ] = "*"
190201
202+ # Pattern matching a leading field=value term in a query string.
203+ _field_eq_val_re : ClassVar [Pattern ] = re .compile (
204+ r'([\w.]+)(?:="[^"]*"|=[^\s")]+)\s*'
205+ )
206+
191207 # Correlations
192208 correlation_methods : ClassVar [Dict [str , str ]] = {
193209 "stats" : "Correlation using stats command (more efficient, static time window)" ,
@@ -330,6 +346,12 @@ def finish_query(
330346 remaining_deferred .append (deferred_expression )
331347
332348 if deferred_regex_or_expressions :
349+ # Collect all condition field names created by deferred OR
350+ # regex expressions before resetting, so finalize methods
351+ # can identify which query parts don't depend on them.
352+ state .processing_state ["deferred_or_condition_fields" ] = (
353+ SplunkDeferredORRegularExpression .get_all_condition_fields ()
354+ )
333355 SplunkDeferredORRegularExpression .reset ()
334356 state .deferred [:] = remaining_deferred
335357 query = (
@@ -348,6 +370,40 @@ def finalize_query_default(
348370 index : int ,
349371 state : ConversionState ,
350372 ) -> str :
373+ # When OR-ed regex expressions are deferred, extract leading field=value
374+ # conditions that don't depend on any deferred eval field and place them
375+ # before the deferred rex/eval pipeline commands. This ensures conditions
376+ # like index/source are at the beginning of the query for efficient
377+ # initial data retrieval.
378+ deferred_condition_fields = state .processing_state .get (
379+ "deferred_or_condition_fields"
380+ )
381+ search_marker = "\n | search "
382+ if deferred_condition_fields and search_marker in query :
383+ marker_idx = query .index (search_marker )
384+ deferred_part = query [:marker_idx ]
385+ search_query = query [marker_idx + len (search_marker ) :]
386+
387+ prefix_parts = []
388+ pos = 0
389+ while pos < len (search_query ):
390+ m = self ._field_eq_val_re .match (search_query , pos )
391+ if m and m .group (1 ) not in deferred_condition_fields :
392+ prefix_parts .append (m .group ().strip ())
393+ pos = m .end ()
394+ else :
395+ break
396+
397+ if prefix_parts :
398+ prefix = " " .join (prefix_parts )
399+ remaining_query = search_query [pos :]
400+ query = (
401+ prefix
402+ + deferred_part
403+ + search_marker
404+ + remaining_query
405+ )
406+
351407 if isinstance (rule , SigmaRule ) and rule .fields :
352408 return query + " | table " + "," .join (rule .fields )
353409 return query
0 commit comments