From 2d473942195f83b293298d8dee4d66e62ba8ed02 Mon Sep 17 00:00:00 2001 From: rabbitstack Date: Mon, 29 Sep 2025 18:54:53 +0200 Subject: [PATCH] fix(filter): Provide accessor default value When the accessor fails or return a nil value, populate the valuer map with a default value. This is important to avoid rule matching misbehaviours. --- pkg/filter/accessor.go | 31 ++++++++++++++++++++++++++++- pkg/filter/fields/fields_windows.go | 3 +++ pkg/filter/filter.go | 13 ++++++------ pkg/filter/filter_test.go | 30 ++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/pkg/filter/accessor.go b/pkg/filter/accessor.go index 21e05dc72..bac4de3a8 100644 --- a/pkg/filter/accessor.go +++ b/pkg/filter/accessor.go @@ -23,7 +23,9 @@ import ( "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/event/params" "github.com/rabbitstack/fibratus/pkg/filter/fields" + "net" "reflect" + "time" ) var ( @@ -62,7 +64,7 @@ func newEventAccessor() Accessor { const timeFmt = "15:04:05" const dateFmt = "2006-01-02" -func (k *evtAccessor) Get(f Field, evt *event.Event) (params.Value, error) { +func (*evtAccessor) Get(f Field, evt *event.Event) (params.Value, error) { switch f.Name { case fields.EvtSeq, fields.KevtSeq: return evt.Seq, nil @@ -238,3 +240,30 @@ func (f *filter) removeAccessor(removed Accessor) { } } } + +// defaultAccessorValue provides the default value for the field. +// This value is typically assigned when the accessor returns an +// error or nil value, but the map valuer must contain the resolved +// field name in case of filters using the not operator. +func defaultAccessorValue(field Field) any { + switch field.Name.Type() { + case params.Uint8, params.Int64, params.Int8, params.Int32, params.Int16, + params.Uint16, params.Port, params.Uint32, params.Uint64, params.PID, + params.TID, params.Flags, params.Flags64: + return 0 + case params.Float, params.Double: + return 0.0 + case params.Time: + return time.Now() + case params.Bool: + return false + case params.IP, params.IPv4, params.IPv6: + return net.IP{} + case params.Binary: + return []byte{} + case params.Slice: + return []string{} + default: + return "" + } +} diff --git a/pkg/filter/fields/fields_windows.go b/pkg/filter/fields/fields_windows.go index 26517e88f..435aa660a 100644 --- a/pkg/filter/fields/fields_windows.go +++ b/pkg/filter/fields/fields_windows.go @@ -609,6 +609,9 @@ const ( // String casts the field type to string. func (f Field) String() string { return string(f) } +// Type returns the data type that this field contains. +func (f Field) Type() params.Type { return fields[f].Type } + func (f Field) IsPsField() bool { return strings.HasPrefix(string(f), "ps.") } func (f Field) IsKevtField() bool { return strings.HasPrefix(string(f), "evt.") } func (f Field) IsThreadField() bool { return strings.HasPrefix(string(f), "thread.") } diff --git a/pkg/filter/filter.go b/pkg/filter/filter.go index ed92de1c4..09d2e7a49 100644 --- a/pkg/filter/filter.go +++ b/pkg/filter/filter.go @@ -423,14 +423,15 @@ func (f *filter) mapValuer(evt *event.Event) map[string]interface{} { continue } v, err := accessor.Get(field, evt) - if err != nil && !errs.IsParamNotFound(err) { - accessorErrors.Add(err.Error(), 1) + if v == nil || err != nil { + valuer[field.Value] = defaultAccessorValue(field) + if err != nil && !errs.IsParamNotFound(err) { + accessorErrors.Add(err.Error(), 1) + } continue } - if v != nil { - valuer[field.Value] = v - break - } + valuer[field.Value] = v + break } } return valuer diff --git a/pkg/filter/filter_test.go b/pkg/filter/filter_test.go index c658da0f8..f889b0d4f 100644 --- a/pkg/filter/filter_test.go +++ b/pkg/filter/filter_test.go @@ -201,6 +201,16 @@ func TestProcFilter(t *testing.T) { }, } + evt2 := &event.Event{ + Type: event.OpenProcess, + Category: event.Process, + Params: event.Params{ + params.DesiredAccess: {Name: params.DesiredAccess, Type: params.Flags, Value: uint32(0x1400), Flags: event.PsAccessRightFlags}, + }, + Name: "OpenProcess", + PID: 1023, + } + var tests = []struct { filter string matches bool @@ -340,6 +350,26 @@ func TestProcFilter(t *testing.T) { t.Errorf("%d. %q ps filter mismatch: exp=%t got=%t", i, tt.filter, tt.matches, matches) } } + + var tests2 = []struct { + filter string + matches bool + }{ + + {`ps.exe = ''`, true}, + } + + for i, tt := range tests2 { + f := New(tt.filter, cfg) + err := f.Compile() + if err != nil { + t.Fatal(err) + } + matches := f.Run(evt2) + if matches != tt.matches { + t.Errorf("%d. %q ps filter mismatch: exp=%t got=%t", i, tt.filter, tt.matches, matches) + } + } } func TestThreadFilter(t *testing.T) {