diff --git a/vql/sigma/details.go b/vql/sigma/details.go index 2e0b83d2433..644f4b281e3 100644 --- a/vql/sigma/details.go +++ b/vql/sigma/details.go @@ -2,6 +2,7 @@ package sigma import ( "context" + "encoding/json" "regexp" "github.com/Velocidex/ordereddict" @@ -46,10 +47,26 @@ func (self *SigmaContext) AddDetail( return in } - res, ok := resolved[0].(string) - if ok { - return res + if len(resolved) == 1 { + res, ok := resolved[0].(string) + if ok { + return res + } + + // If it is not a string, serialize to json and + // interpolate instead. + serialized, err := json.Marshal(resolved[0]) + if err == nil { + return string(serialized) + } + } + + // Handle lists and dicts + serialized, err := json.Marshal(resolved) + if err == nil { + return string(serialized) } + return in }) } diff --git a/vql/sigma/fixtures/TestSigma.golden b/vql/sigma/fixtures/TestSigma.golden index 7428dc3fefe..f080cb6c099 100644 --- a/vql/sigma/fixtures/TestSigma.golden +++ b/vql/sigma/fixtures/TestSigma.golden @@ -2,6 +2,16 @@ "Match single field": [ { "Foo": "Bar", + "Integer": 4, + "List": [ + 1, + 2, + 3 + ], + "Dict": { + "X": 1, + "Y": 2 + }, "Baz": "Hello", "Details": null, "_Match": { @@ -48,8 +58,18 @@ "Rule With Details": [ { "Foo": "Bar", + "Integer": 4, + "List": [ + 1, + 2, + 3 + ], + "Dict": { + "X": 1, + "Y": 2 + }, "Baz": "Hello", - "Details": "This is column Foo=Bar", + "Details": "This is column Foo=Bar Int=4 List=[1,2,3] Dict={\"X\":1,\"Y\":2}", "_Match": { "Match": true, "SearchResults": { @@ -75,6 +95,12 @@ "Values": [ "Bar" ] + }, + { + "Field": "Integer", + "Values": [ + 4 + ] } ] ] @@ -89,7 +115,7 @@ ] }, "AdditionalFields": { - "details": "This is column Foo=%Foo%" + "details": "This is column Foo=%Foo% Int=%Integer% List=%List% Dict=%Dict%" } } } @@ -97,6 +123,16 @@ "Default Details in callback": [ { "Foo": "Bar", + "Integer": 4, + "List": [ + 1, + 2, + 3 + ], + "Dict": { + "X": 1, + "Y": 2 + }, "Baz": "Hello", "Details": "I'm a scope var:Default Detail Foo=Bar", "_Match": { @@ -143,6 +179,16 @@ "Match field with regex": [ { "Foo": "Bar", + "Integer": 4, + "List": [ + 1, + 2, + 3 + ], + "Dict": { + "X": 1, + "Y": 2 + }, "Baz": "Hello", "Details": null, "_Match": { @@ -192,6 +238,16 @@ "Match field with logical operators": [ { "Foo": "Bar", + "Integer": 4, + "List": [ + 1, + 2, + 3 + ], + "Dict": { + "X": 1, + "Y": 2 + }, "Baz": "Hello", "Details": null, "_Match": { @@ -262,6 +318,16 @@ "Match field with logical or operator": [ { "Foo": "Bar", + "Integer": 4, + "List": [ + 1, + 2, + 3 + ], + "Dict": { + "X": 1, + "Y": 2 + }, "Baz": "Hello", "Details": null, "_Match": { diff --git a/vql/sigma/sigma_test.go b/vql/sigma/sigma_test.go index a637f4bb638..2c1b19b66e4 100644 --- a/vql/sigma/sigma_test.go +++ b/vql/sigma/sigma_test.go @@ -67,6 +67,11 @@ detection: testRows = []*ordereddict.Dict{ ordereddict.NewDict(). Set("Foo", "Bar"). + Set("Integer", 4). + Set("List", []int64{1, 2, 3}). + Set("Dict", map[string]interface{}{ + "X": 1, "Y": 2, + }). Set("Baz", "Hello"), ordereddict.NewDict(). Set("System", ordereddict.NewDict(). @@ -94,7 +99,7 @@ detection: description: "Rule With Details", rule: ` title: RuleWithDetails -details: This is column Foo=%Foo% +details: This is column Foo=%Foo% Int=%Integer% List=%List% Dict=%Dict% logsource: product: windows service: application @@ -102,6 +107,7 @@ logsource: detection: selection: Foo: Bar + Integer: 4 condition: selection `, fieldmappings: ordereddict.NewDict(),