-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpostprocess.go
More file actions
103 lines (85 loc) · 2.6 KB
/
Copy pathpostprocess.go
File metadata and controls
103 lines (85 loc) · 2.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package sigma
import "strings"
// groupORConditions merges consecutive OR conditions on the same field and operator
// into a single Condition with Alternatives. Ported from spl-parser.
func groupORConditions(conditions []Condition) []Condition {
if len(conditions) == 0 {
return conditions
}
result := make([]Condition, 0, len(conditions))
for i := 0; i < len(conditions); i++ {
cond := conditions[i]
// Look ahead for OR conditions on the same field
if i+1 < len(conditions) && conditions[i+1].LogicalOp == "OR" && sameConditionGroup(cond, conditions[i+1]) {
alternatives := conditionAlternatives(cond)
j := i + 1
for j < len(conditions) {
next := conditions[j]
if next.LogicalOp == "OR" && sameConditionGroup(cond, next) {
alternatives = append(alternatives, conditionAlternatives(next)...)
j++
} else {
break
}
}
if len(alternatives) > 1 {
cond.Alternatives = deduplicateStrings(alternatives)
result = append(result, cond)
i = j - 1
continue
}
}
result = append(result, cond)
}
return result
}
// comparisonOperators are ordering operators for which OR-runs must NOT be
// folded into Alternatives: `f > a OR f > b` is not a membership test over
// {a, b}, so merging would silently drop a bound (and, because the merge is
// position-dependent, make extraction non-deterministic).
var comparisonOperators = map[string]bool{">": true, ">=": true, "<": true, "<=": true}
func sameConditionGroup(a, b Condition) bool {
if comparisonOperators[a.Operator] {
return false
}
return strings.EqualFold(a.Field, b.Field) &&
a.Operator == b.Operator &&
a.Negated == b.Negated &&
a.CaseSensitive == b.CaseSensitive
}
func conditionAlternatives(cond Condition) []string {
if len(cond.Alternatives) > 0 {
return append([]string(nil), cond.Alternatives...)
}
return []string{cond.Value}
}
// deduplicateConditions removes duplicate conditions by field+operator+value.
func deduplicateConditions(conditions []Condition) []Condition {
if len(conditions) == 0 {
return conditions
}
seen := make(map[string]bool)
result := make([]Condition, 0, len(conditions))
for _, cond := range conditions {
key := conditionDedupKey(cond)
if !seen[key] {
seen[key] = true
result = append(result, cond)
}
}
return result
}
func conditionDedupKey(cond Condition) string {
return strings.ToLower(cond.Field) + "|" +
cond.Operator + "|" +
cond.Value + "|" +
boolKey(cond.Negated) + "|" +
boolKey(cond.CaseSensitive) + "|" +
strings.Join(cond.Alternatives, "\x00")
}
func boolKey(value bool) string {
if value {
return "1"
}
return "0"
}