Skip to content

Commit

Permalink
tests(inst): add writable data source test
Browse files Browse the repository at this point in the history
  • Loading branch information
NDStrahilevitz committed Dec 14, 2023
1 parent 3168f18 commit 17ffd77
Show file tree
Hide file tree
Showing 10 changed files with 712 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ env:
CONTAINERS_DATA_SOURCE
PROCTREE_DATA_SOURCE
DNS_DATA_SOURCE
WRITABLE_DATA_SOURCE
jobs:
#
# DOC VERIFICATION
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,8 @@ E2E_INST_SRC := $(shell find $(E2E_INST_DIR) \
-type f \
-name '*.go' \
! -name '*_test.go' \
! -path '$(E2E_INST_DIR)/scripts/*' \
! -path '$(E2E_INST_DIR)/datasourcetest/*' \
)

.PHONY: e2e-inst-signatures
Expand Down
90 changes: 90 additions & 0 deletions tests/e2e-inst-signatures/datasourcetest/write.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package datasourcetest

import (
"encoding/json"

lru "github.com/hashicorp/golang-lru/v2"

"github.com/aquasecurity/tracee/types/detect"
)

type e2eWritable struct {
cache *lru.Cache[string, string]
}

func New() detect.DataSource {
cache, _ := lru.New[string, string](1024)
return &e2eWritable{
cache,
}
}

func (ctx *e2eWritable) Get(key interface{}) (map[string]interface{}, error) {
val, ok := key.(string)
if !ok {
return nil, detect.ErrKeyNotSupported
}

res, ok := ctx.cache.Get(val)
if !ok {
return nil, detect.ErrDataNotFound
}

return map[string]interface{}{
"value": res,
}, nil
}

func (ctx *e2eWritable) Version() uint {
return 1
}

func (ctx *e2eWritable) Keys() []string {
return []string{"string"}
}

func (ctx *e2eWritable) Schema() string {
schema := map[string]interface{}{
"value": "string",
}

s, _ := json.Marshal(schema)
return string(s)
}

func (ctx *e2eWritable) Namespace() string {
return "e2e_inst"
}

func (ctx *e2eWritable) ID() string {
return "demo"
}

func (ctx *e2eWritable) Write(data map[interface{}]interface{}) error {
for key, val := range data {
err := ctx.writeDataEntry(key, val)
if err != nil {
return err
}
}
return nil
}

func (ctx *e2eWritable) writeDataEntry(key, value any) error {
keyStr, ok := key.(string)
if !ok {
return detect.ErrFailedToUnmarshal
}

valueStr, ok := value.(string)
if !ok {
return detect.ErrFailedToUnmarshal
}

ctx.cache.Add(keyStr, valueStr)
return nil
}

func (ctx *e2eWritable) Values() []string {
return []string{"string"}
}
88 changes: 88 additions & 0 deletions tests/e2e-inst-signatures/e2e-writeable_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package main

import (
"fmt"

"github.com/aquasecurity/tracee/types/detect"
"github.com/aquasecurity/tracee/types/protocol"
"github.com/aquasecurity/tracee/types/trace"
)

type e2eWritableDatasourceSig struct {
cb detect.SignatureHandler
writable detect.DataSource
}

func (sig *e2eWritableDatasourceSig) Init(ctx detect.SignatureContext) error {
sig.cb = ctx.Callback
writable, ok := ctx.GetDataSource("e2e_inst", "demo")
if !ok {
return fmt.Errorf("containers data source not registered")
}
if writable.Version() > 1 {
return fmt.Errorf("containers data source version not supported, please update this signature")
}
sig.writable = writable
return nil
}

func (sig *e2eWritableDatasourceSig) GetMetadata() (detect.SignatureMetadata, error) {
return detect.SignatureMetadata{
ID: "WRITABLE_DATA_SOURCE",
EventName: "WRITABLE_DATA_SOURCE",
Version: "0.1.0",
Name: "Writable Data Source Test",
Description: "Instrumentation events E2E Tests: Writable Data Source Test",
Tags: []string{"e2e", "instrumentation"},
}, nil
}

func (sig *e2eWritableDatasourceSig) GetSelectedEvents() ([]detect.SignatureEventSelector, error) {
return []detect.SignatureEventSelector{
{Source: "tracee", Name: "sched_process_exit"},
}, nil
}

