diff --git a/docGen/files.go b/docGen/files.go index f7ce860..5aadaf4 100644 --- a/docGen/files.go +++ b/docGen/files.go @@ -3,7 +3,6 @@ package main import ( "os" "path/filepath" - "strings" ) func getAllFiles(dir string) ([]string, error) { @@ -25,18 +24,3 @@ func getAllFiles(dir string) ([]string, error) { } return filesFound, nil } - -func getAllFilesOfKind(dir string, include string, exclude string) ([]string, error) { // TODO: include and exclude should be slices/variadic - var filteredFiles []string - files, err := getAllFiles(dir) - if err != nil { - return nil, err - } - - for _, f := range files { - if strings.Contains(f, include) && !strings.Contains(f, exclude) { - filteredFiles = append(filteredFiles, f) - } - } - return filteredFiles, nil -} diff --git a/docGen/go.mod b/docGen/go.mod index 1c149d0..e60d469 100644 --- a/docGen/go.mod +++ b/docGen/go.mod @@ -6,7 +6,6 @@ toolchain go1.22.2 require ( github.com/Masterminds/semver v1.5.0 - github.com/aquasecurity/tracee v0.7.0 github.com/aquasecurity/trivy v0.57.0 github.com/aquasecurity/vuln-list-update v0.0.0-20191016075347-3d158c2bf9a2 github.com/leekchan/gtf v0.0.0-20190214083521-5fba33c5b00b @@ -56,7 +55,6 @@ require ( github.com/agext/levenshtein v1.2.3 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/alecthomas/chroma v0.10.0 // indirect - github.com/aquasecurity/tracee/types v0.0.0-20220228102148-dffb469aed94 // indirect github.com/aquasecurity/trivy-checks v1.2.2 // indirect github.com/araddon/dateparse v0.0.0-20190426192744-0d74ffceef83 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect diff --git a/docGen/go.sum b/docGen/go.sum index 2272b9e..f9d3ca8 100644 --- a/docGen/go.sum +++ b/docGen/go.sum @@ -19,10 +19,6 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= -github.com/aquasecurity/tracee v0.7.0 h1:xAsERkCvhTTfjxTh4236ghGnoA0q3zqau3NEEwLQXIA= -github.com/aquasecurity/tracee v0.7.0/go.mod h1:gdYXY4zhvEYAE8QShFKRHF9xkOKjPI74O0wJGTggH5g= -github.com/aquasecurity/tracee/types v0.0.0-20220228102148-dffb469aed94 h1:8dNst7rq3V688n0CyVGy0aNV179s5jGfi6i+C8fCeh4= -github.com/aquasecurity/tracee/types v0.0.0-20220228102148-dffb469aed94/go.mod h1:l8MikK8yNCxoFVFq+WqvRg3kiDUX4wDTQwo7oD7YnWM= github.com/aquasecurity/trivy v0.57.0 h1:W3L+VVvAQjYsJsyiBsThs5xec66g2LpbVkWQJNcaZE0= github.com/aquasecurity/trivy v0.57.0/go.mod h1:MPExNeIDQASo9nHkVjN4pSsx7Vxoka96FnjryoSnhk0= github.com/aquasecurity/trivy-checks v1.2.2 h1:EVHi0gthYzDLfqdAqBBwVGfg2l/gdZ622pIlC9rP+lU= @@ -39,7 +35,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= diff --git a/docGen/main.go b/docGen/main.go index 1ab6f32..2c948ba 100644 --- a/docGen/main.go +++ b/docGen/main.go @@ -45,7 +45,9 @@ func main() { generateDefsecComplianceSpecPages("../avd-repo/trivy-policies-repo/rules/specs/compliance", "../avd-repo/content/compliance") generateKubeHunterPages("../avd-repo/kube-hunter-repo/docs/_kb", "../avd-repo/content/misconfig/kubernetes") generateCloudSploitPages("../avd-repo/cloudsploit-repo/plugins", "../avd-repo/content/misconfig", "../avd-repo/remediations-repo/en") - generateTraceePages("../avd-repo/tracee-repo/signatures", "../avd-repo/content/tracee", realClock{}) + if err := generateTraceePages("../avd-repo/tracee-repo/signatures", "../avd-repo/content/tracee", realClock{}); err != nil { + fail(err) + } generateDefsecPages("../avd-repo/trivy-policies-repo/avd_docs", "../avd-repo/content/misconfig") nvdGenerator := NewNvdGenerator() diff --git a/docGen/tracee.go b/docGen/tracee.go index e02d99c..8af2390 100644 --- a/docGen/tracee.go +++ b/docGen/tracee.go @@ -1,9 +1,7 @@ package main import ( - "encoding/json" "fmt" - "io/ioutil" "log" "os" "path/filepath" @@ -14,7 +12,6 @@ import ( "github.com/aquasecurity/avd-generator/menu" "github.com/aquasecurity/avd-generator/util" - "github.com/aquasecurity/tracee/pkg/rules/regosig" ) var ( @@ -83,21 +80,18 @@ func TraceePostToMarkdown(tp TraceePost, outputFile *os.File) error { return nil } -func generateTraceePages(rulesDir, postsDir string, clock Clock) { +func generateTraceePages(rulesDir, postsDir string, clock Clock) error { err := os.MkdirAll(postsDir, 0755) if err != nil { - log.Fatal("unable to create tracee directory ", err) + return fmt.Errorf("create dir: %w", err) } log.Println("generating tracee pages in: ", postsDir) - if err := generateRegoSigPages(rulesDir, postsDir, clock); err != nil { - log.Fatal("failed to generate rego sig pages: ", err) - } - if err := generateGoSigPages(rulesDir, postsDir, clock); err != nil { - log.Fatal("failed to generate go sig pages: ", err) + return fmt.Errorf("generate go sig pages: %w", err) } + return nil } func generateGoSigPages(rulesDir string, postsDir string, clock Clock) error { @@ -119,7 +113,7 @@ func generateGoSigPages(rulesDir string, postsDir string, clock Clock) error { return } - b, _ := ioutil.ReadFile(file) + b, _ := os.ReadFile(file) r := strings.NewReplacer(`"`, ``) rTitle := strings.NewReplacer("/", "-", `"`, "") @@ -191,93 +185,6 @@ func getRegexMatch(regex, str string) string { return strings.TrimSpace(parts[1]) } -func generateRegoSigPages(rulesDir string, postsDir string, clock Clock) error { - files, err := getAllFilesOfKind(rulesDir, "rego", "_test") - if err != nil { - log.Println("unable to get rego signature files: ", err) - return err - } - - helpers, err := ioutil.ReadFile(filepath.Join(rulesDir, "rego", "helpers.rego")) - if err != nil { - log.Println("unable to read helpers.rego file: ", err) - return err - } - - for _, file := range files { - if findSubstringsInString(file, []string{"helpers", "example", ".go", "aio", "disabled"}) { // TODO: This should be handled by a filter in GetAllFilesOfKind - continue - } - - log.Printf("Processing Tracee rego signature file: %s", file) - - b, err := ioutil.ReadFile(file) - if err != nil { - log.Printf("unable to read signature file: %s, %s\n", file, err) - return err - } - - sig, err := regosig.NewRegoSignature("rego", false, string(b), string(helpers)) - if err != nil { - log.Printf("unable to create new rego signature in file %s: %s\n", file, err) - return err - } - m, _ := sig.GetMetadata() - - var severity int64 - if m.Properties["Severity"] != nil { - severity, _ = m.Properties["Severity"].(json.Number).Int64() - } - var ma string - if m.Properties["MITRE ATT&CK"] != nil { - ma = m.Properties["MITRE ATT&CK"].(string) - } - - topLevelIDName := strings.TrimSpace(strings.Split(ma, ":")[0]) - topLevelID := strings.ToLower(strings.ReplaceAll(topLevelIDName, " ", "-")) - runTimeSecurityMenu.AddNode(topLevelID, strings.Title(topLevelIDName), postsDir, "tracee", []string{"runtime"}, []menu.BreadCrumb{ - {Name: "Tracee", Url: "/tracee"}, - }, "tracee", false) - parentID := topLevelID - - outputFilepath := filepath.Join(postsDir, parentID, fmt.Sprintf("%s.md", strings.ReplaceAll(m.ID, "-", ""))) - if err := os.MkdirAll(filepath.Dir(outputFilepath), 0755); err != nil { - log.Printf("error occurred while creating target directory: %s, %s", filepath.Dir(outputFilepath), err) - } - - f, err := os.Create(outputFilepath) - if err != nil { - log.Printf("unable to create tracee markdown file: %s for sig: %s, skipping...\n", err, m.ID) - continue - } - - if err = TraceePostToMarkdown(TraceePost{ - Title: util.Nicify(strings.Title(m.Name)), - ParentID: parentID, - ParentName: strings.Title(topLevelIDName), - AliasID: strings.ToLower(strings.ReplaceAll(m.ID, "-", "")), - TopLevelID: parentID, - Date: clock.Now("2006-01-02"), - Signature: Signature{ - ID: m.ID, - Version: m.Version, - Name: strings.ReplaceAll(m.Name, " ", "-"), - Description: m.Description, - Severity: SeverityNames[severity], - MitreAttack: ma, - RegoPolicy: string(b), - }, - }, f); err != nil { - log.Printf("unable to write tracee signature markdown: %s.md, err: %s", m.ID, err) - continue - } - - // TODO: Add MITRE classification details - // TODO: Add ability to append custom aqua blog post from another markdown - } - return nil -} - const signaturePostTemplate = `--- title: {{.Title}} id: {{.Signature.ID}} diff --git a/docGen/tracee_test.go b/docGen/tracee_test.go index 1dd304b..6e71a99 100644 --- a/docGen/tracee_test.go +++ b/docGen/tracee_test.go @@ -1,8 +1,6 @@ package main import ( - "fmt" - "io/ioutil" "os" "path/filepath" "regexp" @@ -15,28 +13,23 @@ import ( ) func Test_generateTraceePages(t *testing.T) { - postsDir, _ := ioutil.TempDir("", "Test_generateTraceePages-*") - defer func() { - _ = os.RemoveAll(postsDir) - }() - generateTraceePages("../goldens/tracee-sigs", filepath.Join(postsDir, "tracee"), fakeClock{}) + postsDir := t.TempDir() + + err := generateTraceePages("../goldens/tracee-sigs", filepath.Join(postsDir, "tracee"), fakeClock{}) + require.NoError(t, err) gotFiles, err := getAllFiles(postsDir) require.NoError(t, err) - require.Equal(t, 3, len(gotFiles)) + require.Equal(t, 1, len(gotFiles)) dirRegex := regexp.MustCompile("(?m).+MITRE ATT&CK\n(.*):") - // check for various files and contents - for i := 1; i <= 3; i++ { - want, err := ioutil.ReadFile(fmt.Sprintf("../goldens/tracee-sigs/generated-mds/TRC%d.md", i)) - require.NoError(t, err) - - dir := strings.ReplaceAll(string(dirRegex.FindSubmatch(want)[1]), " ", "-") + want, err := os.ReadFile("../goldens/tracee-sigs/generated-mds/TRC1.md") + require.NoError(t, err) - got, err := ioutil.ReadFile(filepath.Join(postsDir, "tracee", strings.ToLower(dir), fmt.Sprintf("TRC%d.md", i))) - require.NoError(t, err) + dir := strings.ReplaceAll(string(dirRegex.FindSubmatch(want)[1]), " ", "-") - assert.Equal(t, string(want), string(got)) - } + got, err := os.ReadFile(filepath.Join(postsDir, "tracee", strings.ToLower(dir), "TRC1.md")) + require.NoError(t, err) + assert.Equal(t, string(want), string(got)) } diff --git a/goldens/tracee-sigs/generated-mds/TRC2.md b/goldens/tracee-sigs/generated-mds/TRC2.md deleted file mode 100644 index a8aaa63..0000000 --- a/goldens/tracee-sigs/generated-mds/TRC2.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Anti Debugging -id: TRC-2 -aliases: [ - "/tracee/trc2" -] -source: Tracee -icon: aqua -shortName: Anti Debugging -severity: high -draft: false -version: 0.1.0 -keywords: "TRC-2" - -category: runsec -date: 2021-04-15T20:55:39Z - -remediations: - -breadcrumbs: - - name: Tracee - path: /tracee - - name: Defense Evasion - path: /tracee/defense-evasion - -avd_page_type: avd_page ---- - -### Anti Debugging -Process uses anti-debugging technique to block debugger - -### MITRE ATT&CK -Defense Evasion: Execution Guardrails - - -### Rego Policy -``` -package tracee.TRC_2 - -__rego_metadoc__ := { - "id": "TRC-2", - "version": "0.1.0", - "name": "Anti-Debugging", - "description": "Process uses anti-debugging technique to block debugger", - "tags": ["linux", "container"], - "properties": { - "Severity": 3, - "MITRE ATT&CK": "Defense Evasion: Execution Guardrails", - } -} - -tracee_selected_events[eventSelector] { - eventSelector := { - "source": "tracee", - "name": "ptrace" - } -} - -tracee_match { - input.eventName == "ptrace" - arg := input.args[_] - arg.name == "request" - arg.value == "PTRACE_TRACEME" -} -``` diff --git a/goldens/tracee-sigs/generated-mds/TRC3.md b/goldens/tracee-sigs/generated-mds/TRC3.md deleted file mode 100644 index a362943..0000000 --- a/goldens/tracee-sigs/generated-mds/TRC3.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: Code Injection -id: TRC-3 -aliases: [ - "/tracee/trc3" -] -source: Tracee -icon: aqua -shortName: Code Injection -severity: high -draft: false -version: 0.1.0 -keywords: "TRC-3" - -category: runsec -date: 2021-04-15T20:55:39Z - -remediations: - -breadcrumbs: - - name: Tracee - path: /tracee - - name: Defense Evasion - path: /tracee/defense-evasion - -avd_page_type: avd_page ---- - -### Code Injection -Possible code injection into another process - -### MITRE ATT&CK -Defense Evasion: Process Injection - - -### Rego Policy -``` -package tracee.TRC_3 - -import data.tracee.helpers - -__rego_metadoc__ := { - "id": "TRC-3", - "version": "0.1.0", - "name": "Code injection", - "description": "Possible code injection into another process", - "tags": ["linux", "container"], - "properties": { - "Severity": 3, - "MITRE ATT&CK": "Defense Evasion: Process Injection", - } -} - -eventSelectors := [ - { - "source": "tracee", - "name": "ptrace" - }, - { - "source": "tracee", - "name": "security_file_open" - }, - { - "source": "tracee", - "name": "process_vm_writev" - } -] - -tracee_selected_events[eventSelector] { - eventSelector := eventSelectors[_] -} - - -tracee_match { - input.eventName == "ptrace" - arg_value = helpers.get_tracee_argument("request") - arg_value == "PTRACE_POKETEXT" -} - -tracee_match = res { - input.eventName == "security_file_open" - flags = helpers.get_tracee_argument("flags") - - helpers.is_file_write(flags) - - pathname := helpers.get_tracee_argument("pathname") - - regex.match(`/proc/(?:\d.+|self)/mem`, pathname) - - res := { - "file flags": flags, - "file path": pathname, - } -} - -tracee_match { - input.eventName == "process_vm_writev" - dst_pid = helpers.get_tracee_argument("pid") - dst_pid != input.processId -} -``` diff --git a/goldens/tracee-sigs/rego/anti_debugging_ptraceme.rego b/goldens/tracee-sigs/rego/anti_debugging_ptraceme.rego deleted file mode 100644 index 298cf2e..0000000 --- a/goldens/tracee-sigs/rego/anti_debugging_ptraceme.rego +++ /dev/null @@ -1,27 +0,0 @@ -package tracee.TRC_2 - -__rego_metadoc__ := { - "id": "TRC-2", - "version": "0.1.0", - "name": "Anti-Debugging", - "description": "Process uses anti-debugging technique to block debugger", - "tags": ["linux", "container"], - "properties": { - "Severity": 3, - "MITRE ATT&CK": "Defense Evasion: Execution Guardrails", - } -} - -tracee_selected_events[eventSelector] { - eventSelector := { - "source": "tracee", - "name": "ptrace" - } -} - -tracee_match { - input.eventName == "ptrace" - arg := input.args[_] - arg.name == "request" - arg.value == "PTRACE_TRACEME" -} \ No newline at end of file diff --git a/goldens/tracee-sigs/rego/code_injection.rego b/goldens/tracee-sigs/rego/code_injection.rego deleted file mode 100644 index b9dde2a..0000000 --- a/goldens/tracee-sigs/rego/code_injection.rego +++ /dev/null @@ -1,63 +0,0 @@ -package tracee.TRC_3 - -import data.tracee.helpers - -__rego_metadoc__ := { - "id": "TRC-3", - "version": "0.1.0", - "name": "Code injection", - "description": "Possible code injection into another process", - "tags": ["linux", "container"], - "properties": { - "Severity": 3, - "MITRE ATT&CK": "Defense Evasion: Process Injection", - } -} - -eventSelectors := [ - { - "source": "tracee", - "name": "ptrace" - }, - { - "source": "tracee", - "name": "security_file_open" - }, - { - "source": "tracee", - "name": "process_vm_writev" - } -] - -tracee_selected_events[eventSelector] { - eventSelector := eventSelectors[_] -} - - -tracee_match { - input.eventName == "ptrace" - arg_value = helpers.get_tracee_argument("request") - arg_value == "PTRACE_POKETEXT" -} - -tracee_match = res { - input.eventName == "security_file_open" - flags = helpers.get_tracee_argument("flags") - - helpers.is_file_write(flags) - - pathname := helpers.get_tracee_argument("pathname") - - regex.match(`/proc/(?:\d.+|self)/mem`, pathname) - - res := { - "file flags": flags, - "file path": pathname, - } -} - -tracee_match { - input.eventName == "process_vm_writev" - dst_pid = helpers.get_tracee_argument("pid") - dst_pid != input.processId -} \ No newline at end of file diff --git a/goldens/tracee-sigs/rego/example.rego b/goldens/tracee-sigs/rego/example.rego deleted file mode 100644 index 9d491b4..0000000 --- a/goldens/tracee-sigs/rego/example.rego +++ /dev/null @@ -1 +0,0 @@ -a mock example file which should be ignored by avd generator \ No newline at end of file diff --git a/goldens/tracee-sigs/rego/helpers.rego b/goldens/tracee-sigs/rego/helpers.rego deleted file mode 100644 index 17bd671..0000000 --- a/goldens/tracee-sigs/rego/helpers.rego +++ /dev/null @@ -1,16 +0,0 @@ -package tracee.helpers - -get_tracee_argument(arg_name) = res { - arg := input.args[_] - arg.name == arg_name - res := arg.value -} - - -default is_file_write(flags) = false -is_file_write(flags) { - contains(lower(flags), "o_wronly") -} -is_file_write(flags) { - contains(lower(flags), "o_rdwr") -} \ No newline at end of file diff --git a/goldens/tracee-sigs/rego/regosig/aio.go b/goldens/tracee-sigs/rego/regosig/aio.go deleted file mode 100644 index 1de5213..0000000 --- a/goldens/tracee-sigs/rego/regosig/aio.go +++ /dev/null @@ -1,253 +0,0 @@ -package regosig - -import ( - _ "embed" - - "context" - "fmt" - "sort" - "strings" - - "github.com/aquasecurity/tracee/tracee-ebpf/external" - "github.com/aquasecurity/tracee/tracee-rules/engine" - "github.com/aquasecurity/tracee/tracee-rules/types" - "github.com/open-policy-agent/opa/ast" - "github.com/open-policy-agent/opa/compile" - "github.com/open-policy-agent/opa/rego" -) - -var ( - //go:embed aio.rego - mainRego string -) - -const ( - moduleMain = "main.rego" - queryMetadataAll = "data.main.__rego_metadoc_all__" - querySelectedEventsAll = "data.main.tracee_selected_events_all" - queryMatchAll = "data.main.tracee_match_all" -) - -// Options holds various Option items that can be passed to the NewAIO constructor. -type Options struct { - // OPATarget optionally specifies which OPA target engine to use for - // evaluation. By default, the `rego` engine is used. - OPATarget string - - // OPAPartial optionally specifies whether to use OPA partial evaluation - // or not. By default, partial evaluation is disabled. - // - // NOTE: On average partial evaluation performs better by leveraging - // OPA rules indexing. However, for some rules we noticed that enabling partial - // evaluation significantly degraded performance. - // - // https://blog.openpolicyagent.org/partial-evaluation-162750eaf422 - OPAPartial bool -} - -type Option func(*Options) - -func OPATarget(target string) Option { - return func(o *Options) { - o.OPATarget = target - } -} - -func OPAPartial(partial bool) Option { - return func(o *Options) { - o.OPAPartial = partial - } -} - -func newDefaultOptions() *Options { - return &Options{ - OPATarget: compile.TargetRego, - OPAPartial: false, - } -} - -type aio struct { - cb types.SignatureHandler - metadata types.SignatureMetadata - - preparedQuery rego.PreparedEvalQuery - sigIDToMetadata map[string]types.SignatureMetadata - selectedEvents []types.SignatureEventSelector -} - -// NewAIO constructs a new types.Signature with the specified Rego modules and Option items. -// -// This implementation compiles all modules once and prepares the single, -// aka all in one, query for evaluation. -func NewAIO(modules map[string]string, opts ...Option) (types.Signature, error) { - options := newDefaultOptions() - - for _, opt := range opts { - opt(options) - } - - modules[moduleMain] = mainRego - ctx := context.TODO() - compiler, err := ast.CompileModules(modules) - if err != nil { - return nil, fmt.Errorf("compiling modules: %w", err) - } - - metadataRS, err := rego.New( - rego.Compiler(compiler), - rego.Query(queryMetadataAll), - ).Eval(ctx) - if err != nil { - return nil, fmt.Errorf("evaluating %s query: %w", queryMetadataAll, err) - } - sigIDToMetadata, err := MapRS(metadataRS).ToSignatureMetadataAll() - if err != nil { - return nil, fmt.Errorf("mapping output to metadata: %w", err) - } - - selectedEventsRS, err := rego.New( - rego.Compiler(compiler), - rego.Query(querySelectedEventsAll), - ).Eval(ctx) - if err != nil { - return nil, fmt.Errorf("evaluating %s query: %w", querySelectedEventsAll, err) - } - sigIDToSelectedEvents, err := MapRS(selectedEventsRS).ToSelectedEventsAll() - if err != nil { - return nil, fmt.Errorf("mapping output to selected events: %w", err) - } - - var peq rego.PreparedEvalQuery - - if options.OPAPartial { - pr, err := rego.New( - rego.Compiler(compiler), - rego.Query(queryMatchAll), - ).PartialResult(ctx) - if err != nil { - return nil, fmt.Errorf("partially evaluating %s query: %w", queryMatchAll, err) - } - peq, err = pr.Rego( - rego.Target(options.OPATarget), - ).PrepareForEval(ctx) - if err != nil { - return nil, fmt.Errorf("preparing %s query: %w", queryMatch, err) - } - } else { - peq, err = rego.New( - rego.Target(options.OPATarget), - rego.Compiler(compiler), - rego.Query(queryMatchAll), - ).PrepareForEval(ctx) - if err != nil { - return nil, fmt.Errorf("preparing %s query: %w", queryMetadataAll, err) - } - } - - var sigIDs []string - var selectedEvents []types.SignatureEventSelector - - selectedEventsSet := make(map[types.SignatureEventSelector]bool) - - for sigID, sigEvents := range sigIDToSelectedEvents { - sigIDs = append(sigIDs, sigID) - - for _, sigEvent := range sigEvents { - if _, value := selectedEventsSet[sigEvent]; !value { - selectedEventsSet[sigEvent] = true - selectedEvents = append(selectedEvents, sigEvent) - } - } - } - - sort.Strings(sigIDs) - metadata := types.SignatureMetadata{ - ID: fmt.Sprintf("TRC-AIO (%s)", strings.Join(sigIDs, ",")), - Version: "1.0.0", - Name: "AIO", - } - - return &aio{ - metadata: metadata, - preparedQuery: peq, - sigIDToMetadata: sigIDToMetadata, - selectedEvents: selectedEvents, - }, nil -} - -func (a *aio) Init(cb types.SignatureHandler) error { - a.cb = cb - return nil -} - -func (a *aio) GetMetadata() (types.SignatureMetadata, error) { - return a.metadata, nil -} - -func (a *aio) GetSelectedEvents() ([]types.SignatureEventSelector, error) { - return a.selectedEvents, nil -} - -func (a *aio) OnEvent(ee types.Event) error { - input, event, err := toInputOption(ee) - if err != nil { - return err - } - - ctx := context.TODO() - rs, err := a.preparedQuery.Eval(ctx, input) - if err != nil { - return err - } - data, err := MapRS(rs).ToDataAll() - if err != nil { - return err - } - for sigID, value := range data { - switch v := value.(type) { - case bool: - if v { - a.cb(types.Finding{ - Data: nil, - Context: event, - SigMetadata: a.sigIDToMetadata[sigID], - }) - } - case map[string]interface{}: - a.cb(types.Finding{ - Data: v, - Context: event, - SigMetadata: a.sigIDToMetadata[sigID], - }) - default: - return fmt.Errorf("unrecognized value: %T", v) - } - } - return nil -} - -func toInputOption(ee types.Event) (rego.EvalOption, external.Event, error) { - var input rego.EvalOption - var event external.Event - - switch ee.(type) { - case external.Event: - event = ee.(external.Event) - input = rego.EvalInput(ee) - case engine.ParsedEvent: - pe := ee.(engine.ParsedEvent) - event = pe.Event - input = rego.EvalParsedInput(pe.Value) - default: - return nil, external.Event{}, fmt.Errorf("unrecognized event type: %T", ee) - } - return input, event, nil -} - -func (a *aio) Close() { - // noop -} - -func (a aio) OnSignal(signal types.Signal) error { - return fmt.Errorf("unsupported operation") -} diff --git a/goldens/tracee-sigs/rego/regosig/aio.rego b/goldens/tracee-sigs/rego/regosig/aio.rego deleted file mode 100644 index 0c0bca8..0000000 --- a/goldens/tracee-sigs/rego/regosig/aio.rego +++ /dev/null @@ -1,24 +0,0 @@ -package main - -# Returns the map of signature identifiers to signature metadata. -__rego_metadoc_all__[id] = resp { - some i - resp := data.tracee[i].__rego_metadoc__ - id := resp.id -} - -# Returns the map of signature identifiers to signature selected events. -tracee_selected_events_all[id] = resp { - some i - resp := data.tracee[i].tracee_selected_events - metadata := data.tracee[i].__rego_metadoc__ - id := metadata.id -} - -# Returns the map of signature identifiers to values matching the input event. -tracee_match_all[id] = resp { - some i - resp := data.tracee[i].tracee_match - metadata := data.tracee[i].__rego_metadoc__ - id := metadata.id -} \ No newline at end of file diff --git a/goldens/tracee-sigs/rego/regosig/aio_test.go b/goldens/tracee-sigs/rego/regosig/aio_test.go deleted file mode 100644 index 984e1ed..0000000 --- a/goldens/tracee-sigs/rego/regosig/aio_test.go +++ /dev/null @@ -1,293 +0,0 @@ -package regosig_test - -import ( - "encoding/json" - "fmt" - "os" - "testing" - - tracee "github.com/aquasecurity/tracee/tracee-ebpf/external" - "github.com/aquasecurity/tracee/tracee-rules/signatures/rego/regosig" - "github.com/aquasecurity/tracee/tracee-rules/types" - "github.com/open-policy-agent/opa/compile" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestAio_GetMetadata(t *testing.T) { - sig, err := regosig.NewAIO(map[string]string{ - "test_boolean.rego": testRegoCodeBoolean, - "test_object.rego": testRegoCodeObject, - }) - require.NoError(t, err) - - metadata, err := sig.GetMetadata() - require.NoError(t, err) - assert.Equal(t, types.SignatureMetadata{ - ID: "TRC-AIO (TRC-BOOL,TRC-OBJECT)", - Version: "1.0.0", - Name: "AIO", - }, metadata) -} - -func TestAio_GetSelectedEvents(t *testing.T) { - sig, err := regosig.NewAIO(map[string]string{ - "test_boolean.rego": testRegoCodeBoolean, - "test_object.rego": testRegoCodeObject, - }) - require.NoError(t, err) - events, err := sig.GetSelectedEvents() - require.NoError(t, err) - - eventsSet := make(map[types.SignatureEventSelector]bool) - for _, event := range events { - if _, value := eventsSet[event]; !value { - eventsSet[event] = true - } - } - - assert.Equal(t, map[types.SignatureEventSelector]bool{ - types.SignatureEventSelector{ - Source: "tracee", - Name: "ptrace", - }: true, - types.SignatureEventSelector{ - Source: "tracee", - Name: "execve", - }: true, - }, eventsSet) -} - -func TestAio_OnEvent(t *testing.T) { - options := []struct { - target string - partial bool - }{ - { - target: compile.TargetRego, - partial: false, - }, - { - target: compile.TargetRego, - partial: true, - }, - //{ - // target: compile.TargetWasm, - // partial: false, - //}, - //{ - // target: compile.TargetWasm, - // partial: true, - //}, - } - - for _, tc := range options { - t.Run(fmt.Sprintf("target=%s,partial=%t", tc.target, tc.partial), func(t *testing.T) { - AioOnEventSpec(t, tc.target, tc.partial) - }) - } - -} - -// AioOnEventSpec describes the behavior of aio.OnEvent. -func AioOnEventSpec(t *testing.T, target string, partial bool) { - testCases := []struct { - name string - modules map[string]string - event tracee.Event - // findings are grouped by signature identifier for comparison - findings map[string]types.Finding - wantError string - }{ - { - name: "Should return finding when single rule matches", - modules: map[string]string{ - "test_boolean.rego": testRegoCodeBoolean, - "test_object.rego": testRegoCodeObject, - }, - event: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - }, - }, - findings: map[string]types.Finding{ - "TRC-BOOL": { - Data: nil, - Context: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - }, - }, - SigMetadata: types.SignatureMetadata{ - ID: "TRC-BOOL", - Version: "0.1.0", - Name: "test name", - Description: "test description", - Tags: []string{ - "tag1", - "tag2", - }, - Properties: map[string]interface{}{ - "p1": "test", - "p2": json.Number("1"), - "p3": true, - }, - }, - }, - }, - }, - { - name: "Should return multiple findings when multiple rules match", - modules: map[string]string{ - "test_boolean.rego": testRegoCodeBoolean, - "test_object.rego": testRegoCodeObject, - }, - event: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: 1337, - }, - }, - }, - findings: map[string]types.Finding{ - "TRC-OBJECT": { - Data: map[string]interface{}{ - "p1": "test", - "p2": json.Number("1"), - "p3": true, - }, - Context: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: 1337, - }, - }, - }, - SigMetadata: types.SignatureMetadata{ - ID: "TRC-OBJECT", - Version: "0.3.0", - Name: "test name", - Description: "test description", - Tags: []string{ - "tag1", - "tag2", - }, - Properties: map[string]interface{}{ - "p1": "test", - "p2": json.Number("1"), - "p3": true, - }, - }, - }, - "TRC-BOOL": { - Data: nil, - Context: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: 1337, - }, - }, - }, - SigMetadata: types.SignatureMetadata{ - ID: "TRC-BOOL", - Version: "0.1.0", - Name: "test name", - Description: "test description", - Tags: []string{ - "tag1", - "tag2", - }, - Properties: map[string]interface{}{ - "p1": "test", - "p2": json.Number("1"), - "p3": true, - }, - }, - }, - }, - }, - { - name: "Should return error when invalid value received", - modules: map[string]string{ - "test_invalid.rego": testRegoCodeInvalidObject, - }, - event: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with invalid", - }, - }, - }, - wantError: "unrecognized value: string", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - sig, err := regosig.NewAIO(tc.modules, - regosig.OPATarget(target), - regosig.OPAPartial(partial), - ) - require.NoError(t, err) - - holder := &findingsHolder{} - err = sig.Init(holder.OnFinding) - require.NoError(t, err) - - err = sig.OnEvent(tc.event) - if tc.wantError != "" { - require.EqualError(t, err, tc.wantError, tc.name) - } else { - require.NoError(t, err, tc.name) - assert.Equal(t, tc.findings, holder.GroupBySigID()) - } - }) - } -} - -func TestAio_OnSignal(t *testing.T) { - sig, err := regosig.NewAIO(map[string]string{}) - require.NoError(t, err) - err = sig.OnSignal(os.Kill) - assert.EqualError(t, err, "unsupported operation") -} diff --git a/goldens/tracee-sigs/rego/regosig/common_test.go b/goldens/tracee-sigs/rego/regosig/common_test.go deleted file mode 100644 index 780c35f..0000000 --- a/goldens/tracee-sigs/rego/regosig/common_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package regosig_test - -import ( - "github.com/aquasecurity/tracee/tracee-rules/types" -) - -const ( - testRegoCodeBoolean = `package tracee.TRC_BOOL - -__rego_metadoc__ := { - "id": "TRC-BOOL", - "version": "0.1.0", - "name": "test name", - "description": "test description", - "tags": [ "tag1", "tag2" ], - "properties": { - "p1": "test", - "p2": 1, - "p3": true - } -} - -tracee_selected_events[eventSelector] { - eventSelector := { - "source": "tracee", - "name": "execve" - } -} - -tracee_match { - endswith(input.args[0].value, "yo") -} -` - testRegoCodeObject = `package tracee.TRC_OBJECT - -__rego_metadoc__ := { - "id": "TRC-OBJECT", - "version": "0.3.0", - "name": "test name", - "description": "test description", - "tags": [ "tag1", "tag2" ], - "properties": { - "p1": "test", - "p2": 1, - "p3": true - } -} - -tracee_selected_events[eventSelector] { - eventSelector := { - "source": "tracee", - "name": "ptrace" - } -} - -tracee_match = res { - endswith(input.args[0].value, "yo") - input.args[1].value == 1337 - res := { - "p1": "test", - "p2": 1, - "p3": true - } -} -` - testRegoCodeInvalidObject = `package tracee.TRC_INVALID -__rego_metadoc__ := { - "id": "TRC-INVALID", - "version": "0.3.0", - "name": "test name", - "description": "test description", - "tags": [ "tag1", "tag2" ], - "properties": { - "p1": "test", - "p2": 1, - "p3": true - } -} - -tracee_selected_events[eventSelector] { - eventSelector := { - "source": "tracee", - "name": "ptrace" - } -} - -tracee_match = res { - endswith(input.args[0].value, "invalid") - res := "foo bar string" -} -` -) - -// findingsHolder is a utility struct that defines types.SignatureHandler callback method -// and holds types.Finding values received as the callback's argument. -type findingsHolder struct { - values []types.Finding -} - -func (h *findingsHolder) OnFinding(f types.Finding) { - h.values = append(h.values, f) -} - -func (h *findingsHolder) GroupBySigID() map[string]types.Finding { - r := make(map[string]types.Finding) - for _, v := range h.values { - r[v.SigMetadata.ID] = v - } - return r -} - -func (h *findingsHolder) FirstValue() *types.Finding { - if len(h.values) == 0 { - return nil - } - return &h.values[0] -} diff --git a/goldens/tracee-sigs/rego/regosig/mapper.go b/goldens/tracee-sigs/rego/regosig/mapper.go deleted file mode 100644 index a8871bd..0000000 --- a/goldens/tracee-sigs/rego/regosig/mapper.go +++ /dev/null @@ -1,73 +0,0 @@ -package regosig - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - - "github.com/aquasecurity/tracee/tracee-rules/types" - "github.com/open-policy-agent/opa/rego" -) - -type Mapper struct { - rego.ResultSet -} - -func MapRS(rs rego.ResultSet) *Mapper { - return &Mapper{ - ResultSet: rs, - } -} - -func (m Mapper) ToSignatureMetadataAll() (map[string]types.SignatureMetadata, error) { - if m.isEmpty() { - return nil, errors.New("empty result set") - } - resJSON, err := json.Marshal(m.ResultSet[0].Expressions[0].Value) - if err != nil { - return nil, err - } - dec := json.NewDecoder(bytes.NewBuffer(resJSON)) - dec.UseNumber() - var res map[string]types.SignatureMetadata - err = dec.Decode(&res) - if err != nil { - return nil, err - } - return res, nil -} - -func (m Mapper) ToSelectedEventsAll() (map[string][]types.SignatureEventSelector, error) { - if m.isEmpty() { - return nil, errors.New("empty result set") - } - resJSON, err := json.Marshal(m.ResultSet[0].Expressions[0].Value) - if err != nil { - return nil, err - } - dec := json.NewDecoder(bytes.NewBuffer(resJSON)) - dec.UseNumber() - var res map[string][]types.SignatureEventSelector - err = dec.Decode(&res) - if err != nil { - return nil, err - } - return res, nil -} - -func (m Mapper) ToDataAll() (map[string]interface{}, error) { - if m.isEmpty() { - return nil, errors.New("empty result set") - } - values, ok := m.ResultSet[0].Expressions[0].Value.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("unrecognized value: %T", m.ResultSet[0].Expressions[0].Value) - } - return values, nil -} - -func (m Mapper) isEmpty() bool { - rs := m.ResultSet - return len(rs) == 0 || len(rs[0].Expressions) == 0 || rs[0].Expressions[0].Value == nil -} diff --git a/goldens/tracee-sigs/rego/regosig/mapper_test.go b/goldens/tracee-sigs/rego/regosig/mapper_test.go deleted file mode 100644 index 0dcb347..0000000 --- a/goldens/tracee-sigs/rego/regosig/mapper_test.go +++ /dev/null @@ -1 +0,0 @@ -package regosig_test diff --git a/goldens/tracee-sigs/rego/regosig/traceerego.go b/goldens/tracee-sigs/rego/regosig/traceerego.go deleted file mode 100644 index 13d0b5c..0000000 --- a/goldens/tracee-sigs/rego/regosig/traceerego.go +++ /dev/null @@ -1,227 +0,0 @@ -package regosig - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "regexp" - "strings" - - tracee "github.com/aquasecurity/tracee/tracee-ebpf/external" - "github.com/aquasecurity/tracee/tracee-rules/engine" - "github.com/aquasecurity/tracee/tracee-rules/types" - "github.com/open-policy-agent/opa/ast" - "github.com/open-policy-agent/opa/rego" -) - -// RegoSignature is an abstract signature that is implemented in rego -// each struct instance is associated with a rego file -// the rego file declares the following rules: -// __rego_metadoc__: a *document* rule that defines the rule's metadata (see GetMetadata()) -// tracee_selected_events: a *set* rule that defines the event selectors (see GetSelectedEvent()) -// tracee_match: a *boolean*, or a *document* rule that defines the logic of the signature (see OnEvent()) -type RegoSignature struct { - cb types.SignatureHandler - compiledRego *ast.Compiler - matchPQ rego.PreparedEvalQuery - metadata types.SignatureMetadata - selectedEvents []types.SignatureEventSelector -} - -const queryMatch string = "data.%s.tracee_match" -const querySelectedEvents string = "data.%s.tracee_selected_events" -const queryMetadata string = "data.%s.__rego_metadoc__" -const packageNameRegex string = `package\s.*` - -// NewRegoSignature creates a new RegoSignature with the provided rego code string -func NewRegoSignature(target string, partialEval bool, regoCodes ...string) (types.Signature, error) { - var err error - res := RegoSignature{} - regoMap := make(map[string]string) - - re := regexp.MustCompile(packageNameRegex) - - var pkgName string - for _, regoCode := range regoCodes { - var regoModuleName string - splittedName := strings.Split(re.FindString(regoCode), " ") - if len(splittedName) > 1 { - regoModuleName = splittedName[1] - } else { - return nil, fmt.Errorf("invalid rego code received") - } - if !strings.Contains(regoCode, "package tracee.helpers") { - pkgName = regoModuleName - } - regoMap[regoModuleName] = regoCode - } - - res.compiledRego, err = ast.CompileModules(regoMap) - if err != nil { - return nil, err - } - - ctx := context.Background() - if partialEval { - pr, err := rego.New( - - rego.Compiler(res.compiledRego), - rego.Query(fmt.Sprintf(queryMatch, pkgName)), - ).PartialResult(ctx) - if err != nil { - return nil, err - } - - res.matchPQ, err = pr.Rego(rego.Target(target)).PrepareForEval(ctx) - if err != nil { - return nil, err - } - } else { - res.matchPQ, err = rego.New( - rego.Target(target), - rego.Compiler(res.compiledRego), - rego.Query(fmt.Sprintf(queryMatch, pkgName)), - ).PrepareForEval(ctx) - if err != nil { - return nil, err - } - } - - res.metadata, err = res.getMetadata(pkgName) - if err != nil { - return nil, err - } - res.selectedEvents, err = res.getSelectedEvents(pkgName) - if err != nil { - return nil, err - } - return &res, nil -} - -// Init implements the Signature interface by resetting internal state -func (sig *RegoSignature) Init(cb types.SignatureHandler) error { - sig.cb = cb - return nil -} - -// GetMetadata implements the Signature interface by evaluating the Rego policy's __rego_metadoc__ rule -// this is a *document* rule that defines the rule's metadata -// based on WIP Rego convention for describing policy metadata: https://hackmd.io/@ZtQnh19kS26YiNlJLqKJnw/H1gAv5nBw -func (sig *RegoSignature) GetMetadata() (types.SignatureMetadata, error) { - return sig.metadata, nil -} - -func (sig *RegoSignature) getMetadata(pkgName string) (types.SignatureMetadata, error) { - evalRes, err := sig.evalQuery(fmt.Sprintf(queryMetadata, pkgName)) - if err != nil { - return types.SignatureMetadata{}, err - } - resJSON, err := json.Marshal(evalRes) - if err != nil { - return types.SignatureMetadata{}, err - } - dec := json.NewDecoder(bytes.NewBuffer(resJSON)) - dec.UseNumber() - var res types.SignatureMetadata - err = dec.Decode(&res) - if err != nil { - return types.SignatureMetadata{}, err - } - return res, nil -} - -// GetSelectedEvents implements the Signature interface by evaluating the Rego policy's tracee_selected_events rule -// this is a *set* rule that defines the rule's SelectedEvents -func (sig *RegoSignature) GetSelectedEvents() ([]types.SignatureEventSelector, error) { - return sig.selectedEvents, nil -} - -func (sig *RegoSignature) getSelectedEvents(pkgName string) ([]types.SignatureEventSelector, error) { - evalRes, err := sig.evalQuery(fmt.Sprintf(querySelectedEvents, pkgName)) - if err != nil { - return nil, err - } - resJSON, err := json.Marshal(evalRes) - if err != nil { - return nil, err - } - var res []types.SignatureEventSelector - err = json.Unmarshal(resJSON, &res) - if err != nil { - return nil, err - } - return res, nil -} - -// OnEvent implements the Signature interface by evaluating the Rego policy's tracee_match rule -// this is a *boolean* or a *document* rule that defines the logic of the signature -// if bool is "returned", a true evaluation will generate a Finding with no data -// if document is "returned", any non-empty evaluation will generate a Finding with the document as the Finding's "Data" -func (sig *RegoSignature) OnEvent(e types.Event) error { - var input rego.EvalOption - var ee tracee.Event - - // TODO(danielpacak) OnEvent is called very often. Hence, check what's the performance impact of Go type switch here. - switch v := e.(type) { - // This case is for backward compatibility. From OPA Go SDK stand point it's more efficient to enter ParsedEvent case. - case tracee.Event: - ee = e.(tracee.Event) - input = rego.EvalInput(e) - case engine.ParsedEvent: - pe := e.(engine.ParsedEvent) - ee = pe.Event - input = rego.EvalParsedInput(pe.Value) - default: - return fmt.Errorf("unrecognized event type: %T", v) - } - results, err := sig.matchPQ.Eval(context.TODO(), input) - if err != nil { - return fmt.Errorf("evaluating rego: %w", err) - } - - if len(results) > 0 && len(results[0].Expressions) > 0 && results[0].Expressions[0].Value != nil { - switch v := results[0].Expressions[0].Value.(type) { - case bool: - if v { - sig.cb(types.Finding{ - Data: nil, - Context: ee, - SigMetadata: sig.metadata, - }) - } - case map[string]interface{}: - sig.cb(types.Finding{ - Data: v, - Context: ee, - SigMetadata: sig.metadata, - }) - } - } - return nil -} - -// OnSignal implements the Signature interface by handling lifecycle events of the signature -func (sig *RegoSignature) OnSignal(signal types.Signal) error { - return fmt.Errorf("function OnSignal is not implemented") -} - -func (sig *RegoSignature) Close() {} - -func (sig *RegoSignature) evalQuery(query string) (interface{}, error) { - pq, err := rego.New( - rego.Compiler(sig.compiledRego), - rego.Query(query), - ).PrepareForEval(context.TODO()) - if err != nil { - return nil, err - } - evalRes, err := pq.Eval(context.TODO()) - if err != nil { - return nil, err - } - if len(evalRes) > 0 && len(evalRes[0].Expressions) > 0 && evalRes[0].Expressions[0].Value != nil { - return evalRes[0].Expressions[0].Value, nil - } - return nil, nil -} diff --git a/goldens/tracee-sigs/rego/regosig/traceerego_test.go b/goldens/tracee-sigs/rego/regosig/traceerego_test.go deleted file mode 100644 index d4905d8..0000000 --- a/goldens/tracee-sigs/rego/regosig/traceerego_test.go +++ /dev/null @@ -1,372 +0,0 @@ -package regosig_test - -import ( - "encoding/json" - "fmt" - "os" - "testing" - - tracee "github.com/aquasecurity/tracee/tracee-ebpf/external" - "github.com/aquasecurity/tracee/tracee-rules/engine" - "github.com/aquasecurity/tracee/tracee-rules/signatures/rego/regosig" - "github.com/aquasecurity/tracee/tracee-rules/types" - "github.com/open-policy-agent/opa/compile" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestRegoSignature_GetMetadata(t *testing.T) { - sig, err := regosig.NewRegoSignature(compile.TargetRego, false, testRegoCodeBoolean) - require.NoError(t, err) - - metadata, err := sig.GetMetadata() - require.NoError(t, err) - assert.Equal(t, types.SignatureMetadata{ - ID: "TRC-BOOL", - Version: "0.1.0", - Name: "test name", - Description: "test description", - Tags: []string{ - "tag1", - "tag2", - }, - Properties: map[string]interface{}{ - "p1": "test", - "p2": json.Number("1"), - "p3": true, - }, - }, metadata) -} - -func TestRegoSignature_GetSelectedEvents(t *testing.T) { - sig, err := regosig.NewRegoSignature(compile.TargetRego, false, testRegoCodeBoolean) - require.NoError(t, err) - events, err := sig.GetSelectedEvents() - require.NoError(t, err) - assert.Equal(t, []types.SignatureEventSelector{ - { - Source: "tracee", - Name: "execve", - }, - }, events) -} - -func TestRegoSignature_OnEvent(t *testing.T) { - options := []struct { - target string - partial bool - }{ - { - target: compile.TargetRego, - partial: false, - }, - { - target: compile.TargetRego, - partial: true, - }, - //{ - // target: compile.TargetWasm, - // partial: false, - //}, - //{ - // target: compile.TargetWasm, - // partial: true, - //}, - } - - for _, tc := range options { - t.Run(fmt.Sprintf("target=%s,partial=%t", tc.target, tc.partial), func(t *testing.T) { - OnEventSpec(t, tc.target, tc.partial) - }) - } - -} - -// OnEventSpec describes the behavior of RegoSignature.OnEvent. -func OnEventSpec(t *testing.T, target string, partial bool) { - testCases := []struct { - name string - regoCode string - event tracee.Event - parseEvent bool - - finding *types.Finding - error string - }{ - { - name: "Should trigger finding when tracee_match rule returns boolean and event matches", - regoCode: testRegoCodeBoolean, - event: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - }, - }, - finding: &types.Finding{ - Data: nil, - Context: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - }, - }, - SigMetadata: types.SignatureMetadata{ - ID: "TRC-BOOL", - Version: "0.1.0", - Name: "test name", - Description: "test description", - Tags: []string{ - "tag1", - "tag2", - }, - Properties: map[string]interface{}{ - "p1": "test", - "p2": json.Number("1"), - "p3": true, - }, - }, - }, - }, - { - name: "Should trigger finding when tracee_match rule returns boolean and parsed event matches", - regoCode: testRegoCodeBoolean, - event: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - }, - }, - parseEvent: true, - finding: &types.Finding{ - Data: nil, - Context: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - }, - }, - SigMetadata: types.SignatureMetadata{ - ID: "TRC-BOOL", - Version: "0.1.0", - Name: "test name", - Description: "test description", - Tags: []string{ - "tag1", - "tag2", - }, - Properties: map[string]interface{}{ - "p1": "test", - "p2": json.Number("1"), - "p3": true, - }, - }, - }, - }, - { - name: "Shouldn't trigger finding when tracee_match rule returns boolean but event doesn't match", - regoCode: testRegoCodeBoolean, - event: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "doesn't end with yo!", - }, - }, - }, - finding: nil, - }, - { - name: "Should trigger finding when tracee_match rule returns object and event matches", - regoCode: testRegoCodeObject, - event: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: 1337, - }, - }, - }, - finding: &types.Finding{ - Data: map[string]interface{}{ - "p1": "test", - "p2": json.Number("1"), - "p3": true, - }, - Context: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: 1337, - }, - }, - }, - SigMetadata: types.SignatureMetadata{ - ID: "TRC-OBJECT", - Version: "0.3.0", - Name: "test name", - Description: "test description", - Tags: []string{ - "tag1", - "tag2", - }, - Properties: map[string]interface{}{ - "p1": "test", - "p2": json.Number("1"), - "p3": true, - }, - }, - }, - }, - { - name: "Should trigger finding when tracee_match rule returns object and parsed event matches", - regoCode: testRegoCodeObject, - event: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: 1337, - }, - }, - }, - parseEvent: true, - finding: &types.Finding{ - Data: map[string]interface{}{ - "p1": "test", - "p2": json.Number("1"), - "p3": true, - }, - Context: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "ends with yo", - }, - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: 1337, - }, - }, - }, - SigMetadata: types.SignatureMetadata{ - ID: "TRC-OBJECT", - Version: "0.3.0", - Name: "test name", - Description: "test description", - Tags: []string{ - "tag1", - "tag2", - }, - Properties: map[string]interface{}{ - "p1": "test", - "p2": json.Number("1"), - "p3": true, - }, - }, - }, - }, - { - name: "Shouldn't trigger finding when tracee_match rule returns object but event doesn't match", - regoCode: testRegoCodeObject, - event: tracee.Event{ - Args: []tracee.Argument{ - { - ArgMeta: tracee.ArgMeta{ - Name: "doesn't matter", - }, - Value: "yo is not at end", - }, - }, - }, - finding: nil, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - sig, err := regosig.NewRegoSignature(target, partial, tc.regoCode) - require.NoError(t, err) - - holder := &findingsHolder{} - err = sig.Init(holder.OnFinding) - require.NoError(t, err) - - var event interface{} - - if tc.parseEvent { - event, err = engine.ToParsedEvent(tc.event) - require.NoError(t, err) - } else { - event = tc.event - } - - err = sig.OnEvent(event) - if tc.error != "" { - assert.EqualError(t, err, tc.error) - } else { - require.NoError(t, err) - assert.Equal(t, tc.finding, holder.FirstValue()) - } - }) - } - - t.Run("Should return error when event has unrecognized type", func(t *testing.T) { - sig, err := regosig.NewRegoSignature(target, partial, testRegoCodeBoolean) - require.NoError(t, err) - - err = sig.OnEvent("UNRECOGNIZED") - assert.EqualError(t, err, "unrecognized event type: string") - }) -} - -func TestRegoSignature_OnSignal(t *testing.T) { - sig, err := regosig.NewRegoSignature(compile.TargetRego, false, testRegoCodeBoolean) - require.NoError(t, err) - err = sig.OnSignal(os.Kill) - assert.EqualError(t, err, "function OnSignal is not implemented") -}