2022-10-17 07:24:07 +00:00
|
|
|
package hubtest
|
2021-10-04 15:14:52 +00:00
|
|
|
|
|
|
|
import (
|
2023-12-07 11:21:04 +00:00
|
|
|
"errors"
|
2021-10-04 15:14:52 +00:00
|
|
|
"fmt"
|
2023-12-07 11:21:04 +00:00
|
|
|
"net/url"
|
2021-10-04 15:14:52 +00:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
2023-11-24 14:57:32 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"gopkg.in/yaml.v2"
|
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
|
2022-04-29 12:24:41 +00:00
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/parser"
|
2021-10-04 15:14:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type HubTestItemConfig struct {
|
2023-12-07 11:21:04 +00:00
|
|
|
Parsers []string `yaml:"parsers,omitempty"`
|
|
|
|
Scenarios []string `yaml:"scenarios,omitempty"`
|
|
|
|
PostOverflows []string `yaml:"postoverflows,omitempty"`
|
|
|
|
AppsecRules []string `yaml:"appsec-rules,omitempty"`
|
|
|
|
NucleiTemplate string `yaml:"nuclei_template,omitempty"`
|
|
|
|
ExpectedNucleiFailure bool `yaml:"expect_failure,omitempty"`
|
|
|
|
LogFile string `yaml:"log_file,omitempty"`
|
|
|
|
LogType string `yaml:"log_type,omitempty"`
|
|
|
|
Labels map[string]string `yaml:"labels,omitempty"`
|
|
|
|
IgnoreParsers bool `yaml:"ignore_parsers,omitempty"` // if we test a scenario, we don't want to assert on Parser
|
|
|
|
OverrideStatics []parser.ExtraField `yaml:"override_statics,omitempty"` //Allow to override statics. Executed before s00
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type HubTestItem struct {
|
|
|
|
Name string
|
|
|
|
Path string
|
|
|
|
|
|
|
|
CrowdSecPath string
|
|
|
|
CscliPath string
|
|
|
|
|
|
|
|
RuntimePath string
|
|
|
|
RuntimeHubPath string
|
|
|
|
RuntimeDataPath string
|
|
|
|
RuntimePatternsPath string
|
|
|
|
RuntimeConfigFilePath string
|
|
|
|
RuntimeProfileFilePath string
|
|
|
|
RuntimeSimulationFilePath string
|
2023-12-07 11:21:04 +00:00
|
|
|
RuntimeAcquisFilePath string
|
2023-11-24 14:57:32 +00:00
|
|
|
RuntimeHubConfig *csconfig.LocalHubCfg
|
2021-10-04 15:14:52 +00:00
|
|
|
|
|
|
|
ResultsPath string
|
|
|
|
ParserResultFile string
|
|
|
|
ScenarioResultFile string
|
|
|
|
BucketPourResultFile string
|
|
|
|
|
2023-12-07 11:21:04 +00:00
|
|
|
HubPath string
|
|
|
|
HubTestPath string
|
|
|
|
HubIndexFile string
|
|
|
|
TemplateConfigPath string
|
|
|
|
TemplateProfilePath string
|
|
|
|
TemplateSimulationPath string
|
|
|
|
TemplateAcquisPath string
|
|
|
|
TemplateAppsecProfilePath string
|
|
|
|
HubIndex *cwhub.Hub
|
2021-10-04 15:14:52 +00:00
|
|
|
|
|
|
|
Config *HubTestItemConfig
|
|
|
|
|
|
|
|
Success bool
|
|
|
|
ErrorsList []string
|
|
|
|
|
|
|
|
AutoGen bool
|
|
|
|
ParserAssert *ParserAssert
|
|
|
|
ScenarioAssert *ScenarioAssert
|
|
|
|
|
|
|
|
CustomItemsLocation []string
|
2023-12-14 15:05:16 +00:00
|
|
|
|
|
|
|
NucleiTargetHost string
|
|
|
|
AppSecHost string
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
ParserAssertFileName = "parser.assert"
|
|
|
|
ParserResultFileName = "parser-dump.yaml"
|
|
|
|
|
|
|
|
ScenarioAssertFileName = "scenario.assert"
|
|
|
|
ScenarioResultFileName = "bucket-dump.yaml"
|
|
|
|
|
|
|
|
BucketPourResultFileName = "bucketpour-dump.yaml"
|
2023-12-07 11:21:04 +00:00
|
|
|
|
|
|
|
TestBouncerApiKey = "this_is_a_bad_password"
|
|
|
|
|
2023-12-21 10:51:04 +00:00
|
|
|
DefaultNucleiTarget = "http://127.0.0.1:7822/"
|
2023-12-07 11:21:04 +00:00
|
|
|
DefaultAppsecHost = "127.0.0.1:4241"
|
2021-10-04 15:14:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func NewTest(name string, hubTest *HubTest) (*HubTestItem, error) {
|
|
|
|
testPath := filepath.Join(hubTest.HubTestPath, name)
|
|
|
|
runtimeFolder := filepath.Join(testPath, "runtime")
|
|
|
|
runtimeHubFolder := filepath.Join(runtimeFolder, "hub")
|
|
|
|
configFilePath := filepath.Join(testPath, "config.yaml")
|
|
|
|
resultPath := filepath.Join(testPath, "results")
|
|
|
|
|
|
|
|
// read test configuration file
|
|
|
|
configFileData := &HubTestItemConfig{}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2022-09-06 11:55:03 +00:00
|
|
|
yamlFile, err := os.ReadFile(configFilePath)
|
2021-10-04 15:14:52 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("no config file found in '%s': %v", testPath, err)
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
err = yaml.Unmarshal(yamlFile, configFileData)
|
|
|
|
if err != nil {
|
2023-10-03 09:17:02 +00:00
|
|
|
return nil, fmt.Errorf("unmarshal: %v", err)
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
parserAssertFilePath := filepath.Join(testPath, ParserAssertFileName)
|
|
|
|
ParserAssert := NewParserAssert(parserAssertFilePath)
|
|
|
|
|
|
|
|
scenarioAssertFilePath := filepath.Join(testPath, ScenarioAssertFileName)
|
|
|
|
ScenarioAssert := NewScenarioAssert(scenarioAssertFilePath)
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
return &HubTestItem{
|
|
|
|
Name: name,
|
|
|
|
Path: testPath,
|
|
|
|
CrowdSecPath: hubTest.CrowdSecPath,
|
|
|
|
CscliPath: hubTest.CscliPath,
|
|
|
|
RuntimePath: filepath.Join(testPath, "runtime"),
|
|
|
|
RuntimeHubPath: runtimeHubFolder,
|
|
|
|
RuntimeDataPath: filepath.Join(runtimeFolder, "data"),
|
|
|
|
RuntimePatternsPath: filepath.Join(runtimeFolder, "patterns"),
|
|
|
|
RuntimeConfigFilePath: filepath.Join(runtimeFolder, "config.yaml"),
|
|
|
|
RuntimeProfileFilePath: filepath.Join(runtimeFolder, "profiles.yaml"),
|
|
|
|
RuntimeSimulationFilePath: filepath.Join(runtimeFolder, "simulation.yaml"),
|
2023-12-07 11:21:04 +00:00
|
|
|
RuntimeAcquisFilePath: filepath.Join(runtimeFolder, "acquis.yaml"),
|
2021-10-04 15:14:52 +00:00
|
|
|
ResultsPath: resultPath,
|
|
|
|
ParserResultFile: filepath.Join(resultPath, ParserResultFileName),
|
|
|
|
ScenarioResultFile: filepath.Join(resultPath, ScenarioResultFileName),
|
|
|
|
BucketPourResultFile: filepath.Join(resultPath, BucketPourResultFileName),
|
2023-11-24 14:57:32 +00:00
|
|
|
RuntimeHubConfig: &csconfig.LocalHubCfg{
|
2023-10-09 19:33:35 +00:00
|
|
|
HubDir: runtimeHubFolder,
|
|
|
|
HubIndexFile: hubTest.HubIndexFile,
|
|
|
|
InstallDir: runtimeFolder,
|
|
|
|
InstallDataDir: filepath.Join(runtimeFolder, "data"),
|
2021-10-04 15:14:52 +00:00
|
|
|
},
|
2023-12-07 11:21:04 +00:00
|
|
|
Config: configFileData,
|
|
|
|
HubPath: hubTest.HubPath,
|
|
|
|
HubTestPath: hubTest.HubTestPath,
|
|
|
|
HubIndexFile: hubTest.HubIndexFile,
|
|
|
|
TemplateConfigPath: hubTest.TemplateConfigPath,
|
|
|
|
TemplateProfilePath: hubTest.TemplateProfilePath,
|
|
|
|
TemplateSimulationPath: hubTest.TemplateSimulationPath,
|
|
|
|
TemplateAcquisPath: hubTest.TemplateAcquisPath,
|
|
|
|
TemplateAppsecProfilePath: hubTest.TemplateAppsecProfilePath,
|
|
|
|
HubIndex: hubTest.HubIndex,
|
|
|
|
ScenarioAssert: ScenarioAssert,
|
|
|
|
ParserAssert: ParserAssert,
|
|
|
|
CustomItemsLocation: []string{hubTest.HubPath, testPath},
|
2023-12-14 15:05:16 +00:00
|
|
|
NucleiTargetHost: hubTest.NucleiTargetHost,
|
|
|
|
AppSecHost: hubTest.AppSecHost,
|
2021-10-04 15:14:52 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2024-01-18 10:09:14 +00:00
|
|
|
func (t *HubTestItem) installHubItems(names []string, installFunc func(string) error) error {
|
|
|
|
for _, name := range names {
|
|
|
|
if name == "" {
|
2021-10-04 15:14:52 +00:00
|
|
|
continue
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2024-01-18 10:09:14 +00:00
|
|
|
if err := installFunc(name); err != nil {
|
|
|
|
return err
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-18 10:09:14 +00:00
|
|
|
return nil
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2024-01-18 10:09:14 +00:00
|
|
|
func (t *HubTestItem) InstallHub() error {
|
|
|
|
if err := t.installHubItems(t.Config.Parsers, t.installParser); err != nil {
|
|
|
|
return err
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
|
|
|
|
2024-01-18 10:09:14 +00:00
|
|
|
if err := t.installHubItems(t.Config.Scenarios, t.installScenario); err != nil {
|
|
|
|
return err
|
2023-12-07 11:21:04 +00:00
|
|
|
}
|
|
|
|
|
2024-01-18 10:09:14 +00:00
|
|
|
if err := t.installHubItems(t.Config.PostOverflows, t.installPostoverflow); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2024-01-18 10:09:14 +00:00
|
|
|
if err := t.installHubItems(t.Config.AppsecRules, t.installAppsecRule); err != nil {
|
|
|
|
return err
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
|
|
|
|
2022-04-29 12:24:41 +00:00
|
|
|
if len(t.Config.OverrideStatics) > 0 {
|
|
|
|
n := parser.Node{
|
|
|
|
Name: "overrides",
|
|
|
|
Filter: "1==1",
|
|
|
|
Statics: t.Config.OverrideStatics,
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2022-04-29 12:24:41 +00:00
|
|
|
b, err := yaml.Marshal(n)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to marshal overrides: %s", err)
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2022-04-29 12:24:41 +00:00
|
|
|
tgtFilename := fmt.Sprintf("%s/parsers/s00-raw/00_overrides.yaml", t.RuntimePath)
|
|
|
|
if err := os.WriteFile(tgtFilename, b, os.ModePerm); err != nil {
|
|
|
|
return fmt.Errorf("unable to write overrides to '%s': %s", tgtFilename, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
// load installed hub
|
2023-12-19 16:20:09 +00:00
|
|
|
hub, err := cwhub.NewHub(t.RuntimeHubConfig, nil, false, nil)
|
2021-10-04 15:14:52 +00:00
|
|
|
if err != nil {
|
2023-11-24 14:57:32 +00:00
|
|
|
log.Fatal(err)
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// install data for parsers if needed
|
2023-11-24 14:57:32 +00:00
|
|
|
ret := hub.GetItemMap(cwhub.PARSERS)
|
2021-10-04 15:14:52 +00:00
|
|
|
for parserName, item := range ret {
|
2023-11-24 14:57:32 +00:00
|
|
|
if item.State.Installed {
|
|
|
|
if err := item.DownloadDataIfNeeded(true); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to download data for parser '%s': %+v", parserName, err)
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2022-04-19 09:25:27 +00:00
|
|
|
log.Debugf("parser '%s' installed successfully in runtime environment", parserName)
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// install data for scenarios if needed
|
2023-11-24 14:57:32 +00:00
|
|
|
ret = hub.GetItemMap(cwhub.SCENARIOS)
|
2021-10-04 15:14:52 +00:00
|
|
|
for scenarioName, item := range ret {
|
2023-11-24 14:57:32 +00:00
|
|
|
if item.State.Installed {
|
|
|
|
if err := item.DownloadDataIfNeeded(true); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to download data for parser '%s': %+v", scenarioName, err)
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2022-04-19 09:25:27 +00:00
|
|
|
log.Debugf("scenario '%s' installed successfully in runtime environment", scenarioName)
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// install data for postoverflows if needed
|
2023-11-24 14:57:32 +00:00
|
|
|
ret = hub.GetItemMap(cwhub.POSTOVERFLOWS)
|
2021-10-04 15:14:52 +00:00
|
|
|
for postoverflowName, item := range ret {
|
2023-11-24 14:57:32 +00:00
|
|
|
if item.State.Installed {
|
|
|
|
if err := item.DownloadDataIfNeeded(true); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to download data for parser '%s': %+v", postoverflowName, err)
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2022-04-19 09:25:27 +00:00
|
|
|
log.Debugf("postoverflow '%s' installed successfully in runtime environment", postoverflowName)
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *HubTestItem) Clean() error {
|
|
|
|
return os.RemoveAll(t.RuntimePath)
|
|
|
|
}
|
|
|
|
|
2023-12-07 11:21:04 +00:00
|
|
|
func (t *HubTestItem) RunWithNucleiTemplate() error {
|
2023-12-13 16:45:56 +00:00
|
|
|
crowdsecLogFile := fmt.Sprintf("%s/log/crowdsec.log", t.RuntimePath)
|
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2023-12-07 11:21:04 +00:00
|
|
|
if err := os.Chdir(testPath); err != nil {
|
|
|
|
return fmt.Errorf("can't 'cd' to '%s': %s", testPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
//machine add
|
2023-12-13 16:45:56 +00:00
|
|
|
cmdArgs := []string{"-c", t.RuntimeConfigFilePath, "machines", "add", "testMachine", "--force", "--auto"}
|
2023-12-07 11:21:04 +00:00
|
|
|
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", "appsectests", "-k", TestBouncerApiKey}
|
|
|
|
cscliBouncerCmd := exec.Command(t.CscliPath, cmdArgs...)
|
|
|
|
|
|
|
|
output, err = cscliBouncerCmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
if !strings.Contains(string(output), "unable to create bouncer: bouncer appsectests 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 appsec port to be available
|
2023-12-14 15:05:16 +00:00
|
|
|
if _, err := IsAlive(t.AppSecHost); err != nil {
|
2023-12-13 16:45:56 +00:00
|
|
|
crowdsecLog, err2 := os.ReadFile(crowdsecLogFile)
|
|
|
|
if err2 != nil {
|
|
|
|
log.Errorf("unable to read crowdsec log file '%s': %s", crowdsecLogFile, err)
|
|
|
|
} else {
|
|
|
|
log.Errorf("crowdsec log file '%s'", crowdsecLogFile)
|
|
|
|
log.Errorf("%s\n", string(crowdsecLog))
|
|
|
|
}
|
2024-01-03 09:55:41 +00:00
|
|
|
|
2023-12-07 11:21:04 +00:00
|
|
|
return fmt.Errorf("appsec is down: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if the target is available
|
2023-12-14 15:05:16 +00:00
|
|
|
nucleiTargetParsedURL, err := url.Parse(t.NucleiTargetHost)
|
2023-12-07 11:21:04 +00:00
|
|
|
if err != nil {
|
2023-12-14 15:05:16 +00:00
|
|
|
return fmt.Errorf("unable to parse target '%s': %s", t.NucleiTargetHost, err)
|
2023-12-07 11:21:04 +00:00
|
|
|
}
|
2024-01-03 09:55:41 +00:00
|
|
|
|
2023-12-07 11:21:04 +00:00
|
|
|
nucleiTargetHost := nucleiTargetParsedURL.Host
|
|
|
|
if _, err := IsAlive(nucleiTargetHost); err != nil {
|
|
|
|
return fmt.Errorf("target is down: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
nucleiConfig := NucleiConfig{
|
|
|
|
Path: "nuclei",
|
|
|
|
OutputDir: t.RuntimePath,
|
|
|
|
CmdLineOptions: []string{"-ev", //allow variables from environment
|
|
|
|
"-nc", //no colors in output
|
|
|
|
"-dresp", //dump response
|
|
|
|
"-j", //json output
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-12-14 15:05:16 +00:00
|
|
|
err = nucleiConfig.RunNucleiTemplate(t.Name, t.Config.NucleiTemplate, t.NucleiTargetHost)
|
2023-12-07 11:21:04 +00:00
|
|
|
if t.Config.ExpectedNucleiFailure {
|
2023-12-14 15:05:16 +00:00
|
|
|
if err != nil && errors.Is(err, ErrNucleiTemplateFail) {
|
2023-12-07 11:21:04 +00:00
|
|
|
log.Infof("Appsec test %s failed as expected", t.Name)
|
|
|
|
t.Success = true
|
|
|
|
} else {
|
|
|
|
log.Errorf("Appsec test %s failed: %s", t.Name, err)
|
|
|
|
crowdsecLog, err := os.ReadFile(crowdsecLogFile)
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("unable to read crowdsec log file '%s': %s", crowdsecLogFile, err)
|
|
|
|
} else {
|
|
|
|
log.Errorf("crowdsec log file '%s'", crowdsecLogFile)
|
|
|
|
log.Errorf("%s\n", string(crowdsecLog))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err == nil {
|
|
|
|
log.Infof("Appsec test %s succeeded", t.Name)
|
|
|
|
t.Success = true
|
|
|
|
} else {
|
|
|
|
log.Errorf("Appsec test %s failed: %s", t.Name, err)
|
|
|
|
crowdsecLog, err := os.ReadFile(crowdsecLogFile)
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("unable to read crowdsec log file '%s': %s", crowdsecLogFile, err)
|
|
|
|
} else {
|
|
|
|
log.Errorf("crowdsec log file '%s'", crowdsecLogFile)
|
|
|
|
log.Errorf("%s\n", string(crowdsecLog))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-01-03 09:55:41 +00:00
|
|
|
|
2023-12-07 11:21:04 +00:00
|
|
|
crowdsecDaemon.Process.Kill()
|
2024-01-03 09:55:41 +00:00
|
|
|
|
2023-12-07 11:21:04 +00:00
|
|
|
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
|
2021-10-04 15:14:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("can't get current directory: %+v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create runtime folder
|
2023-11-24 14:57:32 +00:00
|
|
|
if err = os.MkdirAll(t.RuntimePath, os.ModePerm); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimePath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create runtime data folder
|
2023-11-24 14:57:32 +00:00
|
|
|
if err = os.MkdirAll(t.RuntimeDataPath, os.ModePerm); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeDataPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create runtime hub folder
|
2023-11-24 14:57:32 +00:00
|
|
|
if err = os.MkdirAll(t.RuntimeHubPath, os.ModePerm); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeHubPath, err)
|
|
|
|
}
|
|
|
|
|
2023-11-24 14:57:32 +00:00
|
|
|
if err = Copy(t.HubIndexFile, filepath.Join(t.RuntimeHubPath, ".index.json")); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to copy .index.json file in '%s': %s", filepath.Join(t.RuntimeHubPath, ".index.json"), err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create results folder
|
2023-11-24 14:57:32 +00:00
|
|
|
if err = os.MkdirAll(t.ResultsPath, os.ModePerm); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to create folder '%s': %+v", t.ResultsPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy template config file to runtime folder
|
2023-11-24 14:57:32 +00:00
|
|
|
if err = Copy(t.TemplateConfigPath, t.RuntimeConfigFilePath); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateConfigPath, t.RuntimeConfigFilePath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy template profile file to runtime folder
|
2023-11-24 14:57:32 +00:00
|
|
|
if err = Copy(t.TemplateProfilePath, t.RuntimeProfileFilePath); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateProfilePath, t.RuntimeProfileFilePath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy template simulation file to runtime folder
|
2023-11-24 14:57:32 +00:00
|
|
|
if err = Copy(t.TemplateSimulationPath, t.RuntimeSimulationFilePath); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateSimulationPath, t.RuntimeSimulationFilePath, err)
|
|
|
|
}
|
|
|
|
|
2023-11-24 14:57:32 +00:00
|
|
|
crowdsecPatternsFolder := csconfig.DefaultConfigPath("patterns")
|
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
// copy template patterns folder to runtime folder
|
2023-11-24 14:57:32 +00:00
|
|
|
if err = CopyDir(crowdsecPatternsFolder, t.RuntimePatternsPath); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to copy 'patterns' from '%s' to '%s': %s", crowdsecPatternsFolder, t.RuntimePatternsPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// install the hub in the runtime folder
|
2023-11-24 14:57:32 +00:00
|
|
|
if err = t.InstallHub(); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("unable to install hub in '%s': %s", t.RuntimeHubPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
logFile := t.Config.LogFile
|
|
|
|
logType := t.Config.LogType
|
|
|
|
dsn := fmt.Sprintf("file://%s", logFile)
|
|
|
|
|
2023-11-24 14:57:32 +00:00
|
|
|
if err = os.Chdir(testPath); err != nil {
|
2021-10-04 15:14:52 +00:00
|
|
|
return fmt.Errorf("can't 'cd' to '%s': %s", testPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
logFileStat, err := os.Stat(logFile)
|
|
|
|
if err != nil {
|
2022-06-22 13:53:53 +00:00
|
|
|
return fmt.Errorf("unable to stat log file '%s': %s", logFile, err)
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
if logFileStat.Size() == 0 {
|
2023-10-03 09:17:02 +00:00
|
|
|
return fmt.Errorf("log file '%s' is empty, please fill it with log", logFile)
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
|
|
|
|
2024-01-03 08:33:52 +00:00
|
|
|
cmdArgs := []string{"-c", t.RuntimeConfigFilePath, "machines", "add", "testMachine", "--force", "--auto"}
|
2021-10-04 15:14:52 +00:00
|
|
|
cscliRegisterCmd := exec.Command(t.CscliPath, cmdArgs...)
|
|
|
|
log.Debugf("%s", cscliRegisterCmd.String())
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-20 09:41:30 +00:00
|
|
|
cmdArgs = []string{"-c", t.RuntimeConfigFilePath, "-type", logType, "-dsn", dsn, "-dump-data", t.ResultsPath, "-order-event"}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-11-17 09:08:46 +00:00
|
|
|
for labelKey, labelValue := range t.Config.Labels {
|
|
|
|
arg := fmt.Sprintf("%s:%s", labelKey, labelValue)
|
|
|
|
cmdArgs = append(cmdArgs, "-label", arg)
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
crowdsecCmd := exec.Command(t.CrowdSecPath, cmdArgs...)
|
|
|
|
log.Debugf("%s", crowdsecCmd.String())
|
|
|
|
output, err = crowdsecCmd.CombinedOutput()
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
if log.GetLevel() >= log.DebugLevel || err != nil {
|
|
|
|
fmt.Println(string(output))
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("fail to run '%s' for test '%s': %v", crowdsecCmd.String(), t.Name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.Chdir(currentDir); err != nil {
|
|
|
|
return fmt.Errorf("can't 'cd' to '%s': %s", currentDir, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// assert parsers
|
|
|
|
if !t.Config.IgnoreParsers {
|
2022-10-14 14:12:21 +00:00
|
|
|
_, err := os.Stat(t.ParserAssert.File)
|
2021-10-04 15:14:52 +00:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
parserAssertFile, err := os.Create(t.ParserAssert.File)
|
|
|
|
if err != nil {
|
2023-10-03 09:17:02 +00:00
|
|
|
return err
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
parserAssertFile.Close()
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2022-10-14 14:12:21 +00:00
|
|
|
assertFileStat, err := os.Stat(t.ParserAssert.File)
|
2021-10-04 15:14:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error while stats '%s': %s", t.ParserAssert.File, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if assertFileStat.Size() == 0 {
|
|
|
|
assertData, err := t.ParserAssert.AutoGenFromFile(t.ParserResultFile)
|
|
|
|
if err != nil {
|
2022-06-22 13:53:53 +00:00
|
|
|
return fmt.Errorf("couldn't generate assertion: %s", err)
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
t.ParserAssert.AutoGenAssertData = assertData
|
|
|
|
t.ParserAssert.AutoGenAssert = true
|
|
|
|
} else {
|
|
|
|
if err := t.ParserAssert.AssertFile(t.ParserResultFile); err != nil {
|
|
|
|
return fmt.Errorf("unable to run assertion on file '%s': %s", t.ParserResultFile, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// assert scenarios
|
|
|
|
nbScenario := 0
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
for _, scenario := range t.Config.Scenarios {
|
|
|
|
if scenario == "" {
|
|
|
|
continue
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
|
|
|
nbScenario++
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
if nbScenario > 0 {
|
2022-10-14 14:12:21 +00:00
|
|
|
_, err := os.Stat(t.ScenarioAssert.File)
|
2021-10-04 15:14:52 +00:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
scenarioAssertFile, err := os.Create(t.ScenarioAssert.File)
|
|
|
|
if err != nil {
|
2023-10-03 09:17:02 +00:00
|
|
|
return err
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
scenarioAssertFile.Close()
|
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2022-10-14 14:12:21 +00:00
|
|
|
assertFileStat, err := os.Stat(t.ScenarioAssert.File)
|
2021-10-04 15:14:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error while stats '%s': %s", t.ScenarioAssert.File, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if assertFileStat.Size() == 0 {
|
|
|
|
assertData, err := t.ScenarioAssert.AutoGenFromFile(t.ScenarioResultFile)
|
|
|
|
if err != nil {
|
2022-06-22 13:53:53 +00:00
|
|
|
return fmt.Errorf("couldn't generate assertion: %s", err)
|
2021-10-04 15:14:52 +00:00
|
|
|
}
|
2023-11-24 14:57:32 +00:00
|
|
|
|
2021-10-04 15:14:52 +00:00
|
|
|
t.ScenarioAssert.AutoGenAssertData = assertData
|
|
|
|
t.ScenarioAssert.AutoGenAssert = true
|
|
|
|
} else {
|
|
|
|
if err := t.ScenarioAssert.AssertFile(t.ScenarioResultFile); err != nil {
|
|
|
|
return fmt.Errorf("unable to run assertion on file '%s': %s", t.ScenarioResultFile, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if t.ParserAssert.AutoGenAssert || t.ScenarioAssert.AutoGenAssert {
|
|
|
|
t.AutoGen = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t.ParserAssert.Success || t.Config.IgnoreParsers) && (nbScenario == 0 || t.ScenarioAssert.Success) {
|
|
|
|
t.Success = true
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2023-12-07 11:21:04 +00:00
|
|
|
|
|
|
|
func (t *HubTestItem) Run() error {
|
|
|
|
var err error
|
2024-01-03 09:55:41 +00:00
|
|
|
|
2023-12-07 11:21:04 +00:00
|
|
|
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 appsec-configs dir
|
|
|
|
if err = os.MkdirAll(filepath.Join(t.RuntimePath, "appsec-configs"), os.ModePerm); err != nil {
|
|
|
|
return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimePath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
//if it's an appsec rule test, we need acquis and appsec profile
|
|
|
|
if len(t.Config.AppsecRules) > 0 {
|
|
|
|
// copy template acquis file to runtime folder
|
2023-12-14 15:05:16 +00:00
|
|
|
log.Debugf("copying %s to %s", t.TemplateAcquisPath, t.RuntimeAcquisFilePath)
|
2024-01-03 09:55:41 +00:00
|
|
|
|
2023-12-07 11:21:04 +00:00
|
|
|
if err = Copy(t.TemplateAcquisPath, t.RuntimeAcquisFilePath); err != nil {
|
|
|
|
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateAcquisPath, t.RuntimeAcquisFilePath, err)
|
|
|
|
}
|
|
|
|
|
2023-12-14 15:05:16 +00:00
|
|
|
log.Debugf("copying %s to %s", t.TemplateAppsecProfilePath, filepath.Join(t.RuntimePath, "appsec-configs", "config.yaml"))
|
2023-12-07 11:21:04 +00:00
|
|
|
// copy template appsec-config file to runtime folder
|
|
|
|
if err = Copy(t.TemplateAppsecProfilePath, filepath.Join(t.RuntimePath, "appsec-configs", "config.yaml")); err != nil {
|
|
|
|
return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateAppsecProfilePath, filepath.Join(t.RuntimePath, "appsec-configs", "config.yaml"), err)
|
|
|
|
}
|
|
|
|
} else { //otherwise we drop a blank acquis file
|
|
|
|
if err = os.WriteFile(t.RuntimeAcquisFilePath, []byte(""), os.ModePerm); err != nil {
|
|
|
|
return fmt.Errorf("unable to write blank acquis file '%s': %s", t.RuntimeAcquisFilePath, 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)
|
|
|
|
}
|
|
|
|
}
|