Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CWS] introduce FIM meta rules #32388

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions pkg/security/secl/compiler/eval/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,13 @@ func newOptsWithParams(constants map[string]interface{}, legacyFields map[Field]
}

func parseRule(expr string, model Model, opts *Opts) (*Rule, error) {
rule := NewRule("id1", expr, opts)

pc := ast.NewParsingContext(false)

if err := rule.Parse(pc); err != nil {
rule, err := NewRule("id1", expr, pc, opts)
if err != nil {
return nil, fmt.Errorf("parsing error: %v", err)
}

if err := rule.GenEvaluator(model, pc); err != nil {
if err := rule.GenEvaluator(model); err != nil {
return rule, fmt.Errorf("compilation error: %v", err)
}

Expand Down Expand Up @@ -1561,8 +1559,7 @@ func BenchmarkPartial(b *testing.B) {
b.Fatal(err)
}

pc := ast.NewParsingContext(false)
if err := rule.GenEvaluator(model, pc); err != nil {
if err := rule.GenEvaluator(model); err != nil {
b.Fatal(err)
}

Expand Down
18 changes: 9 additions & 9 deletions pkg/security/secl/compiler/eval/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ type RuleEvaluator struct {
}

// NewRule returns a new rule
func NewRule(id string, expression string, opts *Opts, tags ...string) *Rule {
func NewRule(id string, expression string, parsingContext *ast.ParsingContext, opts *Opts, tags ...string) (*Rule, error) {
if opts.MacroStore == nil {
opts.WithMacroStore(&MacroStore{})
}
Expand All @@ -66,13 +66,19 @@ func NewRule(id string, expression string, opts *Opts, tags ...string) *Rule {
panic(err)
}

astRule, err := parsingContext.ParseRule(expression)
if err != nil {
return nil, err
}

return &Rule{
ID: id,
Expression: expression,
Opts: opts,
Tags: tags,
pprofLabels: labelSet,
}
ast: astRule,
}, nil
}

// IsPartialAvailable checks if partial have been generated for the given Field
Expand Down Expand Up @@ -274,15 +280,9 @@ func NewRuleEvaluator(rule *ast.Rule, model Model, opts *Opts) (*RuleEvaluator,
}

// GenEvaluator - Compile and generates the RuleEvaluator
func (r *Rule) GenEvaluator(model Model, parsingCtx *ast.ParsingContext) error {
func (r *Rule) GenEvaluator(model Model) error {
r.Model = model

if r.ast == nil {
if err := r.Parse(parsingCtx); err != nil {
return err
}
}

evaluator, err := NewRuleEvaluator(r.ast, model, r.Opts)
if err != nil {
if err, ok := err.(*ErrAstToEval); ok {
Expand Down
7 changes: 3 additions & 4 deletions pkg/security/secl/rules/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,12 @@ func (a *Action) CompileFilter(parsingContext *ast.ParsingContext, model eval.Mo

expression := *a.Def.Filter

eval := eval.NewRule("action_rule", expression, evalOpts)

if err := eval.Parse(parsingContext); err != nil {
eval, err := eval.NewRule("action_rule", expression, parsingContext, evalOpts)
if err != nil {
return &ErrActionFilter{Expression: expression, Err: err}
}

if err := eval.GenEvaluator(model, parsingContext); err != nil {
if err := eval.GenEvaluator(model); err != nil {
return &ErrActionFilter{Expression: expression, Err: err}
}

Expand Down
6 changes: 0 additions & 6 deletions pkg/security/secl/rules/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ type RuleBucket struct {

// AddRule adds a rule to the bucket
func (rb *RuleBucket) AddRule(rule *Rule) error {
for _, r := range rb.rules {
if r.Def.ID == rule.Def.ID {
return &ErrRuleLoad{Rule: rule.PolicyRule, Err: ErrDefinitionIDConflict}
}
}

for _, field := range rule.GetEvaluator().GetFields() {
index := sort.SearchStrings(rb.fields, field)
if index < len(rb.fields) && rb.fields[index] == field {
Expand Down
3 changes: 3 additions & 0 deletions pkg/security/secl/rules/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ var (

// ErrRuleAgentFilter is returned when an agent rule was filtered
ErrRuleAgentFilter = errors.New("agent rule filtered")

// ErrMultipleEventCategories is returned when multile event categories are in the same expansion
ErrMultipleEventCategories = errors.New("multiple event categories in the same rule expansion")
)

// ErrFieldTypeUnknown is returned when a field has an unknown type
Expand Down
23 changes: 23 additions & 0 deletions pkg/security/secl/rules/fim_others.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build !unix

// Package rules holds rules related files
package rules

type expandedRule struct {
id string
expr string
}

func expandFim(baseID, _, baseExpr string) []expandedRule {
return []expandedRule{
{
id: baseID,
expr: baseExpr,
},
}
}
107 changes: 107 additions & 0 deletions pkg/security/secl/rules/fim_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build unix

// Package rules holds rules related files
package rules

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestExpandFIM(t *testing.T) {
entries := []struct {
id string
expr string
expected []expandedRule
}{
{
id: "test",
expr: "fim.write.file.path == \"/tmp/test\"",
expected: []expandedRule{
{
id: "__fim_expanded_open__test",
expr: "(open.file.path == \"/tmp/test\") && open.flags & (O_CREAT|O_TRUNC|O_APPEND|O_RDWR|O_WRONLY) > 0",
},
{
id: "__fim_expanded_chmod__test",
expr: "chmod.file.path == \"/tmp/test\"",
},
{
id: "__fim_expanded_chown__test",
expr: "chown.file.path == \"/tmp/test\"",
},
{
id: "__fim_expanded_link__test",
expr: "link.file.path == \"/tmp/test\"",
},
{
id: "__fim_expanded_rename__test",
expr: "rename.file.path == \"/tmp/test\"",
},
{
id: "__fim_expanded_rename_destination__test",
expr: "rename.file.destination.path == \"/tmp/test\"",
},
{
id: "__fim_expanded_unlink__test",
expr: "unlink.file.path == \"/tmp/test\"",
},
{
id: "__fim_expanded_utimes__test",
expr: "utimes.file.path == \"/tmp/test\"",
},
},
},
{
id: "complex",
expr: "(fim.write.file.path == \"/tmp/test\" || fim.write.file.name == \"abc\") && process.file.name == \"def\" && container.id != \"\"",
expected: []expandedRule{
{
id: "__fim_expanded_open__complex",
expr: "((open.file.path == \"/tmp/test\" || open.file.name == \"abc\") && process.file.name == \"def\" && container.id != \"\") && open.flags & (O_CREAT|O_TRUNC|O_APPEND|O_RDWR|O_WRONLY) > 0",
},
{
id: "__fim_expanded_chmod__complex",
expr: "(chmod.file.path == \"/tmp/test\" || chmod.file.name == \"abc\") && process.file.name == \"def\" && container.id != \"\"",
},
{
id: "__fim_expanded_chown__complex",
expr: "(chown.file.path == \"/tmp/test\" || chown.file.name == \"abc\") && process.file.name == \"def\" && container.id != \"\"",
},
{
id: "__fim_expanded_link__complex",
expr: "(link.file.path == \"/tmp/test\" || link.file.name == \"abc\") && process.file.name == \"def\" && container.id != \"\"",
},
{
id: "__fim_expanded_rename__complex",
expr: "(rename.file.path == \"/tmp/test\" || rename.file.name == \"abc\") && process.file.name == \"def\" && container.id != \"\"",
},
{
id: "__fim_expanded_rename_destination__complex",
expr: "(rename.file.destination.path == \"/tmp/test\" || rename.file.destination.name == \"abc\") && process.file.name == \"def\" && container.id != \"\"",
},
{
id: "__fim_expanded_unlink__complex",
expr: "(unlink.file.path == \"/tmp/test\" || unlink.file.name == \"abc\") && process.file.name == \"def\" && container.id != \"\"",
},
{
id: "__fim_expanded_utimes__complex",
expr: "(utimes.file.path == \"/tmp/test\" || utimes.file.name == \"abc\") && process.file.name == \"def\" && container.id != \"\"",
},
},
},
}

for _, entry := range entries {
t.Run(entry.id, func(t *testing.T) {
actual := expandFim(entry.id, "", entry.expr)
assert.Equal(t, entry.expected, actual)
})
}
}
55 changes: 55 additions & 0 deletions pkg/security/secl/rules/fim_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build unix

// Package rules holds rules related files
package rules

import (
"fmt"
"strings"
)

type expandedRule struct {
id string
expr string
}

func expandFim(baseID, groupID, baseExpr string) []expandedRule {
if !strings.Contains(baseExpr, "fim.write.file.") {
paulcacheux marked this conversation as resolved.
Show resolved Hide resolved
return []expandedRule{
{
id: baseID,
expr: baseExpr,
},
}
}

var expandedRules []expandedRule
for _, eventType := range []string{"open", "chmod", "chown", "link", "rename", "unlink", "utimes"} {
expr := strings.Replace(baseExpr, "fim.write.file.", fmt.Sprintf("%s.file.", eventType), -1)
if eventType == "open" {
expr = fmt.Sprintf("(%s) && open.flags & (O_CREAT|O_TRUNC|O_APPEND|O_RDWR|O_WRONLY) > 0", expr)
}

id := fmt.Sprintf("__fim_expanded_%s_%s_%s", eventType, groupID, baseID)
expandedRules = append(expandedRules, expandedRule{
id: id,
expr: expr,
})

if eventType == "rename" {
expr := strings.Replace(baseExpr, "fim.write.file.", "rename.file.destination.", -1)
id := fmt.Sprintf("__fim_expanded_%s_%s_%s", "rename_destination", groupID, baseID)
expandedRules = append(expandedRules, expandedRule{
id: id,
expr: expr,
})
}
}

return expandedRules
}
Loading
Loading