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  }