Repository navigation
[high] Parenthesise AND groups under OR (Splunk search evaluates OR before AND) - #79
Merged
Merged
Conversation
The Splunk search command evaluates OR before AND (parentheses, NOT, OR, AND), unlike eval/where. Group every AND nested in an OR (rule and extended correlation conditions) so the emitted query is correct under either precedence reading, keeping the OR-under-AND grouping from SigmaHQ#77. Only hoist leading terms in front of the rex/eval pipeline when the search expression has no top-level OR. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The semantic fixes are narrowly scoped and backed by comprehensive new/updated tests, with only a minor test robustness improvement suggested.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
This PR fixes incorrect boolean grouping in the classic Splunk SPL backend where Splunk search evaluates OR before implicit AND, which can change rule semantics when an AND-group is emitted directly under an OR. It also tightens the “hoisting” optimization so leading terms aren’t moved ahead of deferred rex/eval pipelines when the remaining search expression contains a top-level disjunction.
Changes:
- Override precedence comparison to always parenthesize
ANDwhen nested underOR(including in extended correlation conditions rendered via| search). - Add
_has_top_level_or()and use it to prevent hoisting terms out of disjunction branches infinalize_query_default. - Update and add regression tests covering AND/OR nesting, hoisting behavior, and
_has_top_level_orparsing cases.
| File | Description |
|---|---|
sigma/backends/splunk/splunk.py |
Adds targeted grouping override and a top-level OR detector to keep emitted SPL semantically correct under Splunk search precedence and to prevent unsafe hoisting across pipelines. |
tests/test_backend_splunk.py |
Updates expectations and adds tests for AND-in-OR grouping, mixed nesting, hoisting guard behavior, and _has_top_level_or. |
tests/test_backend_splunk_correlations.py |
Adds a regression test ensuring extended temporal correlation conditions also group AND nested under OR in the trailing ` |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
thomaspatzke
self-requested a review
September 27, 2026 08:55
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
thomaspatzke
approved these changes
Sep 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

BLUF
searchcommand evaluates OR before AND, so an AND group placed under an OR without parentheses changes the query's meaning. Since fix: OR/AND precedence inversion widens emitted queries in SPL and SPL2 backends #77,1 of selection_*with multi-field selections is emitted asA B OR C D, which Splunk reads asA AND (B OR C) AND D.mainevery one of them is emitted ungrouped. Example:azure_mfa_interruptednow misses both of its intended detections.rex/evalpipeline only when the search expression has no top-level OR. This stops(f=1 and x|re) or h=3from turning intof=1 AND (x OR h=3). It affected 3 SigmaHQ rules; after the fix, none.Priority: high
Details
Splunk's documented evaluation order
From the Splunk Search Reference,
searchcommand, Boolean expressions:Relation to #73 / #77
Thanks to @cristianchiriac for #73/#77. They were right that an OR nested inside an AND must be parenthesised, and that part stays as it is. #77 did this by changing the precedence tuple to
(NOT, AND, OR). That tuple also tells pySigma that an AND nested inside an OR needs no parentheses, and under the documentedsearchorder it does. So #77 fixed one direction and broke the other:test_splunk_or_and_expressionwas changed from(fieldA="valueA1" fieldB="valueB1") OR (…)tofieldA="valueA1" fieldB="valueB1" OR ….This PR keeps #77's tuple and overrides
compare_precedenceso that an AND under an OR is always grouped. Every mixed AND/OR nesting is now parenthesised, which is correct whichever order Splunk actually uses. Queries with a single operator (such asa b cora OR b OR c), and NOT, are unchanged.I also considered
parenthesize = True. It is equally correct, but it wraps every NOT and changes 42 existing test expectations. The targeted override adds parentheses only where the two readings disagree.The extended correlation condition (
temporalwithand/or) is also rendered into a| search, soCorrelationConditionANDunderCorrelationConditionORis grouped as well. SPL2 (spl2.py) emits explicitAND/ORtokens and is not touched.Example: SigmaHQ
azure_mfa_interrupted.ymlBefore (
main):Under the documented order this is
ResultType=50074 AND (ResultDescription="*Strong Auth required*" OR ResultType=500121) AND ResultDescription="*Authentication failed during strong authentication request*". A 50074 event now also needs the 500121 description text, and a 500121 event can never match. Neither intended detection fires.After:
Corpus impact
I converted the SigmaHQ
masterrules (3140 rules, no pipeline) with the real backend:mainall of them are emitted without grouping.proc_creation_lnx_file_and_directory_discoveryand two macOS rules). After the fix there are 0. The conversion error count is unchanged.Hoisting in
finalize_query_defaultThe previous guard only checked whether the remaining query starts with
OR. For(f=1 and x|re) or h=3the output onmainis:Here
f=1became a conjunct of the whole disjunction, and that is wrong under either precedence reading. The new_has_top_level_orhelper scans the search expression for anORoutside quotes and parentheses, and skips hoisting when it finds one. Hoisting ofindex=/source=conditions on top-level conjunctions is unchanged;test_splunk_regex_query_explicit_or_with_add_conditionstill passes.Changed test expectations
test_splunk_or_and_expression: back to its pre-fix: OR/AND precedence inversion widens emitted queries in SPL and SPL2 backends #77 grouped form.test_splunk_regex_query_explicit_or_with_add_condition:(EventID=4688 CommandLineCondition="true" OR ImageCondition="true")becomes((EventID=4688 CommandLineCondition="true") OR ImageCondition="true"). Under the documented order, the old form requiredEventID=4688for theImagebranch too.Testing
azure_mfa_interruptedshape(a and (b or c)) or d_has_top_level_orcases, including quotedORand escaped quotes(r1 and r2) or r3pytest151 passed, with Python 3.12 and pySigma 1.2.0 frompoetry.lock, run the same way as CI (poetry install+pytest).black==24.1.1(pre-commit pin) reports nothing on the changed code.🤖 Generated with Claude Code