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 }