allow testing of waap rules

This commit is contained in:
bui 2023-11-17 15:37:12 +01:00
parent 9af30e2a3d
commit 6718d82765

View file

@ -1,11 +1,14 @@
package hubtest package hubtest
import ( import (
"errors"
"fmt" "fmt"
"net"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@ -16,14 +19,17 @@ import (
) )
type HubTestItemConfig struct { type HubTestItemConfig struct {
Parsers []string `yaml:"parsers"` Parsers []string `yaml:"parsers"`
Scenarios []string `yaml:"scenarios"` Scenarios []string `yaml:"scenarios"`
PostOVerflows []string `yaml:"postoverflows"` PostOverflows []string `yaml:"postoverflows"`
LogFile string `yaml:"log_file"` WaapRules []string `yaml:"waap-rules"`
LogType string `yaml:"log_type"` NucleiTemplate string `yaml:"nuclei_template"`
Labels map[string]string `yaml:"labels"` ExpectedNucleiFailure bool `yaml:"expect_failure"`
IgnoreParsers bool `yaml:"ignore_parsers"` // if we test a scenario, we don't want to assert on Parser LogFile string `yaml:"log_file"`
OverrideStatics []parser.ExtraField `yaml:"override_statics"` //Allow to override statics. Executed before s00 LogType string `yaml:"log_type"`
Labels map[string]string `yaml:"labels"`
IgnoreParsers bool `yaml:"ignore_parsers"` // if we test a scenario, we don't want to assert on Parser
OverrideStatics []parser.ExtraField `yaml:"override_statics"` //Allow to override statics. Executed before s00
} }
type HubTestItem struct { type HubTestItem struct {
@ -40,6 +46,7 @@ type HubTestItem struct {
RuntimeConfigFilePath string RuntimeConfigFilePath string
RuntimeProfileFilePath string RuntimeProfileFilePath string
RuntimeSimulationFilePath string RuntimeSimulationFilePath string
RuntimeAcquisFilePath string
RuntimeHubConfig *csconfig.LocalHubCfg RuntimeHubConfig *csconfig.LocalHubCfg
ResultsPath string ResultsPath string
@ -47,13 +54,15 @@ type HubTestItem struct {
ScenarioResultFile string ScenarioResultFile string
BucketPourResultFile string BucketPourResultFile string
HubPath string HubPath string
HubTestPath string HubTestPath string
HubIndexFile string HubIndexFile string
TemplateConfigPath string TemplateConfigPath string
TemplateProfilePath string TemplateProfilePath string
TemplateSimulationPath string TemplateSimulationPath string
HubIndex *cwhub.Hub TemplateAcquisPath string
TemplateWaapProfilePath string
HubIndex *cwhub.Hub
Config *HubTestItemConfig Config *HubTestItemConfig
@ -115,6 +124,7 @@ func NewTest(name string, hubTest *HubTest) (*HubTestItem, error) {
RuntimeConfigFilePath: filepath.Join(runtimeFolder, "config.yaml"), RuntimeConfigFilePath: filepath.Join(runtimeFolder, "config.yaml"),
RuntimeProfileFilePath: filepath.Join(runtimeFolder, "profiles.yaml"), RuntimeProfileFilePath: filepath.Join(runtimeFolder, "profiles.yaml"),
RuntimeSimulationFilePath: filepath.Join(runtimeFolder, "simulation.yaml"), RuntimeSimulationFilePath: filepath.Join(runtimeFolder, "simulation.yaml"),
RuntimeAcquisFilePath: filepath.Join(runtimeFolder, "acquis.yaml"),
ResultsPath: resultPath, ResultsPath: resultPath,
ParserResultFile: filepath.Join(resultPath, ParserResultFileName), ParserResultFile: filepath.Join(resultPath, ParserResultFileName),
ScenarioResultFile: filepath.Join(resultPath, ScenarioResultFileName), ScenarioResultFile: filepath.Join(resultPath, ScenarioResultFileName),
@ -125,17 +135,19 @@ func NewTest(name string, hubTest *HubTest) (*HubTestItem, error) {
InstallDir: runtimeFolder, InstallDir: runtimeFolder,
InstallDataDir: filepath.Join(runtimeFolder, "data"), InstallDataDir: filepath.Join(runtimeFolder, "data"),
}, },
Config: configFileData, Config: configFileData,
HubPath: hubTest.HubPath, HubPath: hubTest.HubPath,
HubTestPath: hubTest.HubTestPath, HubTestPath: hubTest.HubTestPath,
HubIndexFile: hubTest.HubIndexFile, HubIndexFile: hubTest.HubIndexFile,
TemplateConfigPath: hubTest.TemplateConfigPath, TemplateConfigPath: hubTest.TemplateConfigPath,
TemplateProfilePath: hubTest.TemplateProfilePath, TemplateProfilePath: hubTest.TemplateProfilePath,
TemplateSimulationPath: hubTest.TemplateSimulationPath, TemplateSimulationPath: hubTest.TemplateSimulationPath,
HubIndex: hubTest.HubIndex, TemplateAcquisPath: hubTest.TemplateAcquisPath,
ScenarioAssert: ScenarioAssert, TemplateWaapProfilePath: hubTest.TemplateWaapProfilePath,
ParserAssert: ParserAssert, HubIndex: hubTest.HubIndex,
CustomItemsLocation: []string{hubTest.HubPath, testPath}, ScenarioAssert: ScenarioAssert,
ParserAssert: ParserAssert,
CustomItemsLocation: []string{hubTest.HubPath, testPath},
}, nil }, nil
} }
@ -297,8 +309,81 @@ func (t *HubTestItem) InstallHub() error {
} }
} }
// install waaprules in runtime environment
for _, waaprule := range t.Config.WaapRules {
log.Infof("adding rule '%s'", waaprule)
if waaprule == "" {
continue
}
if hubWaapRule, ok := t.HubIndex.Items[cwhub.WAAP_RULES][waaprule]; ok {
waapRuleSource, err := filepath.Abs(filepath.Join(t.HubPath, hubWaapRule.RemotePath))
if err != nil {
return fmt.Errorf("can't get absolute path of '%s': %s", waapRuleSource, err)
}
waapRuleFilename := filepath.Base(waapRuleSource)
// runtime/hub/waap-rules/author/waap-rule
hubDirWaapRuleDest := filepath.Join(t.RuntimeHubPath, filepath.Dir(hubWaapRule.RemotePath))
// runtime/waap-rules/
waapRuleDirDest := fmt.Sprintf("%s/waap-rules/", t.RuntimePath)
if err := os.MkdirAll(hubDirWaapRuleDest, os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %s", hubDirWaapRuleDest, err)
}
if err := os.MkdirAll(waapRuleDirDest, os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %s", waapRuleDirDest, err)
}
// runtime/hub/waap-rules/crowdsecurity/rule.yaml
hubDirWaapRulePath := filepath.Join(waapRuleDirDest, waapRuleFilename)
if err := Copy(waapRuleSource, hubDirWaapRulePath); err != nil {
return fmt.Errorf("unable to copy '%s' to '%s': %s", waapRuleSource, hubDirWaapRulePath, err)
}
// runtime/waap-rules/rule.yaml
waapRulePath := filepath.Join(waapRuleDirDest, waapRuleFilename)
if err := os.Symlink(hubDirWaapRulePath, waapRulePath); err != nil {
if !os.IsExist(err) {
return fmt.Errorf("unable to symlink waap-rule '%s' to '%s': %s", hubDirWaapRulePath, waapRulePath, err)
}
}
} else {
customWaapRuleExist := false
for _, customPath := range t.CustomItemsLocation {
// we check if its a custom waap-rule
customWaapRulePath := filepath.Join(customPath, waaprule)
if _, err := os.Stat(customWaapRulePath); os.IsNotExist(err) {
continue
}
customWaapRulePathSplit := strings.Split(customWaapRulePath, "/")
customWappRuleName := customWaapRulePathSplit[len(customWaapRulePathSplit)-1]
waapRuleDirDest := fmt.Sprintf("%s/waap-rules/", t.RuntimePath)
if err := os.MkdirAll(waapRuleDirDest, os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %s", waapRuleDirDest, err)
}
// runtime/waap-rules/
customWaapRuleDest := fmt.Sprintf("%s/waap-rules/%s", t.RuntimePath, customWappRuleName)
// if path to postoverflow exist, copy it
if err := Copy(customWaapRulePath, customWaapRuleDest); err != nil {
continue
}
customWaapRuleExist = true
break
}
if !customWaapRuleExist {
return fmt.Errorf("couldn't find custom waap-rule '%s' in the following location: %+v", waaprule, t.CustomItemsLocation)
}
}
}
// install postoverflows in runtime environment // install postoverflows in runtime environment
for _, postoverflow := range t.Config.PostOVerflows { for _, postoverflow := range t.Config.PostOverflows {
if postoverflow == "" { if postoverflow == "" {
continue continue
} }
@ -449,71 +534,106 @@ func (t *HubTestItem) Clean() error {
return os.RemoveAll(t.RuntimePath) return os.RemoveAll(t.RuntimePath)
} }
func (t *HubTestItem) Run() error { func (t *HubTestItem) RunWithNucleiTemplate() error {
t.Success = false
t.ErrorsList = make([]string, 0)
testPath := filepath.Join(t.HubTestPath, t.Name) testPath := filepath.Join(t.HubTestPath, t.Name)
if _, err := os.Stat(testPath); os.IsNotExist(err) { if _, err := os.Stat(testPath); os.IsNotExist(err) {
return fmt.Errorf("test '%s' doesn't exist in '%s', exiting", t.Name, t.HubTestPath) return fmt.Errorf("test '%s' doesn't exist in '%s', exiting", t.Name, t.HubTestPath)
} }
currentDir, err := os.Getwd() if err := os.Chdir(testPath); err != nil {
return fmt.Errorf("can't 'cd' to '%s': %s", testPath, err)
}
//machine add
cmdArgs := []string{"-c", t.RuntimeConfigFilePath, "machines", "add", "testMachine", "--auto"}
cscliRegisterCmd := exec.Command(t.CscliPath, cmdArgs...)
output, err := cscliRegisterCmd.CombinedOutput()
if err != nil {
if !strings.Contains(string(output), "unable to create machine: user 'testMachine': user already exist") {
fmt.Println(string(output))
return fmt.Errorf("fail to run '%s' for test '%s': %v", cscliRegisterCmd.String(), t.Name, err)
}
}
//hardcode bouncer key
cmdArgs = []string{"-c", t.RuntimeConfigFilePath, "bouncers", "add", "waaptests", "-k", "this_is_a_bad_password"}
cscliBouncerCmd := exec.Command(t.CscliPath, cmdArgs...)
output, err = cscliBouncerCmd.CombinedOutput()
if err != nil {
if !strings.Contains(string(output), "unable to create bouncer: bouncer waaptests already exists") {
fmt.Println(string(output))
return fmt.Errorf("fail to run '%s' for test '%s': %v", cscliRegisterCmd.String(), t.Name, err)
}
}
//start crowdsec service
cmdArgs = []string{"-c", t.RuntimeConfigFilePath}
crowdsecDaemon := exec.Command(t.CrowdSecPath, cmdArgs...)
crowdsecDaemon.Start()
//wait for the waap port to be available
start := time.Now()
for {
conn, err := net.Dial("tcp", "127.0.0.1:4241")
if err == nil {
log.Debugf("waap is up after %s", time.Since(start))
conn.Close()
break
}
time.Sleep(500 * time.Millisecond)
if time.Since(start) > 10*time.Second {
log.Fatalf("took more than 10s for waap to be available, abort")
}
}
nucleiConfig := NucleiConfig{
Path: "nuclei",
OutputDir: testPath,
CmdLineOptions: []string{"-ev", //allow variables from environment
"-nc", //no colors in output
"-dresp", //dump response
"-j", //json output
},
}
err = nucleiConfig.RunNucleiTemplate(t.Name, t.Config.NucleiTemplate, "http://127.0.0.1:80/")
if t.Config.ExpectedNucleiFailure {
if err != nil && errors.Is(err, NucleiTemplateFail) {
log.Infof("WAAP test %s failed as expected", t.Name)
t.Success = true
} else {
log.Errorf("WAAP test %s failed: %s", t.Name, err)
}
} else {
if err == nil {
log.Infof("WAAP test %s succeeded", t.Name)
t.Success = true
} else {
log.Errorf("WAAP test %s failed: %s", t.Name, err)
}
}
crowdsecDaemon.Process.Kill()
return nil
}
func (t *HubTestItem) RunWithLogFile() error {
testPath := filepath.Join(t.HubTestPath, t.Name)
if _, err := os.Stat(testPath); os.IsNotExist(err) {
return fmt.Errorf("test '%s' doesn't exist in '%s', exiting", t.Name, t.HubTestPath)
}
currentDir, err := os.Getwd() //xx
if err != nil { if err != nil {
return fmt.Errorf("can't get current directory: %+v", err) return fmt.Errorf("can't get current directory: %+v", err)
} }
// create runtime folder
if err = os.MkdirAll(t.RuntimePath, os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimePath, err)
}
// create runtime data folder
if err = os.MkdirAll(t.RuntimeDataPath, os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeDataPath, err)
}
// create runtime hub folder
if err = os.MkdirAll(t.RuntimeHubPath, os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeHubPath, err)
}
if err = Copy(t.HubIndexFile, filepath.Join(t.RuntimeHubPath, ".index.json")); err != nil {
return fmt.Errorf("unable to copy .index.json file in '%s': %s", filepath.Join(t.RuntimeHubPath, ".index.json"), err)
}
// create results folder
if err = os.MkdirAll(t.ResultsPath, os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %+v", t.ResultsPath, err)
}
// copy template config file to runtime folder
if err = Copy(t.TemplateConfigPath, t.RuntimeConfigFilePath); err != nil {
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateConfigPath, t.RuntimeConfigFilePath, err)
}
// copy template profile file to runtime folder
if err = Copy(t.TemplateProfilePath, t.RuntimeProfileFilePath); err != nil {
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateProfilePath, t.RuntimeProfileFilePath, err)
}
// copy template simulation file to runtime folder
if err = Copy(t.TemplateSimulationPath, t.RuntimeSimulationFilePath); err != nil {
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateSimulationPath, t.RuntimeSimulationFilePath, err)
}
crowdsecPatternsFolder := csconfig.DefaultConfigPath("patterns")
// copy template patterns folder to runtime folder
if err = CopyDir(crowdsecPatternsFolder, t.RuntimePatternsPath); err != nil {
return fmt.Errorf("unable to copy 'patterns' from '%s' to '%s': %s", crowdsecPatternsFolder, t.RuntimePatternsPath, err)
}
// install the hub in the runtime folder
if err = t.InstallHub(); err != nil {
return fmt.Errorf("unable to install hub in '%s': %s", t.RuntimeHubPath, err)
}
logFile := t.Config.LogFile logFile := t.Config.LogFile
logType := t.Config.LogType logType := t.Config.LogType
dsn := fmt.Sprintf("file://%s", logFile) dsn := fmt.Sprintf("file://%s", logFile)
@ -650,3 +770,88 @@ func (t *HubTestItem) Run() error {
return nil return nil
} }
func (t *HubTestItem) Run() error {
var err error
t.Success = false
t.ErrorsList = make([]string, 0)
// create runtime folder
if err = os.MkdirAll(t.RuntimePath, os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimePath, err)
}
// create runtime data folder
if err = os.MkdirAll(t.RuntimeDataPath, os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeDataPath, err)
}
// create runtime hub folder
if err = os.MkdirAll(t.RuntimeHubPath, os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeHubPath, err)
}
if err = Copy(t.HubIndexFile, filepath.Join(t.RuntimeHubPath, ".index.json")); err != nil {
return fmt.Errorf("unable to copy .index.json file in '%s': %s", filepath.Join(t.RuntimeHubPath, ".index.json"), err)
}
// create results folder
if err = os.MkdirAll(t.ResultsPath, os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %+v", t.ResultsPath, err)
}
// copy template config file to runtime folder
if err = Copy(t.TemplateConfigPath, t.RuntimeConfigFilePath); err != nil {
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateConfigPath, t.RuntimeConfigFilePath, err)
}
// copy template profile file to runtime folder
if err = Copy(t.TemplateProfilePath, t.RuntimeProfileFilePath); err != nil {
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateProfilePath, t.RuntimeProfileFilePath, err)
}
// copy template simulation file to runtime folder
if err = Copy(t.TemplateSimulationPath, t.RuntimeSimulationFilePath); err != nil {
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateSimulationPath, t.RuntimeSimulationFilePath, err)
}
crowdsecPatternsFolder := csconfig.DefaultConfigPath("patterns")
// copy template patterns folder to runtime folder
if err = CopyDir(crowdsecPatternsFolder, t.RuntimePatternsPath); err != nil {
return fmt.Errorf("unable to copy 'patterns' from '%s' to '%s': %s", crowdsecPatternsFolder, t.RuntimePatternsPath, err)
}
// create the waap-configs dir
if err = os.MkdirAll(filepath.Join(t.RuntimePath, "waap-configs"), os.ModePerm); err != nil {
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimePath, err)
}
// copy the waap-config file and acquis *only* if nuclei template is set -> it means we're testing waap
if t.Config.NucleiTemplate != "" {
// copy template acquis file to runtime folder
log.Infof("copying %s to %s", t.TemplateAcquisPath, t.RuntimeAcquisFilePath)
if err = Copy(t.TemplateAcquisPath, t.RuntimeAcquisFilePath); err != nil {
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateAcquisPath, t.RuntimeAcquisFilePath, err)
}
log.Infof("copying %s to %s", t.TemplateWaapProfilePath, filepath.Join(t.RuntimePath, "waap-configs", "config.yaml"))
// copy template waap-config file to runtime folder
if err = Copy(t.TemplateWaapProfilePath, filepath.Join(t.RuntimePath, "waap-configs", "config.yaml")); err != nil {
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateWaapProfilePath, filepath.Join(t.RuntimePath, "waap-configs", "config.yaml"), err)
}
}
// install the hub in the runtime folder
if err = t.InstallHub(); err != nil {
return fmt.Errorf("unable to install hub in '%s': %s", t.RuntimeHubPath, err)
}
if t.Config.LogFile != "" {
return t.RunWithLogFile()
} else if t.Config.NucleiTemplate != "" {
return t.RunWithNucleiTemplate()
} else {
return fmt.Errorf("log file or nuclei template must be set in '%s'", t.Name)
}
}