Skip to content

Commit 9df026f

Browse files
committed
feat(filter): Intersects operator
The intersects operator and its case-insensitive variant, operate on strings slices. If all elements in the RHS slice are present in the slice given by LHS, the operator evaluates to true. Otherwise, it evaluates to false.
1 parent 913b71a commit 9df026f

File tree

5 files changed

+124
-2
lines changed

5 files changed

+124
-2
lines changed

pkg/filter/filter_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ func TestProcFilter(t *testing.T) {
153153
Username: "SYSTEM",
154154
Domain: "NT AUTHORITY",
155155
SID: "S-1-5-18",
156+
Args: []string{"-k", "DcomLaunch", "-p", "-s", "LSM"},
156157
Envs: map[string]string{"ALLUSERSPROFILE": "C:\\ProgramData", "OS": "Windows_NT", "ProgramFiles(x86)": "C:\\Program Files (x86)"},
157158
Modules: []pstypes.Module{
158159
{Name: "C:\\Windows\\System32\\kernel32.dll", Size: 12354, Checksum: 23123343, BaseAddress: va.Address(4294066175), DefaultBaseAddress: va.Address(4293993725)},
@@ -245,6 +246,11 @@ func TestProcFilter(t *testing.T) {
245246
{`ps.ancestor[any].name contains ('Sys')`, true},
246247
{`ps.ancestor[any].name icontains ('sys')`, true},
247248
{`ps.ancestor[any].pid in (2034, 343)`, true},
249+
250+
{`ps.args intersects ('-k', 'DcomLaunch')`, true},
251+
{`ps.args intersects ('-w', 'DcomLaunch')`, false},
252+
{`ps.args iintersects ('-K', 'DComLaunch')`, true},
253+
{`ps.args iintersects ('-W', 'DcomLaunch')`, false},
248254
}
249255

250256
psnap := new(ps.SnapshotterMock)

pkg/filter/ql/ast.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package ql
2222

2323
import (
2424
fuzzysearch "github.com/lithammer/fuzzysearch/fuzzy"
25+
"github.com/rabbitstack/fibratus/pkg/util/sets"
2526
"github.com/rabbitstack/fibratus/pkg/util/wildcard"
2627
"net"
2728
"strconv"
@@ -1152,6 +1153,18 @@ func (v *ValuerEval) evalBinaryExpr(expr *BinaryExpr) interface{} {
11521153
}
11531154
}
11541155
return false
1156+
case Intersects:
1157+
rhs, ok := rhs.([]string)
1158+
if !ok {
1159+
return false
1160+
}
1161+
return len(sets.IntersectionStrings(lhs, rhs, false)) == len(rhs)
1162+
case IIntersects:
1163+
rhs, ok := rhs.([]string)
1164+
if !ok {
1165+
return false
1166+
}
1167+
return len(sets.IntersectionStrings(lhs, rhs, true)) == len(rhs)
11551168
}
11561169
}
11571170

pkg/filter/ql/token.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ const (
6363
IFuzzy // ifuzzy
6464
Fuzzynorm // fuzzynorm
6565
IFuzzynorm // ifuzzynorm
66+
Intersects // intersects
67+
IIntersects // iintersects
6668
Eq // =
6769
IEq // ~=
6870
Neq // !=
@@ -91,7 +93,7 @@ func init() {
9193
for _, tok := range []token{And, Or, Contains, IContains, In,
9294
IIn, Not, Startswith, IStartswith, Endswith, IEndswith,
9395
Matches, IMatches, Fuzzy, IFuzzy, Fuzzynorm, IFuzzynorm,
94-
Seq, MaxSpan, By, As} {
96+
Intersects, IIntersects, Seq, MaxSpan, By, As} {
9597
keywords[strings.ToLower(tokens[tok])] = tok
9698
}
9799
keywords["true"] = True
@@ -134,6 +136,8 @@ var tokens = [...]string{
134136
IFuzzy: "IFUZZY",
135137
Fuzzynorm: "FUZZYNORM",
136138
IFuzzynorm: "IFUZZYNORM",
139+
Intersects: "INTERSECTS",
140+
IIntersects: "IINTERSECTS",
137141

138142
Eq: "=",
139143
IEq: "~=",
@@ -178,7 +182,7 @@ func (tok token) precedence() int {
178182
case Eq, IEq, Neq, Lt, Lte, Gt, Gte:
179183
return 4
180184
case In, IIn, Contains, IContains, Startswith, IStartswith, Endswith, IEndswith,
181-
Matches, IMatches, Fuzzy, IFuzzy, Fuzzynorm, IFuzzynorm:
185+
Matches, IMatches, Fuzzy, IFuzzy, Fuzzynorm, IFuzzynorm, Intersects, IIntersects:
182186
return 5
183187
}
184188
return 0

pkg/util/sets/intersection.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2021-2022 by Nedim Sabic Sabic
3+
* https://www.fibratus.io
4+
* All Rights Reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package sets
20+
21+
import "strings"
22+
23+
// IntersectionStrings computes the intersection of two
24+
// string slices. The boolean argument specifies if the
25+
// string comparison is case-sensitive or not.
26+
func IntersectionStrings(s1, s2 []string, ignoreCase bool) []string {
27+
inter := make([]string, 0)
28+
bucket := map[string]bool{}
29+
30+
for _, i := range s1 {
31+
for _, j := range s2 {
32+
var eq bool
33+
if ignoreCase {
34+
eq = strings.EqualFold(i, j) && !bucket[i]
35+
} else {
36+
eq = i == j && !bucket[i]
37+
}
38+
if eq {
39+
inter = append(inter, i)
40+
bucket[i] = true
41+
}
42+
}
43+
}
44+
45+
return inter
46+
}

pkg/util/sets/intersection_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2021-2022 by Nedim Sabic Sabic
3+
* https://www.fibratus.io
4+
* All Rights Reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package sets
20+
21+
import (
22+
"github.com/stretchr/testify/assert"
23+
"strings"
24+
"testing"
25+
)
26+
27+
func TestIntersectionStrings(t *testing.T) {
28+
var tests = []struct {
29+
s1 []string
30+
s2 []string
31+
ignoreCase bool
32+
in []string
33+
}{
34+
{
35+
[]string{"-k", "DcomLaunch", "-p", "-s", "LSM"}, []string{"DcomLaunch", "-s"}, true, []string{"DcomLaunch", "-s"},
36+
},
37+
{
38+
[]string{"-k", "DcomLaunch", "-p", "-s", "LSM"}, []string{"DComLaunch", "-s"}, false, []string{"-s"},
39+
},
40+
{
41+
[]string{"-k", "DcomLaunch", "-p", "-s", "LSM"}, []string{"LocalSystemNetworkRestricted"}, true, []string{},
42+
},
43+
{
44+
[]string{"LSM", "-s"}, []string{"-S", "lsm"}, true, []string{"LSM", "-s"},
45+
},
46+
}
47+
48+
for _, tt := range tests {
49+
t.Run(strings.Join(tt.in, ","), func(t *testing.T) {
50+
assert.Equal(t, tt.in, IntersectionStrings(tt.s1, tt.s2, tt.ignoreCase))
51+
})
52+
}
53+
}

0 commit comments

Comments
 (0)