Skip to content

Commit 9b06895

Browse files
committed
fix(rule_engine): Permit sequence link sets
Events can end up decorated with more than one sequence value. For example, a freshly created process can be assigned the ps.uuid or ps.name join field, and the rule engine would effectively override the last matched event. For this reason, it is necessary to allow sequence value sets, so that sequence matching can consider multiple values extracted from the join field.
1 parent 78d8b87 commit 9b06895

File tree

3 files changed

+88
-32
lines changed

3 files changed

+88
-32
lines changed

pkg/event/event.go

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ const (
4444
YaraMatchesKey MetadataKey = "yara.matches"
4545
// RuleNameKey identifies the rule that was triggered by the event
4646
RuleNameKey MetadataKey = "rule.name"
47-
// RuleSequenceLink represents the join link value in sequence rules
48-
RuleSequenceLink MetadataKey = "rule.seq.link"
47+
// RuleSequenceLink represents the join link values in sequence rules
48+
RuleSequenceLinks MetadataKey = "rule.seq.links"
4949
// RuleSequenceOOOKey the presence of this metadata key indicates the
5050
// event in the partials list arrived out of order and requires reevaluation
5151
RuleSequenceOOOKey MetadataKey = "rule.seq.ooo"
@@ -292,6 +292,20 @@ func (e *Event) ContainsMeta(k MetadataKey) bool {
292292
return e.Metadata[k] != nil
293293
}
294294

295+
// AddSequenceLink adds a new sequence link to the set.
296+
func (e *Event) AddSequenceLink(link any) {
297+
if e.ContainsMeta(RuleSequenceLinks) {
298+
links, ok := e.GetMeta(RuleSequenceLinks).(map[any]struct{})
299+
if !ok {
300+
return
301+
}
302+
links[link] = struct{}{}
303+
e.AddMeta(RuleSequenceLinks, links)
304+
} else {
305+
e.AddMeta(RuleSequenceLinks, map[any]struct{}{link: {}})
306+
}
307+
}
308+
295309
// AppendParam adds a new parameter to this event.
296310
func (e *Event) AppendParam(name string, typ params.Type, value params.Value, opts ...ParamOption) {
297311
e.Params.Append(name, typ, value, opts...)
@@ -326,9 +340,17 @@ func (e *Event) GetFlagsAsSlice(name string) []string {
326340
return strings.Split(e.GetParamAsString(name), "|")
327341
}
328342

329-
// SequenceLink returns the sequence link value from event metadata.
330-
func (e *Event) SequenceLink() any {
343+
// SequenceLink returns the sequence link values from event metadata.
344+
func (e *Event) SequenceLinks() []any {
331345
e.mmux.RLock()
332346
defer e.mmux.RUnlock()
333-
return e.Metadata[RuleSequenceLink]
347+
links, ok := e.Metadata[RuleSequenceLinks].(map[any]struct{})
348+
if !ok {
349+
return nil
350+
}
351+
s := make([]any, 0, len(links))
352+
for v := range links {
353+
s = append(s, v)
354+
}
355+
return s
334356
}

pkg/filter/filter.go

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@ import (
2222
"errors"
2323
"expvar"
2424
"fmt"
25+
"net"
26+
"regexp"
27+
"strconv"
28+
"strings"
29+
2530
errs "github.com/rabbitstack/fibratus/pkg/errors"
2631
"github.com/rabbitstack/fibratus/pkg/event"
2732
"github.com/rabbitstack/fibratus/pkg/filter/fields"
2833
"github.com/rabbitstack/fibratus/pkg/filter/ql"
2934
"github.com/rabbitstack/fibratus/pkg/util/bytes"
3035
"github.com/rabbitstack/fibratus/pkg/util/hashers"
31-
"net"
32-
"regexp"
33-
"strconv"
34-
"strings"
3536
)
3637

3738
var (
@@ -292,8 +293,8 @@ func (f *filter) RunSequence(e *event.Event, seqID int, partials map[int][]*even
292293
match = ql.Eval(expr.Expr, valuer, f.hasFunctions)
293294
if match {
294295
// compute sequence key hash to tie the events
295-
evt.AddMeta(event.RuleSequenceLink, hashers.FnvUint64(hash))
296-
e.AddMeta(event.RuleSequenceLink, hashers.FnvUint64(hash))
296+
evt.AddSequenceLink(hashers.FnvUint64(hash))
297+
e.AddSequenceLink(hashers.FnvUint64(hash))
297298
break
298299
}
299300
}
@@ -310,7 +311,7 @@ func (f *filter) RunSequence(e *event.Event, seqID int, partials map[int][]*even
310311
outer:
311312
for i := 0; i < seqID; i++ {
312313
for _, p := range partials[i] {
313-
if CompareSeqLink(joinID, p.SequenceLink()) {
314+
if CompareSeqLink(joinID, p.SequenceLinks()) {
314315
joins[i] = true
315316
continue outer
316317
}
@@ -323,7 +324,7 @@ func (f *filter) RunSequence(e *event.Event, seqID int, partials map[int][]*even
323324

324325
if match && by != nil {
325326
if v := valuer[by.Value]; v != nil {
326-
e.AddMeta(event.RuleSequenceLink, v)
327+
e.AddSequenceLink(v)
327328
}
328329
}
329330
}
@@ -511,57 +512,89 @@ func (f *filter) checkBoundRefs() error {
511512
return nil
512513
}
513514

514-
// CompareSeqLink returns true if both values
515-
// representing the sequence joins are equal.
516-
func CompareSeqLink(s1, s2 any) bool {
517-
if s1 == nil || s2 == nil {
515+
// CompareSeqLink returns true if any value
516+
// in the sequence link slice equals to the
517+
// given LHS value.
518+
func CompareSeqLink(lhs any, rhs []any) bool {
519+
if lhs == nil || rhs == nil {
518520
return false
519521
}
520-
switch v := s1.(type) {
522+
for _, v := range rhs {
523+
if compareSeqLink(lhs, v) {
524+
return true
525+
}
526+
}
527+
return false
528+
}
529+
530+
// CompareSeqLinks returns true any LHS sequence
531+
// link values equal to the RHS sequence link values.
532+
func CompareSeqLinks(lhs []any, rhs []any) bool {
533+
if lhs == nil || rhs == nil {
534+
return false
535+
}
536+
for _, v1 := range lhs {
537+
for _, v2 := range rhs {
538+
if compareSeqLink(v1, v2) {
539+
return true
540+
}
541+
}
542+
}
543+
return false
544+
}
545+
546+
func compareSeqLink(lhs any, rhs any) bool {
547+
if lhs == nil || rhs == nil {
548+
return false
549+
}
550+
551+
switch v := lhs.(type) {
521552
case string:
522-
s, ok := s2.(string)
553+
s, ok := rhs.(string)
523554
if !ok {
524555
return false
525556
}
526557
return strings.EqualFold(v, s)
527558
case uint8:
528-
n, ok := s2.(uint8)
559+
n, ok := rhs.(uint8)
529560
if !ok {
530561
return false
531562
}
532563
return v == n
533564
case uint16:
534-
n, ok := s2.(uint16)
565+
n, ok := rhs.(uint16)
535566
if !ok {
536567
return false
537568
}
538569
return v == n
539570
case uint32:
540-
n, ok := s2.(uint32)
571+
n, ok := rhs.(uint32)
541572
if !ok {
542573
return false
543574
}
544575
return v == n
545576
case uint64:
546-
n, ok := s2.(uint64)
577+
n, ok := rhs.(uint64)
547578
if !ok {
548579
return false
549580
}
550-
return v == n
581+
if v == n {
582+
return true
583+
}
551584
case int:
552-
n, ok := s2.(int)
585+
n, ok := rhs.(int)
553586
if !ok {
554587
return false
555588
}
556589
return v == n
557590
case uint:
558-
n, ok := s2.(uint)
591+
n, ok := rhs.(uint)
559592
if !ok {
560593
return false
561594
}
562595
return v == n
563596
case net.IP:
564-
ip, ok := s2.(net.IP)
597+
ip, ok := rhs.(net.IP)
565598
if !ok {
566599
return false
567600
}

pkg/rules/sequence.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ package rules
2121
import (
2222
"context"
2323
"expvar"
24+
"sort"
25+
"sync"
26+
"sync/atomic"
27+
"time"
28+
2429
fsm "github.com/qmuntal/stateless"
2530
"github.com/rabbitstack/fibratus/pkg/config"
2631
"github.com/rabbitstack/fibratus/pkg/event"
@@ -29,10 +34,6 @@ import (
2934
"github.com/rabbitstack/fibratus/pkg/filter/ql"
3035
"github.com/rabbitstack/fibratus/pkg/ps"
3136
log "github.com/sirupsen/logrus"
32-
"sort"
33-
"sync"
34-
"sync/atomic"
35-
"time"
3637
)
3738

3839
const (
@@ -520,7 +521,7 @@ func (s *sequenceState) runSequence(e *event.Event) bool {
520521
for seqID := 0; seqID < len(s.partials); seqID++ {
521522
for _, outer := range s.partials[seqID] {
522523
for _, inner := range s.partials[seqID+1] {
523-
if filter.CompareSeqLink(outer.SequenceLink(), inner.SequenceLink()) {
524+
if filter.CompareSeqLinks(outer.SequenceLinks(), inner.SequenceLinks()) {
524525
setMatch(seqID, outer)
525526
setMatch(seqID+1, inner)
526527
}

0 commit comments

Comments
 (0)