github.com/crowdsecurity/crowdsec@v1.6.1/pkg/appsec/appsec_rules_collection.go (about) 1 package appsec 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/crowdsecurity/crowdsec/pkg/appsec/appsec_rule" 10 "github.com/crowdsecurity/crowdsec/pkg/exprhelpers" 11 12 log "github.com/sirupsen/logrus" 13 ) 14 15 type AppsecCollection struct { 16 collectionName string 17 Rules []string 18 } 19 20 var APPSEC_RULE = "appsec-rule" 21 22 // to be filled w/ seb update 23 type AppsecCollectionConfig struct { 24 Type string `yaml:"type"` 25 Name string `yaml:"name"` 26 Debug bool `yaml:"debug"` 27 Description string `yaml:"description"` 28 SecLangFilesRules []string `yaml:"seclang_files_rules"` 29 SecLangRules []string `yaml:"seclang_rules"` 30 Rules []appsec_rule.CustomRule `yaml:"rules"` 31 32 Labels map[string]interface{} `yaml:"labels"` //Labels is K:V list aiming at providing context the overflow 33 34 Data interface{} `yaml:"data"` //Ignore it 35 hash string `yaml:"-"` 36 version string `yaml:"-"` 37 } 38 39 type RulesDetails struct { 40 LogLevel log.Level 41 Hash string 42 Version string 43 Name string 44 } 45 46 // FIXME: this shouldn't be a global 47 // Is using the id is a good idea ? might be too specific to coraza and not easily reusable 48 var AppsecRulesDetails = make(map[int]RulesDetails) 49 50 func LoadCollection(pattern string, logger *log.Entry) ([]AppsecCollection, error) { 51 ret := make([]AppsecCollection, 0) 52 53 for _, appsecRule := range appsecRules { 54 55 tmpMatch, err := exprhelpers.Match(pattern, appsecRule.Name) 56 57 if err != nil { 58 logger.Errorf("unable to match %s with %s : %s", appsecRule.Name, pattern, err) 59 continue 60 } 61 62 matched, ok := tmpMatch.(bool) 63 64 if !ok { 65 logger.Errorf("unable to match %s with %s : %s", appsecRule.Name, pattern, err) 66 continue 67 } 68 69 if !matched { 70 continue 71 } 72 73 appsecCol := AppsecCollection{ 74 collectionName: appsecRule.Name, 75 } 76 77 if appsecRule.SecLangFilesRules != nil { 78 for _, rulesFile := range appsecRule.SecLangFilesRules { 79 logger.Debugf("Adding rules from %s", rulesFile) 80 fullPath := filepath.Join(hub.GetDataDir(), rulesFile) 81 c, err := os.ReadFile(fullPath) 82 if err != nil { 83 logger.Errorf("unable to read file %s : %s", rulesFile, err) 84 continue 85 } 86 for _, line := range strings.Split(string(c), "\n") { 87 if strings.HasPrefix(line, "#") { 88 continue 89 } 90 if strings.TrimSpace(line) == "" { 91 continue 92 } 93 appsecCol.Rules = append(appsecCol.Rules, line) 94 } 95 } 96 } 97 98 if appsecRule.SecLangRules != nil { 99 logger.Tracef("Adding inline rules %+v", appsecRule.SecLangRules) 100 appsecCol.Rules = append(appsecCol.Rules, appsecRule.SecLangRules...) 101 } 102 103 if appsecRule.Rules != nil { 104 for _, rule := range appsecRule.Rules { 105 strRule, rulesId, err := rule.Convert(appsec_rule.ModsecurityRuleType, appsecRule.Name) 106 if err != nil { 107 logger.Errorf("unable to convert rule %s : %s", appsecRule.Name, err) 108 return nil, err 109 } 110 logger.Debugf("Adding rule %s", strRule) 111 appsecCol.Rules = append(appsecCol.Rules, strRule) 112 113 //We only take the first id, as it's the one of the "main" rule 114 if _, ok := AppsecRulesDetails[int(rulesId[0])]; !ok { 115 AppsecRulesDetails[int(rulesId[0])] = RulesDetails{ 116 LogLevel: log.InfoLevel, 117 Hash: appsecRule.hash, 118 Version: appsecRule.version, 119 Name: appsecRule.Name, 120 } 121 } else { 122 logger.Warnf("conflicting id %d for rule %s !", rulesId[0], rule.Name) 123 } 124 125 for _, id := range rulesId { 126 SetRuleDebug(int(id), appsecRule.Debug) 127 } 128 } 129 } 130 ret = append(ret, appsecCol) 131 } 132 if len(ret) == 0 { 133 return nil, fmt.Errorf("no appsec-rules found for pattern %s", pattern) 134 } 135 return ret, nil 136 } 137 138 func (w AppsecCollection) String() string { 139 ret := "" 140 for _, rule := range w.Rules { 141 ret += rule + "\n" 142 } 143 return ret 144 }