github.com/crowdsecurity/crowdsec@v1.6.1/pkg/hubtest/coverage.go (about) 1 package hubtest 2 3 import ( 4 "bufio" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/crowdsecurity/crowdsec/pkg/appsec/appsec_rule" 11 "github.com/crowdsecurity/crowdsec/pkg/cwhub" 12 "github.com/crowdsecurity/go-cs-lib/maptools" 13 log "github.com/sirupsen/logrus" 14 "gopkg.in/yaml.v2" 15 ) 16 17 type Coverage struct { 18 Name string 19 TestsCount int 20 PresentIn map[string]bool //poorman's set 21 } 22 23 func (h *HubTest) GetAppsecCoverage() ([]Coverage, error) { 24 if len(h.HubIndex.GetItemMap(cwhub.APPSEC_RULES)) == 0 { 25 return nil, fmt.Errorf("no appsec rules in hub index") 26 } 27 28 // populate from hub, iterate in alphabetical order 29 pkeys := maptools.SortedKeys(h.HubIndex.GetItemMap(cwhub.APPSEC_RULES)) 30 coverage := make([]Coverage, len(pkeys)) 31 32 for i, name := range pkeys { 33 coverage[i] = Coverage{ 34 Name: name, 35 TestsCount: 0, 36 PresentIn: make(map[string]bool), 37 } 38 } 39 40 // parser the expressions a-la-oneagain 41 appsecTestConfigs, err := filepath.Glob(".appsec-tests/*/config.yaml") 42 if err != nil { 43 return nil, fmt.Errorf("while find appsec-tests config: %s", err) 44 } 45 46 for _, appsecTestConfigPath := range appsecTestConfigs { 47 configFileData := &HubTestItemConfig{} 48 yamlFile, err := os.ReadFile(appsecTestConfigPath) 49 if err != nil { 50 log.Printf("unable to open appsec test config file '%s': %s", appsecTestConfigPath, err) 51 continue 52 } 53 err = yaml.Unmarshal(yamlFile, configFileData) 54 if err != nil { 55 return nil, fmt.Errorf("unmarshal: %v", err) 56 } 57 58 for _, appsecRulesFile := range configFileData.AppsecRules { 59 appsecRuleData := &appsec_rule.CustomRule{} 60 yamlFile, err := os.ReadFile(appsecRulesFile) 61 if err != nil { 62 log.Printf("unable to open appsec rule '%s': %s", appsecRulesFile, err) 63 } 64 err = yaml.Unmarshal(yamlFile, appsecRuleData) 65 if err != nil { 66 return nil, fmt.Errorf("unmarshal: %v", err) 67 } 68 appsecRuleName := appsecRuleData.Name 69 70 for idx, cov := range coverage { 71 if cov.Name == appsecRuleName { 72 coverage[idx].TestsCount++ 73 coverage[idx].PresentIn[appsecTestConfigPath] = true 74 } 75 } 76 } 77 } 78 79 return coverage, nil 80 } 81 82 func (h *HubTest) GetParsersCoverage() ([]Coverage, error) { 83 if len(h.HubIndex.GetItemMap(cwhub.PARSERS)) == 0 { 84 return nil, fmt.Errorf("no parsers in hub index") 85 } 86 87 // populate from hub, iterate in alphabetical order 88 pkeys := maptools.SortedKeys(h.HubIndex.GetItemMap(cwhub.PARSERS)) 89 coverage := make([]Coverage, len(pkeys)) 90 91 for i, name := range pkeys { 92 coverage[i] = Coverage{ 93 Name: name, 94 TestsCount: 0, 95 PresentIn: make(map[string]bool), 96 } 97 } 98 99 // parser the expressions a-la-oneagain 100 passerts, err := filepath.Glob(".tests/*/parser.assert") 101 if err != nil { 102 return nil, fmt.Errorf("while find parser asserts : %s", err) 103 } 104 105 for _, assert := range passerts { 106 file, err := os.Open(assert) 107 if err != nil { 108 return nil, fmt.Errorf("while reading %s : %s", assert, err) 109 } 110 111 scanner := bufio.NewScanner(file) 112 for scanner.Scan() { 113 line := scanner.Text() 114 log.Debugf("assert line : %s", line) 115 116 match := parserResultRE.FindStringSubmatch(line) 117 if len(match) == 0 { 118 log.Debugf("%s doesn't match", line) 119 continue 120 } 121 122 sidx := parserResultRE.SubexpIndex("parser") 123 capturedParser := match[sidx] 124 125 for idx, pcover := range coverage { 126 if pcover.Name == capturedParser { 127 coverage[idx].TestsCount++ 128 coverage[idx].PresentIn[assert] = true 129 130 continue 131 } 132 133 parserNameSplit := strings.Split(pcover.Name, "/") 134 parserNameOnly := parserNameSplit[len(parserNameSplit)-1] 135 136 if parserNameOnly == capturedParser { 137 coverage[idx].TestsCount++ 138 coverage[idx].PresentIn[assert] = true 139 140 continue 141 } 142 143 capturedParserSplit := strings.Split(capturedParser, "/") 144 capturedParserName := capturedParserSplit[len(capturedParserSplit)-1] 145 146 if capturedParserName == parserNameOnly { 147 coverage[idx].TestsCount++ 148 coverage[idx].PresentIn[assert] = true 149 150 continue 151 } 152 153 if capturedParserName == parserNameOnly+"-logs" { 154 coverage[idx].TestsCount++ 155 coverage[idx].PresentIn[assert] = true 156 157 continue 158 } 159 } 160 } 161 162 file.Close() 163 } 164 165 return coverage, nil 166 } 167 168 func (h *HubTest) GetScenariosCoverage() ([]Coverage, error) { 169 if len(h.HubIndex.GetItemMap(cwhub.SCENARIOS)) == 0 { 170 return nil, fmt.Errorf("no scenarios in hub index") 171 } 172 173 // populate from hub, iterate in alphabetical order 174 pkeys := maptools.SortedKeys(h.HubIndex.GetItemMap(cwhub.SCENARIOS)) 175 coverage := make([]Coverage, len(pkeys)) 176 177 for i, name := range pkeys { 178 coverage[i] = Coverage{ 179 Name: name, 180 TestsCount: 0, 181 PresentIn: make(map[string]bool), 182 } 183 } 184 185 // parser the expressions a-la-oneagain 186 passerts, err := filepath.Glob(".tests/*/scenario.assert") 187 if err != nil { 188 return nil, fmt.Errorf("while find scenario asserts : %s", err) 189 } 190 191 for _, assert := range passerts { 192 file, err := os.Open(assert) 193 if err != nil { 194 return nil, fmt.Errorf("while reading %s : %s", assert, err) 195 } 196 197 scanner := bufio.NewScanner(file) 198 for scanner.Scan() { 199 line := scanner.Text() 200 log.Debugf("assert line : %s", line) 201 match := scenarioResultRE.FindStringSubmatch(line) 202 203 if len(match) == 0 { 204 log.Debugf("%s doesn't match", line) 205 continue 206 } 207 208 sidx := scenarioResultRE.SubexpIndex("scenario") 209 scannerName := match[sidx] 210 211 for idx, pcover := range coverage { 212 if pcover.Name == scannerName { 213 coverage[idx].TestsCount++ 214 coverage[idx].PresentIn[assert] = true 215 216 continue 217 } 218 219 scenarioNameSplit := strings.Split(pcover.Name, "/") 220 scenarioNameOnly := scenarioNameSplit[len(scenarioNameSplit)-1] 221 222 if scenarioNameOnly == scannerName { 223 coverage[idx].TestsCount++ 224 coverage[idx].PresentIn[assert] = true 225 226 continue 227 } 228 229 fixedProbingWord := strings.ReplaceAll(pcover.Name, "probbing", "probing") 230 fixedProbingAssert := strings.ReplaceAll(scannerName, "probbing", "probing") 231 232 if fixedProbingWord == fixedProbingAssert { 233 coverage[idx].TestsCount++ 234 coverage[idx].PresentIn[assert] = true 235 236 continue 237 } 238 239 if fmt.Sprintf("%s-detection", pcover.Name) == scannerName { 240 coverage[idx].TestsCount++ 241 coverage[idx].PresentIn[assert] = true 242 243 continue 244 } 245 246 if fmt.Sprintf("%s-detection", fixedProbingWord) == fixedProbingAssert { 247 coverage[idx].TestsCount++ 248 coverage[idx].PresentIn[assert] = true 249 250 continue 251 } 252 } 253 } 254 file.Close() 255 } 256 257 return coverage, nil 258 }