-
Notifications
You must be signed in to change notification settings - Fork 470
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cscli refact: package 'clihubtest' (#3174)
* cscli refact: package 'clihubtest' * split hubtest.go subcommands in files * extract function getCoverage() * common function hubTestCoverageTable() * update cyclomatic lint * lint
- Loading branch information
Showing
13 changed files
with
820 additions
and
793 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package clihubtest | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
func (cli *cliHubTest) NewCleanCmd() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "clean", | ||
Short: "clean [test_name]", | ||
Args: cobra.MinimumNArgs(1), | ||
DisableAutoGenTag: true, | ||
RunE: func(_ *cobra.Command, args []string) error { | ||
for _, testName := range args { | ||
test, err := hubPtr.LoadTestItem(testName) | ||
if err != nil { | ||
return fmt.Errorf("unable to load test '%s': %w", testName, err) | ||
} | ||
if err := test.Clean(); err != nil { | ||
return fmt.Errorf("unable to clean test '%s' env: %w", test.Name, err) | ||
} | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package clihubtest | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"math" | ||
|
||
"github.com/fatih/color" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/crowdsecurity/crowdsec/pkg/hubtest" | ||
) | ||
|
||
// getCoverage returns the coverage and the percentage of tests that passed | ||
func getCoverage(show bool, getCoverageFunc func() ([]hubtest.Coverage, error)) ([]hubtest.Coverage, int, error) { | ||
if !show { | ||
return nil, 0, nil | ||
} | ||
|
||
coverage, err := getCoverageFunc() | ||
if err != nil { | ||
return nil, 0, fmt.Errorf("while getting coverage: %w", err) | ||
} | ||
|
||
tested := 0 | ||
|
||
for _, test := range coverage { | ||
if test.TestsCount > 0 { | ||
tested++ | ||
} | ||
} | ||
|
||
// keep coverage 0 if there's no tests? | ||
percent := 0 | ||
if len(coverage) > 0 { | ||
percent = int(math.Round((float64(tested) / float64(len(coverage)) * 100))) | ||
} | ||
|
||
return coverage, percent, nil | ||
} | ||
|
||
func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAppsecCov bool, showOnlyPercent bool) error { | ||
cfg := cli.cfg() | ||
|
||
// for this one we explicitly don't do for appsec | ||
if err := HubTest.LoadAllTests(); err != nil { | ||
return fmt.Errorf("unable to load all tests: %+v", err) | ||
} | ||
|
||
var err error | ||
|
||
// if all are false (flag by default), show them | ||
if !showParserCov && !showScenarioCov && !showAppsecCov { | ||
showParserCov = true | ||
showScenarioCov = true | ||
showAppsecCov = true | ||
} | ||
|
||
parserCoverage, parserCoveragePercent, err := getCoverage(showParserCov, HubTest.GetParsersCoverage) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
scenarioCoverage, scenarioCoveragePercent, err := getCoverage(showScenarioCov, HubTest.GetScenariosCoverage) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
appsecRuleCoverage, appsecRuleCoveragePercent, err := getCoverage(showAppsecCov, HubTest.GetAppsecCoverage) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if showOnlyPercent { | ||
switch { | ||
case showParserCov: | ||
fmt.Printf("parsers=%d%%", parserCoveragePercent) | ||
case showScenarioCov: | ||
fmt.Printf("scenarios=%d%%", scenarioCoveragePercent) | ||
case showAppsecCov: | ||
fmt.Printf("appsec_rules=%d%%", appsecRuleCoveragePercent) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
switch cfg.Cscli.Output { | ||
case "human": | ||
if showParserCov { | ||
hubTestCoverageTable(color.Output, cfg.Cscli.Color, []string{"Parser", "Status", "Number of tests"}, parserCoverage) | ||
} | ||
|
||
if showScenarioCov { | ||
hubTestCoverageTable(color.Output, cfg.Cscli.Color, []string{"Scenario", "Status", "Number of tests"}, parserCoverage) | ||
} | ||
|
||
if showAppsecCov { | ||
hubTestCoverageTable(color.Output, cfg.Cscli.Color, []string{"Appsec Rule", "Status", "Number of tests"}, parserCoverage) | ||
} | ||
|
||
fmt.Println() | ||
|
||
if showParserCov { | ||
fmt.Printf("PARSERS : %d%% of coverage\n", parserCoveragePercent) | ||
} | ||
|
||
if showScenarioCov { | ||
fmt.Printf("SCENARIOS : %d%% of coverage\n", scenarioCoveragePercent) | ||
} | ||
|
||
if showAppsecCov { | ||
fmt.Printf("APPSEC RULES : %d%% of coverage\n", appsecRuleCoveragePercent) | ||
} | ||
case "json": | ||
dump, err := json.MarshalIndent(parserCoverage, "", " ") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fmt.Printf("%s", dump) | ||
|
||
dump, err = json.MarshalIndent(scenarioCoverage, "", " ") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fmt.Printf("%s", dump) | ||
|
||
dump, err = json.MarshalIndent(appsecRuleCoverage, "", " ") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fmt.Printf("%s", dump) | ||
default: | ||
return errors.New("only human/json output modes are supported") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (cli *cliHubTest) NewCoverageCmd() *cobra.Command { | ||
var ( | ||
showParserCov bool | ||
showScenarioCov bool | ||
showOnlyPercent bool | ||
showAppsecCov bool | ||
) | ||
|
||
cmd := &cobra.Command{ | ||
Use: "coverage", | ||
Short: "coverage", | ||
DisableAutoGenTag: true, | ||
RunE: func(_ *cobra.Command, _ []string) error { | ||
return cli.coverage(showScenarioCov, showParserCov, showAppsecCov, showOnlyPercent) | ||
}, | ||
} | ||
|
||
cmd.PersistentFlags().BoolVar(&showOnlyPercent, "percent", false, "Show only percentages of coverage") | ||
cmd.PersistentFlags().BoolVar(&showParserCov, "parsers", false, "Show only parsers coverage") | ||
cmd.PersistentFlags().BoolVar(&showScenarioCov, "scenarios", false, "Show only scenarios coverage") | ||
cmd.PersistentFlags().BoolVar(&showAppsecCov, "appsec", false, "Show only appsec coverage") | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package clihubtest | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"text/template" | ||
|
||
"github.com/spf13/cobra" | ||
"gopkg.in/yaml.v3" | ||
|
||
"github.com/crowdsecurity/crowdsec/pkg/hubtest" | ||
) | ||
|
||
func (cli *cliHubTest) NewCreateCmd() *cobra.Command { | ||
var ( | ||
ignoreParsers bool | ||
labels map[string]string | ||
logType string | ||
) | ||
|
||
parsers := []string{} | ||
postoverflows := []string{} | ||
scenarios := []string{} | ||
|
||
cmd := &cobra.Command{ | ||
Use: "create", | ||
Short: "create [test_name]", | ||
Example: `cscli hubtest create my-awesome-test --type syslog | ||
cscli hubtest create my-nginx-custom-test --type nginx | ||
cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios crowdsecurity/http-probing`, | ||
Args: cobra.ExactArgs(1), | ||
DisableAutoGenTag: true, | ||
RunE: func(_ *cobra.Command, args []string) error { | ||
testName := args[0] | ||
testPath := filepath.Join(hubPtr.HubTestPath, testName) | ||
if _, err := os.Stat(testPath); os.IsExist(err) { | ||
return fmt.Errorf("test '%s' already exists in '%s', exiting", testName, testPath) | ||
} | ||
|
||
if isAppsecTest { | ||
logType = "appsec" | ||
} | ||
|
||
if logType == "" { | ||
return errors.New("please provide a type (--type) for the test") | ||
} | ||
|
||
if err := os.MkdirAll(testPath, os.ModePerm); err != nil { | ||
return fmt.Errorf("unable to create folder '%s': %+v", testPath, err) | ||
} | ||
|
||
configFilePath := filepath.Join(testPath, "config.yaml") | ||
|
||
configFileData := &hubtest.HubTestItemConfig{} | ||
if logType == "appsec" { | ||
// create empty nuclei template file | ||
nucleiFileName := testName + ".yaml" | ||
nucleiFilePath := filepath.Join(testPath, nucleiFileName) | ||
|
||
nucleiFile, err := os.OpenFile(nucleiFilePath, os.O_RDWR|os.O_CREATE, 0o755) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
ntpl := template.Must(template.New("nuclei").Parse(hubtest.TemplateNucleiFile)) | ||
if ntpl == nil { | ||
return errors.New("unable to parse nuclei template") | ||
} | ||
ntpl.ExecuteTemplate(nucleiFile, "nuclei", struct{ TestName string }{TestName: testName}) | ||
nucleiFile.Close() | ||
configFileData.AppsecRules = []string{"./appsec-rules/<author>/your_rule_here.yaml"} | ||
configFileData.NucleiTemplate = nucleiFileName | ||
fmt.Println() | ||
fmt.Printf(" Test name : %s\n", testName) | ||
fmt.Printf(" Test path : %s\n", testPath) | ||
fmt.Printf(" Config File : %s\n", configFilePath) | ||
fmt.Printf(" Nuclei Template : %s\n", nucleiFilePath) | ||
} else { | ||
// create empty log file | ||
logFileName := testName + ".log" | ||
logFilePath := filepath.Join(testPath, logFileName) | ||
logFile, err := os.Create(logFilePath) | ||
if err != nil { | ||
return err | ||
} | ||
logFile.Close() | ||
|
||
// create empty parser assertion file | ||
parserAssertFilePath := filepath.Join(testPath, hubtest.ParserAssertFileName) | ||
parserAssertFile, err := os.Create(parserAssertFilePath) | ||
if err != nil { | ||
return err | ||
} | ||
parserAssertFile.Close() | ||
// create empty scenario assertion file | ||
scenarioAssertFilePath := filepath.Join(testPath, hubtest.ScenarioAssertFileName) | ||
scenarioAssertFile, err := os.Create(scenarioAssertFilePath) | ||
if err != nil { | ||
return err | ||
} | ||
scenarioAssertFile.Close() | ||
|
||
parsers = append(parsers, "crowdsecurity/syslog-logs") | ||
parsers = append(parsers, "crowdsecurity/dateparse-enrich") | ||
|
||
if len(scenarios) == 0 { | ||
scenarios = append(scenarios, "") | ||
} | ||
|
||
if len(postoverflows) == 0 { | ||
postoverflows = append(postoverflows, "") | ||
} | ||
configFileData.Parsers = parsers | ||
configFileData.Scenarios = scenarios | ||
configFileData.PostOverflows = postoverflows | ||
configFileData.LogFile = logFileName | ||
configFileData.LogType = logType | ||
configFileData.IgnoreParsers = ignoreParsers | ||
configFileData.Labels = labels | ||
fmt.Println() | ||
fmt.Printf(" Test name : %s\n", testName) | ||
fmt.Printf(" Test path : %s\n", testPath) | ||
fmt.Printf(" Log file : %s (please fill it with logs)\n", logFilePath) | ||
fmt.Printf(" Parser assertion file : %s (please fill it with assertion)\n", parserAssertFilePath) | ||
fmt.Printf(" Scenario assertion file : %s (please fill it with assertion)\n", scenarioAssertFilePath) | ||
fmt.Printf(" Configuration File : %s (please fill it with parsers, scenarios...)\n", configFilePath) | ||
} | ||
|
||
fd, err := os.Create(configFilePath) | ||
if err != nil { | ||
return fmt.Errorf("open: %w", err) | ||
} | ||
data, err := yaml.Marshal(configFileData) | ||
if err != nil { | ||
return fmt.Errorf("marshal: %w", err) | ||
} | ||
_, err = fd.Write(data) | ||
if err != nil { | ||
return fmt.Errorf("write: %w", err) | ||
} | ||
if err := fd.Close(); err != nil { | ||
return fmt.Errorf("close: %w", err) | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
cmd.PersistentFlags().StringVarP(&logType, "type", "t", "", "Log type of the test") | ||
cmd.Flags().StringSliceVarP(&parsers, "parsers", "p", parsers, "Parsers to add to test") | ||
cmd.Flags().StringSliceVar(&postoverflows, "postoverflows", postoverflows, "Postoverflows to add to test") | ||
cmd.Flags().StringSliceVarP(&scenarios, "scenarios", "s", scenarios, "Scenarios to add to test") | ||
cmd.PersistentFlags().BoolVar(&ignoreParsers, "ignore-parsers", false, "Don't run test on parsers") | ||
|
||
return cmd | ||
} |
Oops, something went wrong.