From 8c890639f60d4ad78d663b06d6e2e3fd56b85fb1 Mon Sep 17 00:00:00 2001 From: Dylan Myers Date: Tue, 9 Jun 2026 14:47:03 -0400 Subject: [PATCH] fix(wel): correct RenderingInfo format (wrap Keywords, add Culture) --- generator/wel/event.go | 10 +++++-- generator/wel/event_test.go | 55 ++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/generator/wel/event.go b/generator/wel/event.go index 45a1d4c..4acf5f6 100644 --- a/generator/wel/event.go +++ b/generator/wel/event.go @@ -78,7 +78,7 @@ func (e *EventRecord) ToXML() string { // RenderingInfo if e.Message != "" || e.LevelName != "" || e.TaskName != "" { - b.WriteString(" \n") + b.WriteString(" \n") if e.Message != "" { fmt.Fprintf(&b, " %s\n", xmlEscape(e.Message)) } @@ -91,8 +91,12 @@ func (e *EventRecord) ToXML() string { if e.OpcodeName != "" { fmt.Fprintf(&b, " %s\n", xmlEscape(e.OpcodeName)) } - for _, kw := range e.KeywordNames { - fmt.Fprintf(&b, " %s\n", xmlEscape(kw)) + if len(e.KeywordNames) > 0 { + b.WriteString(" \n") + for _, kw := range e.KeywordNames { + fmt.Fprintf(&b, " %s\n", xmlEscape(kw)) + } + b.WriteString(" \n") } b.WriteString(" \n") } diff --git a/generator/wel/event_test.go b/generator/wel/event_test.go index 1329c51..aaa83de 100644 --- a/generator/wel/event_test.go +++ b/generator/wel/event_test.go @@ -55,7 +55,7 @@ func TestEventRecordToXML(t *testing.T) { `S-1-5-18`, `jsmith`, `3`, - ``, + ``, `An account was successfully logged on.`, `Information`, `Logon`, @@ -69,6 +69,59 @@ func TestEventRecordToXML(t *testing.T) { } } +func TestEventRecordToXMLRenderingInfoKeywordsWrapper(t *testing.T) { + rec := &EventRecord{ + ProviderName: "Microsoft-Windows-Security-Auditing", + EventID: 4624, + Channel: "Security", + Computer: "WIN-SERVER01.contoso.com", + Keywords: 0x8020000000000000, + KeywordNames: []string{"Audit Success"}, + Message: "An account was successfully logged on.", + LevelName: "Information", + TimeCreated: time.Date(2024, 3, 15, 10, 30, 0, 0, time.UTC), + EventRecordID: 12345, + } + + xml := rec.ToXML() + + // Per the WEL schema, elements inside must be + // wrapped in a container. This is distinct from the System-level + // 0x... hex bitmask. + wantBlock := " \n Audit Success\n \n" + if !strings.Contains(xml, wantBlock) { + t.Errorf("RenderingInfo keywords not wrapped in container.\nwant block:\n%s\nfull XML:\n%s", wantBlock, xml) + } + + // A bare directly under (4-space indent, no wrapper) + // is the non-conformant form and must not appear. + if strings.Contains(xml, " \n ") { + t.Errorf("found bare directly under (missing wrapper)\n\nfull XML:\n%s", xml) + } +} + +func TestEventRecordToXMLRenderingInfoCulture(t *testing.T) { + rec := &EventRecord{ + ProviderName: "Microsoft-Windows-Security-Auditing", + EventID: 4624, + Channel: "Security", + Computer: "WIN-SERVER01.contoso.com", + KeywordNames: []string{"Audit Success"}, + Message: "An account was successfully logged on.", + LevelName: "Information", + TimeCreated: time.Date(2024, 3, 15, 10, 30, 0, 0, time.UTC), + EventRecordID: 12345, + } + + xml := rec.ToXML() + + // Per the WEL schema, the RenderingInfo element carries a required Culture + // attribute, and real (WEC-forwarded, rendered-text) events always include it. + if !strings.Contains(xml, ``) { + t.Errorf("RenderingInfo missing required Culture attribute.\n\nfull XML:\n%s", xml) + } +} + func TestEventRecordToXMLNoGUID(t *testing.T) { rec := &EventRecord{ ProviderName: "Service Control Manager",