Skip to content

Commit 12587d4

Browse files
committed
feat(rule-engine): Match all strategy
The match all rule engine strategy permits a single event to trigger multiple rules.
1 parent 6a35851 commit 12587d4

File tree

7 files changed

+34
-11
lines changed

7 files changed

+34
-11
lines changed

configs/fibratus.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ filament:
131131
# For local file system rule paths, it is possible to use the glob expression to load the
132132
# rules from different directory locations.
133133
filters:
134+
# Indicates if the rule engine match all strategy is enabled. When the match all strategy
135+
# is enabled, a single event can trigger multiple rules.
136+
match-all: true
137+
134138
rules:
135139
# Indicates if the rule engine is enabled and rules loaded
136140
enabled: true

pkg/config/config_windows.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ func (c *Config) addFlags() {
380380
c.flags.StringSlice(rulesFromPaths, []string{filepath.Join(dir, "*")}, "Comma-separated list of rules files")
381381
c.flags.StringSlice(macrosFromPaths, []string{filepath.Join(dir, "Macros", "*")}, "Comma-separated list of macro files")
382382
c.flags.StringSlice(rulesFromURLs, []string{}, "Comma-separated list of rules URL resources")
383+
c.flags.Bool(matchAll, true, "Indicates if the match all strategy is enabled for the rule engine. If the match all strategy is enabled, a single event can trigger multiple rules")
383384
}
384385
if c.opts.capture {
385386
c.flags.StringP(kcapFile, "o", "", "The path of the output kcap file")

pkg/config/filters.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,13 @@ func (f FilterConfig) HasLabel(l string) bool { return f.Labels[l] != "" }
102102

103103
// Filters contains references to rule and macro definitions.
104104
type Filters struct {
105-
Rules Rules `json:"rules" yaml:"rules"`
106-
Macros Macros `json:"macros" yaml:"macros"`
107-
macros map[string]*Macro
108-
filters []*FilterConfig
105+
Rules Rules `json:"rules" yaml:"rules"`
106+
Macros Macros `json:"macros" yaml:"macros"`
107+
// MatchAll indicates if the match all strategy is enabled for the rule engine.
108+
// If the match all strategy is enabled, a single event can trigger multiple rules.
109+
MatchAll bool `json:"match-all" yaml:"match-all"`
110+
macros map[string]*Macro
111+
filters []*FilterConfig
109112
}
110113

111114
// FiltersWithMacros builds the filter config with the map of
@@ -241,13 +244,15 @@ const (
241244
rulesFromPaths = "filters.rules.from-paths"
242245
rulesFromURLs = "filters.rules.from-urls"
243246
macrosFromPaths = "filters.macros.from-paths"
247+
matchAll = "filters.match-all"
244248
)
245249

246250
func (f *Filters) initFromViper(v *viper.Viper) {
247251
f.Rules.Enabled = v.GetBool(rulesEnabled)
248252
f.Rules.FromPaths = v.GetStringSlice(rulesFromPaths)
249253
f.Rules.FromURLs = v.GetStringSlice(rulesFromURLs)
250254
f.Macros.FromPaths = v.GetStringSlice(macrosFromPaths)
255+
f.MatchAll = v.GetBool(matchAll)
251256
}
252257

253258
func (f Filters) HasMacros() bool { return len(f.macros) > 0 }

pkg/config/filters_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func TestLoadRulesFromPaths(t *testing.T) {
3737
},
3838
},
3939
Macros{FromPaths: nil},
40+
false,
4041
map[string]*Macro{},
4142
[]*FilterConfig{},
4243
}
@@ -78,6 +79,7 @@ func TestLoadRulesFromPathsWithTemplate(t *testing.T) {
7879
},
7980
},
8081
Macros{FromPaths: nil},
82+
false,
8183
map[string]*Macro{},
8284
[]*FilterConfig{},
8385
}
@@ -116,6 +118,7 @@ func TestLoadGroupsFromURLs(t *testing.T) {
116118
},
117119
},
118120
Macros{FromPaths: nil},
121+
false,
119122
map[string]*Macro{},
120123
[]*FilterConfig{},
121124
}

pkg/config/schema_windows.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ var schema = `
146146
"filters": {
147147
"type": "object",
148148
"properties": {
149+
"match-all": {"type": "boolean"},
149150
"rules": {
150151
"type": "object",
151152
"properties": {
@@ -510,7 +511,7 @@ var rulesSchema = `
510511
"id": {"type": "string", "minLength": 36, "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"},
511512
"version": {"type": "string", "minLength": 5, "pattern": "^([0-9]+.)([0-9]+.)([0-9]+)$"},
512513
"name": {"type": "string", "minLength": 3},
513-
"description": {"type": "string"},
514+
"description": {"type": "string"},
514515
"output": {"type": "string", "minLength": 5},
515516
"notes": {"type": "string"},
516517
"severity": {"type": "string", "enum": ["low", "medium", "high", "critical"]},

pkg/rules/engine.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,14 @@ func (e *Engine) RegisterMatchFunc(fn RuleMatchFunc) {
231231
func (*Engine) CanEnqueue() bool { return true }
232232

233233
// ProcessEvent processes the system event against compiled filters.
234-
// Filter is the internal lingo to designate the rule condition. The
235-
// filters can be simple direct-event matchers or sequence states that
234+
// Filter is the internal lingo that designates a rule condition.
235+
// Filters can be simple direct-event matchers or sequence states that
236236
// track an ordered series of events over a short period of time.
237237
func (e *Engine) ProcessEvent(evt *kevent.Kevent) (bool, error) {
238238
if len(e.filters) == 0 {
239239
return true, nil
240240
}
241+
var matches bool
241242
if evt.IsTerminateProcess() {
242243
// expire all sequences if the
243244
// process referenced in any
@@ -260,10 +261,17 @@ func (e *Engine) ProcessEvent(evt *kevent.Kevent) (bool, error) {
260261
if err != nil {
261262
log.Errorf("unable to execute rule action: %v", err)
262263
}
263-
return true, nil
264+
switch {
265+
case e.config.Filters.MatchAll:
266+
if !matches {
267+
matches = true
268+
}
269+
default:
270+
return true, nil
271+
}
264272
}
265273
}
266-
return false, nil
274+
return matches, nil
267275
}
268276

269277
// findFilters collects all compiled filters for a

pkg/rules/engine_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,9 @@ func TestRunSimpleAndSequenceRules(t *testing.T) {
333333
log.SetLevel(log.DebugLevel)
334334

335335
expectedMatches := make(map[string][]uint64)
336-
337-
e := NewEngine(new(ps.SnapshotterMock), newConfig("_fixtures/simple_and_sequence_rules/*.yml"))
336+
c := newConfig("_fixtures/simple_and_sequence_rules/*.yml")
337+
c.Filters.MatchAll = true
338+
e := NewEngine(new(ps.SnapshotterMock), c)
338339
e.RegisterMatchFunc(func(f *config.FilterConfig, evts ...*kevent.Kevent) {
339340
ids := make([]uint64, 0)
340341
for _, evt := range evts {

0 commit comments

Comments
 (0)