From d87f1a6fe508d7ec59ebfd906a9a24896cc55dab Mon Sep 17 00:00:00 2001 From: armstrmi Date: Tue, 26 Apr 2022 16:25:36 -0400 Subject: [PATCH 1/3] initial commit --- operator/input/windows/xml.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/operator/input/windows/xml.go b/operator/input/windows/xml.go index 5b94ae43..78b489e4 100644 --- a/operator/input/windows/xml.go +++ b/operator/input/windows/xml.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build windows // +build windows package windows @@ -32,11 +33,11 @@ type EventXML struct { Channel string `xml:"System>Channel"` RecordID uint64 `xml:"System>EventRecordID"` TimeCreated TimeCreated `xml:"System>TimeCreated"` - Message string `xml:"RenderingInfo>Message"` - Level string `xml:"RenderingInfo>Level"` - Task string `xml:"RenderingInfo>Task"` - Opcode string `xml:"RenderingInfo>Opcode"` - Keywords []string `xml:"RenderingInfo>Keywords>Keyword"` + Message string `xml:"EventData>Data"` + Level string `xml:"System>Level"` + Task string `xml:"System>Task"` + Opcode string `xml:"System>Opcode"` + Keywords []string `xml:"System>Keywords>Keyword"` } // parseTimestamp will parse the timestamp of the event. From 6236e6a2941ab81f333e4f4a6306a6060563fc28 Mon Sep 17 00:00:00 2001 From: armstrmi Date: Tue, 3 May 2022 15:31:42 +0000 Subject: [PATCH 2/3] added separate fields for RenderingInfo and EventData --- operator/input/windows/testdata/xmlSample.xml | 22 +++ operator/input/windows/xml.go | 50 +++--- operator/input/windows/xml_test.go | 144 +++++++++++++++--- 3 files changed, 179 insertions(+), 37 deletions(-) create mode 100644 operator/input/windows/testdata/xmlSample.xml diff --git a/operator/input/windows/testdata/xmlSample.xml b/operator/input/windows/testdata/xmlSample.xml new file mode 100644 index 00000000..0a5e712a --- /dev/null +++ b/operator/input/windows/testdata/xmlSample.xml @@ -0,0 +1,22 @@ + + + + 16384 + 0 + 4 + 0 + 0 + 0x80000000000000 + + 23401 + + + Application + computer + + + + 2022-04-28T19:48:52Z + RulesEngine + + diff --git a/operator/input/windows/xml.go b/operator/input/windows/xml.go index 78b489e4..fe993da2 100644 --- a/operator/input/windows/xml.go +++ b/operator/input/windows/xml.go @@ -27,17 +27,22 @@ import ( // EventXML is the rendered xml of an event. type EventXML struct { - EventID EventID `xml:"System>EventID"` - Provider Provider `xml:"System>Provider"` - Computer string `xml:"System>Computer"` - Channel string `xml:"System>Channel"` - RecordID uint64 `xml:"System>EventRecordID"` - TimeCreated TimeCreated `xml:"System>TimeCreated"` - Message string `xml:"EventData>Data"` - Level string `xml:"System>Level"` - Task string `xml:"System>Task"` - Opcode string `xml:"System>Opcode"` - Keywords []string `xml:"System>Keywords>Keyword"` + EventID EventID `xml:"System>EventID"` + Provider Provider `xml:"System>Provider"` + Computer string `xml:"System>Computer"` + Channel string `xml:"System>Channel"` + RecordID uint64 `xml:"System>EventRecordID"` + TimeCreated TimeCreated `xml:"System>TimeCreated"` + Message string `xml:"RenderingInfo>Message"` + RenderedLevel string `xml:"RenderingInfo>Level"` + Level string `xml:"System>Level"` + RenderedTask string `xml:"RenderingInfo>Task"` + Task string `xml:"System>Task"` + RenderedOpcode string `xml:"RenderingInfo>Opcode"` + Opcode string `xml:"System>Opcode"` + RenderedKeywords []string `xml:"RenderingInfo>Keywords>Keyword"` + Keywords []string `xml:"System>Keywords"` + EventData []string `xml:"EventData>Data"` } // parseTimestamp will parse the timestamp of the event. @@ -77,15 +82,20 @@ func (e *EventXML) parseBody() map[string]interface{} { "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, - "message": message, - "task": e.Task, - "opcode": e.Opcode, - "keywords": e.Keywords, + "system_time": e.TimeCreated.SystemTime, + "computer": e.Computer, + "channel": e.Channel, + "record_id": e.RecordID, + "level": e.Level, + "rendered_level": e.RenderedLevel, + "message": message, + "task": e.Task, + "rendered_task": e.RenderedTask, + "opcode": e.Opcode, + "rendered_opcode": e.RenderedOpcode, + "keywords": e.Keywords, + "rendered_keywords": e.RenderedKeywords, + "event_data": e.EventData, } if len(details) > 0 { body["details"] = details diff --git a/operator/input/windows/xml_test.go b/operator/input/windows/xml_test.go index 7d4b92ef..eeb6cdae 100644 --- a/operator/input/windows/xml_test.go +++ b/operator/input/windows/xml_test.go @@ -12,11 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build windows // +build windows package windows import ( + "io/ioutil" + "path/filepath" "testing" "time" @@ -75,14 +78,19 @@ func TestParseBody(t *testing.T) { 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"}, + Computer: "computer", + Channel: "application", + RecordID: 1, + Level: "Information", + Message: "message", + Task: "task", + Opcode: "opcode", + Keywords: []string{"keyword"}, + EventData: []string{"this", "is", "some", "sample", "data"}, + RenderedLevel: "rendered_level", + RenderedTask: "rendered_task", + RenderedOpcode: "rendered_opcode", + RenderedKeywords: []string{"RenderedKeywords"}, } expected := map[string]interface{}{ @@ -95,16 +103,118 @@ func TestParseBody(t *testing.T) { "guid": "guid", "event_source": "event source", }, - "system_time": "2020-07-30T01:01:01.123456789Z", - "computer": "computer", - "channel": "application", - "record_id": uint64(1), - "level": "Information", - "message": "message", - "task": "task", - "opcode": "opcode", - "keywords": []string{"keyword"}, + "system_time": "2020-07-30T01:01:01.123456789Z", + "computer": "computer", + "channel": "application", + "record_id": uint64(1), + "level": "Information", + "message": "message", + "task": "task", + "opcode": "opcode", + "keywords": []string{"keyword"}, + "rendered_level": "rendered_level", + "rendered_task": "rendered_task", + "rendered_opcode": "rendered_opcode", + "rendered_keywords": []string{"RenderedKeywords"}, + "event_data": []string{"this", "is", "some", "sample", "data"}, } require.Equal(t, expected, xml.parseBody()) } + +func TestParseBody2(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: "Security", + RecordID: 1, + Level: "Information", + Message: "message", + Task: "task", + Opcode: "opcode", + Keywords: []string{"keyword"}, + EventData: []string{"this", "is", "some", "sample", "data"}, + RenderedLevel: "rendered_level", + RenderedTask: "rendered_task", + RenderedOpcode: "rendered_opcode", + RenderedKeywords: []string{"RenderedKeywords"}, + } + + expected := map[string]interface{}{ + "event_id": map[string]interface{}{ + "id": uint32(1), + "qualifiers": uint16(2), + }, + "provider": map[string]interface{}{ + "name": "provider", + "guid": "guid", + "event_source": "event source", + }, + "system_time": "2020-07-30T01:01:01.123456789Z", + "computer": "computer", + "channel": "Security", + "record_id": uint64(1), + "level": "Information", + "message": "message", + "task": "task", + "opcode": "opcode", + "keywords": []string{"keyword"}, + "rendered_level": "rendered_level", + "rendered_task": "rendered_task", + "rendered_opcode": "rendered_opcode", + "rendered_keywords": []string{"RenderedKeywords"}, + "event_data": []string{"this", "is", "some", "sample", "data"}, + } + + require.Equal(t, expected, xml.parseBody()) +} + +func TestInvalidUnmarshal(t *testing.T) { + _, err := unmarshalEventXML([]byte("Test \n Invalid \t Unmarshal")) + require.Error(t, err) + +} +func TestUnmarshal(t *testing.T) { + data, err := ioutil.ReadFile(filepath.Join("testdata", "xmlSample.xml")) + require.NoError(t, err) + + event, err := unmarshalEventXML(data) + require.NoError(t, err) + + xml := EventXML{ + EventID: EventID{ + ID: 16384, + Qualifiers: 16384, + }, + Provider: Provider{ + Name: "Microsoft-Windows-Security-SPP", + GUID: "{E23B33B0-C8C9-472C-A5F9-F2BDFEA0F156}", + EventSourceName: "Software Protection Platform Service", + }, + TimeCreated: TimeCreated{ + SystemTime: "2022-04-22T10:20:52.3778625Z", + }, + Computer: "computer", + Channel: "Application", + RecordID: 23401, + Level: "4", + Message: "", + Task: "0", + Opcode: "0", + EventData: []string{"2022-04-28T19:48:52Z", "RulesEngine"}, + Keywords: []string{"0x80000000000000"}, + } + + require.Equal(t, xml, event) +} From df95d8a79f61d6d0349d02adf2ca6f7f8b4e0b1a Mon Sep 17 00:00:00 2001 From: armstrmi Date: Wed, 4 May 2022 15:12:59 +0000 Subject: [PATCH 3/3] changed xml.go to only populate output with necessary fields --- operator/input/windows/operator.go | 2 +- operator/input/windows/xml.go | 69 +++++++++++---- operator/input/windows/xml_test.go | 130 ++++++++++++++++++++--------- 3 files changed, 144 insertions(+), 57 deletions(-) diff --git a/operator/input/windows/operator.go b/operator/input/windows/operator.go index dda57773..12fbe085 100644 --- a/operator/input/windows/operator.go +++ b/operator/input/windows/operator.go @@ -230,7 +230,7 @@ func (e *EventLogInput) sendEvent(ctx context.Context, eventXML EventXML) { } entry.Timestamp = eventXML.parseTimestamp() - entry.Severity = eventXML.parseSeverity() + entry.Severity = eventXML.parseRenderedSeverity() e.Write(ctx, entry) } diff --git a/operator/input/windows/xml.go b/operator/input/windows/xml.go index fe993da2..64336a54 100644 --- a/operator/input/windows/xml.go +++ b/operator/input/windows/xml.go @@ -53,9 +53,11 @@ func (e *EventXML) parseTimestamp() time.Time { return time.Now() } -// parseSeverity will parse the severity of the event. -func (e *EventXML) parseSeverity() entry.Severity { - switch e.Level { +// parseRenderedSeverity will parse the severity of the event. +func (e *EventXML) parseRenderedSeverity() entry.Severity { + switch e.RenderedLevel { + case "": + return e.parseSeverity() case "Critical": return entry.Fatal case "Error": @@ -69,9 +71,46 @@ func (e *EventXML) parseSeverity() entry.Severity { } } +// parseSeverity will parse the severity of the event when RenderingInfo is not populated +func (e *EventXML) parseSeverity() entry.Severity { + switch e.Level { + case "1": + return entry.Fatal + case "2": + return entry.Error + case "3": + return entry.Warn + case "4": + return entry.Info + default: + return entry.Default + } +} + // parseBody will parse a body from the event. func (e *EventXML) parseBody() map[string]interface{} { message, details := e.parseMessage() + + level := e.RenderedLevel + if level == "" { + level = e.Level + } + + task := e.RenderedTask + if task == "" { + task = e.Task + } + + opcode := e.RenderedOpcode + if opcode == "" { + opcode = e.Opcode + } + + keywords := e.RenderedKeywords + if keywords == nil { + keywords = e.Keywords + } + body := map[string]interface{}{ "event_id": map[string]interface{}{ "qualifiers": e.EventID.Qualifiers, @@ -82,20 +121,16 @@ func (e *EventXML) parseBody() map[string]interface{} { "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": message, - "task": e.Task, - "rendered_task": e.RenderedTask, - "opcode": e.Opcode, - "rendered_opcode": e.RenderedOpcode, - "keywords": e.Keywords, - "rendered_keywords": e.RenderedKeywords, - "event_data": e.EventData, + "system_time": e.TimeCreated.SystemTime, + "computer": e.Computer, + "channel": e.Channel, + "record_id": e.RecordID, + "level": level, + "message": message, + "task": task, + "opcode": opcode, + "keywords": keywords, + "event_data": e.EventData, } if len(details) > 0 { body["details"] = details diff --git a/operator/input/windows/xml_test.go b/operator/input/windows/xml_test.go index eeb6cdae..caffe98e 100644 --- a/operator/input/windows/xml_test.go +++ b/operator/input/windows/xml_test.go @@ -52,16 +52,26 @@ func TestParseInvalidTimestamp(t *testing.T) { } func TestParseSeverity(t *testing.T) { - xmlCritical := EventXML{Level: "Critical"} - xmlError := EventXML{Level: "Error"} - xmlWarning := EventXML{Level: "Warning"} - xmlInformation := EventXML{Level: "Information"} - xmlUnknown := EventXML{Level: "Unknown"} - require.Equal(t, entry.Fatal, xmlCritical.parseSeverity()) - require.Equal(t, entry.Error, xmlError.parseSeverity()) - require.Equal(t, entry.Warn, xmlWarning.parseSeverity()) - require.Equal(t, entry.Info, xmlInformation.parseSeverity()) - require.Equal(t, entry.Default, xmlUnknown.parseSeverity()) + xmlRenderedCritical := EventXML{RenderedLevel: "Critical"} + xmlRenderedError := EventXML{RenderedLevel: "Error"} + xmlRenderedWarning := EventXML{RenderedLevel: "Warning"} + xmlRenderedInformation := EventXML{RenderedLevel: "Information"} + xmlRenderedUnknown := EventXML{RenderedLevel: "Unknown"} + xmlCritical := EventXML{Level: "1"} + xmlError := EventXML{Level: "2"} + xmlWarning := EventXML{Level: "3"} + xmlInformation := EventXML{Level: "4"} + xmlUnknown := EventXML{Level: "0"} + require.Equal(t, entry.Fatal, xmlRenderedCritical.parseRenderedSeverity()) + require.Equal(t, entry.Error, xmlRenderedError.parseRenderedSeverity()) + require.Equal(t, entry.Warn, xmlRenderedWarning.parseRenderedSeverity()) + require.Equal(t, entry.Info, xmlRenderedInformation.parseRenderedSeverity()) + require.Equal(t, entry.Default, xmlRenderedUnknown.parseRenderedSeverity()) + require.Equal(t, entry.Fatal, xmlCritical.parseRenderedSeverity()) + require.Equal(t, entry.Error, xmlError.parseRenderedSeverity()) + require.Equal(t, entry.Warn, xmlWarning.parseRenderedSeverity()) + require.Equal(t, entry.Info, xmlInformation.parseRenderedSeverity()) + require.Equal(t, entry.Default, xmlUnknown.parseRenderedSeverity()) } func TestParseBody(t *testing.T) { @@ -103,26 +113,72 @@ func TestParseBody(t *testing.T) { "guid": "guid", "event_source": "event source", }, - "system_time": "2020-07-30T01:01:01.123456789Z", - "computer": "computer", - "channel": "application", - "record_id": uint64(1), - "level": "Information", - "message": "message", - "task": "task", - "opcode": "opcode", - "keywords": []string{"keyword"}, - "rendered_level": "rendered_level", - "rendered_task": "rendered_task", - "rendered_opcode": "rendered_opcode", - "rendered_keywords": []string{"RenderedKeywords"}, - "event_data": []string{"this", "is", "some", "sample", "data"}, + "system_time": "2020-07-30T01:01:01.123456789Z", + "computer": "computer", + "channel": "application", + "record_id": uint64(1), + "level": "rendered_level", + "message": "message", + "task": "rendered_task", + "opcode": "rendered_opcode", + "keywords": []string{"RenderedKeywords"}, + "event_data": []string{"this", "is", "some", "sample", "data"}, } require.Equal(t, expected, xml.parseBody()) } -func TestParseBody2(t *testing.T) { +func TestParseNoRendered(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: []string{"this", "is", "some", "sample", "data"}, + } + + expected := map[string]interface{}{ + "event_id": map[string]interface{}{ + "id": uint32(1), + "qualifiers": uint16(2), + }, + "provider": map[string]interface{}{ + "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", + "message": "message", + "task": "task", + "opcode": "opcode", + "keywords": []string{"keyword"}, + "event_data": []string{"this", "is", "some", "sample", "data"}, + } + + require.Equal(t, expected, xml.parseBody()) +} + +func TestParseBodySecurity(t *testing.T) { xml := EventXML{ EventID: EventID{ ID: 1, @@ -161,20 +217,16 @@ func TestParseBody2(t *testing.T) { "guid": "guid", "event_source": "event source", }, - "system_time": "2020-07-30T01:01:01.123456789Z", - "computer": "computer", - "channel": "Security", - "record_id": uint64(1), - "level": "Information", - "message": "message", - "task": "task", - "opcode": "opcode", - "keywords": []string{"keyword"}, - "rendered_level": "rendered_level", - "rendered_task": "rendered_task", - "rendered_opcode": "rendered_opcode", - "rendered_keywords": []string{"RenderedKeywords"}, - "event_data": []string{"this", "is", "some", "sample", "data"}, + "system_time": "2020-07-30T01:01:01.123456789Z", + "computer": "computer", + "channel": "Security", + "record_id": uint64(1), + "level": "rendered_level", + "message": "message", + "task": "rendered_task", + "opcode": "rendered_opcode", + "keywords": []string{"RenderedKeywords"}, + "event_data": []string{"this", "is", "some", "sample", "data"}, } require.Equal(t, expected, xml.parseBody())