Skip to content

Commit 4ec9267

Browse files
authored
Support addition on dicts (#1785)
* Support addition on dicts This results in a merge of the two dicts dict(A=1, B=2) + dict(A=3, X=1) -> {B:2,A:3,X:1} * Hash VQL function should return a dict.
1 parent 4c65fea commit 4ec9267

File tree

6 files changed

+120
-45
lines changed

6 files changed

+120
-45
lines changed

artifacts/testdata/server/testcases/functions.in.yaml

+6-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ Queries:
1616
})
1717
}) AS Filtered FROM scope()
1818

19+
# Test dict addition
20+
- SELECT dict(A=1, B=2) + dict(A=3, X=1)
21+
FROM scope()
22+
1923
# Test cache functions (first 2 should be same value due to caching)
2024
- LET Foo(X) = if(condition=log(message=format(format="I Ran with %v", args=X)),
2125
then=X + 5)
@@ -62,6 +66,6 @@ Queries:
6266
FROM scope()
6367

6468
# Test entropy function
65-
- SELECT
69+
- SELECT
6670
entropy(string="Hello world")
67-
FROM scope()
71+
FROM scope()

artifacts/testdata/server/testcases/functions.out.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ LET rows <= SELECT * FROM scope()[]SELECT len(list=["a", "b"]), len(list="hello"
1111
"Z": 3
1212
}
1313
}
14+
]SELECT dict(A=1, B=2) + dict(A=3, X=1) FROM scope()[
15+
{
16+
"dict(A=1, B=2) + dict(A=3, X=1)": {
17+
"B": 2,
18+
"A": 3,
19+
"X": 1
20+
}
21+
}
1422
]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()[
1523
{
1624
"cache(func=Foo(X=5), key=5)": 10,

utils/nil.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,22 @@ import (
1010
// We need to do this stupid check because Go does not allow
1111
// comparison to nil with interfaces.
1212
func IsNil(v interface{}) bool {
13+
if v == nil {
14+
return true
15+
}
16+
1317
switch v.(type) {
1418
case types.Null, *types.Null:
1519
return true
20+
}
21+
22+
switch reflect.TypeOf(v).Kind() {
23+
case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Slice:
24+
//use of IsNil method
25+
return reflect.ValueOf(v).IsNil()
26+
1627
default:
17-
return v == nil || (reflect.ValueOf(v).Kind() == reflect.Ptr &&
18-
reflect.ValueOf(v).IsNil())
28+
return false
1929
}
2030
}
2131

vql/functions/hash.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ type HashResult struct {
5252
sha256 hash.Hash
5353
}
5454

55+
func (self *HashResult) ToDict() *ordereddict.Dict {
56+
return ordereddict.NewDict().
57+
Set("MD5", self.MD5).
58+
Set("SHA1", self.SHA1).
59+
Set("SHA256", self.SHA256)
60+
}
61+
5562
type HashFunctionArgs struct {
5663
Path *accessors.OSPath `vfilter:"required,field=path,doc=Path to open and hash."`
5764
Accessor string `vfilter:"optional,field=accessor,doc=The accessor to use"`
@@ -96,10 +103,10 @@ func (self *HashFunction) Call(ctx context.Context,
96103
}
97104
defer file.Close()
98105

99-
result := HashResult{}
106+
result := &HashResult{}
100107

101108
if arg.HashSelect == nil {
102-
result = HashResult{
109+
result = &HashResult{
103110
md5: md5.New(),
104111
sha1: sha1.New(),
105112
sha256: sha256.New(),
@@ -146,7 +153,7 @@ func (self *HashFunction) Call(ctx context.Context,
146153
result.SHA256 = fmt.Sprintf(
147154
"%x", result.sha256.Sum(nil))
148155
}
149-
return result
156+
return result.ToDict()
150157
}
151158

152159
} else if err != nil {

vql/protocols/dict.go

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package protocols
2+
3+
import (
4+
"context"
5+
"reflect"
6+
7+
"github.com/Velocidex/ordereddict"
8+
vql_subsystem "www.velocidex.com/golang/velociraptor/vql"
9+
"www.velocidex.com/golang/vfilter"
10+
"www.velocidex.com/golang/vfilter/types"
11+
)
12+
13+
type _BoolDict struct{}
14+
15+
func (self _BoolDict) Applicable(a vfilter.Any) bool {
16+
switch a.(type) {
17+
case ordereddict.Dict, *ordereddict.Dict:
18+
return true
19+
}
20+
21+
rt := reflect.TypeOf(a)
22+
if rt == nil {
23+
return false
24+
}
25+
return rt.Kind() == reflect.Slice || rt.Kind() == reflect.Map
26+
}
27+
28+
func (self _BoolDict) Bool(ctx context.Context, scope vfilter.Scope, a vfilter.Any) bool {
29+
switch t := a.(type) {
30+
case ordereddict.Dict:
31+
return t.Len() > 0
32+
case *ordereddict.Dict:
33+
return t.Len() > 0
34+
}
35+
36+
rt := reflect.TypeOf(a)
37+
if rt == nil {
38+
return false
39+
}
40+
if rt.Kind() == reflect.Slice || rt.Kind() == reflect.Map {
41+
a_slice := reflect.ValueOf(a)
42+
return a_slice.Len() > 0
43+
}
44+
45+
return false
46+
}
47+
48+
type _AddDict struct{}
49+
50+
func (self _AddDict) Applicable(a types.Any, b types.Any) bool {
51+
_, a_ok := a.(*ordereddict.Dict)
52+
_, b_ok := b.(*ordereddict.Dict)
53+
return a_ok && b_ok
54+
}
55+
56+
func (self _AddDict) Add(scope types.Scope, a types.Any, b types.Any) types.Any {
57+
a_dict, a_ok := a.(*ordereddict.Dict)
58+
if !a_ok {
59+
return &vfilter.Null{}
60+
}
61+
b_dict, b_ok := b.(*ordereddict.Dict)
62+
if !b_ok {
63+
return &vfilter.Null{}
64+
}
65+
66+
res := ordereddict.NewDict()
67+
68+
for _, k := range a_dict.Keys() {
69+
v, _ := a_dict.Get(k)
70+
res.Set(k, v)
71+
}
72+
73+
for _, k := range b_dict.Keys() {
74+
v, _ := b_dict.Get(k)
75+
res.Set(k, v)
76+
}
77+
78+
return res
79+
}
80+
81+
func init() {
82+
vql_subsystem.RegisterProtocol(&_BoolDict{})
83+
vql_subsystem.RegisterProtocol(&_AddDict{})
84+
}

vql/protocols/protocols.go

-38
Original file line numberDiff line numberDiff line change
@@ -2,52 +2,15 @@ package protocols
22

33
import (
44
"context"
5-
"reflect"
65
"time"
76

8-
"github.com/Velocidex/ordereddict"
97
"www.velocidex.com/golang/velociraptor/accessors"
108
vql_subsystem "www.velocidex.com/golang/velociraptor/vql"
119
"www.velocidex.com/golang/vfilter"
1210
"www.velocidex.com/golang/vfilter/protocols"
1311
"www.velocidex.com/golang/vfilter/types"
1412
)
1513

16-
type _BoolDict struct{}
17-
18-
func (self _BoolDict) Applicable(a vfilter.Any) bool {
19-
switch a.(type) {
20-
case ordereddict.Dict, *ordereddict.Dict:
21-
return true
22-
}
23-
24-
rt := reflect.TypeOf(a)
25-
if rt == nil {
26-
return false
27-
}
28-
return rt.Kind() == reflect.Slice || rt.Kind() == reflect.Map
29-
}
30-
31-
func (self _BoolDict) Bool(ctx context.Context, scope vfilter.Scope, a vfilter.Any) bool {
32-
switch t := a.(type) {
33-
case ordereddict.Dict:
34-
return t.Len() > 0
35-
case *ordereddict.Dict:
36-
return t.Len() > 0
37-
}
38-
39-
rt := reflect.TypeOf(a)
40-
if rt == nil {
41-
return false
42-
}
43-
if rt.Kind() == reflect.Slice || rt.Kind() == reflect.Map {
44-
a_slice := reflect.ValueOf(a)
45-
return a_slice.Len() > 0
46-
}
47-
48-
return false
49-
}
50-
5114
type _BoolTime struct{}
5215

5316
func (self _BoolTime) Applicable(a vfilter.Any) bool {
@@ -135,7 +98,6 @@ func (self _GlobFileInfoAssociative) GetMembers(
13598
}
13699

137100
func init() {
138-
vql_subsystem.RegisterProtocol(&_BoolDict{})
139101
vql_subsystem.RegisterProtocol(&_BoolTime{})
140102
vql_subsystem.RegisterProtocol(&_BoolEq{})
141103
vql_subsystem.RegisterProtocol(&_GlobFileInfoAssociative{})

0 commit comments

Comments
 (0)