diff --git a/artifacts/testdata/server/testcases/functions.in.yaml b/artifacts/testdata/server/testcases/functions.in.yaml index 865135903a..7588b1a71c 100644 --- a/artifacts/testdata/server/testcases/functions.in.yaml +++ b/artifacts/testdata/server/testcases/functions.in.yaml @@ -16,6 +16,10 @@ Queries: }) }) AS Filtered FROM scope() + # Test dict addition + - SELECT dict(A=1, B=2) + dict(A=3, X=1) + FROM scope() + # Test cache functions (first 2 should be same value due to caching) - LET Foo(X) = if(condition=log(message=format(format="I Ran with %v", args=X)), then=X + 5) @@ -62,6 +66,6 @@ Queries: FROM scope() # Test entropy function - - SELECT + - SELECT entropy(string="Hello world") - FROM scope() \ No newline at end of file + FROM scope() diff --git a/artifacts/testdata/server/testcases/functions.out.yaml b/artifacts/testdata/server/testcases/functions.out.yaml index fc6e773dcd..eee3c0d845 100644 --- a/artifacts/testdata/server/testcases/functions.out.yaml +++ b/artifacts/testdata/server/testcases/functions.out.yaml @@ -11,6 +11,14 @@ LET rows <= SELECT * FROM scope()[]SELECT len(list=["a", "b"]), len(list="hello" "Z": 3 } } +]SELECT dict(A=1, B=2) + dict(A=3, X=1) FROM scope()[ + { + "dict(A=1, B=2) + dict(A=3, X=1)": { + "B": 2, + "A": 3, + "X": 1 + } + } ]LET Foo(X) = if(condition=log(message=format(format="I Ran with %v", args=X)), then=X + 5)[]SELECT cache(func=Foo(X=5), key=5), cache(func=Foo(X=10), key=5), cache(func=Foo(X=10), key=10) FROM scope()[ { "cache(func=Foo(X=5), key=5)": 10, diff --git a/utils/nil.go b/utils/nil.go index 0753215863..f3dddc82f9 100644 --- a/utils/nil.go +++ b/utils/nil.go @@ -10,12 +10,22 @@ import ( // We need to do this stupid check because Go does not allow // comparison to nil with interfaces. func IsNil(v interface{}) bool { + if v == nil { + return true + } + switch v.(type) { case types.Null, *types.Null: return true + } + + switch reflect.TypeOf(v).Kind() { + case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Slice: + //use of IsNil method + return reflect.ValueOf(v).IsNil() + default: - return v == nil || (reflect.ValueOf(v).Kind() == reflect.Ptr && - reflect.ValueOf(v).IsNil()) + return false } } diff --git a/vql/functions/hash.go b/vql/functions/hash.go index 1b361a0eee..ff6fc5df79 100644 --- a/vql/functions/hash.go +++ b/vql/functions/hash.go @@ -52,6 +52,13 @@ type HashResult struct { sha256 hash.Hash } +func (self *HashResult) ToDict() *ordereddict.Dict { + return ordereddict.NewDict(). + Set("MD5", self.MD5). + Set("SHA1", self.SHA1). + Set("SHA256", self.SHA256) +} + type HashFunctionArgs struct { Path *accessors.OSPath `vfilter:"required,field=path,doc=Path to open and hash."` Accessor string `vfilter:"optional,field=accessor,doc=The accessor to use"` @@ -96,10 +103,10 @@ func (self *HashFunction) Call(ctx context.Context, } defer file.Close() - result := HashResult{} + result := &HashResult{} if arg.HashSelect == nil { - result = HashResult{ + result = &HashResult{ md5: md5.New(), sha1: sha1.New(), sha256: sha256.New(), @@ -146,7 +153,7 @@ func (self *HashFunction) Call(ctx context.Context, result.SHA256 = fmt.Sprintf( "%x", result.sha256.Sum(nil)) } - return result + return result.ToDict() } } else if err != nil { diff --git a/vql/protocols/dict.go b/vql/protocols/dict.go new file mode 100644 index 0000000000..a5fb0c9a09 --- /dev/null +++ b/vql/protocols/dict.go @@ -0,0 +1,84 @@ +package protocols + +import ( + "context" + "reflect" + + "github.com/Velocidex/ordereddict" + vql_subsystem "www.velocidex.com/golang/velociraptor/vql" + "www.velocidex.com/golang/vfilter" + "www.velocidex.com/golang/vfilter/types" +) + +type _BoolDict struct{} + +func (self _BoolDict) Applicable(a vfilter.Any) bool { + switch a.(type) { + case ordereddict.Dict, *ordereddict.Dict: + return true + } + + rt := reflect.TypeOf(a) + if rt == nil { + return false + } + return rt.Kind() == reflect.Slice || rt.Kind() == reflect.Map +} + +func (self _BoolDict) Bool(ctx context.Context, scope vfilter.Scope, a vfilter.Any) bool { + switch t := a.(type) { + case ordereddict.Dict: + return t.Len() > 0 + case *ordereddict.Dict: + return t.Len() > 0 + } + + rt := reflect.TypeOf(a) + if rt == nil { + return false + } + if rt.Kind() == reflect.Slice || rt.Kind() == reflect.Map { + a_slice := reflect.ValueOf(a) + return a_slice.Len() > 0 + } + + return false +} + +type _AddDict struct{} + +func (self _AddDict) Applicable(a types.Any, b types.Any) bool { + _, a_ok := a.(*ordereddict.Dict) + _, b_ok := b.(*ordereddict.Dict) + return a_ok && b_ok +} + +func (self _AddDict) Add(scope types.Scope, a types.Any, b types.Any) types.Any { + a_dict, a_ok := a.(*ordereddict.Dict) + if !a_ok { + return &vfilter.Null{} + } + b_dict, b_ok := b.(*ordereddict.Dict) + if !b_ok { + return &vfilter.Null{} + } + + res := ordereddict.NewDict() + + for _, k := range a_dict.Keys() { + v, _ := a_dict.Get(k) + res.Set(k, v) + } + + for _, k := range b_dict.Keys() { + v, _ := b_dict.Get(k) + res.Set(k, v) + } + + return res +} + +func init() { + vql_subsystem.RegisterProtocol(&_BoolDict{}) + vql_subsystem.RegisterProtocol(&_AddDict{}) +} diff --git a/vql/protocols/protocols.go b/vql/protocols/protocols.go index 0ee38d49db..1e756a2ec9 100644 --- a/vql/protocols/protocols.go +++ b/vql/protocols/protocols.go @@ -2,10 +2,8 @@ package protocols import ( "context" - "reflect" "time" - "github.com/Velocidex/ordereddict" "www.velocidex.com/golang/velociraptor/accessors" vql_subsystem "www.velocidex.com/golang/velociraptor/vql" "www.velocidex.com/golang/vfilter" @@ -13,41 +11,6 @@ import ( "www.velocidex.com/golang/vfilter/types" ) -type _BoolDict struct{} - -func (self _BoolDict) Applicable(a vfilter.Any) bool { - switch a.(type) { - case ordereddict.Dict, *ordereddict.Dict: - return true - } - - rt := reflect.TypeOf(a) - if rt == nil { - return false - } - return rt.Kind() == reflect.Slice || rt.Kind() == reflect.Map -} - -func (self _BoolDict) Bool(ctx context.Context, scope vfilter.Scope, a vfilter.Any) bool { - switch t := a.(type) { - case ordereddict.Dict: - return t.Len() > 0 - case *ordereddict.Dict: - return t.Len() > 0 - } - - rt := reflect.TypeOf(a) - if rt == nil { - return false - } - if rt.Kind() == reflect.Slice || rt.Kind() == reflect.Map { - a_slice := reflect.ValueOf(a) - return a_slice.Len() > 0 - } - - return false -} - type _BoolTime struct{} func (self _BoolTime) Applicable(a vfilter.Any) bool { @@ -135,7 +98,6 @@ func (self _GlobFileInfoAssociative) GetMembers( } func init() { - vql_subsystem.RegisterProtocol(&_BoolDict{}) vql_subsystem.RegisterProtocol(&_BoolTime{}) vql_subsystem.RegisterProtocol(&_BoolEq{}) vql_subsystem.RegisterProtocol(&_GlobFileInfoAssociative{})