Skip to content

Commit

Permalink
fix(filters): handle syscall arg
Browse files Browse the repository at this point in the history
Handle syscall arg when set as a syscall name (string).

E.g.: sys_enter.args.syscall=bpf

This also adds unit tests for Filter().
  • Loading branch information
geyslan committed Feb 23, 2024
1 parent 5cb5760 commit 3d57ddd
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 3 deletions.
20 changes: 17 additions & 3 deletions pkg/filters/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package filters

import (
"fmt"
"strconv"
"strings"

"github.com/aquasecurity/tracee/pkg/errfmt"
"github.com/aquasecurity/tracee/pkg/events"
"github.com/aquasecurity/tracee/pkg/logger"
"github.com/aquasecurity/tracee/pkg/utils"
"github.com/aquasecurity/tracee/types/trace"
)
Expand Down Expand Up @@ -45,6 +47,7 @@ func (filter *ArgFilter) Filter(eventID events.ID, args []trace.Argument) bool {
for argName, filter := range filter.filters[eventID] {
found := false
var argVal interface{}

for _, arg := range args {
if arg.Name == argName {
found = true
Expand All @@ -55,10 +58,21 @@ func (filter *ArgFilter) Filter(eventID events.ID, args []trace.Argument) bool {
if !found {
return false
}
// TODO: use type assertion instead of string conversion
if argName != "syscall" {
argVal = fmt.Sprint(argVal)

argVal = fmt.Sprint(argVal)
if argName == "syscall" {
// TODO: this only handles args set as syscall name, not syscall id.
// To handle syscall id, changes are needed in the parser to store
// an unique identifier (or syscall name or the syscall id).
syscallID, err := strconv.Atoi(argVal.(string))
if err != nil {
logger.Errorw("failed to convert syscall id to int", "syscall", argVal, "error", err)
return false
}

argVal = events.Core.GetDefinitionByID(events.ID(syscallID)).GetName()
}

res := filter.Filter(argVal)
if !res {
return false
Expand Down
124 changes: 124 additions & 0 deletions pkg/filters/args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/aquasecurity/tracee/pkg/events"
"github.com/aquasecurity/tracee/types/trace"
)

func TestArgsFilterClone(t *testing.T) {
Expand All @@ -29,3 +30,126 @@ func TestArgsFilterClone(t *testing.T) {
t.Errorf("Changes to copied filter affected the original")
}
}

func newArgument(argName, argType string, argValue interface{}) trace.Argument {
return trace.Argument{
ArgMeta: trace.ArgMeta{
Name: argName,
Type: argType,
},
Value: argValue,
}
}

func TestArgsFilter_Filter(t *testing.T) {
t.Parallel()

tt := []struct {
name string
parseFilterName string
parseOperatorAndValues string
parseEventNamesToID map[string]events.ID
eventID events.ID
args []trace.Argument
expected bool
}{
{
name: "Matching argument value as int",
parseFilterName: "read.args.fd",
parseOperatorAndValues: "=3",
parseEventNamesToID: events.Core.NamesToIDs(),
eventID: events.Read,
args: []trace.Argument{
newArgument("fd", "int", 3),
},
expected: true,
},
{
name: "Non-matching argument value as int",
parseFilterName: "read.args.fd",
parseOperatorAndValues: "=3",
parseEventNamesToID: events.Core.NamesToIDs(),
eventID: events.Read,
args: []trace.Argument{
newArgument("fd", "int", 4),
},
expected: false,
},
{
name: "Matching argument value as string",
parseFilterName: "open.args.pathname",
parseOperatorAndValues: "=/etc/passwd",
parseEventNamesToID: events.Core.NamesToIDs(),
eventID: events.Open,
args: []trace.Argument{
newArgument("pathname", "string", "/etc/passwd"),
},
expected: true,
},
{
name: "Non-matching argument value as string",
parseFilterName: "open.args.pathname",
parseOperatorAndValues: "=/etc/passwd",
parseEventNamesToID: events.Core.NamesToIDs(),
eventID: events.Open,
args: []trace.Argument{
newArgument("pathname", "string", "/etc/shadow"),
},
expected: false,
},

// Test cases for syscall argument value
//
// 'syscall' arg value is stored as a string in the parsing logic.
// In the Filter method, it is received as an int (event id) from the kernel,
// and is then used to look up the syscall name from the event table.
{
name: "Matching 'syscall' argument value as string",
parseFilterName: "sys_enter.args.syscall",
parseOperatorAndValues: "=open", // string value (syscall name)
parseEventNamesToID: events.Core.NamesToIDs(),
eventID: events.SysEnter,
args: []trace.Argument{
newArgument("syscall", "int", 2),
},
expected: true,
},
{
name: "Non-matching 'syscall' argument value as string",
parseFilterName: "sys_exit.args.syscall",
parseOperatorAndValues: "=open", // string value (syscall name)
parseEventNamesToID: events.Core.NamesToIDs(),
eventID: events.SysExit,
args: []trace.Argument{
newArgument("syscall", "int", 3),
},
expected: false,
},
{
name: "Non-matching 'syscall' argument value as int",
parseFilterName: "sys_exit.args.syscall",
parseOperatorAndValues: "=2", // int value (syscall number), fails to match
parseEventNamesToID: events.Core.NamesToIDs(),
eventID: events.SysExit,
args: []trace.Argument{
newArgument("syscall", "int", 2),
},
expected: false,
},
}

for _, tc := range tt {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

filter := NewArgFilter()
err := filter.Parse(tc.parseFilterName, tc.parseOperatorAndValues, tc.parseEventNamesToID)
require.NoError(t, err)

result := filter.Filter(tc.eventID, tc.args)
require.Equal(t, tc.expected, result)
})
}
}

0 comments on commit 3d57ddd

Please sign in to comment.