github.com/crowdsecurity/crowdsec@v1.6.1/pkg/hubtest/hubtest_item.go (about)

     1  package hubtest
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/url"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	log "github.com/sirupsen/logrus"
    13  	"gopkg.in/yaml.v2"
    14  
    15  	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
    16  	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
    17  	"github.com/crowdsecurity/crowdsec/pkg/parser"
    18  )
    19  
    20  type HubTestItemConfig struct {
    21  	Parsers               []string            `yaml:"parsers,omitempty"`
    22  	Scenarios             []string            `yaml:"scenarios,omitempty"`
    23  	PostOverflows         []string            `yaml:"postoverflows,omitempty"`
    24  	AppsecRules           []string            `yaml:"appsec-rules,omitempty"`
    25  	NucleiTemplate        string              `yaml:"nuclei_template,omitempty"`
    26  	ExpectedNucleiFailure bool                `yaml:"expect_failure,omitempty"`
    27  	LogFile               string              `yaml:"log_file,omitempty"`
    28  	LogType               string              `yaml:"log_type,omitempty"`
    29  	Labels                map[string]string   `yaml:"labels,omitempty"`
    30  	IgnoreParsers         bool                `yaml:"ignore_parsers,omitempty"`   // if we test a scenario, we don't want to assert on Parser
    31  	OverrideStatics       []parser.ExtraField `yaml:"override_statics,omitempty"` //Allow to override statics. Executed before s00
    32  }
    33  
    34  type HubTestItem struct {
    35  	Name string
    36  	Path string
    37  
    38  	CrowdSecPath string
    39  	CscliPath    string
    40  
    41  	RuntimePath               string
    42  	RuntimeHubPath            string
    43  	RuntimeDataPath           string
    44  	RuntimePatternsPath       string
    45  	RuntimeConfigFilePath     string
    46  	RuntimeProfileFilePath    string
    47  	RuntimeSimulationFilePath string
    48  	RuntimeAcquisFilePath     string
    49  	RuntimeHubConfig          *csconfig.LocalHubCfg
    50  
    51  	ResultsPath          string
    52  	ParserResultFile     string
    53  	ScenarioResultFile   string
    54  	BucketPourResultFile string
    55  
    56  	HubPath                   string
    57  	HubTestPath               string
    58  	HubIndexFile              string
    59  	TemplateConfigPath        string
    60  	TemplateProfilePath       string
    61  	TemplateSimulationPath    string
    62  	TemplateAcquisPath        string
    63  	TemplateAppsecProfilePath string
    64  	HubIndex                  *cwhub.Hub
    65  
    66  	Config *HubTestItemConfig
    67  
    68  	Success    bool
    69  	ErrorsList []string
    70  
    71  	AutoGen        bool
    72  	ParserAssert   *ParserAssert
    73  	ScenarioAssert *ScenarioAssert
    74  
    75  	CustomItemsLocation []string
    76  
    77  	NucleiTargetHost string
    78  	AppSecHost       string
    79  }
    80  
    81  const (
    82  	ParserAssertFileName = "parser.assert"
    83  	ParserResultFileName = "parser-dump.yaml"
    84  
    85  	ScenarioAssertFileName = "scenario.assert"
    86  	ScenarioResultFileName = "bucket-dump.yaml"
    87  
    88  	BucketPourResultFileName = "bucketpour-dump.yaml"
    89  
    90  	TestBouncerApiKey = "this_is_a_bad_password"
    91  
    92  	DefaultNucleiTarget = "http://127.0.0.1:7822/"
    93  	DefaultAppsecHost   = "127.0.0.1:4241"
    94  )
    95  
    96  func NewTest(name string, hubTest *HubTest) (*HubTestItem, error) {
    97  	testPath := filepath.Join(hubTest.HubTestPath, name)
    98  	runtimeFolder := filepath.Join(testPath, "runtime")
    99  	runtimeHubFolder := filepath.Join(runtimeFolder, "hub")
   100  	configFilePath := filepath.Join(testPath, "config.yaml")
   101  	resultPath := filepath.Join(testPath, "results")
   102  
   103  	// read test configuration file
   104  	configFileData := &HubTestItemConfig{}
   105  
   106  	yamlFile, err := os.ReadFile(configFilePath)
   107  	if err != nil {
   108  		log.Printf("no config file found in '%s': %v", testPath, err)
   109  	}
   110  
   111  	err = yaml.Unmarshal(yamlFile, configFileData)
   112  	if err != nil {
   113  		return nil, fmt.Errorf("unmarshal: %v", err)
   114  	}
   115  
   116  	parserAssertFilePath := filepath.Join(testPath, ParserAssertFileName)
   117  	ParserAssert := NewParserAssert(parserAssertFilePath)
   118  
   119  	scenarioAssertFilePath := filepath.Join(testPath, ScenarioAssertFileName)
   120  	ScenarioAssert := NewScenarioAssert(scenarioAssertFilePath)
   121  
   122  	return &HubTestItem{
   123  		Name:                      name,
   124  		Path:                      testPath,
   125  		CrowdSecPath:              hubTest.CrowdSecPath,
   126  		CscliPath:                 hubTest.CscliPath,
   127  		RuntimePath:               filepath.Join(testPath, "runtime"),
   128  		RuntimeHubPath:            runtimeHubFolder,
   129  		RuntimeDataPath:           filepath.Join(runtimeFolder, "data"),
   130  		RuntimePatternsPath:       filepath.Join(runtimeFolder, "patterns"),
   131  		RuntimeConfigFilePath:     filepath.Join(runtimeFolder, "config.yaml"),
   132  		RuntimeProfileFilePath:    filepath.Join(runtimeFolder, "profiles.yaml"),
   133  		RuntimeSimulationFilePath: filepath.Join(runtimeFolder, "simulation.yaml"),
   134  		RuntimeAcquisFilePath:     filepath.Join(runtimeFolder, "acquis.yaml"),
   135  		ResultsPath:               resultPath,
   136  		ParserResultFile:          filepath.Join(resultPath, ParserResultFileName),
   137  		ScenarioResultFile:        filepath.Join(resultPath, ScenarioResultFileName),
   138  		BucketPourResultFile:      filepath.Join(resultPath, BucketPourResultFileName),
   139  		RuntimeHubConfig: &csconfig.LocalHubCfg{
   140  			HubDir:         runtimeHubFolder,
   141  			HubIndexFile:   hubTest.HubIndexFile,
   142  			InstallDir:     runtimeFolder,
   143  			InstallDataDir: filepath.Join(runtimeFolder, "data"),
   144  		},
   145  		Config:                    configFileData,
   146  		HubPath:                   hubTest.HubPath,
   147  		HubTestPath:               hubTest.HubTestPath,
   148  		HubIndexFile:              hubTest.HubIndexFile,
   149  		TemplateConfigPath:        hubTest.TemplateConfigPath,
   150  		TemplateProfilePath:       hubTest.TemplateProfilePath,
   151  		TemplateSimulationPath:    hubTest.TemplateSimulationPath,
   152  		TemplateAcquisPath:        hubTest.TemplateAcquisPath,
   153  		TemplateAppsecProfilePath: hubTest.TemplateAppsecProfilePath,
   154  		HubIndex:                  hubTest.HubIndex,
   155  		ScenarioAssert:            ScenarioAssert,
   156  		ParserAssert:              ParserAssert,
   157  		CustomItemsLocation:       []string{hubTest.HubPath, testPath},
   158  		NucleiTargetHost:          hubTest.NucleiTargetHost,
   159  		AppSecHost:                hubTest.AppSecHost,
   160  	}, nil
   161  }
   162  
   163  func (t *HubTestItem) installHubItems(names []string, installFunc func(string) error) error {
   164  	for _, name := range names {
   165  		if name == "" {
   166  			continue
   167  		}
   168  
   169  		if err := installFunc(name); err != nil {
   170  			return err
   171  		}
   172  	}
   173  
   174  	return nil
   175  }
   176  
   177  func (t *HubTestItem) InstallHub() error {
   178  	if err := t.installHubItems(t.Config.Parsers, t.installParser); err != nil {
   179  		return err
   180  	}
   181  
   182  	if err := t.installHubItems(t.Config.Scenarios, t.installScenario); err != nil {
   183  		return err
   184  	}
   185  
   186  	if err := t.installHubItems(t.Config.PostOverflows, t.installPostoverflow); err != nil {
   187  		return err
   188  	}
   189  
   190  	if err := t.installHubItems(t.Config.AppsecRules, t.installAppsecRule); err != nil {
   191  		return err
   192  	}
   193  
   194  	if len(t.Config.OverrideStatics) > 0 {
   195  		n := parser.Node{
   196  			Name:    "overrides",
   197  			Filter:  "1==1",
   198  			Statics: t.Config.OverrideStatics,
   199  		}
   200  
   201  		b, err := yaml.Marshal(n)
   202  		if err != nil {
   203  			return fmt.Errorf("unable to marshal overrides: %s", err)
   204  		}
   205  
   206  		tgtFilename := fmt.Sprintf("%s/parsers/s00-raw/00_overrides.yaml", t.RuntimePath)
   207  		if err := os.WriteFile(tgtFilename, b, os.ModePerm); err != nil {
   208  			return fmt.Errorf("unable to write overrides to '%s': %s", tgtFilename, err)
   209  		}
   210  	}
   211  
   212  	// load installed hub
   213  	hub, err := cwhub.NewHub(t.RuntimeHubConfig, nil, false, nil)
   214  	if err != nil {
   215  		log.Fatal(err)
   216  	}
   217  
   218  	// install data for parsers if needed
   219  	ret := hub.GetItemMap(cwhub.PARSERS)
   220  	for parserName, item := range ret {
   221  		if item.State.Installed {
   222  			if err := item.DownloadDataIfNeeded(true); err != nil {
   223  				return fmt.Errorf("unable to download data for parser '%s': %+v", parserName, err)
   224  			}
   225  
   226  			log.Debugf("parser '%s' installed successfully in runtime environment", parserName)
   227  		}
   228  	}
   229  
   230  	// install data for scenarios if needed
   231  	ret = hub.GetItemMap(cwhub.SCENARIOS)
   232  	for scenarioName, item := range ret {
   233  		if item.State.Installed {
   234  			if err := item.DownloadDataIfNeeded(true); err != nil {
   235  				return fmt.Errorf("unable to download data for parser '%s': %+v", scenarioName, err)
   236  			}
   237  
   238  			log.Debugf("scenario '%s' installed successfully in runtime environment", scenarioName)
   239  		}
   240  	}
   241  
   242  	// install data for postoverflows if needed
   243  	ret = hub.GetItemMap(cwhub.POSTOVERFLOWS)
   244  	for postoverflowName, item := range ret {
   245  		if item.State.Installed {
   246  			if err := item.DownloadDataIfNeeded(true); err != nil {
   247  				return fmt.Errorf("unable to download data for parser '%s': %+v", postoverflowName, err)
   248  			}
   249  
   250  			log.Debugf("postoverflow '%s' installed successfully in runtime environment", postoverflowName)
   251  		}
   252  	}
   253  
   254  	return nil
   255  }
   256  
   257  func (t *HubTestItem) Clean() error {
   258  	return os.RemoveAll(t.RuntimePath)
   259  }
   260  
   261  func (t *HubTestItem) RunWithNucleiTemplate() error {
   262  	crowdsecLogFile := fmt.Sprintf("%s/log/crowdsec.log", t.RuntimePath)
   263  
   264  	testPath := filepath.Join(t.HubTestPath, t.Name)
   265  	if _, err := os.Stat(testPath); os.IsNotExist(err) {
   266  		return fmt.Errorf("test '%s' doesn't exist in '%s', exiting", t.Name, t.HubTestPath)
   267  	}
   268  
   269  	if err := os.Chdir(testPath); err != nil {
   270  		return fmt.Errorf("can't 'cd' to '%s': %s", testPath, err)
   271  	}
   272  
   273  	//machine add
   274  	cmdArgs := []string{"-c", t.RuntimeConfigFilePath, "machines", "add", "testMachine", "--force", "--auto"}
   275  	cscliRegisterCmd := exec.Command(t.CscliPath, cmdArgs...)
   276  
   277  	output, err := cscliRegisterCmd.CombinedOutput()
   278  	if err != nil {
   279  		if !strings.Contains(string(output), "unable to create machine: user 'testMachine': user already exist") {
   280  			fmt.Println(string(output))
   281  			return fmt.Errorf("fail to run '%s' for test '%s': %v", cscliRegisterCmd.String(), t.Name, err)
   282  		}
   283  	}
   284  
   285  	//hardcode bouncer key
   286  	cmdArgs = []string{"-c", t.RuntimeConfigFilePath, "bouncers", "add", "appsectests", "-k", TestBouncerApiKey}
   287  	cscliBouncerCmd := exec.Command(t.CscliPath, cmdArgs...)
   288  
   289  	output, err = cscliBouncerCmd.CombinedOutput()
   290  	if err != nil {
   291  		if !strings.Contains(string(output), "unable to create bouncer: bouncer appsectests already exists") {
   292  			fmt.Println(string(output))
   293  			return fmt.Errorf("fail to run '%s' for test '%s': %v", cscliRegisterCmd.String(), t.Name, err)
   294  		}
   295  	}
   296  
   297  	//start crowdsec service
   298  	cmdArgs = []string{"-c", t.RuntimeConfigFilePath}
   299  	crowdsecDaemon := exec.Command(t.CrowdSecPath, cmdArgs...)
   300  
   301  	crowdsecDaemon.Start()
   302  
   303  	//wait for the appsec port to be available
   304  	if _, err := IsAlive(t.AppSecHost); err != nil {
   305  		crowdsecLog, err2 := os.ReadFile(crowdsecLogFile)
   306  		if err2 != nil {
   307  			log.Errorf("unable to read crowdsec log file '%s': %s", crowdsecLogFile, err)
   308  		} else {
   309  			log.Errorf("crowdsec log file '%s'", crowdsecLogFile)
   310  			log.Errorf("%s\n", string(crowdsecLog))
   311  		}
   312  
   313  		return fmt.Errorf("appsec is down: %s", err)
   314  	}
   315  
   316  	// check if the target is available
   317  	nucleiTargetParsedURL, err := url.Parse(t.NucleiTargetHost)
   318  	if err != nil {
   319  		return fmt.Errorf("unable to parse target '%s': %s", t.NucleiTargetHost, err)
   320  	}
   321  
   322  	nucleiTargetHost := nucleiTargetParsedURL.Host
   323  	if _, err := IsAlive(nucleiTargetHost); err != nil {
   324  		return fmt.Errorf("target is down: %s", err)
   325  	}
   326  
   327  	nucleiConfig := NucleiConfig{
   328  		Path:      "nuclei",
   329  		OutputDir: t.RuntimePath,
   330  		CmdLineOptions: []string{"-ev", //allow variables from environment
   331  			"-nc",    //no colors in output
   332  			"-dresp", //dump response
   333  			"-j",     //json output
   334  		},
   335  	}
   336  
   337  	err = nucleiConfig.RunNucleiTemplate(t.Name, t.Config.NucleiTemplate, t.NucleiTargetHost)
   338  	if t.Config.ExpectedNucleiFailure {
   339  		if err != nil && errors.Is(err, ErrNucleiTemplateFail) {
   340  			log.Infof("Appsec test %s failed as expected", t.Name)
   341  			t.Success = true
   342  		} else {
   343  			log.Errorf("Appsec test %s failed:  %s", t.Name, err)
   344  			crowdsecLog, err := os.ReadFile(crowdsecLogFile)
   345  			if err != nil {
   346  				log.Errorf("unable to read crowdsec log file '%s': %s", crowdsecLogFile, err)
   347  			} else {
   348  				log.Errorf("crowdsec log file '%s'", crowdsecLogFile)
   349  				log.Errorf("%s\n", string(crowdsecLog))
   350  			}
   351  		}
   352  	} else {
   353  		if err == nil {
   354  			log.Infof("Appsec test %s succeeded", t.Name)
   355  			t.Success = true
   356  		} else {
   357  			log.Errorf("Appsec test %s failed:  %s", t.Name, err)
   358  			crowdsecLog, err := os.ReadFile(crowdsecLogFile)
   359  			if err != nil {
   360  				log.Errorf("unable to read crowdsec log file '%s': %s", crowdsecLogFile, err)
   361  			} else {
   362  				log.Errorf("crowdsec log file '%s'", crowdsecLogFile)
   363  				log.Errorf("%s\n", string(crowdsecLog))
   364  			}
   365  		}
   366  	}
   367  
   368  	crowdsecDaemon.Process.Kill()
   369  
   370  	return nil
   371  }
   372  
   373  func (t *HubTestItem) RunWithLogFile() error {
   374  	testPath := filepath.Join(t.HubTestPath, t.Name)
   375  	if _, err := os.Stat(testPath); os.IsNotExist(err) {
   376  		return fmt.Errorf("test '%s' doesn't exist in '%s', exiting", t.Name, t.HubTestPath)
   377  	}
   378  
   379  	currentDir, err := os.Getwd() //xx
   380  	if err != nil {
   381  		return fmt.Errorf("can't get current directory: %+v", err)
   382  	}
   383  
   384  	// create runtime folder
   385  	if err = os.MkdirAll(t.RuntimePath, os.ModePerm); err != nil {
   386  		return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimePath, err)
   387  	}
   388  
   389  	// create runtime data folder
   390  	if err = os.MkdirAll(t.RuntimeDataPath, os.ModePerm); err != nil {
   391  		return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeDataPath, err)
   392  	}
   393  
   394  	// create runtime hub folder
   395  	if err = os.MkdirAll(t.RuntimeHubPath, os.ModePerm); err != nil {
   396  		return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeHubPath, err)
   397  	}
   398  
   399  	if err = Copy(t.HubIndexFile, filepath.Join(t.RuntimeHubPath, ".index.json")); err != nil {
   400  		return fmt.Errorf("unable to copy .index.json file in '%s': %s", filepath.Join(t.RuntimeHubPath, ".index.json"), err)
   401  	}
   402  
   403  	// create results folder
   404  	if err = os.MkdirAll(t.ResultsPath, os.ModePerm); err != nil {
   405  		return fmt.Errorf("unable to create folder '%s': %+v", t.ResultsPath, err)
   406  	}
   407  
   408  	// copy template config file to runtime folder
   409  	if err = Copy(t.TemplateConfigPath, t.RuntimeConfigFilePath); err != nil {
   410  		return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateConfigPath, t.RuntimeConfigFilePath, err)
   411  	}
   412  
   413  	// copy template profile file to runtime folder
   414  	if err = Copy(t.TemplateProfilePath, t.RuntimeProfileFilePath); err != nil {
   415  		return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateProfilePath, t.RuntimeProfileFilePath, err)
   416  	}
   417  
   418  	// copy template simulation file to runtime folder
   419  	if err = Copy(t.TemplateSimulationPath, t.RuntimeSimulationFilePath); err != nil {
   420  		return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateSimulationPath, t.RuntimeSimulationFilePath, err)
   421  	}
   422  
   423  	crowdsecPatternsFolder := csconfig.DefaultConfigPath("patterns")
   424  
   425  	// copy template patterns folder to runtime folder
   426  	if err = CopyDir(crowdsecPatternsFolder, t.RuntimePatternsPath); err != nil {
   427  		return fmt.Errorf("unable to copy 'patterns' from '%s' to '%s': %s", crowdsecPatternsFolder, t.RuntimePatternsPath, err)
   428  	}
   429  
   430  	// install the hub in the runtime folder
   431  	if err = t.InstallHub(); err != nil {
   432  		return fmt.Errorf("unable to install hub in '%s': %s", t.RuntimeHubPath, err)
   433  	}
   434  
   435  	logFile := t.Config.LogFile
   436  	logType := t.Config.LogType
   437  	dsn := fmt.Sprintf("file://%s", logFile)
   438  
   439  	if err = os.Chdir(testPath); err != nil {
   440  		return fmt.Errorf("can't 'cd' to '%s': %s", testPath, err)
   441  	}
   442  
   443  	logFileStat, err := os.Stat(logFile)
   444  	if err != nil {
   445  		return fmt.Errorf("unable to stat log file '%s': %s", logFile, err)
   446  	}
   447  
   448  	if logFileStat.Size() == 0 {
   449  		return fmt.Errorf("log file '%s' is empty, please fill it with log", logFile)
   450  	}
   451  
   452  	cmdArgs := []string{"-c", t.RuntimeConfigFilePath, "machines", "add", "testMachine", "--force", "--auto"}
   453  	cscliRegisterCmd := exec.Command(t.CscliPath, cmdArgs...)
   454  	log.Debugf("%s", cscliRegisterCmd.String())
   455  
   456  	output, err := cscliRegisterCmd.CombinedOutput()
   457  	if err != nil {
   458  		if !strings.Contains(string(output), "unable to create machine: user 'testMachine': user already exist") {
   459  			fmt.Println(string(output))
   460  			return fmt.Errorf("fail to run '%s' for test '%s': %v", cscliRegisterCmd.String(), t.Name, err)
   461  		}
   462  	}
   463  
   464  	cmdArgs = []string{"-c", t.RuntimeConfigFilePath, "-type", logType, "-dsn", dsn, "-dump-data", t.ResultsPath, "-order-event"}
   465  
   466  	for labelKey, labelValue := range t.Config.Labels {
   467  		arg := fmt.Sprintf("%s:%s", labelKey, labelValue)
   468  		cmdArgs = append(cmdArgs, "-label", arg)
   469  	}
   470  
   471  	crowdsecCmd := exec.Command(t.CrowdSecPath, cmdArgs...)
   472  	log.Debugf("%s", crowdsecCmd.String())
   473  	output, err = crowdsecCmd.CombinedOutput()
   474  
   475  	if log.GetLevel() >= log.DebugLevel || err != nil {
   476  		fmt.Println(string(output))
   477  	}
   478  
   479  	if err != nil {
   480  		return fmt.Errorf("fail to run '%s' for test '%s': %v", crowdsecCmd.String(), t.Name, err)
   481  	}
   482  
   483  	if err := os.Chdir(currentDir); err != nil {
   484  		return fmt.Errorf("can't 'cd' to '%s': %s", currentDir, err)
   485  	}
   486  
   487  	// assert parsers
   488  	if !t.Config.IgnoreParsers {
   489  		_, err := os.Stat(t.ParserAssert.File)
   490  		if os.IsNotExist(err) {
   491  			parserAssertFile, err := os.Create(t.ParserAssert.File)
   492  			if err != nil {
   493  				return err
   494  			}
   495  
   496  			parserAssertFile.Close()
   497  		}
   498  
   499  		assertFileStat, err := os.Stat(t.ParserAssert.File)
   500  		if err != nil {
   501  			return fmt.Errorf("error while stats '%s': %s", t.ParserAssert.File, err)
   502  		}
   503  
   504  		if assertFileStat.Size() == 0 {
   505  			assertData, err := t.ParserAssert.AutoGenFromFile(t.ParserResultFile)
   506  			if err != nil {
   507  				return fmt.Errorf("couldn't generate assertion: %s", err)
   508  			}
   509  
   510  			t.ParserAssert.AutoGenAssertData = assertData
   511  			t.ParserAssert.AutoGenAssert = true
   512  		} else {
   513  			if err := t.ParserAssert.AssertFile(t.ParserResultFile); err != nil {
   514  				return fmt.Errorf("unable to run assertion on file '%s': %s", t.ParserResultFile, err)
   515  			}
   516  		}
   517  	}
   518  
   519  	// assert scenarios
   520  	nbScenario := 0
   521  
   522  	for _, scenario := range t.Config.Scenarios {
   523  		if scenario == "" {
   524  			continue
   525  		}
   526  
   527  		nbScenario++
   528  	}
   529  
   530  	if nbScenario > 0 {
   531  		_, err := os.Stat(t.ScenarioAssert.File)
   532  		if os.IsNotExist(err) {
   533  			scenarioAssertFile, err := os.Create(t.ScenarioAssert.File)
   534  			if err != nil {
   535  				return err
   536  			}
   537  
   538  			scenarioAssertFile.Close()
   539  		}
   540  
   541  		assertFileStat, err := os.Stat(t.ScenarioAssert.File)
   542  		if err != nil {
   543  			return fmt.Errorf("error while stats '%s': %s", t.ScenarioAssert.File, err)
   544  		}
   545  
   546  		if assertFileStat.Size() == 0 {
   547  			assertData, err := t.ScenarioAssert.AutoGenFromFile(t.ScenarioResultFile)
   548  			if err != nil {
   549  				return fmt.Errorf("couldn't generate assertion: %s", err)
   550  			}
   551  
   552  			t.ScenarioAssert.AutoGenAssertData = assertData
   553  			t.ScenarioAssert.AutoGenAssert = true
   554  		} else {
   555  			if err := t.ScenarioAssert.AssertFile(t.ScenarioResultFile); err != nil {
   556  				return fmt.Errorf("unable to run assertion on file '%s': %s", t.ScenarioResultFile, err)
   557  			}
   558  		}
   559  	}
   560  
   561  	if t.ParserAssert.AutoGenAssert || t.ScenarioAssert.AutoGenAssert {
   562  		t.AutoGen = true
   563  	}
   564  
   565  	if (t.ParserAssert.Success || t.Config.IgnoreParsers) && (nbScenario == 0 || t.ScenarioAssert.Success) {
   566  		t.Success = true
   567  	}
   568  
   569  	return nil
   570  }
   571  
   572  func (t *HubTestItem) Run() error {
   573  	var err error
   574  
   575  	t.Success = false
   576  	t.ErrorsList = make([]string, 0)
   577  
   578  	// create runtime folder
   579  	if err = os.MkdirAll(t.RuntimePath, os.ModePerm); err != nil {
   580  		return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimePath, err)
   581  	}
   582  
   583  	// create runtime data folder
   584  	if err = os.MkdirAll(t.RuntimeDataPath, os.ModePerm); err != nil {
   585  		return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeDataPath, err)
   586  	}
   587  
   588  	// create runtime hub folder
   589  	if err = os.MkdirAll(t.RuntimeHubPath, os.ModePerm); err != nil {
   590  		return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimeHubPath, err)
   591  	}
   592  
   593  	if err = Copy(t.HubIndexFile, filepath.Join(t.RuntimeHubPath, ".index.json")); err != nil {
   594  		return fmt.Errorf("unable to copy .index.json file in '%s': %s", filepath.Join(t.RuntimeHubPath, ".index.json"), err)
   595  	}
   596  
   597  	// create results folder
   598  	if err = os.MkdirAll(t.ResultsPath, os.ModePerm); err != nil {
   599  		return fmt.Errorf("unable to create folder '%s': %+v", t.ResultsPath, err)
   600  	}
   601  
   602  	// copy template config file to runtime folder
   603  	if err = Copy(t.TemplateConfigPath, t.RuntimeConfigFilePath); err != nil {
   604  		return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateConfigPath, t.RuntimeConfigFilePath, err)
   605  	}
   606  
   607  	// copy template profile file to runtime folder
   608  	if err = Copy(t.TemplateProfilePath, t.RuntimeProfileFilePath); err != nil {
   609  		return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateProfilePath, t.RuntimeProfileFilePath, err)
   610  	}
   611  
   612  	// copy template simulation file to runtime folder
   613  	if err = Copy(t.TemplateSimulationPath, t.RuntimeSimulationFilePath); err != nil {
   614  		return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateSimulationPath, t.RuntimeSimulationFilePath, err)
   615  	}
   616  
   617  	crowdsecPatternsFolder := csconfig.DefaultConfigPath("patterns")
   618  
   619  	// copy template patterns folder to runtime folder
   620  	if err = CopyDir(crowdsecPatternsFolder, t.RuntimePatternsPath); err != nil {
   621  		return fmt.Errorf("unable to copy 'patterns' from '%s' to '%s': %s", crowdsecPatternsFolder, t.RuntimePatternsPath, err)
   622  	}
   623  
   624  	// create the appsec-configs dir
   625  	if err = os.MkdirAll(filepath.Join(t.RuntimePath, "appsec-configs"), os.ModePerm); err != nil {
   626  		return fmt.Errorf("unable to create folder '%s': %+v", t.RuntimePath, err)
   627  	}
   628  
   629  	//if it's an appsec rule test, we need acquis and appsec profile
   630  	if len(t.Config.AppsecRules) > 0 {
   631  		// copy template acquis file to runtime folder
   632  		log.Debugf("copying %s to %s", t.TemplateAcquisPath, t.RuntimeAcquisFilePath)
   633  
   634  		if err = Copy(t.TemplateAcquisPath, t.RuntimeAcquisFilePath); err != nil {
   635  			return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateAcquisPath, t.RuntimeAcquisFilePath, err)
   636  		}
   637  
   638  		log.Debugf("copying %s to %s", t.TemplateAppsecProfilePath, filepath.Join(t.RuntimePath, "appsec-configs", "config.yaml"))
   639  		// copy template appsec-config file to runtime folder
   640  		if err = Copy(t.TemplateAppsecProfilePath, filepath.Join(t.RuntimePath, "appsec-configs", "config.yaml")); err != nil {
   641  			return fmt.Errorf("unable to copy '%s' to '%s': %v", t.TemplateAppsecProfilePath, filepath.Join(t.RuntimePath, "appsec-configs", "config.yaml"), err)
   642  		}
   643  	} else { //otherwise we drop a blank acquis file
   644  		if err = os.WriteFile(t.RuntimeAcquisFilePath, []byte(""), os.ModePerm); err != nil {
   645  			return fmt.Errorf("unable to write blank acquis file '%s': %s", t.RuntimeAcquisFilePath, err)
   646  		}
   647  	}
   648  
   649  	// install the hub in the runtime folder
   650  	if err = t.InstallHub(); err != nil {
   651  		return fmt.Errorf("unable to install hub in '%s': %s", t.RuntimeHubPath, err)
   652  	}
   653  
   654  	if t.Config.LogFile != "" {
   655  		return t.RunWithLogFile()
   656  	} else if t.Config.NucleiTemplate != "" {
   657  		return t.RunWithNucleiTemplate()
   658  	} else {
   659  		return fmt.Errorf("log file or nuclei template must be set in '%s'", t.Name)
   660  	}
   661  }