github.com/DataDog/datadog-agent/pkg/security/secl@v0.55.0-devel.0.20240517055856-10c4965fea94/rules/ruleset.go (about) 1 // Unless explicitly stated otherwise all files in this repository are licensed 2 // under the Apache License Version 2.0. 3 // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 // Copyright 2016-present Datadog, Inc. 5 6 // Package rules holds rules related files 7 package rules 8 9 import ( 10 "errors" 11 "fmt" 12 "reflect" 13 "slices" 14 "sync" 15 "time" 16 17 "github.com/spf13/cast" 18 19 "github.com/hashicorp/go-multierror" 20 21 "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/ast" 22 "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" 23 "github.com/DataDog/datadog-agent/pkg/security/secl/log" 24 "github.com/DataDog/datadog-agent/pkg/security/secl/model" 25 "github.com/DataDog/datadog-agent/pkg/security/secl/utils" 26 ) 27 28 // MacroID represents the ID of a macro 29 type MacroID = string 30 31 // CombinePolicy represents the policy to use to combine rules and macros 32 type CombinePolicy = string 33 34 // Combine policies 35 const ( 36 NoPolicy CombinePolicy = "" 37 MergePolicy CombinePolicy = "merge" 38 OverridePolicy CombinePolicy = "override" 39 ) 40 41 // OverrideField defines a combine field 42 type OverrideField = string 43 44 const ( 45 // OverrideAllFields used to override all the fields 46 OverrideAllFields OverrideField = "all" 47 // OverrideExpressionField used to override the expression 48 OverrideExpressionField OverrideField = "expression" 49 // OverrideActionFields used to override the actions 50 OverrideActionFields OverrideField = "actions" 51 // OverrideEveryField used to override the every field 52 OverrideEveryField OverrideField = "every" 53 // OverrideTagsField used to override the tags 54 OverrideTagsField OverrideField = "tags" 55 ) 56 57 // OverrideOptions defines combine options 58 type OverrideOptions struct { 59 Fields []OverrideField `yaml:"fields"` 60 } 61 62 // Ruleset loading operations 63 const ( 64 RuleSetTagKey = "ruleset" 65 DefaultRuleSetTagValue = "probe_evaluation" 66 ) 67 68 // MacroDefinition holds the definition of a macro 69 type MacroDefinition struct { 70 ID MacroID `yaml:"id"` 71 Expression string `yaml:"expression"` 72 Description string `yaml:"description"` 73 AgentVersionConstraint string `yaml:"agent_version"` 74 Filters []string `yaml:"filters"` 75 Values []string `yaml:"values"` 76 Combine CombinePolicy `yaml:"combine"` 77 } 78 79 // MergeWith merges macro m2 into m 80 func (m *MacroDefinition) MergeWith(m2 *MacroDefinition) error { 81 switch m2.Combine { 82 case MergePolicy: 83 if m.Expression != "" || m2.Expression != "" { 84 return &ErrMacroLoad{Definition: m2, Err: ErrCannotMergeExpression} 85 } 86 m.Values = append(m.Values, m2.Values...) 87 case OverridePolicy: 88 m.Values = m2.Values 89 default: 90 return &ErrMacroLoad{Definition: m2, Err: ErrDefinitionIDConflict} 91 } 92 return nil 93 } 94 95 // Macro describes a macro of a ruleset 96 type Macro struct { 97 *eval.Macro 98 Definition *MacroDefinition 99 } 100 101 // RuleID represents the ID of a rule 102 type RuleID = string 103 104 // RuleDefinition holds the definition of a rule 105 type RuleDefinition struct { 106 ID RuleID `yaml:"id"` 107 Version string `yaml:"version"` 108 Expression string `yaml:"expression"` 109 Description string `yaml:"description"` 110 Tags map[string]string `yaml:"tags"` 111 AgentVersionConstraint string `yaml:"agent_version"` 112 Filters []string `yaml:"filters"` 113 Disabled bool `yaml:"disabled"` 114 Combine CombinePolicy `yaml:"combine"` 115 OverrideOptions OverrideOptions `yaml:"override_options"` 116 Actions []*ActionDefinition `yaml:"actions"` 117 Every time.Duration `yaml:"every"` 118 Silent bool `yaml:"silent"` 119 GroupID string `yaml:"group_id"` 120 Policy *Policy 121 } 122 123 // GetTag returns the tag value associated with a tag key 124 func (rd *RuleDefinition) GetTag(tagKey string) (string, bool) { 125 tagValue, ok := rd.Tags[tagKey] 126 if ok { 127 return tagValue, true 128 } 129 return "", false 130 } 131 132 func applyOverride(rd1, rd2 *RuleDefinition) { 133 // keep track of the combine 134 rd1.Combine = rd2.Combine 135 136 // for backward compatibility, by default only the expression is copied if no options 137 if len(rd2.OverrideOptions.Fields) == 0 { 138 rd1.Expression = rd2.Expression 139 } else if slices.Contains(rd2.OverrideOptions.Fields, OverrideAllFields) { 140 // keep the original policy 141 policy := rd1.Policy 142 143 *rd1 = *rd2 144 rd1.Policy = policy 145 } else { 146 if slices.Contains(rd2.OverrideOptions.Fields, OverrideExpressionField) { 147 rd1.Expression = rd2.Expression 148 } 149 if slices.Contains(rd2.OverrideOptions.Fields, OverrideActionFields) { 150 rd1.Actions = rd2.Actions 151 } 152 if slices.Contains(rd2.OverrideOptions.Fields, OverrideEveryField) { 153 rd1.Every = rd2.Every 154 } 155 if slices.Contains(rd2.OverrideOptions.Fields, OverrideTagsField) { 156 rd1.Tags = rd2.Tags 157 } 158 } 159 } 160 161 // MergeWith merges rule rd2 into rd 162 func (rd *RuleDefinition) MergeWith(rd2 *RuleDefinition) error { 163 switch rd2.Combine { 164 case OverridePolicy: 165 applyOverride(rd, rd2) 166 default: 167 if !rd2.Disabled { 168 return &ErrRuleLoad{Definition: rd2, Err: ErrDefinitionIDConflict} 169 } 170 } 171 rd.Disabled = rd2.Disabled 172 return nil 173 } 174 175 // Rule describes a rule of a ruleset 176 type Rule struct { 177 *eval.Rule 178 Definition *RuleDefinition 179 } 180 181 // RuleSetListener describes the methods implemented by an object used to be 182 // notified of events on a rule set. 183 type RuleSetListener interface { 184 RuleMatch(rule *Rule, event eval.Event) bool 185 EventDiscarderFound(rs *RuleSet, event eval.Event, field eval.Field, eventType eval.EventType) 186 } 187 188 // RuleSet holds a list of rules, grouped in bucket. An event can be evaluated 189 // against it. If the rule matches, the listeners for this rule set are notified 190 type RuleSet struct { 191 opts *Opts 192 evalOpts *eval.Opts 193 eventRuleBuckets map[eval.EventType]*RuleBucket 194 rules map[eval.RuleID]*Rule 195 policies []*Policy 196 fieldEvaluators map[string]eval.Evaluator 197 model eval.Model 198 eventCtor func() eval.Event 199 listenersLock sync.RWMutex 200 listeners []RuleSetListener 201 globalVariables eval.GlobalVariables 202 scopedVariables map[Scope]VariableProvider 203 // fields holds the list of event field queries (like "process.uid") used by the entire set of rules 204 fields []string 205 logger log.Logger 206 pool *eval.ContextPool 207 208 // event collector, used for tests 209 eventCollector EventCollector 210 } 211 212 // ListRuleIDs returns the list of RuleIDs from the ruleset 213 func (rs *RuleSet) ListRuleIDs() []RuleID { 214 var ids []string 215 for ruleID := range rs.rules { 216 ids = append(ids, ruleID) 217 } 218 return ids 219 } 220 221 // GetRules returns the active rules 222 func (rs *RuleSet) GetRules() map[eval.RuleID]*Rule { 223 return rs.rules 224 } 225 226 // GetRuleSetTag gets the value of the "ruleset" tag, which is the tag of the rules that belong in this rule set 227 func (rs *RuleSet) GetRuleSetTag() eval.RuleSetTagValue { 228 return rs.opts.RuleSetTag[RuleSetTagKey] 229 } 230 231 // ListMacroIDs returns the list of MacroIDs from the ruleset 232 func (rs *RuleSet) ListMacroIDs() []MacroID { 233 var ids []string 234 for _, macro := range rs.evalOpts.MacroStore.List() { 235 ids = append(ids, macro.ID) 236 } 237 return ids 238 } 239 240 // AddMacros parses the macros AST and adds them to the list of macros of the ruleset 241 func (rs *RuleSet) AddMacros(parsingContext *ast.ParsingContext, macros []*MacroDefinition) *multierror.Error { 242 var result *multierror.Error 243 244 // Build the list of macros for the ruleset 245 for _, macroDef := range macros { 246 if _, err := rs.AddMacro(parsingContext, macroDef); err != nil { 247 result = multierror.Append(result, err) 248 } 249 } 250 251 return result 252 } 253 254 // AddMacro parses the macro AST and adds it to the list of macros of the ruleset 255 func (rs *RuleSet) AddMacro(parsingContext *ast.ParsingContext, macroDef *MacroDefinition) (*eval.Macro, error) { 256 var err error 257 258 if rs.evalOpts.MacroStore.Contains(macroDef.ID) { 259 return nil, &ErrMacroLoad{Definition: macroDef, Err: ErrDefinitionIDConflict} 260 } 261 262 macro := &Macro{Definition: macroDef} 263 264 switch { 265 case macroDef.Expression != "" && len(macroDef.Values) > 0: 266 return nil, &ErrMacroLoad{Definition: macroDef, Err: errors.New("only one of 'expression' and 'values' can be defined")} 267 case macroDef.Expression != "": 268 if macro.Macro, err = eval.NewMacro(macroDef.ID, macroDef.Expression, rs.model, parsingContext, rs.evalOpts); err != nil { 269 return nil, &ErrMacroLoad{Definition: macroDef, Err: err} 270 } 271 default: 272 if macro.Macro, err = eval.NewStringValuesMacro(macroDef.ID, macroDef.Values, rs.evalOpts); err != nil { 273 return nil, &ErrMacroLoad{Definition: macroDef, Err: err} 274 } 275 } 276 277 rs.evalOpts.MacroStore.Add(macro.Macro) 278 279 return macro.Macro, nil 280 } 281 282 // AddRules adds rules to the ruleset and generate their partials 283 func (rs *RuleSet) AddRules(parsingContext *ast.ParsingContext, rules []*RuleDefinition) *multierror.Error { 284 var result *multierror.Error 285 286 for _, ruleDef := range rules { 287 if _, err := rs.AddRule(parsingContext, ruleDef); err != nil { 288 result = multierror.Append(result, err) 289 } 290 } 291 292 return result 293 } 294 295 func (rs *RuleSet) populateFieldsWithRuleActionsData(policyRules []*RuleDefinition, opts PolicyLoaderOpts) *multierror.Error { 296 var errs *multierror.Error 297 298 for _, rule := range policyRules { 299 for _, action := range rule.Actions { 300 if err := action.Check(opts); err != nil { 301 errs = multierror.Append(errs, fmt.Errorf("invalid action: %w", err)) 302 continue 303 } 304 305 switch { 306 case action.Set != nil: 307 varName := action.Set.Name 308 if action.Set.Scope != "" { 309 varName = string(action.Set.Scope) + "." + varName 310 } 311 312 if _, err := rs.eventCtor().GetFieldValue(varName); err == nil { 313 errs = multierror.Append(errs, fmt.Errorf("variable '%s' conflicts with field", varName)) 314 continue 315 } 316 317 if _, found := rs.evalOpts.Constants[varName]; found { 318 errs = multierror.Append(errs, fmt.Errorf("variable '%s' conflicts with constant", varName)) 319 continue 320 } 321 322 var variableValue interface{} 323 324 if action.Set.Value != nil { 325 switch value := action.Set.Value.(type) { 326 case int: 327 action.Set.Value = []int{value} 328 case string: 329 action.Set.Value = []string{value} 330 case []interface{}: 331 if len(value) == 0 { 332 errs = multierror.Append(errs, fmt.Errorf("unable to infer item type for '%s'", action.Set.Name)) 333 continue 334 } 335 336 switch arrayType := value[0].(type) { 337 case int: 338 action.Set.Value = cast.ToIntSlice(value) 339 case string: 340 action.Set.Value = cast.ToStringSlice(value) 341 default: 342 errs = multierror.Append(errs, fmt.Errorf("unsupported item type '%s' for array '%s'", reflect.TypeOf(arrayType), action.Set.Name)) 343 continue 344 } 345 } 346 347 variableValue = action.Set.Value 348 } else if action.Set.Field != "" { 349 kind, err := rs.eventCtor().GetFieldType(action.Set.Field) 350 if err != nil { 351 errs = multierror.Append(errs, fmt.Errorf("failed to get field '%s': %w", action.Set.Field, err)) 352 continue 353 } 354 355 switch kind { 356 case reflect.String: 357 variableValue = []string{} 358 case reflect.Int: 359 variableValue = []int{} 360 case reflect.Bool: 361 variableValue = false 362 default: 363 errs = multierror.Append(errs, fmt.Errorf("unsupported field type '%s' for variable '%s'", kind, action.Set.Name)) 364 continue 365 } 366 } 367 368 var variable eval.VariableValue 369 var variableProvider VariableProvider 370 371 if action.Set.Scope != "" { 372 stateScopeBuilder := rs.opts.StateScopes[action.Set.Scope] 373 if stateScopeBuilder == nil { 374 errs = multierror.Append(errs, fmt.Errorf("invalid scope '%s'", action.Set.Scope)) 375 continue 376 } 377 378 if _, found := rs.scopedVariables[action.Set.Scope]; !found { 379 rs.scopedVariables[action.Set.Scope] = stateScopeBuilder() 380 } 381 382 variableProvider = rs.scopedVariables[action.Set.Scope] 383 } else { 384 variableProvider = &rs.globalVariables 385 } 386 387 variable, err := variableProvider.GetVariable(action.Set.Name, variableValue) 388 if err != nil { 389 errs = multierror.Append(errs, fmt.Errorf("invalid type '%s' for variable '%s': %w", reflect.TypeOf(action.Set.Value), action.Set.Name, err)) 390 continue 391 } 392 393 if existingVariable := rs.evalOpts.VariableStore.Get(varName); existingVariable != nil && reflect.TypeOf(variable) != reflect.TypeOf(existingVariable) { 394 errs = multierror.Append(errs, fmt.Errorf("conflicting types for variable '%s'", varName)) 395 continue 396 } 397 398 rs.evalOpts.VariableStore.Add(varName, variable) 399 } 400 } 401 } 402 return errs 403 } 404 405 // ListFields returns all the fields accessed by all rules of this rule set 406 func (rs *RuleSet) ListFields() []string { 407 return rs.fields 408 } 409 410 // GetRuleEventType return the rule EventType. Currently rules support only one eventType 411 func GetRuleEventType(rule *eval.Rule) (eval.EventType, error) { 412 eventTypes, err := rule.GetEventTypes() 413 if err != nil { 414 return "", err 415 } 416 417 if len(eventTypes) == 0 { 418 return "", ErrRuleWithoutEvent 419 } 420 421 // TODO: this contraints could be removed, but currently approver resolution can't handle multiple event type approver 422 if len(eventTypes) > 1 { 423 return "", ErrRuleWithMultipleEvents 424 } 425 426 return eventTypes[0], nil 427 } 428 429 // AddRule creates the rule evaluator and adds it to the bucket of its events 430 func (rs *RuleSet) AddRule(parsingContext *ast.ParsingContext, ruleDef *RuleDefinition) (*eval.Rule, error) { 431 if ruleDef.Disabled { 432 return nil, nil 433 } 434 435 for _, id := range rs.opts.ReservedRuleIDs { 436 if id == ruleDef.ID { 437 return nil, &ErrRuleLoad{Definition: ruleDef, Err: ErrInternalIDConflict} 438 } 439 } 440 441 if _, exists := rs.rules[ruleDef.ID]; exists { 442 return nil, &ErrRuleLoad{Definition: ruleDef, Err: ErrDefinitionIDConflict} 443 } 444 445 var tags []string 446 for k, v := range ruleDef.Tags { 447 tags = append(tags, k+":"+v) 448 } 449 450 rule := &Rule{ 451 Rule: eval.NewRule(ruleDef.ID, ruleDef.Expression, rs.evalOpts, tags...), 452 Definition: ruleDef, 453 } 454 455 if err := rule.Parse(parsingContext); err != nil { 456 return nil, &ErrRuleLoad{Definition: ruleDef, Err: &ErrRuleSyntax{Err: err}} 457 } 458 459 if err := rule.GenEvaluator(rs.model, parsingContext); err != nil { 460 return nil, &ErrRuleLoad{Definition: ruleDef, Err: err} 461 } 462 463 eventType, err := GetRuleEventType(rule.Rule) 464 if err != nil { 465 return nil, &ErrRuleLoad{Definition: ruleDef, Err: err} 466 } 467 468 // ignore event types not supported 469 if _, exists := rs.opts.EventTypeEnabled["*"]; !exists { 470 if _, exists := rs.opts.EventTypeEnabled[eventType]; !exists { 471 return nil, &ErrRuleLoad{Definition: ruleDef, Err: ErrEventTypeNotEnabled} 472 } 473 } 474 475 for _, action := range rule.Definition.Actions { 476 // compile action filter 477 if action.Filter != nil { 478 if err := action.CompileFilter(parsingContext, rs.model, rs.evalOpts); err != nil { 479 return nil, &ErrRuleLoad{Definition: ruleDef, Err: err} 480 } 481 } 482 483 if action.Set != nil && action.Set.Field != "" { 484 if _, found := rs.fieldEvaluators[action.Set.Field]; !found { 485 evaluator, err := rs.model.GetEvaluator(action.Set.Field, "") 486 if err != nil { 487 return nil, err 488 } 489 rs.fieldEvaluators[action.Set.Field] = evaluator 490 } 491 } 492 } 493 494 for _, event := range rule.GetEvaluator().EventTypes { 495 bucket, exists := rs.eventRuleBuckets[event] 496 if !exists { 497 bucket = &RuleBucket{} 498 rs.eventRuleBuckets[event] = bucket 499 } 500 501 if err := bucket.AddRule(rule); err != nil { 502 return nil, err 503 } 504 } 505 506 // Merge the fields of the new rule with the existing list of fields of the ruleset 507 rs.AddFields(rule.GetEvaluator().GetFields()) 508 509 rs.rules[ruleDef.ID] = rule 510 511 return rule.Rule, nil 512 } 513 514 // NotifyRuleMatch notifies all the ruleset listeners that an event matched a rule 515 func (rs *RuleSet) NotifyRuleMatch(rule *Rule, event eval.Event) { 516 rs.listenersLock.RLock() 517 defer rs.listenersLock.RUnlock() 518 519 for _, listener := range rs.listeners { 520 if !listener.RuleMatch(rule, event) { 521 break 522 } 523 } 524 } 525 526 // NotifyDiscarderFound notifies all the ruleset listeners that a discarder was found for an event 527 func (rs *RuleSet) NotifyDiscarderFound(event eval.Event, field eval.Field, eventType eval.EventType) { 528 rs.listenersLock.RLock() 529 defer rs.listenersLock.RUnlock() 530 531 for _, listener := range rs.listeners { 532 listener.EventDiscarderFound(rs, event, field, eventType) 533 } 534 } 535 536 // AddListener adds a listener on the ruleset 537 func (rs *RuleSet) AddListener(listener RuleSetListener) { 538 rs.listenersLock.Lock() 539 defer rs.listenersLock.Unlock() 540 541 rs.listeners = append(rs.listeners, listener) 542 } 543 544 // HasRulesForEventType returns if there is at least one rule for the given event type 545 func (rs *RuleSet) HasRulesForEventType(eventType eval.EventType) bool { 546 bucket, found := rs.eventRuleBuckets[eventType] 547 if !found { 548 return false 549 } 550 return len(bucket.rules) > 0 551 } 552 553 // GetBucket returns rule bucket for the given event type 554 func (rs *RuleSet) GetBucket(eventType eval.EventType) *RuleBucket { 555 if bucket, exists := rs.eventRuleBuckets[eventType]; exists { 556 return bucket 557 } 558 return nil 559 } 560 561 // GetApprovers returns all approvers 562 func (rs *RuleSet) GetApprovers(fieldCaps map[eval.EventType]FieldCapabilities) (map[eval.EventType]Approvers, error) { 563 approvers := make(map[eval.EventType]Approvers) 564 for _, eventType := range rs.GetEventTypes() { 565 caps, exists := fieldCaps[eventType] 566 if !exists { 567 continue 568 } 569 570 eventApprovers, err := rs.GetEventApprovers(eventType, caps) 571 if err != nil || len(eventApprovers) == 0 { 572 continue 573 } 574 approvers[eventType] = eventApprovers 575 } 576 577 return approvers, nil 578 } 579 580 // GetEventApprovers returns approvers for the given event type and the fields 581 func (rs *RuleSet) GetEventApprovers(eventType eval.EventType, fieldCaps FieldCapabilities) (Approvers, error) { 582 bucket, exists := rs.eventRuleBuckets[eventType] 583 if !exists { 584 return nil, ErrNoEventTypeBucket{EventType: eventType} 585 } 586 587 return GetApprovers(bucket.rules, model.NewFakeEvent(), fieldCaps) 588 } 589 590 // GetFieldValues returns all the values of the given field 591 func (rs *RuleSet) GetFieldValues(field eval.Field) []eval.FieldValue { 592 var values []eval.FieldValue 593 594 for _, rule := range rs.rules { 595 rv := rule.GetFieldValues(field) 596 if len(rv) > 0 { 597 values = append(values, rv...) 598 } 599 } 600 601 return values 602 } 603 604 // IsDiscarder partially evaluates an Event against a field 605 func IsDiscarder(ctx *eval.Context, field eval.Field, rules []*Rule) (bool, error) { 606 for _, rule := range rules { 607 isTrue, err := rule.PartialEval(ctx, field) 608 if err != nil || isTrue { 609 return false, err 610 } 611 } 612 return true, nil 613 } 614 615 // IsDiscarder partially evaluates an Event against a field 616 func (rs *RuleSet) IsDiscarder(event eval.Event, field eval.Field) (bool, error) { 617 eventType, err := event.GetFieldEventType(field) 618 if err != nil { 619 return false, err 620 } 621 622 bucket, exists := rs.eventRuleBuckets[eventType] 623 if !exists { 624 return false, &ErrNoEventTypeBucket{EventType: eventType} 625 } 626 627 ctx := rs.pool.Get(event) 628 defer rs.pool.Put(ctx) 629 630 return IsDiscarder(ctx, field, bucket.rules) 631 } 632 633 func (rs *RuleSet) runRuleActions(_ eval.Event, ctx *eval.Context, rule *Rule) error { 634 for _, action := range rule.Definition.Actions { 635 if !action.IsAccepted(ctx) { 636 continue 637 } 638 639 switch { 640 // action.Kill has to handled by a ruleset listener 641 case action.Set != nil: 642 name := string(action.Set.Scope) 643 if name != "" { 644 name += "." 645 } 646 name += action.Set.Name 647 648 variable := rs.evalOpts.VariableStore.Get(name) 649 if variable == nil { 650 return fmt.Errorf("unknown variable: %s", name) 651 } 652 653 if mutable, ok := variable.(eval.MutableVariable); ok { 654 value := action.Set.Value 655 if field := action.Set.Field; field != "" { 656 if evaluator := rs.fieldEvaluators[field]; evaluator != nil { 657 value = evaluator.Eval(ctx) 658 } 659 } 660 661 if action.Set.Append { 662 if err := mutable.Append(ctx, value); err != nil { 663 return fmt.Errorf("append is not supported for %s", reflect.TypeOf(value)) 664 } 665 } else { 666 if err := mutable.Set(ctx, value); err != nil { 667 return err 668 } 669 } 670 } 671 } 672 } 673 674 return nil 675 } 676 677 // Evaluate the specified event against the set of rules 678 func (rs *RuleSet) Evaluate(event eval.Event) bool { 679 ctx := rs.pool.Get(event) 680 defer rs.pool.Put(ctx) 681 682 eventType := event.GetType() 683 684 bucket, exists := rs.eventRuleBuckets[eventType] 685 if !exists { 686 return false 687 } 688 689 // Since logger is an interface this call cannot be inlined, requiring to pass the trace call arguments 690 // through the heap. To improve this situation we first check if we actually need to call the function. 691 if rs.logger.IsTracing() { 692 rs.logger.Tracef("Evaluating event of type `%s` against set of %d rules", eventType, len(bucket.rules)) 693 } 694 695 result := false 696 697 for _, rule := range bucket.rules { 698 utils.PprofDoWithoutContext(rule.GetPprofLabels(), func() { 699 if rule.GetEvaluator().Eval(ctx) { 700 701 if rs.logger.IsTracing() { 702 rs.logger.Tracef("Rule `%s` matches with event `%s`\n", rule.ID, event) 703 } 704 705 if err := rs.runRuleActions(event, ctx, rule); err != nil { 706 rs.logger.Errorf("Error while executing rule actions: %s", err) 707 } 708 709 rs.NotifyRuleMatch(rule, event) 710 result = true 711 } 712 }) 713 } 714 715 // no-op in the general case, only used to collect events in functional tests 716 // for debugging purposes 717 rs.eventCollector.CollectEvent(rs, event, result) 718 719 return result 720 } 721 722 // EvaluateDiscarders evaluates the discarders for the given event if any 723 func (rs *RuleSet) EvaluateDiscarders(event eval.Event) { 724 ctx := rs.pool.Get(event) 725 defer rs.pool.Put(ctx) 726 727 eventType := event.GetType() 728 bucket, exists := rs.eventRuleBuckets[eventType] 729 if !exists { 730 return 731 } 732 733 if rs.logger.IsTracing() { 734 rs.logger.Tracef("Looking for discarders for event of type `%s`", eventType) 735 } 736 737 var mdiscsToCheck []*multiDiscarderCheck 738 739 for _, field := range bucket.fields { 740 if check := rs.getValidMultiDiscarder(field); check != nil { 741 value, err := event.GetFieldValue(field) 742 if err != nil { 743 rs.logger.Debugf("Failed to get field value for %s: %s", field, err) 744 continue 745 } 746 747 // currently only support string values 748 if valueStr, ok := value.(string); ok { 749 check.value = valueStr 750 mdiscsToCheck = append(mdiscsToCheck, check) 751 } 752 } 753 754 if rs.opts.SupportedDiscarders != nil { 755 if _, exists := rs.opts.SupportedDiscarders[field]; !exists { 756 continue 757 } 758 } 759 760 if isDiscarder, _ := IsDiscarder(ctx, field, bucket.rules); isDiscarder { 761 rs.NotifyDiscarderFound(event, field, eventType) 762 } 763 } 764 765 for _, check := range mdiscsToCheck { 766 isMultiDiscarder := true 767 for _, entry := range check.mdisc.Entries { 768 bucket := rs.eventRuleBuckets[entry.EventType.String()] 769 if bucket == nil || len(bucket.rules) == 0 { 770 continue 771 } 772 773 dctx, err := buildDiscarderCtx(entry.EventType, entry.Field, check.value) 774 if err != nil { 775 rs.logger.Errorf("failed to build discarder context: %v", err) 776 isMultiDiscarder = false 777 break 778 } 779 780 if isDiscarder, _ := IsDiscarder(dctx, entry.Field, bucket.rules); !isDiscarder { 781 isMultiDiscarder = false 782 break 783 } 784 } 785 786 if isMultiDiscarder { 787 rs.NotifyDiscarderFound(event, check.mdisc.FinalField, check.mdisc.FinalEventType.String()) 788 } 789 } 790 } 791 792 func (rs *RuleSet) getValidMultiDiscarder(field string) *multiDiscarderCheck { 793 for _, mdisc := range rs.opts.SupportedMultiDiscarders { 794 for _, entry := range mdisc.Entries { 795 if entry.Field == field { 796 return &multiDiscarderCheck{ 797 mdisc: mdisc, 798 } 799 } 800 } 801 } 802 803 return nil 804 } 805 806 type multiDiscarderCheck struct { 807 mdisc *MultiDiscarder 808 value string 809 } 810 811 func buildDiscarderCtx(eventType model.EventType, field string, value interface{}) (*eval.Context, error) { 812 ev := model.NewFakeEvent() 813 ev.BaseEvent.Type = uint32(eventType) 814 if err := ev.SetFieldValue(field, value); err != nil { 815 return nil, err 816 } 817 return eval.NewContext(ev), nil 818 } 819 820 // GetEventTypes returns all the event types handled by the ruleset 821 func (rs *RuleSet) GetEventTypes() []eval.EventType { 822 eventTypes := make([]string, 0, len(rs.eventRuleBuckets)) 823 for eventType := range rs.eventRuleBuckets { 824 eventTypes = append(eventTypes, eventType) 825 } 826 return eventTypes 827 } 828 829 // AddFields merges the provided set of fields with the existing set of fields of the ruleset 830 func (rs *RuleSet) AddFields(fields []eval.EventType) { 831 NewFields: 832 for _, newField := range fields { 833 for _, oldField := range rs.fields { 834 if oldField == newField { 835 continue NewFields 836 } 837 } 838 rs.fields = append(rs.fields, newField) 839 } 840 } 841 842 // StopEventCollector stops the event collector 843 func (rs *RuleSet) StopEventCollector() []CollectedEvent { 844 return rs.eventCollector.Stop() 845 } 846 847 // NewEvent returns a new event using the embedded constructor 848 func (rs *RuleSet) NewEvent() eval.Event { 849 return rs.eventCtor() 850 } 851 852 // NewRuleSet returns a new ruleset for the specified data model 853 func NewRuleSet(model eval.Model, eventCtor func() eval.Event, opts *Opts, evalOpts *eval.Opts) *RuleSet { 854 logger := log.OrNullLogger(opts.Logger) 855 856 if evalOpts.MacroStore == nil { 857 evalOpts.WithMacroStore(&eval.MacroStore{}) 858 } 859 860 if evalOpts.VariableStore == nil { 861 evalOpts.WithVariableStore(&eval.VariableStore{}) 862 } 863 864 return &RuleSet{ 865 model: model, 866 eventCtor: eventCtor, 867 opts: opts, 868 evalOpts: evalOpts, 869 eventRuleBuckets: make(map[eval.EventType]*RuleBucket), 870 rules: make(map[eval.RuleID]*Rule), 871 logger: logger, 872 pool: eval.NewContextPool(), 873 fieldEvaluators: make(map[string]eval.Evaluator), 874 scopedVariables: make(map[Scope]VariableProvider), 875 } 876 }