diff --git a/pkg/filter/filter_test.go b/pkg/filter/filter_test.go index aae29344f..ad1af6048 100644 --- a/pkg/filter/filter_test.go +++ b/pkg/filter/filter_test.go @@ -153,6 +153,7 @@ func TestProcFilter(t *testing.T) { Username: "SYSTEM", Domain: "NT AUTHORITY", SID: "S-1-5-18", + Args: []string{"-k", "DcomLaunch", "-p", "-s", "LSM"}, Envs: map[string]string{"ALLUSERSPROFILE": "C:\\ProgramData", "OS": "Windows_NT", "ProgramFiles(x86)": "C:\\Program Files (x86)"}, Modules: []pstypes.Module{ {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) { {`ps.ancestor[any].name contains ('Sys')`, true}, {`ps.ancestor[any].name icontains ('sys')`, true}, {`ps.ancestor[any].pid in (2034, 343)`, true}, + + {`ps.args intersects ('-k', 'DcomLaunch')`, true}, + {`ps.args intersects ('-w', 'DcomLaunch')`, false}, + {`ps.args iintersects ('-K', 'DComLaunch')`, true}, + {`ps.args iintersects ('-W', 'DcomLaunch')`, false}, } psnap := new(ps.SnapshotterMock) diff --git a/pkg/filter/ql/ast.go b/pkg/filter/ql/ast.go index 348201e3c..98ce1a0ed 100644 --- a/pkg/filter/ql/ast.go +++ b/pkg/filter/ql/ast.go @@ -22,6 +22,7 @@ package ql import ( fuzzysearch "github.com/lithammer/fuzzysearch/fuzzy" + "github.com/rabbitstack/fibratus/pkg/util/sets" "github.com/rabbitstack/fibratus/pkg/util/wildcard" "net" "strconv" @@ -1152,6 +1153,18 @@ func (v *ValuerEval) evalBinaryExpr(expr *BinaryExpr) interface{} { } } return false + case Intersects: + rhs, ok := rhs.([]string) + if !ok { + return false + } + return len(sets.IntersectionStrings(lhs, rhs, false)) == len(rhs) + case IIntersects: + rhs, ok := rhs.([]string) + if !ok { + return false + } + return len(sets.IntersectionStrings(lhs, rhs, true)) == len(rhs) } } diff --git a/pkg/filter/ql/token.go b/pkg/filter/ql/token.go index 93f139e8f..211256bc3 100644 --- a/pkg/filter/ql/token.go +++ b/pkg/filter/ql/token.go @@ -63,6 +63,8 @@ const ( IFuzzy // ifuzzy Fuzzynorm // fuzzynorm IFuzzynorm // ifuzzynorm + Intersects // intersects + IIntersects // iintersects Eq // = IEq // ~= Neq // != @@ -91,7 +93,7 @@ func init() { for _, tok := range []token{And, Or, Contains, IContains, In, IIn, Not, Startswith, IStartswith, Endswith, IEndswith, Matches, IMatches, Fuzzy, IFuzzy, Fuzzynorm, IFuzzynorm, - Seq, MaxSpan, By, As} { + Intersects, IIntersects, Seq, MaxSpan, By, As} { keywords[strings.ToLower(tokens[tok])] = tok } keywords["true"] = True @@ -134,6 +136,8 @@ var tokens = [...]string{ IFuzzy: "IFUZZY", Fuzzynorm: "FUZZYNORM", IFuzzynorm: "IFUZZYNORM", + Intersects: "INTERSECTS", + IIntersects: "IINTERSECTS", Eq: "=", IEq: "~=", @@ -178,7 +182,7 @@ func (tok token) precedence() int { case Eq, IEq, Neq, Lt, Lte, Gt, Gte: return 4 case In, IIn, Contains, IContains, Startswith, IStartswith, Endswith, IEndswith, - Matches, IMatches, Fuzzy, IFuzzy, Fuzzynorm, IFuzzynorm: + Matches, IMatches, Fuzzy, IFuzzy, Fuzzynorm, IFuzzynorm, Intersects, IIntersects: return 5 } return 0 diff --git a/pkg/util/sets/intersection.go b/pkg/util/sets/intersection.go new file mode 100644 index 000000000..b9a8bdcbb --- /dev/null +++ b/pkg/util/sets/intersection.go @@ -0,0 +1,46 @@ +/* + * Copyright 2021-2022 by Nedim Sabic Sabic + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sets + +import "strings" + +// IntersectionStrings computes the intersection of two +// string slices. The boolean argument specifies if the +// string comparison is case-sensitive or not. +func IntersectionStrings(s1, s2 []string, ignoreCase bool) []string { + inter := make([]string, 0) + bucket := map[string]bool{} + + for _, i := range s1 { + for _, j := range s2 { + var eq bool + if ignoreCase { + eq = strings.EqualFold(i, j) && !bucket[i] + } else { + eq = i == j && !bucket[i] + } + if eq { + inter = append(inter, i) + bucket[i] = true + } + } + } + + return inter +} diff --git a/pkg/util/sets/intersection_test.go b/pkg/util/sets/intersection_test.go new file mode 100644 index 000000000..d2ba2172c --- /dev/null +++ b/pkg/util/sets/intersection_test.go @@ -0,0 +1,53 @@ +/* + * Copyright 2021-2022 by Nedim Sabic Sabic + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sets + +import ( + "github.com/stretchr/testify/assert" + "strings" + "testing" +) + +func TestIntersectionStrings(t *testing.T) { + var tests = []struct { + s1 []string + s2 []string + ignoreCase bool + in []string + }{ + { + []string{"-k", "DcomLaunch", "-p", "-s", "LSM"}, []string{"DcomLaunch", "-s"}, true, []string{"DcomLaunch", "-s"}, + }, + { + []string{"-k", "DcomLaunch", "-p", "-s", "LSM"}, []string{"DComLaunch", "-s"}, false, []string{"-s"}, + }, + { + []string{"-k", "DcomLaunch", "-p", "-s", "LSM"}, []string{"LocalSystemNetworkRestricted"}, true, []string{}, + }, + { + []string{"LSM", "-s"}, []string{"-S", "lsm"}, true, []string{"LSM", "-s"}, + }, + } + + for _, tt := range tests { + t.Run(strings.Join(tt.in, ","), func(t *testing.T) { + assert.Equal(t, tt.in, IntersectionStrings(tt.s1, tt.s2, tt.ignoreCase)) + }) + } +}