diff --git a/pkg/stanza/operator/input/windows/config.schema.yaml b/pkg/stanza/operator/input/windows/config.schema.yaml index 0a8af6a65d99..5b5c0b6bb8c1 100644 --- a/pkg/stanza/operator/input/windows/config.schema.yaml +++ b/pkg/stanza/operator/input/windows/config.schema.yaml @@ -23,6 +23,8 @@ $defs: query: x-pointer: true type: string + extendedFormat: + type: boolean raw: type: boolean remote: diff --git a/pkg/stanza/operator/input/windows/config_all.go b/pkg/stanza/operator/input/windows/config_all.go index 175eef3a22bc..148dd4e9cab1 100644 --- a/pkg/stanza/operator/input/windows/config_all.go +++ b/pkg/stanza/operator/input/windows/config_all.go @@ -36,6 +36,7 @@ type Config struct { StartAt string `mapstructure:"start_at,omitempty"` PollInterval time.Duration `mapstructure:"poll_interval,omitempty"` MaxEventsPerPoll int `mapstructure:"max_events_per_poll,omitempty"` + ExtendedFormat bool `mapstructure:"extended_format,omitempty"` Raw bool `mapstructure:"raw,omitempty"` IncludeLogRecordOriginal bool `mapstructure:"include_log_record_original,omitempty"` SuppressRenderingInfo bool `mapstructure:"suppress_rendering_info,omitempty"` diff --git a/pkg/stanza/operator/input/windows/input.go b/pkg/stanza/operator/input/windows/input.go index 0773a19a8f0f..44c4346c7ed3 100644 --- a/pkg/stanza/operator/input/windows/input.go +++ b/pkg/stanza/operator/input/windows/input.go @@ -34,6 +34,7 @@ type Input struct { currentMaxReads int startAt string raw bool + extendedFormat bool includeLogRecordOriginal bool excludeProviders map[string]struct{} pollInterval time.Duration @@ -352,6 +353,15 @@ func (i *Input) processEventWithRenderingInfo(ctx context.Context, event Event) // sendEvent will send EventXML as an entry to the operator's output. func (i *Input) sendEvent(ctx context.Context, eventXML *EventXML) error { var body any = eventXML.Original + + if i.extendedFormat { + body = extendedFormattedBody(eventXML) + e, err := i.NewEntry(body) + if err != nil { + return fmt.Errorf("create entry: %w", err) + } + return i.Write(ctx, e) + } if !i.raw { body = formattedBody(eventXML) } diff --git a/pkg/stanza/operator/input/windows/xml.go b/pkg/stanza/operator/input/windows/xml.go index e590bd7d623e..19fc131902de 100644 --- a/pkg/stanza/operator/input/windows/xml.go +++ b/pkg/stanza/operator/input/windows/xml.go @@ -141,6 +141,52 @@ func formattedBody(e *EventXML) map[string]any { return body } +// extendedFormattedBody will parse a body from the event. +func extendedFormattedBody(e *EventXML) map[string]any { + body := map[string]any{ + "event_id": map[string]any{ + "qualifiers": e.EventID.Qualifiers, + "id": e.EventID.ID, + }, + "provider": map[string]any{ + "name": e.Provider.Name, + "guid": e.Provider.GUID, + "event_source": e.Provider.EventSourceName, + }, + "system_time": e.TimeCreated.SystemTime, + "computer": e.Computer, + "channel": e.Channel, + "record_id": e.RecordID, + "level": e.Level, + "rendered_level": e.RenderedLevel, + "message": e.Message, + "task": e.Task, + "rendered_task": e.RenderedTask, + "opcode": e.Opcode, + "rendered_opcode": e.RenderedOpcode, + "keywords": e.Keywords, + "rendered_keywords": e.RenderedKeywords, + "event_data": parseEventData(e.EventData), + "version": e.Version, + } + + if e.Security != nil && e.Security.UserID != "" { + body["security"] = map[string]any{ + "user_id": e.Security.UserID, + } + } + + if e.Execution != nil { + body["execution"] = e.Execution.asMap() + } + + if e.Correlation != nil { + body["correlation"] = e.Correlation.asMap() + } + + return body +} + // parseMessage will attempt to parse a message into a message and details func parseMessage(channel, message string) (string, map[string]any) { switch channel { diff --git a/pkg/stanza/operator/input/windows/xml_test.go b/pkg/stanza/operator/input/windows/xml_test.go index 079abc3fb1e8..cffec45880d1 100644 --- a/pkg/stanza/operator/input/windows/xml_test.go +++ b/pkg/stanza/operator/input/windows/xml_test.go @@ -103,6 +103,73 @@ func TestParseBody(t *testing.T) { require.Equal(t, expected, formattedBody(xml)) } +func TestExtendedParseBody(t *testing.T) { + xml := &EventXML{ + EventID: EventID{ + ID: 1, + Qualifiers: 2, + }, + Provider: Provider{ + Name: "provider", + GUID: "guid", + EventSourceName: "event source", + }, + TimeCreated: TimeCreated{ + SystemTime: "2020-07-30T01:01:01.123456789Z", + }, + Computer: "computer", + Channel: "application", + RecordID: 1, + Level: "Information", + Message: "message", + Task: "task", + Opcode: "opcode", + Keywords: []string{"keyword"}, + EventData: EventData{ + Data: []Data{{Name: "1st_name", Value: "value"}, {Name: "2nd_name", Value: "another_value"}}, + }, + RenderedLevel: "rendered_level", + RenderedTask: "rendered_task", + RenderedOpcode: "rendered_opcode", + RenderedKeywords: []string{"RenderedKeywords"}, + Version: 0, + } + + expected := map[string]any{ + "event_id": map[string]any{ + "id": uint32(1), + "qualifiers": uint16(2), + }, + "provider": map[string]any{ + "name": "provider", + "guid": "guid", + "event_source": "event source", + }, + "system_time": "2020-07-30T01:01:01.123456789Z", + "computer": "computer", + "channel": "application", + "record_id": uint64(1), + "level": "Information", + "rendered_level": "rendered_level", + "message": "message", + "task": "task", + "rendered_task": "rendered_task", + "opcode": "opcode", + "rendered_opcode": "rendered_opcode", + "keywords": []string{"keyword"}, + "rendered_keywords": []string{"RenderedKeywords"}, + "event_data": map[string]any{ + "data": []any{ + map[string]any{"1st_name": "value"}, + map[string]any{"2nd_name": "another_value"}, + }, + }, + "version": uint8(0), + } + + require.Equal(t, expected, extendedFormattedBody(xml)) +} + func TestParseBodySecurityExecution(t *testing.T) { xml := &EventXML{ EventID: EventID{