Conversation
📝 WalkthroughWalkthroughThe PR refactors the CEL rule evaluation system to support context-based evaluation methods. It separates evaluation context creation from rule evaluation, introduces expression indexing by event type in the Rule struct, adds a new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…n CEL evaluation Signed-off-by: Yakir Oren <yakiroren@gmail.com>
4cca818 to
9a69a13
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pkg/rulemanager/rulecreator/factory.go (1)
71-80:⚠️ Potential issue | 🟠 MajorPre-existing bug:
CreateRulePolicyRulesByEventTypeappends to the slice it's iterating over.This isn't part of the current PR changes, but this method iterates over
rulesand conditionally appends torulesin the same loop (line 75), which will cause duplicate entries and potentially an infinite loop (in Go,rangeevaluates the length once, so it won't be infinite, but duplicates will still be added).Proposed fix
func (r *RuleCreatorImpl) CreateRulePolicyRulesByEventType(eventType utils.EventType) []typesv1.Rule { - rules := r.CreateRulesByEventType(eventType) - for _, rule := range rules { + allRules := r.CreateRulesByEventType(eventType) + var rules []typesv1.Rule + for _, rule := range allRules { if rule.SupportPolicy { rules = append(rules, rule) } } - return rules }
🧹 Nitpick comments (4)
pkg/rulemanager/cel/libraries/parse/parse.go (1)
31-41: Consider usingpath.Basefrom the standard library.The stdlib
path.Basehandles additional edge cases (e.g., trailing slashes, empty strings,"."paths) and is well-tested. Your current implementation returns""for a trailing-slash input like"/usr/bin/", whereaspath.Basewould return"bin".If the empty-string-on-trailing-slash behavior is intentional for your CEL rules, this is fine as-is — just worth documenting that choice. Otherwise,
path.Baseis a drop-in replacement here.♻️ If stdlib behavior is acceptable
-import ( - "strings" - - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/kubescape/node-agent/pkg/rulemanager/cel/libraries/celparse" -) +import ( + "path" + + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/kubescape/node-agent/pkg/rulemanager/cel/libraries/celparse" +)func (l *parseLibrary) basename(path ref.Val) ref.Val { s, ok := path.Value().(string) if !ok { return types.MaybeNoSuchOverloadErr(path) } - idx := strings.LastIndex(s, "/") - if idx == -1 { - return types.String(s) - } - return types.String(s[idx+1:]) + return types.String(path.Base(s)) }Note: there would be a name collision between the
pathparameter and thepathimport — rename the parameter (e.g.,val) if you go this route.pkg/rulemanager/cel/libraries/parse/parsing_test.go (1)
45-64: Consider adding a test for the empty-string edge case.An empty string input (
parse.basename('')) would exercise theLastIndex == -1branch and return"". It's a plausible real-world input (e.g., unset field) worth covering.pkg/rulemanager/rule_manager.go (1)
373-386:getUniqueIdAndMessage: only the last error is returned.If the message evaluation (line 374) fails and uniqueID evaluation (line 378) succeeds,
erris overwritten toniland the message error is silently lost. The function would return an empty message with no error indication. This appears to be a pre-existing pattern, but now that both calls use the same eval context, it's worth noting.Optional: propagate the first error
func (rm *RuleManager) getUniqueIdAndMessage(evalContext map[string]any, rule typesv1.Rule) (string, string, error) { message, err := rm.celEvaluator.EvaluateExpressionWithContext(evalContext, rule.Expressions.Message) if err != nil { logger.L().Error("RuleManager - failed to evaluate message", helpers.Error(err)) + return "", "", fmt.Errorf("failed to evaluate message: %w", err) } uniqueID, err := rm.celEvaluator.EvaluateExpressionWithContext(evalContext, rule.Expressions.UniqueID) if err != nil { logger.L().Error("RuleManager - failed to evaluate unique ID", helpers.Error(err)) + return "", "", fmt.Errorf("failed to evaluate unique ID: %w", err) }pkg/rulemanager/types/v1/types.go (1)
36-48: Good design: pre-indexed expressions avoid per-event scanning.The
json:"-" yaml:"-"tags and the pointer-receiverInit()are correct. One subtle point: ifInit()is ever not called,ExpressionsByEventTyperemainsnil. In Go, indexing anilmap returns the zero value (empty slice) without panicking, so rules would be silently skipped rather than producing an error. The factory currently callsInit()in all mutation paths (RegisterRule, SyncRules, UpdateRule), ensuring all rules in circulation are initialized. However,CreateRuleByIDandCreateRuleByNamecan return an empty Rule{} on failed lookup; consider adding a defensive nil-check in the consumer (rule_manager.go) to guard against future code paths.Optional: lazy-init guard in the lookup
In
rule_manager.gowhere you look up expressions:+ if rule.ExpressionsByEventType == nil { + logger.L().Warning("Rule not initialized, skipping", helpers.String("rule", rule.ID)) + continue + } ruleExpressions := rule.ExpressionsByEventType[eventType] if len(ruleExpressions) == 0 {
matthyx
left a comment
There was a problem hiding this comment.
did you run a Bench to see the improvements? maybe we should commit it too...
|
|
||
| // Init pre-computes the ExpressionsByEventType index. | ||
| // Must be called after a Rule is created or updated. | ||
| func (r *Rule) Init() { |
There was a problem hiding this comment.
if called twice, it recreates the map and loses the original data, maybe add a guard?
| func (r *Rule) Init() { | |
| func (r *Rule) Init() { | |
| if r.ExpressionsByEventType != nil { | |
| return | |
| } |
Summary
Summary by CodeRabbit
Release Notes
New Features
parse.basename()function to extract filenames from file paths.Refactor