diff --git a/auditbeat/module/audit/file/action.go b/auditbeat/module/audit/file/action.go new file mode 100644 index 000000000000..64d65a20d439 --- /dev/null +++ b/auditbeat/module/audit/file/action.go @@ -0,0 +1,154 @@ +package file + +import ( + "math/bits" + "strings" +) + +// Action is a description of the changes described by an event. +type Action uint8 + +// ActionArray is just syntactic sugar to invoke methods on []Action receiver +type ActionArray []Action + +// List of possible Actions. +const ( + None Action = 0 + AttributesModified = 1 << (iota - 1) + Created + Deleted + Updated + Moved + ConfigChange +) + +var actionNames = map[Action]string{ + None: "none", + AttributesModified: "attributes_modified", + Created: "created", + Deleted: "deleted", + Updated: "updated", + Moved: "moved", + ConfigChange: "config_change", +} + +type actionOrderKey struct { + ExistsBefore, ExistsNow bool + Action Action +} + +// Given the previous and current state of the file, and an action mask +// returns a meaningful ordering for the actions in the mask +var actionOrderMap = map[actionOrderKey]ActionArray{ + {false, false, Created | Deleted}: {Created, Deleted}, + {true, true, Created | Deleted}: {Deleted, Created}, + {false, false, Moved | Created}: {Created, Moved}, + {true, true, Moved | Created}: {Moved, Created}, + {true, true, Moved | Deleted}: {Deleted, Moved}, + {false, false, Moved | Deleted}: {Moved, Deleted}, + {false, true, Updated | Created}: {Created, Updated}, + {true, false, Updated | Deleted}: {Updated, Deleted}, + {false, true, Updated | Moved}: {Moved, Updated}, + {true, false, Updated | Moved}: {Updated, Moved}, + {false, true, Moved | Created | Deleted}: {Created, Deleted, Moved}, + {true, false, Moved | Created | Deleted}: {Deleted, Created, Moved}, + {false, false, Updated | Moved | Created}: {Created, Updated, Moved}, + {true, true, Updated | Moved | Created}: {Moved, Created, Updated}, + {false, false, Updated | Moved | Deleted}: {Moved, Updated, Deleted}, + {true, true, Updated | Moved | Deleted}: {Deleted, Moved, Updated}, + {false, false, Updated | Created | Deleted}: {Created, Updated, Deleted}, + {true, true, Updated | Created | Deleted}: {Deleted, Created, Updated}, + {false, true, Updated | Moved | Created | Deleted}: {Created, Deleted, Moved, Updated}, + {true, false, Updated | Moved | Created | Deleted}: {Deleted, Created, Updated, Moved}, +} + +func (action Action) isMultiple() bool { + return bits.OnesCount8(uint8(action)) > 1 +} + +func (action Action) String() string { + if name, found := actionNames[action]; found { + return name + } + var list []string + for flag, name := range actionNames { + if action&flag != 0 { + action ^= flag + list = append(list, name) + } + } + if action != 0 { + list = append(list, "unknown") + } + return strings.Join(list, "|") +} + +func resolveActionOrder(action Action, existedBefore, existsNow bool) ActionArray { + if action == None { + return nil + } + if !action.isMultiple() { + return []Action{action} + } + key := actionOrderKey{existedBefore, existsNow, action} + if result, ok := actionOrderMap[key]; ok { + return result + } + + // Can't resolve a meaningful order for the actions, usually the file + // has received further actions after the event being processed + return action.InAnyOrder() +} + +func (action Action) InOrder(existedBefore, existsNow bool) ActionArray { + hasConfigChange := 0 != action&ConfigChange + hasUpdate := 0 != action&Updated + hasAttrMod := 0 != action&AttributesModified + action = Action(int(action) & int(^(ConfigChange | AttributesModified))) + if hasAttrMod { + action |= Updated + } + + result := resolveActionOrder(action, existedBefore, existsNow) + + if hasConfigChange { + result = append(result, ConfigChange) + } + + if hasAttrMod { + for idx, value := range result { + if value == Updated { + if !hasUpdate { + result[idx] = AttributesModified + } else { + result = append(result, None) + copy(result[idx+2:], result[idx+1:]) + result[idx+1] = AttributesModified + } + break + } + } + } + return result +} + +func (action Action) InAnyOrder() ActionArray { + if !action.isMultiple() { + return []Action{action} + } + var result []Action + for k := range actionNames { + if 0 != action&k { + result = append(result, k) + } + } + return result +} + +func (actions ActionArray) StringArray() []string { + result := make([]string, len(actions)) + for index, value := range actions { + result[index] = value.String() + } + return result +} diff --git a/auditbeat/module/audit/file/event.go b/auditbeat/module/audit/file/event.go index c0ff00c94fbe..b17bb3b4fc91 100644 --- a/auditbeat/module/audit/file/event.go +++ b/auditbeat/module/audit/file/event.go @@ -25,37 +25,6 @@ import ( "github.com/elastic/beats/metricbeat/mb" ) -// Action is a description of the change that occurred. -type Action uint8 - -func (a Action) String() string { - if name, found := actionNames[a]; found { - return name - } - return "unknown" -} - -// List of possible Actions. -const ( - None = iota << 1 - AttributesModified - Created - Deleted - Updated - Moved - ConfigChange -) - -var actionNames = map[Action]string{ - None: "none", - AttributesModified: "attributes_modified", - Created: "created", - Deleted: "deleted", - Updated: "updated", - Moved: "moved", - ConfigChange: "config_change", -} - // Source identifies the source of an event (i.e. what triggered it). type Source uint8 @@ -198,11 +167,11 @@ func NewEvent( maxFileSize uint64, hashTypes []HashType, ) Event { - if action == Deleted { - return NewEventFromFileInfo(path, nil, nil, action, source, maxFileSize, hashTypes) - } - info, err := os.Lstat(path) + if err != nil && os.IsNotExist(err) { + // deleted file is signaled by info == nil + err = nil + } err = errors.Wrap(err, "failed to lstat") return NewEventFromFileInfo(path, info, err, action, source, maxFileSize, hashTypes) } @@ -212,7 +181,7 @@ func (e *Event) String() string { return string(data) } -func buildMapStr(e *Event) common.MapStr { +func buildMapStr(e *Event, existedBefore bool) common.MapStr { m := common.MapStr{ "@timestamp": e.Timestamp, "path": e.Path, @@ -221,7 +190,7 @@ func buildMapStr(e *Event) common.MapStr { } if e.Action > 0 { - m["action"] = e.Action.String() + m["action"] = e.Action.InOrder(existedBefore, e.Info != nil).StringArray() } if e.TargetPath != "" { @@ -287,9 +256,12 @@ func diffEvents(old, new *Event) (Action, bool) { return Moved, true } + result := None + // Test if new.Hashes is a subset of old.Hashes. hasAllHashes := true for hashType, newValue := range new.Hashes { + oldValue, found := old.Hashes[hashType] if !found { hasAllHashes = false @@ -298,14 +270,15 @@ func diffEvents(old, new *Event) (Action, bool) { // The Updated action takes precedence over a new hash type being configured. if !bytes.Equal(oldValue, newValue) { - return Updated, true + result |= Updated + break } } if old.TargetPath != new.TargetPath || (old.Info == nil && new.Info != nil) || (old.Info != nil && new.Info == nil) { - return AttributesModified, true + result |= AttributesModified } // Test if metadata has changed. @@ -313,22 +286,21 @@ func diffEvents(old, new *Event) (Action, bool) { // The owner and group names are ignored (they aren't persisted). if o.Inode != n.Inode || o.UID != n.UID || o.GID != n.GID || o.SID != n.SID || o.Mode != n.Mode || o.Type != n.Type { - return AttributesModified, true + result |= AttributesModified } // For files consider mtime and size. if n.Type == FileType && (!o.MTime.Equal(n.MTime) || o.Size != n.Size) { - return AttributesModified, true + result |= AttributesModified } } // The old event didn't have all the requested hash types. if !hasAllHashes { - return ConfigChange, true + result |= ConfigChange } - // No change. - return None, false + return result, result != None } func hashFile(name string, hashType ...HashType) (map[HashType][]byte, error) { diff --git a/auditbeat/module/audit/file/event_test.go b/auditbeat/module/audit/file/event_test.go index 5ef6ca19e795..0c01019faf19 100644 --- a/auditbeat/module/audit/file/event_test.go +++ b/auditbeat/module/audit/file/event_test.go @@ -126,7 +126,7 @@ func TestDiffEvents(t *testing.T) { action, changed := diffEvents(testEvent(), e) assert.True(t, changed) - assert.EqualValues(t, Updated, action) + assert.EqualValues(t, Updated|AttributesModified, action) }) } diff --git a/auditbeat/module/audit/file/eventreader_fsevents.go b/auditbeat/module/audit/file/eventreader_fsevents.go new file mode 100644 index 000000000000..59a01d6d53de --- /dev/null +++ b/auditbeat/module/audit/file/eventreader_fsevents.go @@ -0,0 +1,196 @@ +// +build darwin + +package file + +import ( + "flag" + "os" + "path/filepath" + "strings" + "time" + + "github.com/fsnotify/fsevents" + "github.com/pkg/errors" + + "github.com/elastic/beats/libbeat/logp" +) + +var underTest = false + +func init() { + underTest = flag.Lookup("test.v") != nil +} + +type fsreader struct { + stream *fsevents.EventStream + config Config + eventC chan Event + watchedDirs []os.FileInfo +} + +var flagToAction = map[fsevents.EventFlags]Action{ + fsevents.MustScanSubDirs: None, + fsevents.UserDropped: None, + fsevents.KernelDropped: None, + fsevents.EventIDsWrapped: None, + fsevents.HistoryDone: None, + // RootChanged signals that a directory along a watched path was moved + // or deleted, or the path was created. Mapping it to `Moved` which + // makes sense in both cases + fsevents.RootChanged: Moved, + fsevents.Mount: None, + fsevents.Unmount: None, + fsevents.ItemCreated: Created, + fsevents.ItemRemoved: Deleted, + fsevents.ItemInodeMetaMod: AttributesModified, + fsevents.ItemRenamed: Moved, + fsevents.ItemModified: Updated, + fsevents.ItemFinderInfoMod: AttributesModified, + fsevents.ItemChangeOwner: AttributesModified, + fsevents.ItemXattrMod: AttributesModified, + fsevents.ItemIsFile: None, + fsevents.ItemIsDir: None, + fsevents.ItemIsSymlink: None, +} + +var flagNames = map[fsevents.EventFlags]string{ + fsevents.MustScanSubDirs: "MustScanSubDirs", + fsevents.UserDropped: "UserDropped", + fsevents.KernelDropped: "KernelDropped", + fsevents.EventIDsWrapped: "EventIDsWrapped", + fsevents.HistoryDone: "HistoryDone", + fsevents.RootChanged: "RootChanged", + fsevents.Mount: "Mount", + fsevents.Unmount: "Unmount", + fsevents.ItemCreated: "ItemCreated", + fsevents.ItemRemoved: "ItemRemoved", + fsevents.ItemInodeMetaMod: "ItemInodeMetaMod", + fsevents.ItemRenamed: "ItemRenamed", + fsevents.ItemModified: "ItemModified", + fsevents.ItemFinderInfoMod: "ItemFinderInfoMod", + fsevents.ItemChangeOwner: "ItemChangeOwner", + fsevents.ItemXattrMod: "ItemXattrMod", + fsevents.ItemIsFile: "ItemIsFile", + fsevents.ItemIsDir: "ItemIsDir", + fsevents.ItemIsSymlink: "ItemIsSymlink", +} + +// NewEventReader creates a new EventProducer backed by FSEvents macOS facility. +func NewEventReader(c Config) (EventProducer, error) { + stream := &fsevents.EventStream{ + Paths: c.Paths, + // NoDefer: Ignore Latency field and send events as fast as possible. + // Useful as latency has one second granularity. + // + // WatchRoot: Will send a notification when some element changes along + // the path being watched (dir moved or deleted). + // + // FileEvents: Get events for files not just directories + Flags: fsevents.NoDefer | fsevents.WatchRoot | fsevents.FileEvents, + } + + // IgnoreSelf: Avoid infinite looping when auditbeat writes to a + // watched directory. If specified tests won't work. + if !underTest { + stream.Flags |= fsevents.IgnoreSelf + } + + var dirs []os.FileInfo + if !c.Recursive { + for _, path := range c.Paths { + if info, err := getFileInfo(path); err == nil { + dirs = append(dirs, info) + } else { + logp.Warn("%v failed to get file info for '%s': %v", logPrefix, path, err) + } + } + } + return &fsreader{ + stream: stream, + config: c, + eventC: make(chan Event, 1), + watchedDirs: dirs, + }, nil +} + +func (r *fsreader) Start(done <-chan struct{}) (<-chan Event, error) { + r.stream.Start() + go r.consumeEvents(done) + logp.Info("%v started FSEvents watcher recursive:%v", logPrefix, r.config.Recursive) + return r.eventC, nil +} + +func (r *fsreader) consumeEvents(done <-chan struct{}) { + defer close(r.eventC) + defer r.stream.Stop() + + for { + select { + case <-done: + debugf("Terminated") + return + case events := <-r.stream.Events: + for _, event := range events { + if !r.isWatched(event.Path) { + debugf("Ignoring FSEvents event: path=%v", event.Path) + continue + } + debugf("Received FSEvents event: id=%d path=%v flags=%s", + event.ID, event.Path, flagsToString(event.Flags)) + start := time.Now() + e := NewEvent(event.Path, flagsToAction(event.Flags), SourceFSNotify, + r.config.MaxFileSizeBytes, r.config.HashTypes) + + e.rtt = time.Since(start) + r.eventC <- e + } + } + } +} + +func flagsToAction(flags fsevents.EventFlags) Action { + action := None + for flag, act := range flagToAction { + if (flags & flag) != 0 { + action |= act + } + } + return action +} + +func flagsToString(flags fsevents.EventFlags) string { + var list []string + for key, name := range flagNames { + if 0 != flags&key { + list = append(list, name) + } + } + return strings.Join(list, "|") +} + +func getFileInfo(path string) (os.FileInfo, error) { + resolved, err := filepath.EvalSymlinks(path) + if err == nil { + path = resolved + } + info, err := os.Lstat(path) + return info, errors.Wrap(err, "failed to stat") +} + +func (r *fsreader) isWatched(path string) bool { + if r.config.Recursive { + return true + } + dir := filepath.Dir(path) + info, err := getFileInfo(dir) + if err != nil { + logp.Warn("%v failed to get event file info for '%s': %v", logPrefix, dir, err) + return false + } + for _, dir := range r.watchedDirs { + if os.SameFile(info, dir) { + return true + } + } + return false +} diff --git a/auditbeat/module/audit/file/eventreader_fsnotify.go b/auditbeat/module/audit/file/eventreader_fsnotify.go index e9402d9c8ed4..39701bcb7485 100644 --- a/auditbeat/module/audit/file/eventreader_fsnotify.go +++ b/auditbeat/module/audit/file/eventreader_fsnotify.go @@ -1,8 +1,9 @@ -// +build linux freebsd openbsd netbsd windows darwin +// +build linux freebsd openbsd netbsd windows package file import ( + "errors" "syscall" "time" @@ -19,6 +20,9 @@ type reader struct { // NewEventReader creates a new EventProducer backed by fsnotify. func NewEventReader(c Config) (EventProducer, error) { + if c.Recursive { + return nil, errors.New("recursive file auditing not supported in this platform (see file.recursive)") + } watcher, err := fsnotify.NewWatcher() if err != nil { return nil, err @@ -44,6 +48,7 @@ func (r *reader) Start(done <-chan struct{}) (<-chan Event, error) { } go r.consumeEvents() + logp.Info("%v started fsnotify watcher", logPrefix) return r.eventC, nil } diff --git a/auditbeat/module/audit/file/eventreader_test.go b/auditbeat/module/audit/file/eventreader_test.go index 6599ac7fed01..afd0de70b091 100644 --- a/auditbeat/module/audit/file/eventreader_test.go +++ b/auditbeat/module/audit/file/eventreader_test.go @@ -23,6 +23,13 @@ func TestEventReader(t *testing.T) { if err != nil { t.Fatal(err) } + // under macOS, temp dir has a symlink in the path (/var -> /private/var) + // and the path returned in events has the symlink resolved + if runtime.GOOS == "darwin" { + if dirAlt, err := filepath.EvalSymlinks(dir); err == nil { + dir = dirAlt + } + } defer os.RemoveAll(dir) // Create a new EventProducer. @@ -49,7 +56,7 @@ func TestEventReader(t *testing.T) { } event := readTimeout(t, events) - assert.EqualValues(t, Created, event.Action) + assert.EqualValues(t, Created, event.Action&Created) assertSameFile(t, txt1, event.Path) if runtime.GOOS != "windows" { assert.EqualValues(t, fileMode, event.Info.Mode) @@ -65,14 +72,29 @@ func TestEventReader(t *testing.T) { if len(received) == 0 { t.Fatal("no events received") } - for _, e := range received { - switch e.Action { - case Moved, Updated: - assert.Equal(t, txt1, e.Path) - case Created: - assertSameFile(t, txt2, e.Path) - default: - t.Errorf("unexpected event: %+v", e) + if runtime.GOOS == "darwin" { + for _, e := range received { + switch { + // Destination file only gets the Moved flag + case e.Action == Moved: + assertSameFile(t, txt2, e.Path) + // Source file is moved and updated + case 0 != e.Action&Moved, 0 != e.Action&Updated: + assertSameFile(t, txt1, e.Path) + default: + t.Errorf("unexpected event: %+v", e) + } + } + } else { + for _, e := range received { + switch { + case 0 != e.Action&Moved, 0 != e.Action&Updated: + assert.Equal(t, txt1, e.Path) + case 0 != e.Action&Created: + assertSameFile(t, txt2, e.Path) + default: + t.Errorf("unexpected event: %+v", e) + } } } }) @@ -89,7 +111,7 @@ func TestEventReader(t *testing.T) { event := readTimeout(t, events) assertSameFile(t, txt2, event.Path) - assert.EqualValues(t, AttributesModified, event.Action) + assert.EqualValues(t, AttributesModified, AttributesModified&event.Action) assert.EqualValues(t, 0644, event.Info.Mode) }) @@ -105,7 +127,7 @@ func TestEventReader(t *testing.T) { event := readTimeout(t, events) assertSameFile(t, txt2, event.Path) - assert.EqualValues(t, Updated, event.Action) + assert.EqualValues(t, Updated, Updated&event.Action) if runtime.GOOS != "windows" { assert.EqualValues(t, 0644, event.Info.Mode) } @@ -120,7 +142,7 @@ func TestEventReader(t *testing.T) { gid := changeGID(t, txt2) event := readTimeout(t, events) assertSameFile(t, txt2, event.Path) - assert.EqualValues(t, AttributesModified, event.Action) + assert.EqualValues(t, AttributesModified, AttributesModified&event.Action) assert.EqualValues(t, gid, event.Info.GID) }) @@ -130,7 +152,7 @@ func TestEventReader(t *testing.T) { } event := readTimeout(t, events) - assert.EqualValues(t, Deleted, event.Action) + assert.EqualValues(t, Deleted, Deleted&event.Action) }) // Create a sub-directory. @@ -160,7 +182,12 @@ func TestEventReader(t *testing.T) { rename(t, moveInOrig, moveIn) event := readTimeout(t, events) - assert.EqualValues(t, Created, event.Action) + + if runtime.GOOS == "darwin" { + assert.EqualValues(t, Moved, event.Action) + } else { + assert.EqualValues(t, Created, event.Action) + } assertSameFile(t, moveIn, event.Path) }) @@ -174,7 +201,7 @@ func TestEventReader(t *testing.T) { if runtime.GOOS == "windows" { assert.EqualValues(t, Deleted, event.Action) } else { - assert.EqualValues(t, Moved, event.Action) + assert.EqualValues(t, Moved, Moved&event.Action) } }) @@ -199,7 +226,7 @@ func readTimeout(t testing.TB, events <-chan Event) Event { if !ok { t.Fatal("failed reading from event channel") } - t.Logf("%+v", buildMapStr(&e).StringToPrint()) + t.Logf("%+v", buildMapStr(&e, false).StringToPrint()) return e } @@ -221,7 +248,7 @@ func readMax(t testing.TB, max int, events <-chan Event) []Event { if !ok { t.Fatal("failed reading from event channel") } - t.Logf("%+v", buildMapStr(&e).StringToPrint()) + t.Logf("%+v", buildMapStr(&e, false).StringToPrint()) received = append(received, e) if len(received) >= max { return received diff --git a/auditbeat/module/audit/file/flatbuffers.go b/auditbeat/module/audit/file/flatbuffers.go index 6e15f4114c5b..c990a373f954 100644 --- a/auditbeat/module/audit/file/flatbuffers.go +++ b/auditbeat/module/audit/file/flatbuffers.go @@ -14,6 +14,15 @@ import ( // Requires the Google flatbuffer compiler. //go:generate flatc --go schema.fbs +var actionMap = map[Action]byte{ + AttributesModified: schema.ActionAttributesModified, + Created: schema.ActionCreated, + Deleted: schema.ActionDeleted, + Updated: schema.ActionUpdated, + Moved: schema.ActionMoved, + ConfigChange: schema.ActionConfigChanged, +} + var bufferPool sync.Pool func init() { @@ -148,20 +157,13 @@ func fbWriteEvent(b *flatbuffers.Builder, e *Event) flatbuffers.UOffsetT { schema.EventAddTargetPath(b, targetPathOffset) } - switch e.Action { - case AttributesModified: - schema.EventAddAction(b, schema.ActionAttributesModified) - case Created: - schema.EventAddAction(b, schema.ActionCreated) - case Deleted: - schema.EventAddAction(b, schema.ActionDeleted) - case Updated: - schema.EventAddAction(b, schema.ActionUpdated) - case Moved: - schema.EventAddAction(b, schema.ActionMoved) - case ConfigChange: - schema.EventAddAction(b, schema.ActionConfigChanged) + var action byte + for k, v := range actionMap { + if 0 != e.Action&k { + action |= v + } } + schema.EventAddAction(b, action) if metadataOffset > 0 { schema.EventAddInfo(b, metadataOffset) @@ -191,19 +193,11 @@ func fbDecodeEvent(path string, buf []byte) *Event { rtn.Source = SourceFSNotify } - switch e.Action() { - case schema.ActionAttributesModified: - rtn.Action = AttributesModified - case schema.ActionCreated: - rtn.Action = Created - case schema.ActionDeleted: - rtn.Action = Deleted - case schema.ActionUpdated: - rtn.Action = Updated - case schema.ActionMoved: - rtn.Action = Moved - case schema.ActionConfigChanged: - rtn.Action = ConfigChange + action := e.Action() + for k, v := range actionMap { + if 0 != action&v { + rtn.Action |= k + } } rtn.Info = fbDecodeMetadata(e) diff --git a/auditbeat/module/audit/file/metricset.go b/auditbeat/module/audit/file/metricset.go index a354566fbccc..72b9a0e5728a 100644 --- a/auditbeat/module/audit/file/metricset.go +++ b/auditbeat/module/audit/file/metricset.go @@ -166,20 +166,20 @@ func (ms *MetricSet) reportEvent(reporter mb.PushReporter, event *Event) bool { event.Action, event.Path, event.errors) } - changed := ms.hasFileChangedSinceLastEvent(event) + changed, lastEvent := ms.hasFileChangedSinceLastEvent(event) if changed { // Publish event if it changed. - if ok := reporter.Event(buildMapStr(event)); !ok { + if ok := reporter.Event(buildMapStr(event, lastEvent != nil)); !ok { return false } } // Persist event locally. - if event.Action == Deleted { + if event.Info == nil { if err := ms.bucket.Delete(event.Path); err != nil { logp.Err("%v %v", logPrefix, err) } - } else if event.Info != nil { + } else { if err := store(ms.bucket, event); err != nil { logp.Err("%v %v", logPrefix, err) } @@ -187,12 +187,12 @@ func (ms *MetricSet) reportEvent(reporter mb.PushReporter, event *Event) bool { return true } -func (ms *MetricSet) hasFileChangedSinceLastEvent(event *Event) bool { +func (ms *MetricSet) hasFileChangedSinceLastEvent(event *Event) (changed bool, lastEvent *Event) { // Load event from DB. lastEvent, err := load(ms.bucket, event.Path) if err != nil { logp.Warn("%v %v", logPrefix, err) - return true + return true, lastEvent } action, changed := diffEvents(lastEvent, event) @@ -204,7 +204,7 @@ func (ms *MetricSet) hasFileChangedSinceLastEvent(event *Event) bool { debugf("file at %v has changed since last seen: old=%v, new=%v", event.Path, lastEvent, event) } - return changed + return changed, lastEvent } func (ms *MetricSet) purgeDeleted(reporter mb.PushReporter) { @@ -217,7 +217,7 @@ func (ms *MetricSet) purgeDeleted(reporter mb.PushReporter) { for _, e := range deleted { // Don't persist! - if !reporter.Event(buildMapStr(e)) { + if !reporter.Event(buildMapStr(e, true)) { return } } diff --git a/auditbeat/module/audit/file/metricset_test.go b/auditbeat/module/audit/file/metricset_test.go index 353ca4d5f0d4..c0270f5b3463 100644 --- a/auditbeat/module/audit/file/metricset_test.go +++ b/auditbeat/module/audit/file/metricset_test.go @@ -90,10 +90,10 @@ func TestDetectDeletedFiles(t *testing.T) { } event := events[0] assert.Equal(t, dir, event["path"]) - assert.Equal(t, "created", event["action"]) + assert.Equal(t, []string{"created"}, event["action"]) event = events[1] assert.Equal(t, e.Path, event["path"]) - assert.Equal(t, "deleted", event["action"]) + assert.Equal(t, []string{"deleted"}, event["action"]) } func setup(t testing.TB) func() { diff --git a/auditbeat/module/audit/file/schema.fbs b/auditbeat/module/audit/file/schema.fbs index 6b287e023c42..97561679f003 100644 --- a/auditbeat/module/audit/file/schema.fbs +++ b/auditbeat/module/audit/file/schema.fbs @@ -1,7 +1,6 @@ namespace schema; -enum Action : ubyte { - None, +enum Action : ubyte (bit_flags) { AttributesModified, Created, Deleted, diff --git a/auditbeat/module/audit/file/schema/Action.go b/auditbeat/module/audit/file/schema/Action.go index 652773ef7727..0a819ede0206 100644 --- a/auditbeat/module/audit/file/schema/Action.go +++ b/auditbeat/module/audit/file/schema/Action.go @@ -3,17 +3,15 @@ package schema const ( - ActionNone = 0 ActionAttributesModified = 1 ActionCreated = 2 - ActionDeleted = 3 - ActionUpdated = 4 - ActionMoved = 5 - ActionConfigChanged = 6 + ActionDeleted = 4 + ActionUpdated = 8 + ActionMoved = 16 + ActionConfigChanged = 32 ) var EnumNamesAction = map[int]string{ - ActionNone: "None", ActionAttributesModified: "AttributesModified", ActionCreated: "Created", ActionDeleted: "Deleted",