Skip to content

Commit d705a03

Browse files
authored
Add windows event id filtering to CWAgent (#1737)
1 parent a8af9da commit d705a03

23 files changed

+586
-64
lines changed

cmd/config-translator/translator_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,26 @@ func TestLogWindowsEventConfig(t *testing.T) {
103103
checkIfSchemaValidateAsExpected(t, "../../translator/config/sampleSchema/invalidLogWindowsEventsWithInvalidEventName.json", false, expectedErrorMap)
104104
expectedErrorMap1 := map[string]int{}
105105
expectedErrorMap1["required"] = 2
106+
expectedErrorMap1["number_any_of"] = 1
106107
checkIfSchemaValidateAsExpected(t, "../../translator/config/sampleSchema/invalidLogWindowsEventsWithMissingEventNameAndLevel.json", false, expectedErrorMap1)
107108
expectedErrorMap2 := map[string]int{}
108109
expectedErrorMap2["invalid_type"] = 1
109110
checkIfSchemaValidateAsExpected(t, "../../translator/config/sampleSchema/invalidLogWindowsEventsWithInvalidEventLevelType.json", false, expectedErrorMap2)
110111
expectedErrorMap3 := map[string]int{}
111112
expectedErrorMap3["enum"] = 1
112113
checkIfSchemaValidateAsExpected(t, "../../translator/config/sampleSchema/invalidLogWindowsEventsWithInvalidEventFormatType.json", false, expectedErrorMap3)
114+
115+
//New tests for event_ids feature
116+
checkIfSchemaValidateAsExpected(t, "../../translator/config/sampleSchema/invalidLogWindowsEventsWithInvalidEventFormatType.json", false, expectedErrorMap3)
117+
expectedErrorMap4 := map[string]int{}
118+
expectedErrorMap4["invalid_type"] = 1
119+
checkIfSchemaValidateAsExpected(t, "../../translator/config/sampleSchema/invalidLogWindowsEventsWithInvalidEventIdsType.json", false, expectedErrorMap4)
120+
121+
expectedErrorMap5 := map[string]int{}
122+
expectedErrorMap5["required"] = 1
123+
expectedErrorMap5["number_any_of"] = 1
124+
checkIfSchemaValidateAsExpected(t, "../../translator/config/sampleSchema/invalidLogWindowsEventsWithMissingEventIdsAndEventLevels.json", false, expectedErrorMap5)
125+
113126
}
114127

115128
func TestMetricsConfig(t *testing.T) {
@@ -198,7 +211,9 @@ func TestSampleConfigSchema(t *testing.T) {
198211
for _, file := range files {
199212
if re.MatchString(file.Name()) {
200213
t.Logf("Validating ../../translator/tocwconfig/sampleConfig/%s\n", file.Name())
214+
201215
checkIfSchemaValidateAsExpected(t, "../../translator/tocwconfig/sampleConfig/"+file.Name(), true, map[string]int{})
216+
202217
t.Logf("Validated ../../translator/tocwconfig/sampleConfig/%s\n", file.Name())
203218
}
204219
}

plugins/inputs/windows_event_log/windows_event_log.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ var startOnlyOnce sync.Once
3131
type EventConfig struct {
3232
Name string `toml:"event_name"`
3333
Levels []string `toml:"event_levels"`
34+
EventIDs []int `toml:"event_ids"`
3435
RenderFormat string `toml:"event_format"`
3536
BatchReadSize int `toml:"batch_read_size"`
3637
LogGroupName string `toml:"log_group_name"`
@@ -39,7 +40,6 @@ type EventConfig struct {
3940
Destination string `toml:"destination"`
4041
Retention int `toml:"retention_in_days"`
4142
}
42-
4343
type Plugin struct {
4444
FileStateFolder string `toml:"file_state_folder"`
4545
Events []EventConfig `toml:"event_config"`
@@ -61,6 +61,7 @@ func (s *Plugin) SampleConfig() string {
6161
[[inputs.windows_event_log.event_config]]
6262
event_name = "System"
6363
event_levels = ["2", "3"]
64+
event_ids = [1001, 1002]
6465
batch_read_size = 1
6566
log_group_name = "System"
6667
log_stream_name = "STREAM_NAME"
@@ -106,6 +107,7 @@ func (s *Plugin) Start(acc telegraf.Accumulator) error {
106107
eventLog := wineventlog.NewEventLog(
107108
eventConfig.Name,
108109
eventConfig.Levels,
110+
eventConfig.EventIDs,
109111
eventConfig.LogGroupName,
110112
eventConfig.LogStreamName,
111113
eventConfig.RenderFormat,

plugins/inputs/windows_event_log/wineventlog/utils.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ const (
2626
bookmarkTemplate = `<BookmarkList><Bookmark Channel="%s" RecordId="%d" IsCurrent="True"/></BookmarkList>`
2727
eventLogQueryTemplate = `<QueryList><Query Id="0"><Select Path="%s">*[System[%s]]</Select></Query></QueryList>`
2828
eventLogLevelFilter = "Level='%s'"
29+
eventLogeventIDFilter = "EventID='%d'"
2930
eventIgnoreOldFilter = "TimeCreated[timediff(@SystemTime) &lt;= %d]"
3031
eventRangeFilter = "EventRecordID &gt; %d and EventRecordID &lt;= %d"
3132
emptySpaceScanLength = 100
3233
UnknownBytesPerCharacter = 0
34+
cutOffPeriod = time.Hour * 24 * 14
3335

3436
CRITICAL = "CRITICAL"
3537
ERROR = "ERROR"
@@ -66,16 +68,16 @@ func CreateBookmark(w WindowsEventAPI, channel string, recordID uint64) (h EvtHa
6668
return h, nil
6769
}
6870

69-
func CreateQuery(path string, levels []string) (*uint16, error) {
70-
return createWindowsEventFilter(path, levels)
71+
func CreateQuery(path string, levels []string, eventIDs []int) (*uint16, error) {
72+
return createWindowsEventFilter(path, levels, eventIDs)
7173
}
7274

73-
func CreateRangeQuery(path string, levels []string, r state.Range) (*uint16, error) {
75+
func CreateRangeQuery(path string, levels []string, eventIDs []int, r state.Range) (*uint16, error) {
7476
rangeFilter := fmt.Sprintf(eventRangeFilter, r.StartOffset(), r.EndOffset())
75-
return createWindowsEventFilter(path, levels, rangeFilter)
77+
return createWindowsEventFilter(path, levels, eventIDs, rangeFilter)
7678
}
7779

78-
func createWindowsEventFilter(path string, levels []string, additionalFilters ...string) (*uint16, error) {
80+
func createWindowsEventFilter(path string, levels []string, eventIDs []int, additionalFilters ...string) (*uint16, error) {
7981
// Add log levels
8082
var levelsFilter string
8183
formattedLevels := make([]string, len(levels))
@@ -86,6 +88,16 @@ func createWindowsEventFilter(path string, levels []string, additionalFilters ..
8688
levelsFilter = fmt.Sprintf("(%s)", strings.Join(formattedLevels, " or "))
8789
}
8890

91+
// Add eventIDs
92+
var eventIDFilter string
93+
formattedeventIDs := make([]string, len(eventIDs))
94+
for i, eventIDs := range eventIDs {
95+
formattedeventIDs[i] = fmt.Sprintf(eventLogeventIDFilter, eventIDs)
96+
}
97+
if len(formattedeventIDs) > 0 {
98+
eventIDFilter = fmt.Sprintf("(%s)", strings.Join(formattedeventIDs, " or "))
99+
}
100+
89101
// Ignore events older than 2 weeks
90102
cutOffPeriod := (time.Hour * 24 * 14).Nanoseconds()
91103
ignoreOlderThanTwoWeeksFilter := fmt.Sprintf(eventIgnoreOldFilter, cutOffPeriod/int64(time.Millisecond))
@@ -94,6 +106,9 @@ func createWindowsEventFilter(path string, levels []string, additionalFilters ..
94106
if levelsFilter != "" {
95107
filters = append(filters, levelsFilter)
96108
}
109+
if eventIDFilter != "" {
110+
filters = append(filters, eventIDFilter)
111+
}
97112
filters = append(filters, ignoreOlderThanTwoWeeksFilter)
98113
// Add any additional filters (e.g. record IDs)
99114
if len(additionalFilters) > 0 {

plugins/inputs/windows_event_log/wineventlog/utils_test.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,11 @@ func TestInsertPlaceholderValues(t *testing.T) {
132132
})
133133
}
134134
}
135-
136135
func TestCreateQuery(t *testing.T) {
137136
tests := []struct {
138137
name string
139138
path string
139+
eventIDs []int
140140
levels []string
141141
expected string
142142
}{
@@ -146,18 +146,43 @@ func TestCreateQuery(t *testing.T) {
146146
levels: []string{"2"},
147147
expected: `<QueryList><Query Id="0"><Select Path="Application">*[System[(Level='2') and TimeCreated[timediff(@SystemTime) &lt;= 1209600000]]]</Select></Query></QueryList>`,
148148
},
149+
{
150+
name: "Single eventIDs filter",
151+
path: "Application",
152+
eventIDs: []int{1002},
153+
expected: `<QueryList><Query Id="0"><Select Path="Application">*[System[(EventID='1002') and TimeCreated[timediff(@SystemTime) &lt;= 1209600000]]]</Select></Query></QueryList>`,
154+
},
149155
{
150156
name: "Multiple level filters",
151157
path: "System",
152158
levels: []string{"2", "3", "4"},
153159
expected: `<QueryList><Query Id="0"><Select Path="System">*[System[(Level='2' or Level='3' or Level='4') and TimeCreated[timediff(@SystemTime) &lt;= 1209600000]]]</Select></Query></QueryList>`,
154160
},
161+
{
162+
name: "Multiple eventIDs filters",
163+
path: "System",
164+
eventIDs: []int{100, 200, 300},
165+
expected: `<QueryList><Query Id="0"><Select Path="System">*[System[(EventID='100' or EventID='200' or EventID='300') and TimeCreated[timediff(@SystemTime) &lt;= 1209600000]]]</Select></Query></QueryList>`,
166+
},
155167
{
156168
name: "No level filters",
157169
path: "Security",
158170
levels: []string{},
159171
expected: `<QueryList><Query Id="0"><Select Path="Security">*[System[TimeCreated[timediff(@SystemTime) &lt;= 1209600000]]]</Select></Query></QueryList>`,
160172
},
173+
{
174+
name: "No eventIDs filters",
175+
path: "Security",
176+
eventIDs: []int{},
177+
expected: `<QueryList><Query Id="0"><Select Path="Security">*[System[TimeCreated[timediff(@SystemTime) &lt;= 1209600000]]]</Select></Query></QueryList>`,
178+
},
179+
{
180+
name: "Both level and eventIDs filters",
181+
path: "Security",
182+
levels: []string{"2"},
183+
eventIDs: []int{100},
184+
expected: `<QueryList><Query Id="0"><Select Path="Security">*[System[(Level='2') and (EventID='100') and TimeCreated[timediff(@SystemTime) &lt;= 1209600000]]]</Select></Query></QueryList>`,
185+
},
161186
{
162187
name: "Empty level filters",
163188
path: "Application",
@@ -174,7 +199,7 @@ func TestCreateQuery(t *testing.T) {
174199

175200
for _, tc := range tests {
176201
t.Run(tc.name, func(t *testing.T) {
177-
ptr, err := CreateQuery(tc.path, tc.levels)
202+
ptr, err := CreateQuery(tc.path, tc.levels, tc.eventIDs)
178203
assert.NoError(t, err)
179204
assert.NotNil(t, ptr)
180205
assert.Equal(t, tc.expected, utf16PtrToString(ptr))
@@ -187,6 +212,7 @@ func TestCreateRangeQuery(t *testing.T) {
187212
name string
188213
path string
189214
levels []string
215+
eventIDs []int
190216
r state.Range
191217
expected string
192218
}{
@@ -211,6 +237,13 @@ func TestCreateRangeQuery(t *testing.T) {
211237
r: state.NewRange(50, 150),
212238
expected: `<QueryList><Query Id="0"><Select Path="Security">*[System[TimeCreated[timediff(@SystemTime) &lt;= 1209600000] and EventRecordID &gt; 50 and EventRecordID &lt;= 150]]</Select></Query></QueryList>`,
213239
},
240+
{
241+
name: "Multiple eventIDs with range",
242+
path: "System",
243+
eventIDs: []int{45, 99},
244+
r: state.NewRange(1000, 2000),
245+
expected: `<QueryList><Query Id="0"><Select Path="System">*[System[(EventID='45' or EventID='99') and TimeCreated[timediff(@SystemTime) &lt;= 1209600000] and EventRecordID &gt; 1000 and EventRecordID &lt;= 2000]]</Select></Query></QueryList>`,
246+
},
214247
{
215248
name: "Empty levels with range",
216249
path: "Application",
@@ -243,7 +276,7 @@ func TestCreateRangeQuery(t *testing.T) {
243276

244277
for _, tc := range tests {
245278
t.Run(tc.name, func(t *testing.T) {
246-
ptr, err := CreateRangeQuery(tc.path, tc.levels, tc.r)
279+
ptr, err := CreateRangeQuery(tc.path, tc.levels, tc.eventIDs, tc.r)
247280
assert.NoError(t, err)
248281
assert.NotNil(t, ptr)
249282
assert.Equal(t, tc.expected, utf16PtrToString(ptr))

plugins/inputs/windows_event_log/wineventlog/wineventlog.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ func (e *wevtAPIError) Error() string {
5252
type windowsEventLog struct {
5353
name string
5454
levels []string
55+
eventIDs []int
5556
logGroupName string
5657
logStreamName string
5758
logGroupClass string
@@ -70,10 +71,11 @@ type windowsEventLog struct {
7071
resubscribeCh chan struct{}
7172
}
7273

73-
func NewEventLog(name string, levels []string, logGroupName, logStreamName, renderFormat, destination string, stateManager state.FileRangeManager, maximumToRead int, retention int, logGroupClass string) *windowsEventLog {
74+
func NewEventLog(name string, levels []string, eventIDs []int, logGroupName, logStreamName, renderFormat, destination string, stateManager state.FileRangeManager, maximumToRead int, retention int, logGroupClass string) *windowsEventLog {
7475
eventLog := &windowsEventLog{
7576
name: name,
7677
levels: levels,
78+
eventIDs: eventIDs,
7779
logGroupName: logGroupName,
7880
logStreamName: logStreamName,
7981
logGroupClass: logGroupClass,
@@ -92,6 +94,17 @@ func NewEventLog(name string, levels []string, logGroupName, logStreamName, rend
9294
}
9395

9496
func (w *windowsEventLog) Init() error {
97+
const (
98+
minEventID = 0
99+
maxEventID = 65535
100+
)
101+
102+
for _, eventID := range w.eventIDs {
103+
if eventID < minEventID || eventID > maxEventID {
104+
return fmt.Errorf("invalid event ID: %d, event IDs must be between %d and %d", eventID, minEventID, maxEventID)
105+
}
106+
}
107+
95108
go w.stateManager.Run(state.Notification{Done: w.done})
96109
restored, _ := w.stateManager.Restore()
97110
// Do note that the end offset is inclusive here as opposed to exclusive like done
@@ -231,7 +244,7 @@ func (w *windowsEventLog) open() error {
231244
if err != nil {
232245
return err
233246
}
234-
query, err := CreateQuery(w.name, w.levels)
247+
query, err := CreateQuery(w.name, w.levels, w.eventIDs)
235248
if err != nil {
236249
return err
237250
}
@@ -248,7 +261,7 @@ func (w *windowsEventLog) openAtRange(r state.Range) (EvtHandle, error) {
248261
if err != nil {
249262
return 0, err
250263
}
251-
query, err := CreateRangeQuery(w.name, w.levels, r)
264+
query, err := CreateRangeQuery(w.name, w.levels, w.eventIDs, r)
252265
if err != nil {
253266
return 0, err
254267
}

0 commit comments

Comments
 (0)