func (sig *e2eWritableDatasourceSig) OnEvent(event protocol.Event) error {
eventObj, ok := event.Payload.(trace.Event)
if !ok {
return fmt.Errorf("failed to cast event's payload")
}

switch eventObj.EventName {
case "sched_process_exit":
if eventObj.ProcessName != "ds_writer" {
return nil
}

container, err := sig.writable.Get("bruh")
if err != nil {
return fmt.Errorf("failed to query key \"bruh\" in data source: %v", err)
}

data, ok := container["value"].(string)
if !ok {
return fmt.Errorf("failed to unwrap value from writable data")
}

if data != "moment" {
return fmt.Errorf("value written in data source not expected (%s)", data)
}

m, _ := sig.GetMetadata()

sig.cb(detect.Finding{
SigMetadata: m,
Event: event,
Data: map[string]interface{}{},
})
}

return nil
}

func (sig *e2eWritableDatasourceSig) OnSignal(s detect.Signal) error {
return nil
}

func (sig *e2eWritableDatasourceSig) Close() {}
8 changes: 6 additions & 2 deletions tests/e2e-inst-signatures/export.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package main

import "github.com/aquasecurity/tracee/types/detect"
import (
"github.com/aquasecurity/tracee/tests/e2e-inst-signatures/datasourcetest"
"github.com/aquasecurity/tracee/types/detect"
)

var ExportedSignatures = []detect.Signature{
// Instrumentation e2e signatures
Expand All @@ -13,8 +16,9 @@ var ExportedSignatures = []detect.Signature{
&e2eHookedSyscall{},
&e2eSignatureDerivation{},
&e2eDnsDataSource{},
&e2eWritableDatasourceSig{},
}

var ExportedDataSources = []detect.DataSource{
// add data-sources here
datasourcetest.New(),
}
1 change: 1 addition & 0 deletions tests/e2e-inst-signatures/scripts/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
proctreetester
ds_writer/ds_writer
88 changes: 88 additions & 0 deletions tests/e2e-inst-signatures/scripts/ds_writer/ds_writer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package main

import (
"context"
"flag"
"fmt"
"math/rand"
"os"

"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/protobuf/types/known/structpb"

"github.com/aquasecurity/tracee/api/v1beta1"
)

func printAndExit(msg string, args ...any) {
fmt.Printf(msg, args...)
os.Exit(1)
}

func chooseWord(list []string) string {
return list[rand.Intn(len(list))]
}

func contaminate(client v1beta1.DataSourceServiceClient) error {
stream, err := client.WriteStream(context.Background())
if err != nil {
return fmt.Errorf("error establishing stream: %v", err)
}
for i := 0; i < 1000; i++ {
randomKey := chooseWord(wordList)
randomValue := chooseWord(wordList)
err := stream.Send(&v1beta1.WriteDataSourceRequest{
Id: "demo",
Namespace: "e2e_inst",
Key: structpb.NewStringValue(randomKey),
Value: structpb.NewStringValue(randomValue),
})
if err != nil {
return err
}
}
_, err = stream.CloseAndRecv()
if err != nil {
return fmt.Errorf("error closing stream: %v", err)
}
return nil
}

func main() {
keyPtr := flag.String("key", "", "key to set in the data source")
valuePtr := flag.String("value", "", "key to set in the data source")
flag.Parse()

key := *keyPtr
value := *valuePtr

if key == "" {
printAndExit("empty key given\n")
}
if value == "" {
printAndExit("empty value given\n")
}

conn, err := grpc.Dial(
"unix:///tmp/tracee.sock",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
printAndExit("failed to dial tracee grpc server: %v\n", err)
}
client := v1beta1.NewDataSourceServiceClient(conn)
err = contaminate(client)
if err != nil {
printAndExit("error contaminating data source: %v\n", err)
}
_, err = client.Write(context.Background(), &v1beta1.WriteDataSourceRequest{
Id: "demo",
Namespace: "e2e_inst",
Key: structpb.NewStringValue("bruh"),
Value: structpb.NewStringValue("moment"),
})

if err != nil {
printAndExit("failed to write to data source: %v\n", err)
}
}
Loading

0 comments on commit 17ffd77

Please sign in to comment.