github.com/DataDog/datadog-agent/pkg/security/secl@v0.55.0-devel.0.20240517055856-10c4965fea94/rules/policy.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  	"io"
    13  
    14  	"github.com/hashicorp/go-multierror"
    15  	"gopkg.in/yaml.v2"
    16  
    17  	"github.com/DataDog/datadog-agent/pkg/security/secl/validators"
    18  )
    19  
    20  // PolicyDef represents a policy file definition
    21  type PolicyDef struct {
    22  	Version string             `yaml:"version"`
    23  	Rules   []*RuleDefinition  `yaml:"rules"`
    24  	Macros  []*MacroDefinition `yaml:"macros"`
    25  }
    26  
    27  // Policy represents a policy file which is composed of a list of rules and macros
    28  type Policy struct {
    29  	Name       string
    30  	Source     string
    31  	Version    string
    32  	Rules      []*RuleDefinition
    33  	Macros     []*MacroDefinition
    34  	IsInternal bool
    35  }
    36  
    37  // AddMacro add a macro to the policy
    38  func (p *Policy) AddMacro(def *MacroDefinition) {
    39  	p.Macros = append(p.Macros, def)
    40  }
    41  
    42  // AddRule adds a rule to the policy
    43  func (p *Policy) AddRule(def *RuleDefinition) {
    44  	def.Policy = p
    45  	p.Rules = append(p.Rules, def)
    46  }
    47  
    48  func parsePolicyDef(name string, source string, def *PolicyDef, macroFilters []MacroFilter, ruleFilters []RuleFilter) (*Policy, error) {
    49  	var errs *multierror.Error
    50  
    51  	policy := &Policy{
    52  		Name:    name,
    53  		Source:  source,
    54  		Version: def.Version,
    55  	}
    56  
    57  MACROS:
    58  	for _, macroDef := range def.Macros {
    59  		for _, filter := range macroFilters {
    60  			isMacroAccepted, err := filter.IsMacroAccepted(macroDef)
    61  			if err != nil {
    62  				errs = multierror.Append(errs, &ErrMacroLoad{Definition: macroDef, Err: fmt.Errorf("error when evaluating one of the macro filters: %w", err)})
    63  			}
    64  			if !isMacroAccepted {
    65  				continue MACROS
    66  			}
    67  		}
    68  
    69  		if macroDef.ID == "" {
    70  			errs = multierror.Append(errs, &ErrMacroLoad{Err: fmt.Errorf("no ID defined for macro with expression `%s`", macroDef.Expression)})
    71  			continue
    72  		}
    73  		if !validators.CheckRuleID(macroDef.ID) {
    74  			errs = multierror.Append(errs, &ErrMacroLoad{Definition: macroDef, Err: fmt.Errorf("ID does not match pattern `%s`", validators.RuleIDPattern)})
    75  			continue
    76  		}
    77  
    78  		policy.AddMacro(macroDef)
    79  	}
    80  
    81  	var skipped []struct {
    82  		ruleDefinition *RuleDefinition
    83  		err            error
    84  	}
    85  
    86  RULES:
    87  	for _, ruleDef := range def.Rules {
    88  		// set the policy so that when we parse the errors we can get the policy associated
    89  		ruleDef.Policy = policy
    90  
    91  		for _, filter := range ruleFilters {
    92  			isRuleAccepted, err := filter.IsRuleAccepted(ruleDef)
    93  			if err != nil {
    94  				errs = multierror.Append(errs, &ErrRuleLoad{Definition: ruleDef, Err: err})
    95  			}
    96  
    97  			if !isRuleAccepted {
    98  				// we do not fail directly because one of the rules with the same id can load properly
    99  				if _, ok := filter.(*AgentVersionFilter); ok {
   100  					skipped = append(skipped, struct {
   101  						ruleDefinition *RuleDefinition
   102  						err            error
   103  					}{ruleDefinition: ruleDef, err: ErrRuleAgentVersion})
   104  				} else if _, ok := filter.(*SECLRuleFilter); ok {
   105  					skipped = append(skipped, struct {
   106  						ruleDefinition *RuleDefinition
   107  						err            error
   108  					}{ruleDefinition: ruleDef, err: ErrRuleAgentFilter})
   109  				}
   110  
   111  				continue RULES
   112  			}
   113  		}
   114  
   115  		if ruleDef.ID == "" {
   116  			errs = multierror.Append(errs, &ErrRuleLoad{Definition: ruleDef, Err: ErrRuleWithoutID})
   117  			continue
   118  		}
   119  		if !validators.CheckRuleID(ruleDef.ID) {
   120  			errs = multierror.Append(errs, &ErrRuleLoad{Definition: ruleDef, Err: ErrRuleIDPattern})
   121  			continue
   122  		}
   123  
   124  		if ruleDef.GroupID != "" && !validators.CheckRuleID(ruleDef.GroupID) {
   125  			errs = multierror.Append(errs, &ErrRuleLoad{Definition: ruleDef, Err: ErrRuleIDPattern})
   126  			continue
   127  		}
   128  
   129  		if ruleDef.Expression == "" && !ruleDef.Disabled && ruleDef.Combine == "" {
   130  			errs = multierror.Append(errs, &ErrRuleLoad{Definition: ruleDef, Err: ErrRuleWithoutExpression})
   131  			continue
   132  		}
   133  
   134  		policy.AddRule(ruleDef)
   135  	}
   136  
   137  LOOP:
   138  	for _, s := range skipped {
   139  		// For every skipped rule, if it doesn't match an ID of a policy rule, add an error.
   140  		for _, r := range policy.Rules {
   141  			if s.ruleDefinition.ID == r.ID {
   142  				continue LOOP
   143  			}
   144  		}
   145  
   146  		// do not report filtered rules
   147  		if !errors.Is(s.err, ErrRuleAgentFilter) {
   148  			errs = multierror.Append(errs, &ErrRuleLoad{Definition: s.ruleDefinition, Err: s.err})
   149  		}
   150  	}
   151  
   152  	return policy, errs.ErrorOrNil()
   153  }
   154  
   155  // LoadPolicy load a policy
   156  func LoadPolicy(name string, source string, reader io.Reader, macroFilters []MacroFilter, ruleFilters []RuleFilter) (*Policy, error) {
   157  	var def PolicyDef
   158  
   159  	decoder := yaml.NewDecoder(reader)
   160  	if err := decoder.Decode(&def); err != nil {
   161  		return nil, &ErrPolicyLoad{Name: name, Err: err}
   162  	}
   163  
   164  	return parsePolicyDef(name, source, &def, macroFilters, ruleFilters)
   165  }