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  }