Skip to content

Commit 0009e58

Browse files
committed
feat(rules_engine): Populate approver predicates
At rule compilation time, the compiler traverses the AST tree and attempts to find event types for which approver predicates can be derived. Most notably, file and registry paths, file extensions, file base names and process executable paths are all candidates for approver predicates.
1 parent 329cbfc commit 0009e58

9 files changed

Lines changed: 508 additions & 33 deletions

File tree

pkg/config/filters.go

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,59 @@ type RulesCompileResult struct {
202202
HasThreadpoolEvents bool
203203
UsedEvents []event.Type
204204
NumberRules int
205+
Approvers Approvers
205206
}
206207

207-
func (r RulesCompileResult) ContainsEvent(Type event.Type) bool {
208-
for _, ktyp := range r.UsedEvents {
209-
if ktyp == Type {
208+
type Approvers struct {
209+
Keys map[string][]string
210+
Paths map[string][]string
211+
Extensions map[string][]string
212+
Bases map[string][]string
213+
Executables map[string][]string
214+
}
215+
216+
func (p *Approvers) AppendKey(op, path string) {
217+
if slices.Contains(p.Keys[op], path) {
218+
return
219+
}
220+
p.Keys[op] = append(p.Keys[op], path)
221+
}
222+
223+
func (p *Approvers) AppendPath(op, path string) {
224+
if slices.Contains(p.Paths[op], path) {
225+
return
226+
}
227+
p.Paths[op] = append(p.Paths[op], path)
228+
}
229+
230+
func (p *Approvers) AppendExtension(op, ext string) {
231+
if slices.Contains(p.Extensions[op], ext) {
232+
return
233+
}
234+
p.Extensions[op] = append(p.Extensions[op], ext)
235+
}
236+
237+
func (p *Approvers) AppendBase(op, base string) {
238+
if slices.Contains(p.Bases[op], base) {
239+
return
240+
}
241+
p.Bases[op] = append(p.Bases[op], base)
242+
}
243+
244+
func (p *Approvers) AppendExecutable(op, exe string) {
245+
if slices.Contains(p.Executables[op], exe) {
246+
return
247+
}
248+
p.Executables[op] = append(p.Executables[op], exe)
249+
}
250+
251+
func (p Approvers) String() string {
252+
return fmt.Sprintf("Keys: %v, Paths: %v, Extensions: %v, Bases: %v, Executables: %v", p.Keys, p.Paths, p.Extensions, p.Bases, p.Executables)
253+
}
254+
255+
func (r RulesCompileResult) ContainsEvent(e event.Type) bool {
256+
for _, typ := range r.UsedEvents {
257+
if typ == e {
210258
return true
211259
}
212260
}
@@ -237,7 +285,8 @@ func (r RulesCompileResult) String() string {
237285
HasAuditAPIEvents: %t
238286
HasDNSEvents: %t
239287
HasThreadpoolEvents: %t
240-
Events: %s`,
288+
Events: %s
289+
Approvers: %s`,
241290
r.HasProcEvents,
242291
r.HasThreadEvents,
243292
r.HasModuleEvents,
@@ -251,6 +300,7 @@ func (r RulesCompileResult) String() string {
251300
r.HasDNSEvents,
252301
r.HasThreadpoolEvents,
253302
strings.Join(events, ", "),
303+
r.Approvers,
254304
)
255305
}
256306

pkg/filter/filter.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ type Filter interface {
6969
GetSequence() *ql.Sequence
7070
// IsSequence determines if this filter is a sequence.
7171
IsSequence() bool
72+
// Expr returns the raw AST expression.
73+
Expr() ql.Expr
7274
}
7375

7476
// Field contains field meta attributes all accessors need to extract the value.
@@ -263,6 +265,10 @@ func (f *filter) EvalWithValuer(e *event.Event, cache *ValuerCache) bool {
263265
return ql.Eval(f.expr, f.mapValuer(e, cache), f.hasFunctions)
264266
}
265267

268+
func (f *filter) Expr() ql.Expr {
269+
return f.expr
270+
}
271+
266272
// evalBoundSequence evaluates the sequence with bound fields
267273
// and returns true if the sequence expression matches or false
268274
// otherwise.

pkg/filter/ql/expr.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (e *ParenExpr) String() string {
4949

5050
// BinaryExpr represents an operation between two expressions.
5151
type BinaryExpr struct {
52-
Op token
52+
Op Token
5353
LHS Expr
5454
RHS Expr
5555
}

pkg/filter/ql/lexer.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func newScanner(r io.Reader) *scanner {
4343
// scan returns the next token and position from the underlying reader.
4444
// Also returns the literal text read for strings, numbers, and duration tokens
4545
// since these token types can have different literal representations.
46-
func (s *scanner) scan() (tok token, pos int, lit string) {
46+
func (s *scanner) scan() (tok Token, pos int, lit string) {
4747
// Read next code point.
4848
ch0, pos := s.r.read()
4949

@@ -124,7 +124,7 @@ func (s *scanner) scan() (tok token, pos int, lit string) {
124124
}
125125

126126
// scanWhitespace consumes the current rune and all contiguous whitespace.
127-
func (s *scanner) scanWhitespace() (tok token, pos int, lit string) {
127+
func (s *scanner) scanWhitespace() (tok Token, pos int, lit string) {
128128
// Create a buffer and read the current character into it.
129129
var buf bytes.Buffer
130130
ch, pos := s.r.curr()
@@ -147,7 +147,7 @@ func (s *scanner) scanWhitespace() (tok token, pos int, lit string) {
147147
return WS, pos, buf.String()
148148
}
149149

150-
func (s *scanner) scanIdent() (tok token, pos int, lit string) {
150+
func (s *scanner) scanIdent() (tok Token, pos int, lit string) {
151151
// Save the starting position of the identifier.
152152
_, pos = s.r.read()
153153
s.r.unread()
@@ -180,7 +180,7 @@ func (s *scanner) scanIdent() (tok token, pos int, lit string) {
180180
}
181181

182182
// scanNumber consumes anything that looks like the start of a number.
183-
func (s *scanner) scanNumber() (tok token, pos int, lit string) {
183+
func (s *scanner) scanNumber() (tok Token, pos int, lit string) {
184184
var buf bytes.Buffer
185185

186186
// Check if the initial rune is a ".".
@@ -322,7 +322,7 @@ func scanBareIdent(r io.RuneScanner) string {
322322

323323
// scanString consumes a contiguous string of non-quote characters.
324324
// Quote characters can be consumed if they're first escaped with a backslash.
325-
func (s *scanner) scanString() (tok token, pos int, lit string) {
325+
func (s *scanner) scanString() (tok Token, pos int, lit string) {
326326
s.r.unread()
327327
_, pos = s.r.curr()
328328

@@ -384,7 +384,7 @@ type bufScanner struct {
384384
i int // buffer index
385385
n int // buffer size
386386
buf [3]struct {
387-
tok token
387+
tok Token
388388
pos int
389389
lit string
390390
}
@@ -396,12 +396,12 @@ func newBufScanner(r io.Reader) *bufScanner {
396396
}
397397

398398
// scan reads the next token from the scanner.
399-
func (s *bufScanner) scan() (tok token, pos int, lit string) {
399+
func (s *bufScanner) scan() (tok Token, pos int, lit string) {
400400
return s.scanFunc(s.s.scan)
401401
}
402402

403403
// scanFunc uses the provided function to scan the next token.
404-
func (s *bufScanner) scanFunc(scan func() (token, int, string)) (tok token, pos int, lit string) {
404+
func (s *bufScanner) scanFunc(scan func() (Token, int, string)) (tok Token, pos int, lit string) {
405405
// If we have unread tokens then read them off the buffer first.
406406
if s.n > 0 {
407407
s.n--
@@ -420,7 +420,7 @@ func (s *bufScanner) scanFunc(scan func() (token, int, string)) (tok token, pos
420420
func (s *bufScanner) unscan() { s.n++ }
421421

422422
// curr returns the last read token.
423-
func (s *bufScanner) curr() (tok token, pos int, lit string) {
423+
func (s *bufScanner) curr() (tok Token, pos int, lit string) {
424424
buf := &s.buf[(s.i-s.n+len(s.buf))%len(s.buf)]
425425
return buf.tok, buf.pos, buf.lit
426426
}

pkg/filter/ql/lexer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
func TestScanner(t *testing.T) {
2727
var tests = []struct {
2828
s string
29-
tok token
29+
tok Token
3030
lit string
3131
pos int
3232
}{

pkg/filter/ql/parser.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -650,10 +650,10 @@ func parseDuration(s string) (time.Duration, error) {
650650
}
651651

652652
// scan returns the next token from the underlying scanner.
653-
func (p *Parser) scan() (tok token, pos int, lit string) { return p.s.scan() }
653+
func (p *Parser) scan() (tok Token, pos int, lit string) { return p.s.scan() }
654654

655655
// scanIgnoreWhitespace scans the next non-whitespace.
656-
func (p *Parser) scanIgnoreWhitespace() (tok token, pos int, lit string) {
656+
func (p *Parser) scanIgnoreWhitespace() (tok Token, pos int, lit string) {
657657
for {
658658
tok, pos, lit = p.scan()
659659
if tok == WS {

pkg/filter/ql/token.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ import (
2323
)
2424

2525
// token represents the lexical token of the filter expression
26-
type token int
26+
type Token int
2727

2828
const (
29-
Illegal token = iota
29+
Illegal Token = iota
3030
WS
3131
EOF
3232

@@ -86,11 +86,11 @@ const (
8686
As // AS
8787
)
8888

89-
var keywords map[string]token
89+
var keywords map[string]Token
9090

9191
func init() {
92-
keywords = make(map[string]token)
93-
for _, tok := range []token{And, Or, Contains, IContains, In,
92+
keywords = make(map[string]Token)
93+
for _, tok := range []Token{And, Or, Contains, IContains, In,
9494
IIn, Not, Startswith, IStartswith, Endswith, IEndswith,
9595
Matches, IMatches, Fuzzy, IFuzzy, Fuzzynorm, IFuzzynorm,
9696
Intersects, IIntersects, Seq, MaxSpan, By, As} {
@@ -161,18 +161,18 @@ var tokens = [...]string{
161161
}
162162

163163
// isOperator determines whether the current token is an operator.
164-
func (tok token) isOperator() bool { return tok > opBeg && tok < opEnd }
164+
func (tok Token) isOperator() bool { return tok > opBeg && tok < opEnd }
165165

166166
// String returns the string representation of the token.
167-
func (tok token) String() string {
168-
if tok >= 0 && tok < token(len(tokens)) {
167+
func (tok Token) String() string {
168+
if tok >= 0 && tok < Token(len(tokens)) {
169169
return tokens[tok]
170170
}
171171
return ""
172172
}
173173

174174
// precedence returns the operator precedence of the binary operator token.
175-
func (tok token) precedence() int {
175+
func (tok Token) precedence() int {
176176
switch tok {
177177
case Or:
178178
return 1
@@ -189,15 +189,15 @@ func (tok token) precedence() int {
189189
return 0
190190
}
191191

192-
func tokstr(tok token, lit string) string {
192+
func tokstr(tok Token, lit string) string {
193193
if lit != "" {
194194
return lit
195195
}
196196
return tok.String()
197197
}
198198

199199
// lookup returns the token associated with a given string.
200-
func lookup(id string) (token, string) {
200+
func lookup(id string) (Token, string) {
201201
if tok, ok := keywords[strings.ToLower(id)]; ok {
202202
return tok, ""
203203
}

0 commit comments

Comments
 (0)