From 17db4cb9703bcaac3a18ff1c2bbc43c57af9d6b6 Mon Sep 17 00:00:00 2001
From: mmetc <92726601+mmetc@users.noreply.github.com>
Date: Thu, 1 Feb 2024 17:22:52 +0100
Subject: [PATCH 001/581] refact "cscli machines" (#2777)
---
cmd/crowdsec-cli/bouncers.go | 5 +-
cmd/crowdsec-cli/flag.go | 28 +++
cmd/crowdsec-cli/items.go | 2 -
cmd/crowdsec-cli/machines.go | 420 ++++++++++++++++++-----------------
cmd/crowdsec-cli/main.go | 22 +-
cmd/crowdsec-cli/support.go | 5 +-
test/bats/30_machines.bats | 11 +-
7 files changed, 268 insertions(+), 225 deletions(-)
create mode 100644 cmd/crowdsec-cli/flag.go
diff --git a/cmd/crowdsec-cli/bouncers.go b/cmd/crowdsec-cli/bouncers.go
index 410827b3159..d2685901ebb 100644
--- a/cmd/crowdsec-cli/bouncers.go
+++ b/cmd/crowdsec-cli/bouncers.go
@@ -16,7 +16,6 @@ import (
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
- "github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/database"
"github.com/crowdsecurity/crowdsec/pkg/types"
)
@@ -38,10 +37,10 @@ func askYesNo(message string, defaultAnswer bool) (bool, error) {
type cliBouncers struct {
db *database.Client
- cfg func() *csconfig.Config
+ cfg configGetter
}
-func NewCLIBouncers(getconfig func() *csconfig.Config) *cliBouncers {
+func NewCLIBouncers(getconfig configGetter) *cliBouncers {
return &cliBouncers{
cfg: getconfig,
}
diff --git a/cmd/crowdsec-cli/flag.go b/cmd/crowdsec-cli/flag.go
new file mode 100644
index 00000000000..402302a1f64
--- /dev/null
+++ b/cmd/crowdsec-cli/flag.go
@@ -0,0 +1,28 @@
+package main
+
+// Custom types for flag validation and conversion.
+
+import (
+ "errors"
+)
+
+type MachinePassword string
+
+func (p *MachinePassword) String() string {
+ return string(*p)
+}
+
+func (p *MachinePassword) Set(v string) error {
+ // a password can't be more than 72 characters
+ // due to bcrypt limitations
+ if len(v) > 72 {
+ return errors.New("password too long (max 72 characters)")
+ }
+ *p = MachinePassword(v)
+
+ return nil
+}
+
+func (p *MachinePassword) Type() string {
+ return "string"
+}
diff --git a/cmd/crowdsec-cli/items.go b/cmd/crowdsec-cli/items.go
index a1d079747fa..851be553f15 100644
--- a/cmd/crowdsec-cli/items.go
+++ b/cmd/crowdsec-cli/items.go
@@ -138,8 +138,6 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item
}
csvwriter.Flush()
- default:
- return fmt.Errorf("unknown output format '%s'", csConfig.Cscli.Output)
}
return nil
diff --git a/cmd/crowdsec-cli/machines.go b/cmd/crowdsec-cli/machines.go
index 581683baa8f..0cabccf76f5 100644
--- a/cmd/crowdsec-cli/machines.go
+++ b/cmd/crowdsec-cli/machines.go
@@ -5,7 +5,6 @@ import (
"encoding/csv"
"encoding/json"
"fmt"
- "io"
"math/big"
"os"
"strings"
@@ -101,85 +100,97 @@ func getLastHeartbeat(m *ent.Machine) (string, bool) {
return hb, true
}
-func getAgents(out io.Writer, dbClient *database.Client) error {
- machines, err := dbClient.ListMachines()
+type cliMachines struct{
+ db *database.Client
+ cfg configGetter
+}
+
+func NewCLIMachines(getconfig configGetter) *cliMachines {
+ return &cliMachines{
+ cfg: getconfig,
+ }
+}
+
+func (cli *cliMachines) NewCommand() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "machines [action]",
+ Short: "Manage local API machines [requires local API]",
+ Long: `To list/add/delete/validate/prune machines.
+Note: This command requires database direct access, so is intended to be run on the local API machine.
+`,
+ Example: `cscli machines [action]`,
+ DisableAutoGenTag: true,
+ Aliases: []string{"machine"},
+ PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
+ var err error
+ if err = require.LAPI(cli.cfg()); err != nil {
+ return err
+ }
+ cli.db, err = database.NewClient(cli.cfg().DbConfig)
+ if err != nil {
+ return fmt.Errorf("unable to create new database client: %s", err)
+ }
+ return nil
+ },
+ }
+
+ cmd.AddCommand(cli.newListCmd())
+ cmd.AddCommand(cli.newAddCmd())
+ cmd.AddCommand(cli.newDeleteCmd())
+ cmd.AddCommand(cli.newValidateCmd())
+ cmd.AddCommand(cli.newPruneCmd())
+
+ return cmd
+}
+
+func (cli *cliMachines) list() error {
+ out := color.Output
+
+ machines, err := cli.db.ListMachines()
if err != nil {
return fmt.Errorf("unable to list machines: %s", err)
}
- switch csConfig.Cscli.Output {
+ switch cli.cfg().Cscli.Output {
case "human":
getAgentsTable(out, machines)
case "json":
enc := json.NewEncoder(out)
enc.SetIndent("", " ")
+
if err := enc.Encode(machines); err != nil {
return fmt.Errorf("failed to marshal")
}
+
return nil
case "raw":
csvwriter := csv.NewWriter(out)
+
err := csvwriter.Write([]string{"machine_id", "ip_address", "updated_at", "validated", "version", "auth_type", "last_heartbeat"})
if err != nil {
return fmt.Errorf("failed to write header: %s", err)
}
+
for _, m := range machines {
validated := "false"
if m.IsValidated {
validated = "true"
}
+
hb, _ := getLastHeartbeat(m)
- err := csvwriter.Write([]string{m.MachineId, m.IpAddress, m.UpdatedAt.Format(time.RFC3339), validated, m.Version, m.AuthType, hb})
- if err != nil {
+
+ if err := csvwriter.Write([]string{m.MachineId, m.IpAddress, m.UpdatedAt.Format(time.RFC3339), validated, m.Version, m.AuthType, hb}); err != nil {
return fmt.Errorf("failed to write raw output: %w", err)
}
}
- csvwriter.Flush()
- default:
- return fmt.Errorf("unknown output '%s'", csConfig.Cscli.Output)
- }
- return nil
-}
-
-type cliMachines struct{}
-
-func NewCLIMachines() *cliMachines {
- return &cliMachines{}
-}
-func (cli cliMachines) NewCommand() *cobra.Command {
- cmd := &cobra.Command{
- Use: "machines [action]",
- Short: "Manage local API machines [requires local API]",
- Long: `To list/add/delete/validate/prune machines.
-Note: This command requires database direct access, so is intended to be run on the local API machine.
-`,
- Example: `cscli machines [action]`,
- DisableAutoGenTag: true,
- Aliases: []string{"machine"},
- PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
- var err error
- if err = require.LAPI(csConfig); err != nil {
- return err
- }
- dbClient, err = database.NewClient(csConfig.DbConfig)
- if err != nil {
- return fmt.Errorf("unable to create new database client: %s", err)
- }
- return nil
- },
+ csvwriter.Flush()
}
- cmd.AddCommand(cli.NewListCmd())
- cmd.AddCommand(cli.NewAddCmd())
- cmd.AddCommand(cli.NewDeleteCmd())
- cmd.AddCommand(cli.NewValidateCmd())
- cmd.AddCommand(cli.NewPruneCmd())
-
- return cmd
+ return nil
}
-func (cli cliMachines) NewListCmd() *cobra.Command {
+func (cli *cliMachines) newListCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "list all machines in the database",
@@ -188,84 +199,60 @@ func (cli cliMachines) NewListCmd() *cobra.Command {
Args: cobra.NoArgs,
DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, _ []string) error {
- err := getAgents(color.Output, dbClient)
- if err != nil {
- return fmt.Errorf("unable to list machines: %s", err)
- }
-
- return nil
+ return cli.list()
},
}
return cmd
}
-func (cli cliMachines) NewAddCmd() *cobra.Command {
+func (cli *cliMachines) newAddCmd() *cobra.Command {
+ var (
+ password MachinePassword
+ dumpFile string
+ apiURL string
+ interactive bool
+ autoAdd bool
+ force bool
+ )
+
cmd := &cobra.Command{
Use: "add",
Short: "add a single machine to the database",
DisableAutoGenTag: true,
Long: `Register a new machine in the database. cscli should be on the same machine as LAPI.`,
- Example: `
-cscli machines add --auto
+ Example: `cscli machines add --auto
cscli machines add MyTestMachine --auto
cscli machines add MyTestMachine --password MyPassword
-`,
- RunE: cli.add,
+cscli machines add -f- --auto > /tmp/mycreds.yaml`,
+ RunE: func(_ *cobra.Command, args []string) error {
+ return cli.add(args, string(password), dumpFile, apiURL, interactive, autoAdd, force)
+ },
}
flags := cmd.Flags()
- flags.StringP("password", "p", "", "machine password to login to the API")
- flags.StringP("file", "f", "", "output file destination (defaults to "+csconfig.DefaultConfigPath("local_api_credentials.yaml")+")")
- flags.StringP("url", "u", "", "URL of the local API")
- flags.BoolP("interactive", "i", false, "interfactive mode to enter the password")
- flags.BoolP("auto", "a", false, "automatically generate password (and username if not provided)")
- flags.Bool("force", false, "will force add the machine if it already exist")
+ flags.VarP(&password, "password", "p", "machine password to login to the API")
+ flags.StringVarP(&dumpFile, "file", "f", "", "output file destination (defaults to "+csconfig.DefaultConfigPath("local_api_credentials.yaml")+")")
+ flags.StringVarP(&apiURL, "url", "u", "", "URL of the local API")
+ flags.BoolVarP(&interactive, "interactive", "i", false, "interfactive mode to enter the password")
+ flags.BoolVarP(&autoAdd, "auto", "a", false, "automatically generate password (and username if not provided)")
+ flags.BoolVar(&force, "force", false, "will force add the machine if it already exist")
return cmd
}
-func (cli cliMachines) add(cmd *cobra.Command, args []string) error {
- flags := cmd.Flags()
-
- machinePassword, err := flags.GetString("password")
- if err != nil {
- return err
- }
-
- dumpFile, err := flags.GetString("file")
- if err != nil {
- return err
- }
-
- apiURL, err := flags.GetString("url")
- if err != nil {
- return err
- }
-
- interactive, err := flags.GetBool("interactive")
- if err != nil {
- return err
- }
-
- autoAdd, err := flags.GetBool("auto")
- if err != nil {
- return err
- }
-
- force, err := flags.GetBool("force")
- if err != nil {
- return err
- }
-
- var machineID string
+func (cli *cliMachines) add(args []string, machinePassword string, dumpFile string, apiURL string, interactive bool, autoAdd bool, force bool) error {
+ var (
+ err error
+ machineID string
+ )
// create machineID if not specified by user
if len(args) == 0 {
if !autoAdd {
- printHelp(cmd)
- return nil
+ return fmt.Errorf("please specify a machine name to add, or use --auto")
}
+
machineID, err = generateID("")
if err != nil {
return fmt.Errorf("unable to generate machine id: %s", err)
@@ -274,15 +261,18 @@ func (cli cliMachines) add(cmd *cobra.Command, args []string) error {
machineID = args[0]
}
+ clientCfg := cli.cfg().API.Client
+ serverCfg := cli.cfg().API.Server
+
/*check if file already exists*/
- if dumpFile == "" && csConfig.API.Client != nil && csConfig.API.Client.CredentialsFilePath != "" {
- credFile := csConfig.API.Client.CredentialsFilePath
+ if dumpFile == "" && clientCfg != nil && clientCfg.CredentialsFilePath != "" {
+ credFile := clientCfg.CredentialsFilePath
// use the default only if the file does not exist
_, err = os.Stat(credFile)
switch {
case os.IsNotExist(err) || force:
- dumpFile = csConfig.API.Client.CredentialsFilePath
+ dumpFile = credFile
case err != nil:
return fmt.Errorf("unable to stat '%s': %s", credFile, err)
default:
@@ -302,49 +292,85 @@ func (cli cliMachines) add(cmd *cobra.Command, args []string) error {
machinePassword = generatePassword(passwordLength)
} else if machinePassword == "" && interactive {
qs := &survey.Password{
- Message: "Please provide a password for the machine",
+ Message: "Please provide a password for the machine:",
}
survey.AskOne(qs, &machinePassword)
}
+
password := strfmt.Password(machinePassword)
- _, err = dbClient.CreateMachine(&machineID, &password, "", true, force, types.PasswordAuthType)
+
+ _, err = cli.db.CreateMachine(&machineID, &password, "", true, force, types.PasswordAuthType)
if err != nil {
return fmt.Errorf("unable to create machine: %s", err)
}
- fmt.Printf("Machine '%s' successfully added to the local API.\n", machineID)
+
+ fmt.Fprintf(os.Stderr, "Machine '%s' successfully added to the local API.\n", machineID)
if apiURL == "" {
- if csConfig.API.Client != nil && csConfig.API.Client.Credentials != nil && csConfig.API.Client.Credentials.URL != "" {
- apiURL = csConfig.API.Client.Credentials.URL
- } else if csConfig.API.Server != nil && csConfig.API.Server.ListenURI != "" {
- apiURL = "http://" + csConfig.API.Server.ListenURI
+ if clientCfg != nil && clientCfg.Credentials != nil && clientCfg.Credentials.URL != "" {
+ apiURL = clientCfg.Credentials.URL
+ } else if serverCfg != nil && serverCfg.ListenURI != "" {
+ apiURL = "http://" + serverCfg.ListenURI
} else {
return fmt.Errorf("unable to dump an api URL. Please provide it in your configuration or with the -u parameter")
}
}
+
apiCfg := csconfig.ApiCredentialsCfg{
Login: machineID,
Password: password.String(),
URL: apiURL,
}
+
apiConfigDump, err := yaml.Marshal(apiCfg)
if err != nil {
return fmt.Errorf("unable to marshal api credentials: %s", err)
}
+
if dumpFile != "" && dumpFile != "-" {
err = os.WriteFile(dumpFile, apiConfigDump, 0o600)
if err != nil {
return fmt.Errorf("write api credentials in '%s' failed: %s", dumpFile, err)
}
- fmt.Printf("API credentials written to '%s'.\n", dumpFile)
+ fmt.Fprintf(os.Stderr, "API credentials written to '%s'.\n", dumpFile)
} else {
- fmt.Printf("%s\n", string(apiConfigDump))
+ fmt.Print(string(apiConfigDump))
}
return nil
}
-func (cli cliMachines) NewDeleteCmd() *cobra.Command {
+func (cli *cliMachines) deleteValid(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ machines, err := cli.db.ListMachines()
+ if err != nil {
+ cobra.CompError("unable to list machines " + err.Error())
+ }
+
+ ret := []string{}
+
+ for _, machine := range machines {
+ if strings.Contains(machine.MachineId, toComplete) && !slices.Contains(args, machine.MachineId) {
+ ret = append(ret, machine.MachineId)
+ }
+ }
+
+ return ret, cobra.ShellCompDirectiveNoFileComp
+}
+
+func (cli *cliMachines) delete(machines []string) error {
+ for _, machineID := range machines {
+ err := cli.db.DeleteWatcher(machineID)
+ if err != nil {
+ log.Errorf("unable to delete machine '%s': %s", machineID, err)
+ return nil
+ }
+ log.Infof("machine '%s' deleted successfully", machineID)
+ }
+
+ return nil
+}
+
+func (cli *cliMachines) newDeleteCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "delete [machine_name]...",
Short: "delete machine(s) by name",
@@ -352,40 +378,75 @@ func (cli cliMachines) NewDeleteCmd() *cobra.Command {
Args: cobra.MinimumNArgs(1),
Aliases: []string{"remove"},
DisableAutoGenTag: true,
- ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
- machines, err := dbClient.ListMachines()
- if err != nil {
- cobra.CompError("unable to list machines " + err.Error())
- }
- ret := make([]string, 0)
- for _, machine := range machines {
- if strings.Contains(machine.MachineId, toComplete) && !slices.Contains(args, machine.MachineId) {
- ret = append(ret, machine.MachineId)
- }
- }
- return ret, cobra.ShellCompDirectiveNoFileComp
+ ValidArgsFunction: cli.deleteValid,
+ RunE: func(_ *cobra.Command, args []string) error {
+ return cli.delete(args)
},
- RunE: cli.delete,
}
return cmd
}
-func (cli cliMachines) delete(_ *cobra.Command, args []string) error {
- for _, machineID := range args {
- err := dbClient.DeleteWatcher(machineID)
- if err != nil {
- log.Errorf("unable to delete machine '%s': %s", machineID, err)
+func (cli *cliMachines) prune(duration time.Duration, notValidOnly bool, force bool) error {
+ if duration < 2*time.Minute && !notValidOnly {
+ if yes, err := askYesNo(
+ "The duration you provided is less than 2 minutes. " +
+ "This can break installations if the machines are only temporarily disconnected. Continue?", false); err != nil {
+ return err
+ } else if !yes {
+ fmt.Println("User aborted prune. No changes were made.")
return nil
}
- log.Infof("machine '%s' deleted successfully", machineID)
}
+ machines := []*ent.Machine{}
+ if pending, err := cli.db.QueryPendingMachine(); err == nil {
+ machines = append(machines, pending...)
+ }
+
+ if !notValidOnly {
+ if pending, err := cli.db.QueryLastValidatedHeartbeatLT(time.Now().UTC().Add(duration)); err == nil {
+ machines = append(machines, pending...)
+ }
+ }
+
+ if len(machines) == 0 {
+ fmt.Println("no machines to prune")
+ return nil
+ }
+
+ getAgentsTable(color.Output, machines)
+
+ if !force {
+ if yes, err := askYesNo(
+ "You are about to PERMANENTLY remove the above machines from the database. " +
+ "These will NOT be recoverable. Continue?", false); err != nil {
+ return err
+ } else if !yes {
+ fmt.Println("User aborted prune. No changes were made.")
+ return nil
+ }
+ }
+
+ deleted, err := cli.db.BulkDeleteWatchers(machines)
+ if err != nil {
+ return fmt.Errorf("unable to prune machines: %s", err)
+ }
+
+ fmt.Fprintf(os.Stderr, "successfully delete %d machines\n", deleted)
+
return nil
}
-func (cli cliMachines) NewPruneCmd() *cobra.Command {
- var parsedDuration time.Duration
+func (cli *cliMachines) newPruneCmd() *cobra.Command {
+ var (
+ duration time.Duration
+ notValidOnly bool
+ force bool
+ )
+
+ const defaultDuration = 10 * time.Minute
+
cmd := &cobra.Command{
Use: "prune",
Short: "prune multiple machines from the database",
@@ -395,76 +456,29 @@ cscli machines prune --duration 1h
cscli machines prune --not-validated-only --force`,
Args: cobra.NoArgs,
DisableAutoGenTag: true,
- PreRunE: func(cmd *cobra.Command, _ []string) error {
- dur, _ := cmd.Flags().GetString("duration")
- var err error
- parsedDuration, err = time.ParseDuration(fmt.Sprintf("-%s", dur))
- if err != nil {
- return fmt.Errorf("unable to parse duration '%s': %s", dur, err)
- }
- return nil
- },
- RunE: func(cmd *cobra.Command, _ []string) error {
- notValidOnly, _ := cmd.Flags().GetBool("not-validated-only")
- force, _ := cmd.Flags().GetBool("force")
- if parsedDuration >= 0-60*time.Second && !notValidOnly {
- var answer bool
- prompt := &survey.Confirm{
- Message: "The duration you provided is less than or equal 60 seconds this can break installations do you want to continue ?",
- Default: false,
- }
- if err := survey.AskOne(prompt, &answer); err != nil {
- return fmt.Errorf("unable to ask about prune check: %s", err)
- }
- if !answer {
- fmt.Println("user aborted prune no changes were made")
- return nil
- }
- }
- machines := make([]*ent.Machine, 0)
- if pending, err := dbClient.QueryPendingMachine(); err == nil {
- machines = append(machines, pending...)
- }
- if !notValidOnly {
- if pending, err := dbClient.QueryLastValidatedHeartbeatLT(time.Now().UTC().Add(parsedDuration)); err == nil {
- machines = append(machines, pending...)
- }
- }
- if len(machines) == 0 {
- fmt.Println("no machines to prune")
- return nil
- }
- getAgentsTable(color.Output, machines)
- if !force {
- var answer bool
- prompt := &survey.Confirm{
- Message: "You are about to PERMANENTLY remove the above machines from the database these will NOT be recoverable, continue ?",
- Default: false,
- }
- if err := survey.AskOne(prompt, &answer); err != nil {
- return fmt.Errorf("unable to ask about prune check: %s", err)
- }
- if !answer {
- fmt.Println("user aborted prune no changes were made")
- return nil
- }
- }
- nbDeleted, err := dbClient.BulkDeleteWatchers(machines)
- if err != nil {
- return fmt.Errorf("unable to prune machines: %s", err)
- }
- fmt.Printf("successfully delete %d machines\n", nbDeleted)
- return nil
+ RunE: func(_ *cobra.Command, _ []string) error {
+ return cli.prune(duration, notValidOnly, force)
},
}
- cmd.Flags().StringP("duration", "d", "10m", "duration of time since validated machine last heartbeat")
- cmd.Flags().Bool("not-validated-only", false, "only prune machines that are not validated")
- cmd.Flags().Bool("force", false, "force prune without asking for confirmation")
+
+ flags := cmd.Flags()
+ flags.DurationVarP(&duration, "duration", "d", defaultDuration, "duration of time since validated machine last heartbeat")
+ flags.BoolVar(¬ValidOnly, "not-validated-only", false, "only prune machines that are not validated")
+ flags.BoolVar(&force, "force", false, "force prune without asking for confirmation")
return cmd
}
-func (cli cliMachines) NewValidateCmd() *cobra.Command {
+func (cli *cliMachines) validate(machineID string) error {
+ if err := cli.db.ValidateMachine(machineID); err != nil {
+ return fmt.Errorf("unable to validate machine '%s': %s", machineID, err)
+ }
+ log.Infof("machine '%s' validated successfully", machineID)
+
+ return nil
+}
+
+func (cli *cliMachines) newValidateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "validate",
Short: "validate a machine to access the local API",
@@ -472,14 +486,8 @@ func (cli cliMachines) NewValidateCmd() *cobra.Command {
Example: `cscli machines validate "machine_name"`,
Args: cobra.ExactArgs(1),
DisableAutoGenTag: true,
- RunE: func(_ *cobra.Command, args []string) error {
- machineID := args[0]
- if err := dbClient.ValidateMachine(machineID); err != nil {
- return fmt.Errorf("unable to validate machine '%s': %s", machineID, err)
- }
- log.Infof("machine '%s' validated successfully", machineID)
-
- return nil
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return cli.validate(args[0])
},
}
diff --git a/cmd/crowdsec-cli/main.go b/cmd/crowdsec-cli/main.go
index fda4cddc2bc..91e31a9778c 100644
--- a/cmd/crowdsec-cli/main.go
+++ b/cmd/crowdsec-cli/main.go
@@ -21,7 +21,7 @@ var ConfigFilePath string
var csConfig *csconfig.Config
var dbClient *database.Client
-var OutputFormat string
+var outputFormat string
var OutputColor string
var mergedConfig string
@@ -29,6 +29,8 @@ var mergedConfig string
// flagBranch overrides the value in csConfig.Cscli.HubBranch
var flagBranch = ""
+type configGetter func() *csconfig.Config
+
func initConfig() {
var err error
@@ -64,16 +66,18 @@ func initConfig() {
csConfig.Cscli.HubBranch = flagBranch
}
- if OutputFormat != "" {
- csConfig.Cscli.Output = OutputFormat
-
- if OutputFormat != "json" && OutputFormat != "raw" && OutputFormat != "human" {
- log.Fatalf("output format %s unknown", OutputFormat)
- }
+ if outputFormat != "" {
+ csConfig.Cscli.Output = outputFormat
}
+
if csConfig.Cscli.Output == "" {
csConfig.Cscli.Output = "human"
}
+
+ if csConfig.Cscli.Output != "human" && csConfig.Cscli.Output != "json" && csConfig.Cscli.Output != "raw" {
+ log.Fatalf("output format '%s' not supported: must be one of human, json, raw", csConfig.Cscli.Output)
+ }
+
if csConfig.Cscli.Output == "json" {
log.SetFormatter(&log.JSONFormatter{})
log.SetLevel(log.ErrorLevel)
@@ -146,7 +150,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
cmd.SetOut(color.Output)
cmd.PersistentFlags().StringVarP(&ConfigFilePath, "config", "c", csconfig.DefaultConfigPath("config.yaml"), "path to crowdsec config file")
- cmd.PersistentFlags().StringVarP(&OutputFormat, "output", "o", "", "Output format: human, json, raw")
+ cmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "", "Output format: human, json, raw")
cmd.PersistentFlags().StringVarP(&OutputColor, "color", "", "auto", "Output color: yes, no, auto")
cmd.PersistentFlags().BoolVar(&dbg_lvl, "debug", false, "Set logging to debug")
cmd.PersistentFlags().BoolVar(&nfo_lvl, "info", false, "Set logging to info")
@@ -197,7 +201,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
cmd.AddCommand(NewCLIAlerts().NewCommand())
cmd.AddCommand(NewCLISimulation().NewCommand())
cmd.AddCommand(NewCLIBouncers(getconfig).NewCommand())
- cmd.AddCommand(NewCLIMachines().NewCommand())
+ cmd.AddCommand(NewCLIMachines(getconfig).NewCommand())
cmd.AddCommand(NewCLICapi().NewCommand())
cmd.AddCommand(NewLapiCmd())
cmd.AddCommand(NewCompletionCmd())
diff --git a/cmd/crowdsec-cli/support.go b/cmd/crowdsec-cli/support.go
index 47768e7c2ee..ed7f7cf2ffd 100644
--- a/cmd/crowdsec-cli/support.go
+++ b/cmd/crowdsec-cli/support.go
@@ -159,10 +159,11 @@ func collectBouncers(dbClient *database.Client) ([]byte, error) {
func collectAgents(dbClient *database.Client) ([]byte, error) {
out := bytes.NewBuffer(nil)
- err := getAgents(out, dbClient)
+ machines, err := dbClient.ListMachines()
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("unable to list machines: %s", err)
}
+ getAgentsTable(out, machines)
return out.Bytes(), nil
}
diff --git a/test/bats/30_machines.bats b/test/bats/30_machines.bats
index c7a72c334b1..f32c376e5b0 100644
--- a/test/bats/30_machines.bats
+++ b/test/bats/30_machines.bats
@@ -34,13 +34,18 @@ teardown() {
rune -0 jq -r '.msg' <(stderr)
assert_output --partial 'already exists: please remove it, use "--force" or specify a different file with "-f"'
rune -0 cscli machines add local -a --force
- assert_output --partial "Machine 'local' successfully added to the local API."
+ assert_stderr --partial "Machine 'local' successfully added to the local API."
+}
+
+@test "passwords have a size limit" {
+ rune -1 cscli machines add local --password "$(printf '%73s' '' | tr ' ' x)"
+ assert_stderr --partial "password too long (max 72 characters)"
}
@test "add a new machine and delete it" {
rune -0 cscli machines add -a -f /dev/null CiTestMachine -o human
- assert_output --partial "Machine 'CiTestMachine' successfully added to the local API"
- assert_output --partial "API credentials written to '/dev/null'"
+ assert_stderr --partial "Machine 'CiTestMachine' successfully added to the local API"
+ assert_stderr --partial "API credentials written to '/dev/null'"
# we now have two machines
rune -0 cscli machines list -o json
From 785fce4dc752581613375b9edc23ee87d51cd98e Mon Sep 17 00:00:00 2001
From: mmetc <92726601+mmetc@users.noreply.github.com>
Date: Thu, 1 Feb 2024 17:24:00 +0100
Subject: [PATCH 002/581] refact "cscli alerts" (#2778)
---
cmd/crowdsec-cli/alerts.go | 161 ++++++++++++++-----------------------
1 file changed, 59 insertions(+), 102 deletions(-)
diff --git a/cmd/crowdsec-cli/alerts.go b/cmd/crowdsec-cli/alerts.go
index 15824d2d067..4ab71be5bbf 100644
--- a/cmd/crowdsec-cli/alerts.go
+++ b/cmd/crowdsec-cli/alerts.go
@@ -11,7 +11,6 @@ import (
"strconv"
"strings"
"text/template"
- "time"
"github.com/fatih/color"
"github.com/go-openapi/strfmt"
@@ -48,52 +47,9 @@ func DecisionsFromAlert(alert *models.Alert) string {
return ret
}
-func DateFromAlert(alert *models.Alert) string {
- ts, err := time.Parse(time.RFC3339, alert.CreatedAt)
- if err != nil {
- log.Infof("while parsing %s with %s : %s", alert.CreatedAt, time.RFC3339, err)
- return alert.CreatedAt
- }
- return ts.Format(time.RFC822)
-}
-
-func SourceFromAlert(alert *models.Alert) string {
-
- //more than one item, just number and scope
- if len(alert.Decisions) > 1 {
- return fmt.Sprintf("%d %ss (%s)", len(alert.Decisions), *alert.Decisions[0].Scope, *alert.Decisions[0].Origin)
- }
-
- //fallback on single decision information
- if len(alert.Decisions) == 1 {
- return fmt.Sprintf("%s:%s", *alert.Decisions[0].Scope, *alert.Decisions[0].Value)
- }
-
- //try to compose a human friendly version
- if *alert.Source.Value != "" && *alert.Source.Scope != "" {
- scope := fmt.Sprintf("%s:%s", *alert.Source.Scope, *alert.Source.Value)
- extra := ""
- if alert.Source.Cn != "" {
- extra = alert.Source.Cn
- }
- if alert.Source.AsNumber != "" {
- extra += fmt.Sprintf("/%s", alert.Source.AsNumber)
- }
- if alert.Source.AsName != "" {
- extra += fmt.Sprintf("/%s", alert.Source.AsName)
- }
-
- if extra != "" {
- scope += " (" + extra + ")"
- }
- return scope
- }
- return ""
-}
-
-func AlertsToTable(alerts *models.GetAlertsResponse, printMachine bool) error {
-
- if csConfig.Cscli.Output == "raw" {
+func alertsToTable(alerts *models.GetAlertsResponse, printMachine bool) error {
+ switch csConfig.Cscli.Output {
+ case "raw":
csvwriter := csv.NewWriter(os.Stdout)
header := []string{"id", "scope", "value", "reason", "country", "as", "decisions", "created_at"}
if printMachine {
@@ -123,7 +79,7 @@ func AlertsToTable(alerts *models.GetAlertsResponse, printMachine bool) error {
}
}
csvwriter.Flush()
- } else if csConfig.Cscli.Output == "json" {
+ case "json":
if *alerts == nil {
// avoid returning "null" in json
// could be cleaner if we used slice of alerts directly
@@ -131,8 +87,8 @@ func AlertsToTable(alerts *models.GetAlertsResponse, printMachine bool) error {
return nil
}
x, _ := json.MarshalIndent(alerts, "", " ")
- fmt.Printf("%s", string(x))
- } else if csConfig.Cscli.Output == "human" {
+ fmt.Print(string(x))
+ case "human":
if len(*alerts) == 0 {
fmt.Println("No active alerts")
return nil
@@ -160,59 +116,60 @@ var alertTemplate = `
`
-func DisplayOneAlert(alert *models.Alert, withDetail bool) error {
- if csConfig.Cscli.Output == "human" {
- tmpl, err := template.New("alert").Parse(alertTemplate)
- if err != nil {
- return err
- }
- err = tmpl.Execute(os.Stdout, alert)
- if err != nil {
- return err
- }
-
- alertDecisionsTable(color.Output, alert)
+func displayOneAlert(alert *models.Alert, withDetail bool) error {
+ tmpl, err := template.New("alert").Parse(alertTemplate)
+ if err != nil {
+ return err
+ }
+ err = tmpl.Execute(os.Stdout, alert)
+ if err != nil {
+ return err
+ }
- if len(alert.Meta) > 0 {
- fmt.Printf("\n - Context :\n")
- sort.Slice(alert.Meta, func(i, j int) bool {
- return alert.Meta[i].Key < alert.Meta[j].Key
- })
- table := newTable(color.Output)
- table.SetRowLines(false)
- table.SetHeaders("Key", "Value")
- for _, meta := range alert.Meta {
- var valSlice []string
- if err := json.Unmarshal([]byte(meta.Value), &valSlice); err != nil {
- return fmt.Errorf("unknown context value type '%s' : %s", meta.Value, err)
- }
- for _, value := range valSlice {
- table.AddRow(
- meta.Key,
- value,
- )
- }
+ alertDecisionsTable(color.Output, alert)
+
+ if len(alert.Meta) > 0 {
+ fmt.Printf("\n - Context :\n")
+ sort.Slice(alert.Meta, func(i, j int) bool {
+ return alert.Meta[i].Key < alert.Meta[j].Key
+ })
+ table := newTable(color.Output)
+ table.SetRowLines(false)
+ table.SetHeaders("Key", "Value")
+ for _, meta := range alert.Meta {
+ var valSlice []string
+ if err := json.Unmarshal([]byte(meta.Value), &valSlice); err != nil {
+ return fmt.Errorf("unknown context value type '%s' : %s", meta.Value, err)
+ }
+ for _, value := range valSlice {
+ table.AddRow(
+ meta.Key,
+ value,
+ )
}
- table.Render()
}
+ table.Render()
+ }
- if withDetail {
- fmt.Printf("\n - Events :\n")
- for _, event := range alert.Events {
- alertEventTable(color.Output, event)
- }
+ if withDetail {
+ fmt.Printf("\n - Events :\n")
+ for _, event := range alert.Events {
+ alertEventTable(color.Output, event)
}
}
+
return nil
}
-type cliAlerts struct{}
+type cliAlerts struct{
+ client *apiclient.ApiClient
+}
func NewCLIAlerts() *cliAlerts {
return &cliAlerts{}
}
-func (cli cliAlerts) NewCommand() *cobra.Command {
+func (cli *cliAlerts) NewCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "alerts [action]",
Short: "Manage alerts",
@@ -228,7 +185,7 @@ func (cli cliAlerts) NewCommand() *cobra.Command {
if err != nil {
return fmt.Errorf("parsing api url %s: %w", apiURL, err)
}
- Client, err = apiclient.NewClient(&apiclient.Config{
+ cli.client, err = apiclient.NewClient(&apiclient.Config{
MachineID: csConfig.API.Client.Credentials.Login,
Password: strfmt.Password(csConfig.API.Client.Credentials.Password),
UserAgent: fmt.Sprintf("crowdsec/%s", version.String()),
@@ -251,7 +208,7 @@ func (cli cliAlerts) NewCommand() *cobra.Command {
return cmd
}
-func (cli cliAlerts) NewListCmd() *cobra.Command {
+func (cli *cliAlerts) NewListCmd() *cobra.Command {
var alertListFilter = apiclient.AlertsListOpts{
ScopeEquals: new(string),
ValueEquals: new(string),
@@ -345,12 +302,12 @@ cscli alerts list --type ban`,
alertListFilter.Contains = new(bool)
}
- alerts, _, err := Client.Alerts.List(context.Background(), alertListFilter)
+ alerts, _, err := cli.client.Alerts.List(context.Background(), alertListFilter)
if err != nil {
return fmt.Errorf("unable to list alerts: %v", err)
}
- err = AlertsToTable(alerts, printMachine)
+ err = alertsToTable(alerts, printMachine)
if err != nil {
return fmt.Errorf("unable to list alerts: %v", err)
}
@@ -376,7 +333,7 @@ cscli alerts list --type ban`,
return cmd
}
-func (cli cliAlerts) NewDeleteCmd() *cobra.Command {
+func (cli *cliAlerts) NewDeleteCmd() *cobra.Command {
var ActiveDecision *bool
var AlertDeleteAll bool
var delAlertByID string
@@ -451,12 +408,12 @@ cscli alerts delete -s crowdsecurity/ssh-bf"`,
var alerts *models.DeleteAlertsResponse
if delAlertByID == "" {
- alerts, _, err = Client.Alerts.Delete(context.Background(), alertDeleteFilter)
+ alerts, _, err = cli.client.Alerts.Delete(context.Background(), alertDeleteFilter)
if err != nil {
return fmt.Errorf("unable to delete alerts : %v", err)
}
} else {
- alerts, _, err = Client.Alerts.DeleteOne(context.Background(), delAlertByID)
+ alerts, _, err = cli.client.Alerts.DeleteOne(context.Background(), delAlertByID)
if err != nil {
return fmt.Errorf("unable to delete alert: %v", err)
}
@@ -478,7 +435,7 @@ cscli alerts delete -s crowdsecurity/ssh-bf"`,
return cmd
}
-func (cli cliAlerts) NewInspectCmd() *cobra.Command {
+func (cli *cliAlerts) NewInspectCmd() *cobra.Command {
var details bool
cmd := &cobra.Command{
Use: `inspect "alert_id"`,
@@ -495,13 +452,13 @@ func (cli cliAlerts) NewInspectCmd() *cobra.Command {
if err != nil {
return fmt.Errorf("bad alert id %s", alertID)
}
- alert, _, err := Client.Alerts.GetByID(context.Background(), id)
+ alert, _, err := cli.client.Alerts.GetByID(context.Background(), id)
if err != nil {
return fmt.Errorf("can't find alert with id %s: %s", alertID, err)
}
switch csConfig.Cscli.Output {
case "human":
- if err := DisplayOneAlert(alert, details); err != nil {
+ if err := displayOneAlert(alert, details); err != nil {
continue
}
case "json":
@@ -528,7 +485,7 @@ func (cli cliAlerts) NewInspectCmd() *cobra.Command {
return cmd
}
-func (cli cliAlerts) NewFlushCmd() *cobra.Command {
+func (cli *cliAlerts) NewFlushCmd() *cobra.Command {
var maxItems int
var maxAge string
cmd := &cobra.Command{
@@ -542,12 +499,12 @@ func (cli cliAlerts) NewFlushCmd() *cobra.Command {
if err := require.LAPI(csConfig); err != nil {
return err
}
- dbClient, err = database.NewClient(csConfig.DbConfig)
+ db, err := database.NewClient(csConfig.DbConfig)
if err != nil {
return fmt.Errorf("unable to create new database client: %s", err)
}
log.Info("Flushing alerts. !! This may take a long time !!")
- err = dbClient.FlushAlerts(maxAge, maxItems)
+ err = db.FlushAlerts(maxAge, maxItems)
if err != nil {
return fmt.Errorf("unable to flush alerts: %s", err)
}
From e6f5d157b8a84ff68393a1446258eea093fc99ad Mon Sep 17 00:00:00 2001
From: mmetc <92726601+mmetc@users.noreply.github.com>
Date: Thu, 1 Feb 2024 17:25:29 +0100
Subject: [PATCH 003/581] refact "cscli hub" (#2800)
---
cmd/crowdsec-cli/hub.go | 84 ++++++++++++++++++++--------------------
cmd/crowdsec-cli/main.go | 2 +-
2 files changed, 44 insertions(+), 42 deletions(-)
diff --git a/cmd/crowdsec-cli/hub.go b/cmd/crowdsec-cli/hub.go
index 3a2913f0513..d3ce380bb6f 100644
--- a/cmd/crowdsec-cli/hub.go
+++ b/cmd/crowdsec-cli/hub.go
@@ -13,13 +13,17 @@ import (
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
)
-type cliHub struct{}
+type cliHub struct{
+ cfg configGetter
+}
-func NewCLIHub() *cliHub {
- return &cliHub{}
+func NewCLIHub(getconfig configGetter) *cliHub {
+ return &cliHub{
+ cfg: getconfig,
+ }
}
-func (cli cliHub) NewCommand() *cobra.Command {
+func (cli *cliHub) NewCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "hub [action]",
Short: "Manage hub index",
@@ -34,23 +38,16 @@ cscli hub upgrade`,
DisableAutoGenTag: true,
}
- cmd.AddCommand(cli.NewListCmd())
- cmd.AddCommand(cli.NewUpdateCmd())
- cmd.AddCommand(cli.NewUpgradeCmd())
- cmd.AddCommand(cli.NewTypesCmd())
+ cmd.AddCommand(cli.newListCmd())
+ cmd.AddCommand(cli.newUpdateCmd())
+ cmd.AddCommand(cli.newUpgradeCmd())
+ cmd.AddCommand(cli.newTypesCmd())
return cmd
}
-func (cli cliHub) list(cmd *cobra.Command, args []string) error {
- flags := cmd.Flags()
-
- all, err := flags.GetBool("all")
- if err != nil {
- return err
- }
-
- hub, err := require.Hub(csConfig, nil, log.StandardLogger())
+func (cli *cliHub) list(all bool) error {
+ hub, err := require.Hub(cli.cfg(), nil, log.StandardLogger())
if err != nil {
return err
}
@@ -80,24 +77,28 @@ func (cli cliHub) list(cmd *cobra.Command, args []string) error {
return nil
}
-func (cli cliHub) NewListCmd() *cobra.Command {
+func (cli *cliHub) newListCmd() *cobra.Command {
+ var all bool
+
cmd := &cobra.Command{
Use: "list [-a]",
Short: "List all installed configurations",
Args: cobra.ExactArgs(0),
DisableAutoGenTag: true,
- RunE: cli.list,
+ RunE: func(_ *cobra.Command, _ []string) error {
+ return cli.list(all)
+ },
}
flags := cmd.Flags()
- flags.BoolP("all", "a", false, "List disabled items as well")
+ flags.BoolVarP(&all, "all", "a", false, "List disabled items as well")
return cmd
}
-func (cli cliHub) update(cmd *cobra.Command, args []string) error {
- local := csConfig.Hub
- remote := require.RemoteHub(csConfig)
+func (cli *cliHub) update() error {
+ local := cli.cfg().Hub
+ remote := require.RemoteHub(cli.cfg())
// don't use require.Hub because if there is no index file, it would fail
hub, err := cwhub.NewHub(local, remote, true, log.StandardLogger())
@@ -112,7 +113,7 @@ func (cli cliHub) update(cmd *cobra.Command, args []string) error {
return nil
}
-func (cli cliHub) NewUpdateCmd() *cobra.Command {
+func (cli *cliHub) newUpdateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "update",
Short: "Download the latest index (catalog of available configurations)",
@@ -121,21 +122,16 @@ Fetches the .index.json file from the hub, containing the list of available conf
`,
Args: cobra.ExactArgs(0),
DisableAutoGenTag: true,
- RunE: cli.update,
+ RunE: func(_ *cobra.Command, _ []string) error {
+ return cli.update()
+ },
}
return cmd
}
-func (cli cliHub) upgrade(cmd *cobra.Command, args []string) error {
- flags := cmd.Flags()
-
- force, err := flags.GetBool("force")
- if err != nil {
- return err
- }
-
- hub, err := require.Hub(csConfig, require.RemoteHub(csConfig), log.StandardLogger())
+func (cli *cliHub) upgrade(force bool) error {
+ hub, err := require.Hub(cli.cfg(), require.RemoteHub(cli.cfg()), log.StandardLogger())
if err != nil {
return err
}
@@ -167,7 +163,9 @@ func (cli cliHub) upgrade(cmd *cobra.Command, args []string) error {
return nil
}
-func (cli cliHub) NewUpgradeCmd() *cobra.Command {
+func (cli *cliHub) newUpgradeCmd() *cobra.Command {
+ var force bool
+
cmd := &cobra.Command{
Use: "upgrade",
Short: "Upgrade all configurations to their latest version",
@@ -176,17 +174,19 @@ Upgrade all configs installed from Crowdsec Hub. Run 'sudo cscli hub update' if
`,
Args: cobra.ExactArgs(0),
DisableAutoGenTag: true,
- RunE: cli.upgrade,
+ RunE: func(_ *cobra.Command, _ []string) error {
+ return cli.upgrade(force)
+ },
}
flags := cmd.Flags()
- flags.Bool("force", false, "Force upgrade: overwrite tainted and outdated files")
+ flags.BoolVar(&force, "force", false, "Force upgrade: overwrite tainted and outdated files")
return cmd
}
-func (cli cliHub) types(cmd *cobra.Command, args []string) error {
- switch csConfig.Cscli.Output {
+func (cli *cliHub) types() error {
+ switch cli.cfg().Cscli.Output {
case "human":
s, err := yaml.Marshal(cwhub.ItemTypes)
if err != nil {
@@ -210,7 +210,7 @@ func (cli cliHub) types(cmd *cobra.Command, args []string) error {
return nil
}
-func (cli cliHub) NewTypesCmd() *cobra.Command {
+func (cli *cliHub) newTypesCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "types",
Short: "List supported item types",
@@ -219,7 +219,9 @@ List the types of supported hub items.
`,
Args: cobra.ExactArgs(0),
DisableAutoGenTag: true,
- RunE: cli.types,
+ RunE: func(_ *cobra.Command, _ []string) error {
+ return cli.types()
+ },
}
return cmd
diff --git a/cmd/crowdsec-cli/main.go b/cmd/crowdsec-cli/main.go
index 91e31a9778c..acbced2e5a3 100644
--- a/cmd/crowdsec-cli/main.go
+++ b/cmd/crowdsec-cli/main.go
@@ -194,7 +194,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
cmd.AddCommand(NewCLIDoc().NewCommand(cmd))
cmd.AddCommand(NewCLIVersion().NewCommand())
cmd.AddCommand(NewConfigCmd())
- cmd.AddCommand(NewCLIHub().NewCommand())
+ cmd.AddCommand(NewCLIHub(getconfig).NewCommand())
cmd.AddCommand(NewMetricsCmd())
cmd.AddCommand(NewCLIDashboard().NewCommand())
cmd.AddCommand(NewCLIDecisions().NewCommand())
From af14f1085f1d31c75364e040a9d317cff22a093d Mon Sep 17 00:00:00 2001
From: mmetc <92726601+mmetc@users.noreply.github.com>
Date: Thu, 1 Feb 2024 17:26:06 +0100
Subject: [PATCH 004/581] refact "cscli 1.2.3.4 will get ban for next 30s for triggering manual 'ban' from 'githubciXXXXXXXXXXXXXXXXXXXXXXXX' on machine githubciXXXXXXXXXXXXXXXXXXXXXXXX. 1.2.3.5 will get ban for next 30s for triggering manual 'ban' from 'githubciXXXXXXXXXXXXXXXXXXXXXXXX' on machine githubciXXXXXXXXXXXXXXXXXXXXXXXX.
+
From 72b6da99258afb781430642d0c72c294c6c9888c Mon Sep 17 00:00:00 2001
From: mmetc <92726601+mmetc@users.noreply.github.com>
Date: Fri, 7 Jun 2024 16:53:23 +0200
Subject: [PATCH 172/581] enable linter: revive (early-return) (#3051)
* enable linter: revive (early-return)
* lint
---
.golangci.yml | 4 +---
pkg/acquisition/modules/s3/s3.go | 22 +++++++++----------
pkg/apiserver/papi.go | 17 ++++++++-------
pkg/appsec/appsec_rule/modsecurity.go | 31 +++++++++++++--------------
pkg/appsec/coraza_logger.go | 11 +++++-----
pkg/csplugin/hclog_adapter.go | 5 ++---
pkg/exprhelpers/crowdsec_cti.go | 7 +++---
pkg/parser/parsing_test.go | 27 +++++++++++------------
8 files changed, 58 insertions(+), 66 deletions(-)
diff --git a/.golangci.yml b/.golangci.yml
index c096ef5f88a..1ec386183e1 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -102,7 +102,6 @@ linters-settings:
- "!**/pkg/csplugin/broker.go"
- "!**/pkg/leakybucket/buckets_test.go"
- "!**/pkg/leakybucket/manager_load.go"
- - "!**/pkg/metabase/metabase.go"
- "!**/pkg/parser/node.go"
- "!**/pkg/parser/node_test.go"
- "!**/pkg/parser/parsing_test.go"
@@ -139,8 +138,6 @@ linters-settings:
disabled: true
- name: defer
disabled: true
- - name: early-return
- disabled: true
- name: empty-block
disabled: true
- name: empty-lines
@@ -382,6 +379,7 @@ issues:
exclude-dirs:
- pkg/time/rate
+ - pkg/metabase
exclude-files:
- pkg/yamlpatch/merge.go
diff --git a/pkg/acquisition/modules/s3/s3.go b/pkg/acquisition/modules/s3/s3.go
index 98b2e685cd0..d1bf881079a 100644
--- a/pkg/acquisition/modules/s3/s3.go
+++ b/pkg/acquisition/modules/s3/s3.go
@@ -131,7 +131,6 @@ func (s *S3Source) newS3Client() error {
}
sess, err := session.NewSessionWithOptions(options)
-
if err != nil {
return fmt.Errorf("failed to create aws session: %w", err)
}
@@ -146,7 +145,7 @@ func (s *S3Source) newS3Client() error {
s.s3Client = s3.New(sess, config)
if s.s3Client == nil {
- return fmt.Errorf("failed to create S3 client")
+ return errors.New("failed to create S3 client")
}
return nil
@@ -167,7 +166,7 @@ func (s *S3Source) newSQSClient() error {
}
if sess == nil {
- return fmt.Errorf("failed to create aws session")
+ return errors.New("failed to create aws session")
}
config := aws.NewConfig()
if s.Config.AwsRegion != "" {
@@ -178,7 +177,7 @@ func (s *S3Source) newSQSClient() error {
}
s.sqsClient = sqs.New(sess, config)
if s.sqsClient == nil {
- return fmt.Errorf("failed to create SQS client")
+ return errors.New("failed to create SQS client")
}
return nil
}
@@ -251,16 +250,15 @@ func (s *S3Source) listPoll() error {
continue
}
for i := len(bucketObjects) - 1; i >= 0; i-- {
- if bucketObjects[i].LastModified.After(lastObjectDate) {
- newObject = true
- logger.Debugf("Found new object %s", *bucketObjects[i].Key)
- s.readerChan <- S3Object{
- Bucket: s.Config.BucketName,
- Key: *bucketObjects[i].Key,
- }
- } else {
+ if !bucketObjects[i].LastModified.After(lastObjectDate) {
break
}
+ newObject = true
+ logger.Debugf("Found new object %s", *bucketObjects[i].Key)
+ s.readerChan <- S3Object{
+ Bucket: s.Config.BucketName,
+ Key: *bucketObjects[i].Key,
+ }
}
if newObject {
lastObjectDate = *bucketObjects[len(bucketObjects)-1].LastModified
diff --git a/pkg/apiserver/papi.go b/pkg/apiserver/papi.go
index 169f1441df4..0d0fd0ecd42 100644
--- a/pkg/apiserver/papi.go
+++ b/pkg/apiserver/papi.go
@@ -141,17 +141,18 @@ func (p *Papi) handleEvent(event longpollclient.Event, sync bool) error {
return errors.New("no source user in header message, skipping")
}
- if operationFunc, ok := operationMap[message.Header.OperationType]; ok {
- logger.Debugf("Calling operation '%s'", message.Header.OperationType)
-
- err := operationFunc(message, p, sync)
- if err != nil {
- return fmt.Errorf("'%s %s failed: %w", message.Header.OperationType, message.Header.OperationCmd, err)
- }
- } else {
+ operationFunc, ok := operationMap[message.Header.OperationType]
+ if !ok {
return fmt.Errorf("operation '%s' unknown, continue", message.Header.OperationType)
}
+ logger.Debugf("Calling operation '%s'", message.Header.OperationType)
+
+ err := operationFunc(message, p, sync)
+ if err != nil {
+ return fmt.Errorf("'%s %s failed: %w", message.Header.OperationType, message.Header.OperationCmd, err)
+ }
+
return nil
}
diff --git a/pkg/appsec/appsec_rule/modsecurity.go b/pkg/appsec/appsec_rule/modsecurity.go
index 03a840cf436..8f58a9589ca 100644
--- a/pkg/appsec/appsec_rule/modsecurity.go
+++ b/pkg/appsec/appsec_rule/modsecurity.go
@@ -1,6 +1,7 @@
package appsec_rule
import (
+ "errors"
"fmt"
"hash/fnv"
"strings"
@@ -67,9 +68,7 @@ var bodyTypeMatch map[string]string = map[string]string{
}
func (m *ModsecurityRule) Build(rule *CustomRule, appsecRuleName string) (string, []uint32, error) {
-
rules, err := m.buildRules(rule, appsecRuleName, false, 0, 0)
-
if err != nil {
return "", nil, err
}
@@ -99,7 +98,7 @@ func (m *ModsecurityRule) buildRules(rule *CustomRule, appsecRuleName string, an
ret := make([]string, 0)
if len(rule.And) != 0 && len(rule.Or) != 0 {
- return nil, fmt.Errorf("cannot have both 'and' and 'or' in the same rule")
+ return nil, errors.New("cannot have both 'and' and 'or' in the same rule")
}
if rule.And != nil {
@@ -166,15 +165,15 @@ func (m *ModsecurityRule) buildRules(rule *CustomRule, appsecRuleName string, an
r.WriteByte(' ')
if rule.Match.Type != "" {
- if match, ok := matchMap[rule.Match.Type]; ok {
- prefix := ""
- if rule.Match.Not {
- prefix = "!"
- }
- r.WriteString(fmt.Sprintf(`"%s%s %s"`, prefix, match, rule.Match.Value))
- } else {
+ match, ok := matchMap[rule.Match.Type]
+ if !ok {
return nil, fmt.Errorf("unknown match type '%s'", rule.Match.Type)
}
+ prefix := ""
+ if rule.Match.Not {
+ prefix = "!"
+ }
+ r.WriteString(fmt.Sprintf(`"%s%s %s"`, prefix, match, rule.Match.Value))
}
//Should phase:2 be configurable?
@@ -186,20 +185,20 @@ func (m *ModsecurityRule) buildRules(rule *CustomRule, appsecRuleName string, an
continue
}
r.WriteByte(',')
- if mappedTransform, ok := transformMap[transform]; ok {
- r.WriteString(mappedTransform)
- } else {
+ mappedTransform, ok := transformMap[transform]
+ if !ok {
return nil, fmt.Errorf("unknown transform '%s'", transform)
}
+ r.WriteString(mappedTransform)
}
}
if rule.BodyType != "" {
- if mappedBodyType, ok := bodyTypeMatch[rule.BodyType]; ok {
- r.WriteString(fmt.Sprintf(",ctl:requestBodyProcessor=%s", mappedBodyType))
- } else {
+ mappedBodyType, ok := bodyTypeMatch[rule.BodyType]
+ if !ok {
return nil, fmt.Errorf("unknown body type '%s'", rule.BodyType)
}
+ r.WriteString(fmt.Sprintf(",ctl:requestBodyProcessor=%s", mappedBodyType))
}
if and {
diff --git a/pkg/appsec/coraza_logger.go b/pkg/appsec/coraza_logger.go
index 7229f038b92..2b7f85d4e46 100644
--- a/pkg/appsec/coraza_logger.go
+++ b/pkg/appsec/coraza_logger.go
@@ -90,14 +90,13 @@ func (e *crzLogEvent) Bool(key string, b bool) dbg.Event {
func (e *crzLogEvent) Int(key string, i int) dbg.Event {
if e.muted {
- // this allows us to have per-rule debug logging
- if key == "rule_id" && GetRuleDebug(i) {
- e.muted = false
- e.fields = map[string]interface{}{}
- e.level = log.DebugLevel
- } else {
+ if key != "rule_id" || !GetRuleDebug(i) {
return e
}
+ // this allows us to have per-rule debug logging
+ e.muted = false
+ e.fields = map[string]interface{}{}
+ e.level = log.DebugLevel
}
e.fields[key] = i
diff --git a/pkg/csplugin/hclog_adapter.go b/pkg/csplugin/hclog_adapter.go
index 58190684ebc..44a22463709 100644
--- a/pkg/csplugin/hclog_adapter.go
+++ b/pkg/csplugin/hclog_adapter.go
@@ -221,11 +221,10 @@ func merge(dst map[string]interface{}, k, v interface{}) {
func safeString(str fmt.Stringer) (s string) {
defer func() {
if panicVal := recover(); panicVal != nil {
- if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
- s = "NULL"
- } else {
+ if v := reflect.ValueOf(str); v.Kind() != reflect.Ptr || !v.IsNil() {
panic(panicVal)
}
+ s = "NULL"
}
}()
diff --git a/pkg/exprhelpers/crowdsec_cti.go b/pkg/exprhelpers/crowdsec_cti.go
index 59a239722e3..268979ee243 100644
--- a/pkg/exprhelpers/crowdsec_cti.go
+++ b/pkg/exprhelpers/crowdsec_cti.go
@@ -86,12 +86,11 @@ func CrowdsecCTI(params ...any) (any, error) {
if val, err := CTICache.Get(ip); err == nil && val != nil {
ctiClient.Logger.Debugf("cti cache fetch for %s", ip)
ret, ok := val.(*cticlient.SmokeItem)
- if !ok {
- ctiClient.Logger.Warningf("CrowdsecCTI: invalid type in cache, removing")
- CTICache.Remove(ip)
- } else {
+ if ok {
return ret, nil
}
+ ctiClient.Logger.Warningf("CrowdsecCTI: invalid type in cache, removing")
+ CTICache.Remove(ip)
}
if !CTIBackOffUntil.IsZero() && time.Now().Before(CTIBackOffUntil) {
diff --git a/pkg/parser/parsing_test.go b/pkg/parser/parsing_test.go
index 3193631f4dd..f142e1bc29a 100644
--- a/pkg/parser/parsing_test.go
+++ b/pkg/parser/parsing_test.go
@@ -278,26 +278,25 @@ func matchEvent(expected types.Event, out types.Event, debug bool) ([]string, bo
for mapIdx := 0; mapIdx < len(expectMaps); mapIdx++ {
for expKey, expVal := range expectMaps[mapIdx] {
- if outVal, ok := outMaps[mapIdx][expKey]; ok {
- if outVal == expVal { //ok entry
- if debug {
- retInfo = append(retInfo, fmt.Sprintf("ok %s[%s] %s == %s", outLabels[mapIdx], expKey, expVal, outVal))
- }
- valid = true
- } else { //mismatch entry
- if debug {
- retInfo = append(retInfo, fmt.Sprintf("mismatch %s[%s] %s != %s", outLabels[mapIdx], expKey, expVal, outVal))
- }
- valid = false
- goto checkFinished
- }
- } else { //missing entry
+ outVal, ok := outMaps[mapIdx][expKey]
+ if !ok {
if debug {
retInfo = append(retInfo, fmt.Sprintf("missing entry %s[%s]", outLabels[mapIdx], expKey))
}
valid = false
goto checkFinished
}
+ if outVal != expVal { //ok entry
+ if debug {
+ retInfo = append(retInfo, fmt.Sprintf("mismatch %s[%s] %s != %s", outLabels[mapIdx], expKey, expVal, outVal))
+ }
+ valid = false
+ goto checkFinished
+ }
+ if debug {
+ retInfo = append(retInfo, fmt.Sprintf("ok %s[%s] %s == %s", outLabels[mapIdx], expKey, expVal, outVal))
+ }
+ valid = true
}
}
checkFinished:
From cad760e605b4f6140b4cdee8665a0951c5b212cd Mon Sep 17 00:00:00 2001
From: mmetc <92726601+mmetc@users.noreply.github.com>
Date: Fri, 7 Jun 2024 16:56:02 +0200
Subject: [PATCH 173/581] pkg/cwhub: use explicit context for version check,
index update (#3064)
* cscli refactor: explicit context for hub (version lookup)
* change order of version checks
* pkg/cwhub: split NewHub() + Update() + Load()
* cscli refactor: explicit context for hub (index update)
* updated pkg/cwhub docs
* lint
---
cmd/crowdsec-cli/config_restore.go | 13 +++---
cmd/crowdsec-cli/hub.go | 27 ++++++++----
cmd/crowdsec-cli/itemcli.go | 25 +++++------
cmd/crowdsec-cli/require/branch.go | 66 ++++++++++++++++++++++++-----
cmd/crowdsec-cli/require/require.go | 13 ++++--
cmd/crowdsec-cli/setup.go | 2 +-
cmd/crowdsec/serve.go | 16 +++++--
pkg/cwhub/cwhub_test.go | 11 ++++-
pkg/cwhub/doc.go | 29 ++++++++++---
pkg/cwhub/hub.go | 41 ++++++++++--------
pkg/cwhub/hub_test.go | 15 +++++--
pkg/cwhub/itemupgrade_test.go | 38 +++++++++++++----
pkg/cwhub/remote.go | 4 +-
pkg/cwversion/version.go | 28 +-----------
pkg/hubtest/hubtest.go | 16 +++++--
pkg/hubtest/hubtest_item.go | 8 +++-
pkg/leakybucket/buckets_test.go | 14 ++++--
test/bats/20_hub_items.bats | 2 +-
18 files changed, 244 insertions(+), 124 deletions(-)
diff --git a/cmd/crowdsec-cli/config_restore.go b/cmd/crowdsec-cli/config_restore.go
index ee7179b73c5..6147a7518c2 100644
--- a/cmd/crowdsec-cli/config_restore.go
+++ b/cmd/crowdsec-cli/config_restore.go
@@ -1,6 +1,7 @@
package main
import (
+ "context"
"encoding/json"
"fmt"
"os"
@@ -13,10 +14,10 @@ import (
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
)
-func (cli *cliConfig) restoreHub(dirPath string) error {
+func (cli *cliConfig) restoreHub(ctx context.Context, dirPath string) error {
cfg := cli.cfg()
- hub, err := require.Hub(cfg, require.RemoteHub(cfg), nil)
+ hub, err := require.Hub(cfg, require.RemoteHub(ctx, cfg), nil)
if err != nil {
return err
}
@@ -126,7 +127,7 @@ func (cli *cliConfig) restoreHub(dirPath string) error {
- Tainted/local/out-of-date scenarios, parsers, postoverflows and collections
- Acquisition files (acquis.yaml, acquis.d/*.yaml)
*/
-func (cli *cliConfig) restore(dirPath string) error {
+func (cli *cliConfig) restore(ctx context.Context, dirPath string) error {
var err error
cfg := cli.cfg()
@@ -237,7 +238,7 @@ func (cli *cliConfig) restore(dirPath string) error {
}
}
- if err = cli.restoreHub(dirPath); err != nil {
+ if err = cli.restoreHub(ctx, dirPath); err != nil {
return fmt.Errorf("failed to restore hub config: %w", err)
}
@@ -258,10 +259,10 @@ func (cli *cliConfig) newRestoreCmd() *cobra.Command {
- Backup of API credentials (local API and online API)`,
Args: cobra.ExactArgs(1),
DisableAutoGenTag: true,
- RunE: func(_ *cobra.Command, args []string) error {
+ RunE: func(cmd *cobra.Command, args []string) error {
dirPath := args[0]
- if err := cli.restore(dirPath); err != nil {
+ if err := cli.restore(cmd.Context(), dirPath); err != nil {
return fmt.Errorf("failed to restore config from %s: %w", dirPath, err)
}
diff --git a/cmd/crowdsec-cli/hub.go b/cmd/crowdsec-cli/hub.go
index 318dd018e06..cf9f7f282a4 100644
--- a/cmd/crowdsec-cli/hub.go
+++ b/cmd/crowdsec-cli/hub.go
@@ -1,6 +1,7 @@
package main
import (
+ "context"
"encoding/json"
"fmt"
@@ -98,16 +99,24 @@ func (cli *cliHub) newListCmd() *cobra.Command {
return cmd
}
-func (cli *cliHub) update() error {
+func (cli *cliHub) update(ctx context.Context) error {
local := cli.cfg().Hub
- remote := require.RemoteHub(cli.cfg())
+ remote := require.RemoteHub(ctx, cli.cfg())
// don't use require.Hub because if there is no index file, it would fail
- hub, err := cwhub.NewHub(local, remote, true, log.StandardLogger())
+ hub, err := cwhub.NewHub(local, remote, log.StandardLogger())
if err != nil {
+ return err
+ }
+
+ if err := hub.Update(ctx); err != nil {
return fmt.Errorf("failed to update hub: %w", err)
}
+ if err := hub.Load(); err != nil {
+ return fmt.Errorf("failed to load hub: %w", err)
+ }
+
for _, v := range hub.Warnings {
log.Info(v)
}
@@ -124,16 +133,16 @@ Fetches the .index.json file from the hub, containing the list of available conf
`,
Args: cobra.ExactArgs(0),
DisableAutoGenTag: true,
- RunE: func(_ *cobra.Command, _ []string) error {
- return cli.update()
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ return cli.update(cmd.Context())
},
}
return cmd
}
-func (cli *cliHub) upgrade(force bool) error {
- hub, err := require.Hub(cli.cfg(), require.RemoteHub(cli.cfg()), log.StandardLogger())
+func (cli *cliHub) upgrade(ctx context.Context, force bool) error {
+ hub, err := require.Hub(cli.cfg(), require.RemoteHub(ctx, cli.cfg()), log.StandardLogger())
if err != nil {
return err
}
@@ -176,8 +185,8 @@ Upgrade all configs installed from Crowdsec Hub. Run 'sudo cscli hub update' if
`,
Args: cobra.ExactArgs(0),
DisableAutoGenTag: true,
- RunE: func(_ *cobra.Command, _ []string) error {
- return cli.upgrade(force)
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ return cli.upgrade(cmd.Context(), force)
},
}
diff --git a/cmd/crowdsec-cli/itemcli.go b/cmd/crowdsec-cli/itemcli.go
index 367e22f2962..dedbab3b914 100644
--- a/cmd/crowdsec-cli/itemcli.go
+++ b/cmd/crowdsec-cli/itemcli.go
@@ -2,6 +2,7 @@ package main
import (
"cmp"
+ "context"
"errors"
"fmt"
"os"
@@ -61,10 +62,10 @@ func (cli cliItem) NewCommand() *cobra.Command {
return cmd
}
-func (cli cliItem) install(args []string, downloadOnly bool, force bool, ignoreError bool) error {
+func (cli cliItem) install(ctx context.Context, args []string, downloadOnly bool, force bool, ignoreError bool) error {
cfg := cli.cfg()
- hub, err := require.Hub(cfg, require.RemoteHub(cfg), log.StandardLogger())
+ hub, err := require.Hub(cfg, require.RemoteHub(ctx, cfg), log.StandardLogger())
if err != nil {
return err
}
@@ -113,8 +114,8 @@ func (cli cliItem) newInstallCmd() *cobra.Command {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compAllItems(cli.name, args, toComplete, cli.cfg)
},
- RunE: func(_ *cobra.Command, args []string) error {
- return cli.install(args, downloadOnly, force, ignoreError)
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return cli.install(cmd.Context(), args, downloadOnly, force, ignoreError)
},
}
@@ -252,10 +253,10 @@ func (cli cliItem) newRemoveCmd() *cobra.Command {
return cmd
}
-func (cli cliItem) upgrade(args []string, force bool, all bool) error {
+func (cli cliItem) upgrade(ctx context.Context, args []string, force bool, all bool) error {
cfg := cli.cfg()
- hub, err := require.Hub(cfg, require.RemoteHub(cfg), log.StandardLogger())
+ hub, err := require.Hub(cfg, require.RemoteHub(ctx, cfg), log.StandardLogger())
if err != nil {
return err
}
@@ -334,8 +335,8 @@ func (cli cliItem) newUpgradeCmd() *cobra.Command {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compInstalledItems(cli.name, args, toComplete, cli.cfg)
},
- RunE: func(_ *cobra.Command, args []string) error {
- return cli.upgrade(args, force, all)
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return cli.upgrade(cmd.Context(), args, force, all)
},
}
@@ -346,7 +347,7 @@ func (cli cliItem) newUpgradeCmd() *cobra.Command {
return cmd
}
-func (cli cliItem) inspect(args []string, url string, diff bool, rev bool, noMetrics bool) error {
+func (cli cliItem) inspect(ctx context.Context, args []string, url string, diff bool, rev bool, noMetrics bool) error {
cfg := cli.cfg()
if rev && !diff {
@@ -360,7 +361,7 @@ func (cli cliItem) inspect(args []string, url string, diff bool, rev bool, noMet
remote := (*cwhub.RemoteHubCfg)(nil)
if diff {
- remote = require.RemoteHub(cfg)
+ remote = require.RemoteHub(ctx, cfg)
}
hub, err := require.Hub(cfg, remote, log.StandardLogger())
@@ -412,8 +413,8 @@ func (cli cliItem) newInspectCmd() *cobra.Command {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compInstalledItems(cli.name, args, toComplete, cli.cfg)
},
- RunE: func(_ *cobra.Command, args []string) error {
- return cli.inspect(args, url, diff, rev, noMetrics)
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return cli.inspect(cmd.Context(), args, url, diff, rev, noMetrics)
},
}
diff --git a/cmd/crowdsec-cli/require/branch.go b/cmd/crowdsec-cli/require/branch.go
index 6fcaaacea2d..503cb6d2326 100644
--- a/cmd/crowdsec-cli/require/branch.go
+++ b/cmd/crowdsec-cli/require/branch.go
@@ -3,27 +3,76 @@ package require
// Set the appropriate hub branch according to config settings and crowdsec version
import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "time"
+
log "github.com/sirupsen/logrus"
"golang.org/x/mod/semver"
- "github.com/crowdsecurity/crowdsec/pkg/cwversion"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
+ "github.com/crowdsecurity/crowdsec/pkg/cwversion"
)
-func chooseBranch(cfg *csconfig.Config) string {
+// lookupLatest returns the latest crowdsec version based on github
+func lookupLatest(ctx context.Context) (string, error) {
+ ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
+ defer cancel()
+
+ url := "https://version.crowdsec.net/latest"
+
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
+ if err != nil {
+ return "", fmt.Errorf("unable to create request for %s: %w", url, err)
+ }
+
+ client := &http.Client{}
+
+ resp, err := client.Do(req)
+ if err != nil {
+ return "", fmt.Errorf("unable to send request to %s: %w", url, err)
+ }
+ defer resp.Body.Close()
+
+ latest := make(map[string]any)
+
+ if err := json.NewDecoder(resp.Body).Decode(&latest); err != nil {
+ return "", fmt.Errorf("unable to decode response from %s: %w", url, err)
+ }
+
+ if _, ok := latest["name"]; !ok {
+ return "", fmt.Errorf("unable to find 'name' key in response from %s", url)
+ }
+
+ name, ok := latest["name"].(string)
+ if !ok {
+ return "", fmt.Errorf("unable to convert 'name' key to string in response from %s", url)
+ }
+
+ return name, nil
+}
+
+func chooseBranch(ctx context.Context, cfg *csconfig.Config) string {
// this was set from config.yaml or flag
if cfg.Cscli.HubBranch != "" {
log.Debugf("Hub override from config: branch '%s'", cfg.Cscli.HubBranch)
return cfg.Cscli.HubBranch
}
- latest, err := cwversion.Latest()
+ latest, err := lookupLatest(ctx)
if err != nil {
log.Warningf("Unable to retrieve latest crowdsec version: %s, using hub branch 'master'", err)
return "master"
}
csVersion := cwversion.VersionStrip()
+ if csVersion == "" {
+ log.Warning("Crowdsec version is not set, using hub branch 'master'")
+ return "master"
+ }
+
if csVersion == latest {
log.Debugf("Latest crowdsec version (%s), using hub branch 'master'", csVersion)
return "master"
@@ -35,22 +84,17 @@ func chooseBranch(cfg *csconfig.Config) string {
return "master"
}
- if csVersion == "" {
- log.Warning("Crowdsec version is not set, using hub branch 'master'")
- return "master"
- }
-
log.Warnf("A new CrowdSec release is available (%s). "+
"Your version is '%s'. Please update it to use new parsers/scenarios/collections.",
latest, csVersion)
+
return csVersion
}
-
// HubBranch sets the branch (in cscli config) and returns its value
// It can be "master", or the branch corresponding to the current crowdsec version, or the value overridden in config/flag
-func HubBranch(cfg *csconfig.Config) string {
- branch := chooseBranch(cfg)
+func HubBranch(ctx context.Context, cfg *csconfig.Config) string {
+ branch := chooseBranch(ctx, cfg)
cfg.Cscli.HubBranch = branch
diff --git a/cmd/crowdsec-cli/require/require.go b/cmd/crowdsec-cli/require/require.go
index 708b2d1c7a2..3ff66254466 100644
--- a/cmd/crowdsec-cli/require/require.go
+++ b/cmd/crowdsec-cli/require/require.go
@@ -1,6 +1,7 @@
package require
import (
+ "context"
"errors"
"fmt"
"io"
@@ -64,14 +65,14 @@ func Notifications(c *csconfig.Config) error {
}
// RemoteHub returns the configuration required to download hub index and items: url, branch, etc.
-func RemoteHub(c *csconfig.Config) *cwhub.RemoteHubCfg {
+func RemoteHub(ctx context.Context, c *csconfig.Config) *cwhub.RemoteHubCfg {
// set branch in config, and log if necessary
- branch := HubBranch(c)
+ branch := HubBranch(ctx, c)
urlTemplate := HubURLTemplate(c)
remote := &cwhub.RemoteHubCfg{
Branch: branch,
URLTemplate: urlTemplate,
- IndexPath: ".index.json",
+ IndexPath: ".index.json",
}
return remote
@@ -91,8 +92,12 @@ func Hub(c *csconfig.Config, remote *cwhub.RemoteHubCfg, logger *logrus.Logger)
logger.SetOutput(io.Discard)
}
- hub, err := cwhub.NewHub(local, remote, false, logger)
+ hub, err := cwhub.NewHub(local, remote, logger)
if err != nil {
+ return nil, err
+ }
+
+ if err := hub.Load(); err != nil {
return nil, fmt.Errorf("failed to read Hub index: %w. Run 'sudo cscli hub update' to download the index again", err)
}
diff --git a/cmd/crowdsec-cli/setup.go b/cmd/crowdsec-cli/setup.go
index 3e12b2465dd..ad02b357b70 100644
--- a/cmd/crowdsec-cli/setup.go
+++ b/cmd/crowdsec-cli/setup.go
@@ -315,7 +315,7 @@ func runSetupInstallHub(cmd *cobra.Command, args []string) error {
return fmt.Errorf("while reading file %s: %w", fromFile, err)
}
- hub, err := require.Hub(csConfig, require.RemoteHub(csConfig), log.StandardLogger())
+ hub, err := require.Hub(csConfig, require.RemoteHub(cmd.Context(), csConfig), log.StandardLogger())
if err != nil {
return err
}
diff --git a/cmd/crowdsec/serve.go b/cmd/crowdsec/serve.go
index da79e50c427..6c15b2d347e 100644
--- a/cmd/crowdsec/serve.go
+++ b/cmd/crowdsec/serve.go
@@ -81,9 +81,13 @@ func reloadHandler(sig os.Signal) (*csconfig.Config, error) {
}
if !cConfig.DisableAgent {
- hub, err := cwhub.NewHub(cConfig.Hub, nil, false, log.StandardLogger())
+ hub, err := cwhub.NewHub(cConfig.Hub, nil, log.StandardLogger())
if err != nil {
- return nil, fmt.Errorf("while loading hub index: %w", err)
+ return nil, err
+ }
+
+ if err := hub.Load(); err != nil {
+ return nil, err
}
csParsers, datasources, err := initCrowdsec(cConfig, hub)
@@ -367,9 +371,13 @@ func Serve(cConfig *csconfig.Config, agentReady chan bool) error {
}
if !cConfig.DisableAgent {
- hub, err := cwhub.NewHub(cConfig.Hub, nil, false, log.StandardLogger())
+ hub, err := cwhub.NewHub(cConfig.Hub, nil, log.StandardLogger())
if err != nil {
- return fmt.Errorf("while loading hub index: %w", err)
+ return err
+ }
+
+ if err := hub.Load(); err != nil {
+ return err
}
csParsers, datasources, err := initCrowdsec(cConfig, hub)
diff --git a/pkg/cwhub/cwhub_test.go b/pkg/cwhub/cwhub_test.go
index 09455fd65a9..17cf258745f 100644
--- a/pkg/cwhub/cwhub_test.go
+++ b/pkg/cwhub/cwhub_test.go
@@ -1,6 +1,7 @@
package cwhub
import (
+ "context"
"fmt"
"io"
"net/http"
@@ -62,7 +63,15 @@ func testHub(t *testing.T, update bool) *Hub {
IndexPath: ".index.json",
}
- hub, err := NewHub(local, remote, update, log.StandardLogger())
+ hub, err := NewHub(local, remote, log.StandardLogger())
+ require.NoError(t, err)
+
+ if update {
+ err := hub.Update(context.TODO())
+ require.NoError(t, err)
+ }
+
+ err = hub.Load()
require.NoError(t, err)
return hub
diff --git a/pkg/cwhub/doc.go b/pkg/cwhub/doc.go
index 85767265048..8cbf77ba00f 100644
--- a/pkg/cwhub/doc.go
+++ b/pkg/cwhub/doc.go
@@ -58,12 +58,21 @@
// InstallDir: "/etc/crowdsec",
// InstallDataDir: "/var/lib/crowdsec/data",
// }
-// hub, err := cwhub.NewHub(localHub, nil, false)
+//
+// hub, err := cwhub.NewHub(localHub, nil, logger)
// if err != nil {
// return fmt.Errorf("unable to initialize hub: %w", err)
// }
//
-// Now you can use the hub to access the existing items:
+// If the logger is nil, the item-by-item messages will be discarded, including warnings.
+// After configuring the hub, you must sync its state with items on disk.
+//
+// err := hub.Load()
+// if err != nil {
+// return fmt.Errorf("unable to load hub: %w", err)
+// }
+//
+// Now you can use the hub object to access the existing items:
//
// // list all the parsers
// for _, parser := range hub.GetItemMap(cwhub.PARSERS) {
@@ -97,8 +106,8 @@
// Branch: "master",
// IndexPath: ".index.json",
// }
-// updateIndex := false
-// hub, err := cwhub.NewHub(localHub, remoteHub, updateIndex)
+//
+// hub, err := cwhub.NewHub(localHub, remoteHub, logger)
// if err != nil {
// return fmt.Errorf("unable to initialize hub: %w", err)
// }
@@ -106,8 +115,14 @@
// The URLTemplate is a string that will be used to build the URL of the remote hub. It must contain two
// placeholders: the branch and the file path (it will be an index or an item).
//
-// Setting the third parameter to true will download the latest version of the index, if available on the
-// specified branch.
-// There is no exported method to update the index once the hub struct is created.
+// Before calling hub.Load(), you can update the index file by calling the Update() method:
+//
+// err := hub.Update(context.Background())
+// if err != nil {
+// return fmt.Errorf("unable to update hub index: %w", err)
+// }
+//
+// Note that the command will fail if the hub has already been synced. If you want to do it (ex. after a configuration
+// change the application is notified with SIGHUP) you have to instantiate a new hub object and dispose of the old one.
//
package cwhub
diff --git a/pkg/cwhub/hub.go b/pkg/cwhub/hub.go
index e7d927f54b1..1293d6fa235 100644
--- a/pkg/cwhub/hub.go
+++ b/pkg/cwhub/hub.go
@@ -1,6 +1,7 @@
package cwhub
import (
+ "context"
"encoding/json"
"errors"
"fmt"
@@ -30,10 +31,11 @@ func (h *Hub) GetDataDir() string {
return h.local.InstallDataDir
}
-// NewHub returns a new Hub instance with local and (optionally) remote configuration, and syncs the local state.
-// If updateIndex is true, the local index file is updated from the remote before reading the state of the items.
+// NewHub returns a new Hub instance with local and (optionally) remote configuration.
+// The hub is not synced automatically. Load() must be called to read the index, sync the local state,
+// and check for unmanaged items.
// All download operations (including updateIndex) return ErrNilRemoteHub if the remote configuration is not set.
-func NewHub(local *csconfig.LocalHubCfg, remote *RemoteHubCfg, updateIndex bool, logger *logrus.Logger) (*Hub, error) {
+func NewHub(local *csconfig.LocalHubCfg, remote *RemoteHubCfg, logger *logrus.Logger) (*Hub, error) {
if local == nil {
return nil, errors.New("no hub configuration found")
}
@@ -50,23 +52,22 @@ func NewHub(local *csconfig.LocalHubCfg, remote *RemoteHubCfg, updateIndex bool,
pathIndex: make(map[string]*Item, 0),
}
- if updateIndex {
- if err := hub.updateIndex(); err != nil {
- return nil, err
- }
- }
+ return hub, nil
+}
- logger.Debugf("loading hub idx %s", local.HubIndexFile)
+// Load reads the state of the items on disk.
+func (h *Hub) Load() error {
+ h.logger.Debugf("loading hub idx %s", h.local.HubIndexFile)
- if err := hub.parseIndex(); err != nil {
- return nil, fmt.Errorf("failed to load index: %w", err)
+ if err := h.parseIndex(); err != nil {
+ return fmt.Errorf("failed to load hub index: %w", err)
}
- if err := hub.localSync(); err != nil {
- return nil, fmt.Errorf("failed to sync items: %w", err)
+ if err := h.localSync(); err != nil {
+ return fmt.Errorf("failed to sync hub items: %w", err)
}
- return hub, nil
+ return nil
}
// parseIndex takes the content of an index file and fills the map of associated parsers/scenarios/collections.
@@ -149,9 +150,15 @@ func (h *Hub) ItemStats() []string {
return ret
}
-// updateIndex downloads the latest version of the index and writes it to disk if it changed.
-func (h *Hub) updateIndex() error {
- downloaded, err := h.remote.fetchIndex(h.local.HubIndexFile)
+// Update downloads the latest version of the index and writes it to disk if it changed. It cannot be called after Load()
+// unless the hub is completely empty.
+func (h *Hub) Update(ctx context.Context) error {
+ if h.pathIndex != nil && len(h.pathIndex) > 0 {
+ // if this happens, it's a bug.
+ return errors.New("cannot update hub after items have been loaded")
+ }
+
+ downloaded, err := h.remote.fetchIndex(ctx, h.local.HubIndexFile)
if err != nil {
return err
}
diff --git a/pkg/cwhub/hub_test.go b/pkg/cwhub/hub_test.go
index d5592a16c39..13c495e2fcc 100644
--- a/pkg/cwhub/hub_test.go
+++ b/pkg/cwhub/hub_test.go
@@ -1,6 +1,7 @@
package cwhub
import (
+ "context"
"fmt"
"os"
"testing"
@@ -18,7 +19,13 @@ func TestInitHubUpdate(t *testing.T) {
IndexPath: ".index.json",
}
- _, err := NewHub(hub.local, remote, true, nil)
+ _, err := NewHub(hub.local, remote, nil)
+ require.NoError(t, err)
+
+ err = hub.Update(context.TODO())
+ require.NoError(t, err)
+
+ err = hub.Load()
require.NoError(t, err)
}
@@ -47,7 +54,7 @@ func TestUpdateIndex(t *testing.T) {
hub.local.HubIndexFile = tmpIndex.Name()
- err = hub.updateIndex()
+ err = hub.Update(context.TODO())
cstest.RequireErrorContains(t, err, "failed to build hub index request: invalid URL template 'x'")
// bad domain
@@ -59,7 +66,7 @@ func TestUpdateIndex(t *testing.T) {
IndexPath: ".index.json",
}
- err = hub.updateIndex()
+ err = hub.Update(context.TODO())
require.NoError(t, err)
// XXX: this is not failing
// cstest.RequireErrorContains(t, err, "failed http request for hub index: Get")
@@ -75,6 +82,6 @@ func TestUpdateIndex(t *testing.T) {
hub.local.HubIndexFile = "/does/not/exist/index.json"
- err = hub.updateIndex()
+ err = hub.Update(context.TODO())
cstest.RequireErrorContains(t, err, "failed to create temporary download file for /does/not/exist/index.json:")
}
diff --git a/pkg/cwhub/itemupgrade_test.go b/pkg/cwhub/itemupgrade_test.go
index 1bd62ad63e8..d86d2094955 100644
--- a/pkg/cwhub/itemupgrade_test.go
+++ b/pkg/cwhub/itemupgrade_test.go
@@ -1,6 +1,7 @@
package cwhub
import (
+ "context"
"testing"
"github.com/stretchr/testify/require"
@@ -39,8 +40,14 @@ func TestUpgradeItemNewScenarioInCollection(t *testing.T) {
IndexPath: ".index.json",
}
- hub, err := NewHub(hub.local, remote, true, nil)
- require.NoError(t, err, "failed to download index: %s", err)
+ hub, err := NewHub(hub.local, remote, nil)
+ require.NoError(t, err)
+
+ err = hub.Update(context.TODO())
+ require.NoError(t, err)
+
+ err = hub.Load()
+ require.NoError(t, err)
hub = getHubOrFail(t, hub.local, remote)
@@ -100,8 +107,14 @@ func TestUpgradeItemInDisabledScenarioShouldNotBeInstalled(t *testing.T) {
require.True(t, hub.GetItem(COLLECTIONS, "crowdsecurity/test_collection").State.Installed)
require.True(t, hub.GetItem(COLLECTIONS, "crowdsecurity/test_collection").State.UpToDate)
- hub, err = NewHub(hub.local, remote, true, nil)
- require.NoError(t, err, "failed to download index: %s", err)
+ hub, err = NewHub(hub.local, remote, nil)
+ require.NoError(t, err)
+
+ err = hub.Update(context.TODO())
+ require.NoError(t, err)
+
+ err = hub.Load()
+ require.NoError(t, err)
item = hub.GetItem(COLLECTIONS, "crowdsecurity/test_collection")
didUpdate, err := item.Upgrade(false)
@@ -114,8 +127,11 @@ func TestUpgradeItemInDisabledScenarioShouldNotBeInstalled(t *testing.T) {
// getHubOrFail refreshes the hub state (load index, sync) and returns the singleton, or fails the test.
func getHubOrFail(t *testing.T, local *csconfig.LocalHubCfg, remote *RemoteHubCfg) *Hub {
- hub, err := NewHub(local, remote, false, nil)
- require.NoError(t, err, "failed to load hub index")
+ hub, err := NewHub(local, remote, nil)
+ require.NoError(t, err)
+
+ err = hub.Load()
+ require.NoError(t, err)
return hub
}
@@ -166,8 +182,14 @@ func TestUpgradeItemNewScenarioIsInstalledWhenReferencedScenarioIsDisabled(t *te
// we just removed. Nor should it install the newly added scenario
pushUpdateToCollectionInHub()
- hub, err = NewHub(hub.local, remote, true, nil)
- require.NoError(t, err, "failed to download index: %s", err)
+ hub, err = NewHub(hub.local, remote, nil)
+ require.NoError(t, err)
+
+ err = hub.Update(context.TODO())
+ require.NoError(t, err)
+
+ err = hub.Load()
+ require.NoError(t, err)
require.False(t, hub.GetItem(SCENARIOS, "crowdsecurity/foobar_scenario").State.Installed)
hub = getHubOrFail(t, hub.local, remote)
diff --git a/pkg/cwhub/remote.go b/pkg/cwhub/remote.go
index 5e42555fa61..abb2ddae2ad 100644
--- a/pkg/cwhub/remote.go
+++ b/pkg/cwhub/remote.go
@@ -31,7 +31,7 @@ func (r *RemoteHubCfg) urlTo(remotePath string) (string, error) {
}
// fetchIndex downloads the index from the hub and returns the content.
-func (r *RemoteHubCfg) fetchIndex(destPath string) (bool, error) {
+func (r *RemoteHubCfg) fetchIndex(ctx context.Context, destPath string) (bool, error) {
if r == nil {
return false, ErrNilRemoteHub
}
@@ -41,8 +41,6 @@ func (r *RemoteHubCfg) fetchIndex(destPath string) (bool, error) {
return false, fmt.Errorf("failed to build hub index request: %w", err)
}
- ctx := context.TODO()
-
downloaded, err := downloader.
New().
WithHTTPClient(hubClient).
diff --git a/pkg/cwversion/version.go b/pkg/cwversion/version.go
index d13767e665b..01509833c1c 100644
--- a/pkg/cwversion/version.go
+++ b/pkg/cwversion/version.go
@@ -1,9 +1,7 @@
package cwversion
import (
- "encoding/json"
"fmt"
- "net/http"
"strings"
goversion "github.com/hashicorp/go-version"
@@ -27,7 +25,7 @@ func versionWithTag() string {
// if the version number already contains the tag, don't duplicate it
ret := version.Version
- if !strings.HasSuffix(ret, version.Tag) && !strings.HasSuffix(ret, "g" + version.Tag + "-dirty") {
+ if !strings.HasSuffix(ret, version.Tag) && !strings.HasSuffix(ret, "g"+version.Tag+"-dirty") {
ret += "-" + version.Tag
}
@@ -65,7 +63,7 @@ func VersionStrip() string {
func Satisfies(strvers string, constraint string) (bool, error) {
vers, err := goversion.NewVersion(strvers)
if err != nil {
- return false, fmt.Errorf("failed to parse '%s' : %v", strvers, err)
+ return false, fmt.Errorf("failed to parse '%s': %w", strvers, err)
}
constraints, err := goversion.NewConstraint(constraint)
@@ -79,25 +77,3 @@ func Satisfies(strvers string, constraint string) (bool, error) {
return true, nil
}
-
-// Latest return latest crowdsec version based on github
-func Latest() (string, error) {
- latest := make(map[string]any)
-
- resp, err := http.Get("https://version.crowdsec.net/latest")
- if err != nil {
- return "", err
- }
- defer resp.Body.Close()
-
- err = json.NewDecoder(resp.Body).Decode(&latest)
- if err != nil {
- return "", err
- }
-
- if _, ok := latest["name"]; !ok {
- return "", fmt.Errorf("unable to find latest release name from github api: %+v", latest)
- }
-
- return latest["name"].(string), nil
-}
diff --git a/pkg/hubtest/hubtest.go b/pkg/hubtest/hubtest.go
index 6610652f78a..a4ca275c310 100644
--- a/pkg/hubtest/hubtest.go
+++ b/pkg/hubtest/hubtest.go
@@ -93,9 +93,13 @@ func NewHubTest(hubPath string, crowdsecPath string, cscliPath string, isAppsecT
InstallDataDir: HubTestPath,
}
- hub, err := cwhub.NewHub(local, nil, false, nil)
+ hub, err := cwhub.NewHub(local, nil, nil)
if err != nil {
- return HubTest{}, fmt.Errorf("unable to load hub: %s", err)
+ return HubTest{}, err
+ }
+
+ if err := hub.Load(); err != nil {
+ return HubTest{}, err
}
return HubTest{
@@ -126,9 +130,13 @@ func NewHubTest(hubPath string, crowdsecPath string, cscliPath string, isAppsecT
InstallDataDir: HubTestPath,
}
- hub, err := cwhub.NewHub(local, nil, false, nil)
+ hub, err := cwhub.NewHub(local, nil, nil)
if err != nil {
- return HubTest{}, fmt.Errorf("unable to load hub: %s", err)
+ return HubTest{}, err
+ }
+
+ if err := hub.Load(); err != nil {
+ return HubTest{}, err
}
return HubTest{
diff --git a/pkg/hubtest/hubtest_item.go b/pkg/hubtest/hubtest_item.go
index 918a10f62e1..096bdcd0a02 100644
--- a/pkg/hubtest/hubtest_item.go
+++ b/pkg/hubtest/hubtest_item.go
@@ -110,7 +110,7 @@ func NewTest(name string, hubTest *HubTest) (*HubTestItem, error) {
err = yaml.Unmarshal(yamlFile, configFileData)
if err != nil {
- return nil, fmt.Errorf("unmarshal: %v", err)
+ return nil, fmt.Errorf("unmarshal: %w", err)
}
parserAssertFilePath := filepath.Join(testPath, ParserAssertFileName)
@@ -210,11 +210,15 @@ func (t *HubTestItem) InstallHub() error {
}
// load installed hub
- hub, err := cwhub.NewHub(t.RuntimeHubConfig, nil, false, nil)
+ hub, err := cwhub.NewHub(t.RuntimeHubConfig, nil, nil)
if err != nil {
return err
}
+ if err := hub.Load(); err != nil {
+ return err
+ }
+
// install data for parsers if needed
ret := hub.GetItemMap(cwhub.PARSERS)
for parserName, item := range ret {
diff --git a/pkg/leakybucket/buckets_test.go b/pkg/leakybucket/buckets_test.go
index 4bb3c96759e..989e03944c3 100644
--- a/pkg/leakybucket/buckets_test.go
+++ b/pkg/leakybucket/buckets_test.go
@@ -16,6 +16,7 @@ import (
"github.com/davecgh/go-spew/spew"
log "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/require"
"gopkg.in/tomb.v2"
yaml "gopkg.in/yaml.v2"
@@ -45,10 +46,11 @@ func TestBucket(t *testing.T) {
InstallDataDir: testdata,
}
- hub, err := cwhub.NewHub(hubCfg, nil, false, nil)
- if err != nil {
- t.Fatalf("failed to init hub: %s", err)
- }
+ hub, err := cwhub.NewHub(hubCfg, nil, nil)
+ require.NoError(t, err)
+
+ err = hub.Load()
+ require.NoError(t, err)
err = exprhelpers.Init(nil)
if err != nil {
@@ -197,9 +199,11 @@ func testFile(t *testing.T, file string, bs string, holders []BucketFactory, res
//just to avoid any race during ingestion of funny scenarios
time.Sleep(50 * time.Millisecond)
var ts time.Time
+
if err := ts.UnmarshalText([]byte(in.MarshaledTime)); err != nil {
t.Fatalf("Failed to unmarshal time from input event : %s", err)
}
+
if latest_ts.IsZero() {
latest_ts = ts
} else if ts.After(latest_ts) {
@@ -208,10 +212,12 @@ func testFile(t *testing.T, file string, bs string, holders []BucketFactory, res
in.ExpectMode = types.TIMEMACHINE
log.Infof("Buckets input : %s", spew.Sdump(in))
+
ok, err := PourItemToHolders(in, holders, buckets)
if err != nil {
t.Fatalf("Failed to pour : %s", err)
}
+
if !ok {
log.Warning("Event wasn't poured")
}
diff --git a/test/bats/20_hub_items.bats b/test/bats/20_hub_items.bats
index 72e09dfa268..c6dbafc0911 100644
--- a/test/bats/20_hub_items.bats
+++ b/test/bats/20_hub_items.bats
@@ -83,7 +83,7 @@ teardown() {
rune -1 cscli collections inspect crowdsecurity/sshd --no-metrics -o json
# XXX: we are on the verbose side here...
rune -0 jq -r ".msg" <(stderr)
- assert_output --regexp "failed to read Hub index: failed to sync items: failed to scan .*: while syncing collections sshd.yaml: 1.2.3.4: Invalid Semantic Version. Run 'sudo cscli hub update' to download the index again"
+ assert_output --regexp "failed to read Hub index: failed to sync hub items: failed to scan .*: while syncing collections sshd.yaml: 1.2.3.4: Invalid Semantic Version. Run 'sudo cscli hub update' to download the index again"
}
@test "removing or purging an item already removed by hand" {
From dd6cf2b844f801fe045dc102471064ae1ef66bdc Mon Sep 17 00:00:00 2001
From: mmetc <92726601+mmetc@users.noreply.github.com>
Date: Fri, 7 Jun 2024 17:32:52 +0200
Subject: [PATCH 174/581] pkg/cwhub: use explicit context for item install,
upgrade (#3067)
---
cmd/crowdsec-cli/config_restore.go | 2 +-
cmd/crowdsec-cli/hub.go | 2 +-
cmd/crowdsec-cli/itemcli.go | 16 ++++++++--------
cmd/crowdsec-cli/setup.go | 2 +-
pkg/cwhub/cwhub_test.go | 3 ++-
pkg/cwhub/dataset.go | 4 +---
pkg/cwhub/hub_test.go | 12 ++++++++----
pkg/cwhub/iteminstall.go | 5 +++--
pkg/cwhub/iteminstall_test.go | 9 +++++++--
pkg/cwhub/itemupgrade.go | 28 +++++++++++++---------------
pkg/cwhub/itemupgrade_test.go | 24 +++++++++++++++---------
pkg/hubtest/hubtest_item.go | 9 ++++++---
pkg/setup/install.go | 11 ++++++-----
13 files changed, 72 insertions(+), 55 deletions(-)
diff --git a/cmd/crowdsec-cli/config_restore.go b/cmd/crowdsec-cli/config_restore.go
index 6147a7518c2..fc3670165f8 100644
--- a/cmd/crowdsec-cli/config_restore.go
+++ b/cmd/crowdsec-cli/config_restore.go
@@ -50,7 +50,7 @@ func (cli *cliConfig) restoreHub(ctx context.Context, dirPath string) error {
continue
}
- if err = item.Install(false, false); err != nil {
+ if err = item.Install(ctx, false, false); err != nil {
log.Errorf("Error while installing %s : %s", toinstall, err)
}
}
diff --git a/cmd/crowdsec-cli/hub.go b/cmd/crowdsec-cli/hub.go
index cf9f7f282a4..737b93d8da8 100644
--- a/cmd/crowdsec-cli/hub.go
+++ b/cmd/crowdsec-cli/hub.go
@@ -158,7 +158,7 @@ func (cli *cliHub) upgrade(ctx context.Context, force bool) error {
log.Infof("Upgrading %s", itemType)
for _, item := range items {
- didUpdate, err := item.Upgrade(force)
+ didUpdate, err := item.Upgrade(ctx, force)
if err != nil {
return err
}
diff --git a/cmd/crowdsec-cli/itemcli.go b/cmd/crowdsec-cli/itemcli.go
index dedbab3b914..55396a10995 100644
--- a/cmd/crowdsec-cli/itemcli.go
+++ b/cmd/crowdsec-cli/itemcli.go
@@ -83,7 +83,7 @@ func (cli cliItem) install(ctx context.Context, args []string, downloadOnly bool
continue
}
- if err := item.Install(force, downloadOnly); err != nil {
+ if err := item.Install(ctx, force, downloadOnly); err != nil {
if !ignoreError {
return fmt.Errorf("error while installing '%s': %w", item.Name, err)
}
@@ -270,7 +270,7 @@ func (cli cliItem) upgrade(ctx context.Context, args []string, force bool, all b
updated := 0
for _, item := range items {
- didUpdate, err := item.Upgrade(force)
+ didUpdate, err := item.Upgrade(ctx, force)
if err != nil {
return err
}
@@ -301,7 +301,7 @@ func (cli cliItem) upgrade(ctx context.Context, args []string, force bool, all b
return fmt.Errorf("can't find '%s' in %s", itemName, cli.name)
}
- didUpdate, err := item.Upgrade(force)
+ didUpdate, err := item.Upgrade(ctx, force)
if err != nil {
return err
}
@@ -376,7 +376,7 @@ func (cli cliItem) inspect(ctx context.Context, args []string, url string, diff
}
if diff {
- fmt.Println(cli.whyTainted(hub, item, rev))
+ fmt.Println(cli.whyTainted(ctx, hub, item, rev))
continue
}
@@ -466,7 +466,7 @@ func (cli cliItem) newListCmd() *cobra.Command {
}
// return the diff between the installed version and the latest version
-func (cli cliItem) itemDiff(item *cwhub.Item, reverse bool) (string, error) {
+func (cli cliItem) itemDiff(ctx context.Context, item *cwhub.Item, reverse bool) (string, error) {
if !item.State.Installed {
return "", fmt.Errorf("'%s' is not installed", item.FQName())
}
@@ -477,7 +477,7 @@ func (cli cliItem) itemDiff(item *cwhub.Item, reverse bool) (string, error) {
}
defer os.Remove(dest.Name())
- _, remoteURL, err := item.FetchContentTo(dest.Name())
+ _, remoteURL, err := item.FetchContentTo(ctx, dest.Name())
if err != nil {
return "", err
}
@@ -508,7 +508,7 @@ func (cli cliItem) itemDiff(item *cwhub.Item, reverse bool) (string, error) {
return fmt.Sprintf("%s", diff), nil
}
-func (cli cliItem) whyTainted(hub *cwhub.Hub, item *cwhub.Item, reverse bool) string {
+func (cli cliItem) whyTainted(ctx context.Context, hub *cwhub.Hub, item *cwhub.Item, reverse bool) string {
if !item.State.Installed {
return fmt.Sprintf("# %s is not installed", item.FQName())
}
@@ -533,7 +533,7 @@ func (cli cliItem) whyTainted(hub *cwhub.Hub, item *cwhub.Item, reverse bool) st
ret = append(ret, err.Error())
}
- diff, err := cli.itemDiff(sub, reverse)
+ diff, err := cli.itemDiff(ctx, sub, reverse)
if err != nil {
ret = append(ret, err.Error())
}
diff --git a/cmd/crowdsec-cli/setup.go b/cmd/crowdsec-cli/setup.go
index ad02b357b70..9f685d0fac1 100644
--- a/cmd/crowdsec-cli/setup.go
+++ b/cmd/crowdsec-cli/setup.go
@@ -320,7 +320,7 @@ func runSetupInstallHub(cmd *cobra.Command, args []string) error {
return err
}
- return setup.InstallHubItems(hub, input, dryRun)
+ return setup.InstallHubItems(cmd.Context(), hub, input, dryRun)
}
func runSetupValidate(cmd *cobra.Command, args []string) error {
diff --git a/pkg/cwhub/cwhub_test.go b/pkg/cwhub/cwhub_test.go
index 17cf258745f..d11ed2b9a95 100644
--- a/pkg/cwhub/cwhub_test.go
+++ b/pkg/cwhub/cwhub_test.go
@@ -67,7 +67,8 @@ func testHub(t *testing.T, update bool) *Hub {
require.NoError(t, err)
if update {
- err := hub.Update(context.TODO())
+ ctx := context.Background()
+ err := hub.Update(ctx)
require.NoError(t, err)
}
diff --git a/pkg/cwhub/dataset.go b/pkg/cwhub/dataset.go
index eb56d8e32a8..6d4f35c285c 100644
--- a/pkg/cwhub/dataset.go
+++ b/pkg/cwhub/dataset.go
@@ -21,7 +21,7 @@ type DataSet struct {
}
// downloadDataSet downloads all the data files for an item.
-func downloadDataSet(dataFolder string, force bool, reader io.Reader, logger *logrus.Logger) error {
+func downloadDataSet(ctx context.Context, dataFolder string, force bool, reader io.Reader, logger *logrus.Logger) error {
dec := yaml.NewDecoder(reader)
for {
@@ -53,8 +53,6 @@ func downloadDataSet(dataFolder string, force bool, reader io.Reader, logger *lo
WithShelfLife(7 * 24 * time.Hour)
}
- ctx := context.TODO()
-
downloaded, err := d.Download(ctx, dataS.SourceURL)
if err != nil {
return fmt.Errorf("while getting data: %w", err)
diff --git a/pkg/cwhub/hub_test.go b/pkg/cwhub/hub_test.go
index 13c495e2fcc..3d4ae5793b3 100644
--- a/pkg/cwhub/hub_test.go
+++ b/pkg/cwhub/hub_test.go
@@ -22,7 +22,9 @@ func TestInitHubUpdate(t *testing.T) {
_, err := NewHub(hub.local, remote, nil)
require.NoError(t, err)
- err = hub.Update(context.TODO())
+ ctx := context.Background()
+
+ err = hub.Update(ctx)
require.NoError(t, err)
err = hub.Load()
@@ -54,7 +56,9 @@ func TestUpdateIndex(t *testing.T) {
hub.local.HubIndexFile = tmpIndex.Name()
- err = hub.Update(context.TODO())
+ ctx := context.Background()
+
+ err = hub.Update(ctx)
cstest.RequireErrorContains(t, err, "failed to build hub index request: invalid URL template 'x'")
// bad domain
@@ -66,7 +70,7 @@ func TestUpdateIndex(t *testing.T) {
IndexPath: ".index.json",
}
- err = hub.Update(context.TODO())
+ err = hub.Update(ctx)
require.NoError(t, err)
// XXX: this is not failing
// cstest.RequireErrorContains(t, err, "failed http request for hub index: Get")
@@ -82,6 +86,6 @@ func TestUpdateIndex(t *testing.T) {
hub.local.HubIndexFile = "/does/not/exist/index.json"
- err = hub.Update(context.TODO())
+ err = hub.Update(ctx)
cstest.RequireErrorContains(t, err, "failed to create temporary download file for /does/not/exist/index.json:")
}
diff --git a/pkg/cwhub/iteminstall.go b/pkg/cwhub/iteminstall.go
index 274e7128a04..6a16ad0a65f 100644
--- a/pkg/cwhub/iteminstall.go
+++ b/pkg/cwhub/iteminstall.go
@@ -1,6 +1,7 @@
package cwhub
import (
+ "context"
"fmt"
)
@@ -39,7 +40,7 @@ func (i *Item) enable() error {
}
// Install installs the item from the hub, downloading it if needed.
-func (i *Item) Install(force bool, downloadOnly bool) error {
+func (i *Item) Install(ctx context.Context, force bool, downloadOnly bool) error {
if downloadOnly && i.State.Downloaded && i.State.UpToDate {
i.hub.logger.Infof("%s is already downloaded and up-to-date", i.Name)
@@ -48,7 +49,7 @@ func (i *Item) Install(force bool, downloadOnly bool) error {
}
}
- downloaded, err := i.downloadLatest(force, true)
+ downloaded, err := i.downloadLatest(ctx, force, true)
if err != nil {
return err
}
diff --git a/pkg/cwhub/iteminstall_test.go b/pkg/cwhub/iteminstall_test.go
index 337f66f95fa..5bfc7e8148e 100644
--- a/pkg/cwhub/iteminstall_test.go
+++ b/pkg/cwhub/iteminstall_test.go
@@ -1,6 +1,7 @@
package cwhub
import (
+ "context"
"os"
"testing"
@@ -9,8 +10,10 @@ import (
)
func testInstall(hub *Hub, t *testing.T, item *Item) {
+ ctx := context.Background()
+
// Install the parser
- _, err := item.downloadLatest(false, false)
+ _, err := item.downloadLatest(ctx, false, false)
require.NoError(t, err, "failed to download %s", item.Name)
err = hub.localSync()
@@ -48,8 +51,10 @@ func testTaint(hub *Hub, t *testing.T, item *Item) {
func testUpdate(hub *Hub, t *testing.T, item *Item) {
assert.False(t, item.State.UpToDate, "%s should not be up-to-date", item.Name)
+ ctx := context.Background()
+
// Update it + check status
- _, err := item.downloadLatest(true, true)
+ _, err := item.downloadLatest(ctx, true, true)
require.NoError(t, err, "failed to update %s", item.Name)
// Local sync and check status
diff --git a/pkg/cwhub/itemupgrade.go b/pkg/cwhub/itemupgrade.go
index 8b3ec7481ef..4dad226fd78 100644
--- a/pkg/cwhub/itemupgrade.go
+++ b/pkg/cwhub/itemupgrade.go
@@ -16,7 +16,7 @@ import (
)
// Upgrade downloads and applies the last version of the item from the hub.
-func (i *Item) Upgrade(force bool) (bool, error) {
+func (i *Item) Upgrade(ctx context.Context, force bool) (bool, error) {
if i.State.IsLocal() {
i.hub.logger.Infof("not upgrading %s: local item", i.Name)
return false, nil
@@ -33,7 +33,7 @@ func (i *Item) Upgrade(force bool) (bool, error) {
if i.State.UpToDate {
i.hub.logger.Infof("%s: up-to-date", i.Name)
- if err := i.DownloadDataIfNeeded(force); err != nil {
+ if err := i.DownloadDataIfNeeded(ctx, force); err != nil {
return false, fmt.Errorf("%s: download failed: %w", i.Name, err)
}
@@ -43,7 +43,7 @@ func (i *Item) Upgrade(force bool) (bool, error) {
}
}
- if _, err := i.downloadLatest(force, true); err != nil {
+ if _, err := i.downloadLatest(ctx, force, true); err != nil {
return false, fmt.Errorf("%s: download failed: %w", i.Name, err)
}
@@ -65,7 +65,7 @@ func (i *Item) Upgrade(force bool) (bool, error) {
}
// downloadLatest downloads the latest version of the item to the hub directory.
-func (i *Item) downloadLatest(overwrite bool, updateOnly bool) (bool, error) {
+func (i *Item) downloadLatest(ctx context.Context, overwrite bool, updateOnly bool) (bool, error) {
i.hub.logger.Debugf("Downloading %s %s", i.Type, i.Name)
for _, sub := range i.SubItems() {
@@ -80,14 +80,14 @@ func (i *Item) downloadLatest(overwrite bool, updateOnly bool) (bool, error) {
if sub.HasSubItems() {
i.hub.logger.Tracef("collection, recurse")
- if _, err := sub.downloadLatest(overwrite, updateOnly); err != nil {
+ if _, err := sub.downloadLatest(ctx, overwrite, updateOnly); err != nil {
return false, err
}
}
downloaded := sub.State.Downloaded
- if _, err := sub.download(overwrite); err != nil {
+ if _, err := sub.download(ctx, overwrite); err != nil {
return false, err
}
@@ -105,11 +105,11 @@ func (i *Item) downloadLatest(overwrite bool, updateOnly bool) (bool, error) {
return false, nil
}
- return i.download(overwrite)
+ return i.download(ctx, overwrite)
}
// FetchContentTo downloads the last version of the item's YAML file to the specified path.
-func (i *Item) FetchContentTo(destPath string) (bool, string, error) {
+func (i *Item) FetchContentTo(ctx context.Context, destPath string) (bool, string, error) {
url, err := i.hub.remote.urlTo(i.RemotePath)
if err != nil {
return false, "", fmt.Errorf("failed to build request: %w", err)
@@ -131,8 +131,6 @@ func (i *Item) FetchContentTo(destPath string) (bool, string, error) {
// TODO: recommend hub update if hash does not match
- ctx := context.TODO()
-
downloaded, err := d.Download(ctx, url)
if err != nil {
return false, "", fmt.Errorf("while downloading %s to %s: %w", i.Name, url, err)
@@ -142,7 +140,7 @@ func (i *Item) FetchContentTo(destPath string) (bool, string, error) {
}
// download downloads the item from the hub and writes it to the hub directory.
-func (i *Item) download(overwrite bool) (bool, error) {
+func (i *Item) download(ctx context.Context, overwrite bool) (bool, error) {
// ensure that target file is within target dir
finalPath, err := i.downloadPath()
if err != nil {
@@ -167,7 +165,7 @@ func (i *Item) download(overwrite bool) (bool, error) {
}
}
- downloaded, _, err := i.FetchContentTo(finalPath)
+ downloaded, _, err := i.FetchContentTo(ctx, finalPath)
if err != nil {
return false, fmt.Errorf("while downloading %s: %w", i.Name, err)
}
@@ -188,7 +186,7 @@ func (i *Item) download(overwrite bool) (bool, error) {
defer reader.Close()
- if err = downloadDataSet(i.hub.local.InstallDataDir, overwrite, reader, i.hub.logger); err != nil {
+ if err = downloadDataSet(ctx, i.hub.local.InstallDataDir, overwrite, reader, i.hub.logger); err != nil {
return false, fmt.Errorf("while downloading data for %s: %w", i.FileName, err)
}
@@ -196,7 +194,7 @@ func (i *Item) download(overwrite bool) (bool, error) {
}
// DownloadDataIfNeeded downloads the data set for the item.
-func (i *Item) DownloadDataIfNeeded(force bool) error {
+func (i *Item) DownloadDataIfNeeded(ctx context.Context, force bool) error {
itemFilePath, err := i.installPath()
if err != nil {
return err
@@ -209,7 +207,7 @@ func (i *Item) DownloadDataIfNeeded(force bool) error {
defer itemFile.Close()
- if err = downloadDataSet(i.hub.local.InstallDataDir, force, itemFile, i.hub.logger); err != nil {
+ if err = downloadDataSet(ctx, i.hub.local.InstallDataDir, force, itemFile, i.hub.logger); err != nil {
return fmt.Errorf("while downloading data for %s: %w", itemFilePath, err)
}
diff --git a/pkg/cwhub/itemupgrade_test.go b/pkg/cwhub/itemupgrade_test.go
index d86d2094955..5d302db3345 100644
--- a/pkg/cwhub/itemupgrade_test.go
+++ b/pkg/cwhub/itemupgrade_test.go
@@ -19,7 +19,9 @@ func TestUpgradeItemNewScenarioInCollection(t *testing.T) {
require.False(t, item.State.Downloaded)
require.False(t, item.State.Installed)
- require.NoError(t, item.Install(false, false))
+ ctx := context.Background()
+
+ require.NoError(t, item.Install(ctx, false, false))
require.True(t, item.State.Downloaded)
require.True(t, item.State.Installed)
@@ -43,7 +45,7 @@ func TestUpgradeItemNewScenarioInCollection(t *testing.T) {
hub, err := NewHub(hub.local, remote, nil)
require.NoError(t, err)
- err = hub.Update(context.TODO())
+ err = hub.Update(ctx)
require.NoError(t, err)
err = hub.Load()
@@ -58,7 +60,7 @@ func TestUpgradeItemNewScenarioInCollection(t *testing.T) {
require.False(t, item.State.UpToDate)
require.False(t, item.State.Tainted)
- didUpdate, err := item.Upgrade(false)
+ didUpdate, err := item.Upgrade(ctx, false)
require.NoError(t, err)
require.True(t, didUpdate)
assertCollectionDepsInstalled(t, hub, "crowdsecurity/test_collection")
@@ -78,7 +80,9 @@ func TestUpgradeItemInDisabledScenarioShouldNotBeInstalled(t *testing.T) {
require.False(t, item.State.Installed)
require.False(t, hub.GetItem(SCENARIOS, "crowdsecurity/foobar_scenario").State.Installed)
- require.NoError(t, item.Install(false, false))
+ ctx := context.Background()
+
+ require.NoError(t, item.Install(ctx, false, false))
require.True(t, item.State.Downloaded)
require.True(t, item.State.Installed)
@@ -110,14 +114,14 @@ func TestUpgradeItemInDisabledScenarioShouldNotBeInstalled(t *testing.T) {
hub, err = NewHub(hub.local, remote, nil)
require.NoError(t, err)
- err = hub.Update(context.TODO())
+ err = hub.Update(ctx)
require.NoError(t, err)
err = hub.Load()
require.NoError(t, err)
item = hub.GetItem(COLLECTIONS, "crowdsecurity/test_collection")
- didUpdate, err := item.Upgrade(false)
+ didUpdate, err := item.Upgrade(ctx, false)
require.NoError(t, err)
require.False(t, didUpdate)
@@ -148,7 +152,9 @@ func TestUpgradeItemNewScenarioIsInstalledWhenReferencedScenarioIsDisabled(t *te
require.False(t, item.State.Installed)
require.False(t, hub.GetItem(SCENARIOS, "crowdsecurity/foobar_scenario").State.Installed)
- require.NoError(t, item.Install(false, false))
+ ctx := context.Background()
+
+ require.NoError(t, item.Install(ctx, false, false))
require.True(t, item.State.Downloaded)
require.True(t, item.State.Installed)
@@ -185,7 +191,7 @@ func TestUpgradeItemNewScenarioIsInstalledWhenReferencedScenarioIsDisabled(t *te
hub, err = NewHub(hub.local, remote, nil)
require.NoError(t, err)
- err = hub.Update(context.TODO())
+ err = hub.Update(ctx)
require.NoError(t, err)
err = hub.Load()
@@ -195,7 +201,7 @@ func TestUpgradeItemNewScenarioIsInstalledWhenReferencedScenarioIsDisabled(t *te
hub = getHubOrFail(t, hub.local, remote)
item = hub.GetItem(COLLECTIONS, "crowdsecurity/test_collection")
- didUpdate, err := item.Upgrade(false)
+ didUpdate, err := item.Upgrade(ctx, false)
require.NoError(t, err)
require.True(t, didUpdate)
diff --git a/pkg/hubtest/hubtest_item.go b/pkg/hubtest/hubtest_item.go
index 096bdcd0a02..4b105777952 100644
--- a/pkg/hubtest/hubtest_item.go
+++ b/pkg/hubtest/hubtest_item.go
@@ -1,6 +1,7 @@
package hubtest
import (
+ "context"
"errors"
"fmt"
"net/url"
@@ -219,11 +220,13 @@ func (t *HubTestItem) InstallHub() error {
return err
}
+ ctx := context.Background()
+
// install data for parsers if needed
ret := hub.GetItemMap(cwhub.PARSERS)
for parserName, item := range ret {
if item.State.Installed {
- if err := item.DownloadDataIfNeeded(true); err != nil {
+ if err := item.DownloadDataIfNeeded(ctx, true); err != nil {
return fmt.Errorf("unable to download data for parser '%s': %+v", parserName, err)
}
@@ -235,7 +238,7 @@ func (t *HubTestItem) InstallHub() error {
ret = hub.GetItemMap(cwhub.SCENARIOS)
for scenarioName, item := range ret {
if item.State.Installed {
- if err := item.DownloadDataIfNeeded(true); err != nil {
+ if err := item.DownloadDataIfNeeded(ctx, true); err != nil {
return fmt.Errorf("unable to download data for parser '%s': %+v", scenarioName, err)
}
@@ -247,7 +250,7 @@ func (t *HubTestItem) InstallHub() error {
ret = hub.GetItemMap(cwhub.POSTOVERFLOWS)
for postoverflowName, item := range ret {
if item.State.Installed {
- if err := item.DownloadDataIfNeeded(true); err != nil {
+ if err := item.DownloadDataIfNeeded(ctx, true); err != nil {
return fmt.Errorf("unable to download data for parser '%s': %+v", postoverflowName, err)
}
diff --git a/pkg/setup/install.go b/pkg/setup/install.go
index dc85706a15c..09ac15971f8 100644
--- a/pkg/setup/install.go
+++ b/pkg/setup/install.go
@@ -2,6 +2,7 @@ package setup
import (
"bytes"
+ "context"
"errors"
"fmt"
"os"
@@ -46,7 +47,7 @@ func decodeSetup(input []byte, fancyErrors bool) (Setup, error) {
}
// InstallHubItems installs the objects recommended in a setup file.
-func InstallHubItems(hub *cwhub.Hub, input []byte, dryRun bool) error {
+func InstallHubItems(ctx context.Context, hub *cwhub.Hub, input []byte, dryRun bool) error {
setupEnvelope, err := decodeSetup(input, false)
if err != nil {
return err
@@ -74,7 +75,7 @@ func InstallHubItems(hub *cwhub.Hub, input []byte, dryRun bool) error {
continue
}
- if err := item.Install(forceAction, downloadOnly); err != nil {
+ if err := item.Install(ctx, forceAction, downloadOnly); err != nil {
return fmt.Errorf("while installing collection %s: %w", item.Name, err)
}
}
@@ -93,7 +94,7 @@ func InstallHubItems(hub *cwhub.Hub, input []byte, dryRun bool) error {
return fmt.Errorf("parser %s not found", parser)
}
- if err := item.Install(forceAction, downloadOnly); err != nil {
+ if err := item.Install(ctx, forceAction, downloadOnly); err != nil {
return fmt.Errorf("while installing parser %s: %w", item.Name, err)
}
}
@@ -112,7 +113,7 @@ func InstallHubItems(hub *cwhub.Hub, input []byte, dryRun bool) error {
return fmt.Errorf("scenario %s not found", scenario)
}
- if err := item.Install(forceAction, downloadOnly); err != nil {
+ if err := item.Install(ctx, forceAction, downloadOnly); err != nil {
return fmt.Errorf("while installing scenario %s: %w", item.Name, err)
}
}
@@ -131,7 +132,7 @@ func InstallHubItems(hub *cwhub.Hub, input []byte, dryRun bool) error {
return fmt.Errorf("postoverflow %s not found", postoverflow)
}
- if err := item.Install(forceAction, downloadOnly); err != nil {
+ if err := item.Install(ctx, forceAction, downloadOnly); err != nil {
return fmt.Errorf("while installing postoverflow %s: %w", item.Name, err)
}
}
From 13fb25257134090d14ec717f70c7d871d9ad730c Mon Sep 17 00:00:00 2001
From: mmetc <92726601+mmetc@users.noreply.github.com>
Date: Fri, 7 Jun 2024 19:03:23 +0200
Subject: [PATCH 175/581] lint: replace cyclop, gocyclo with revive; basic
pkg/hubtest helper (#3065)
---
.golangci.yml | 24 +++++++++--------
pkg/hubtest/appsecrule.go | 8 ++----
pkg/hubtest/hubtest_item.go | 52 ++++++++++++-------------------------
pkg/hubtest/parser.go | 8 ++----
pkg/hubtest/postoverflow.go | 8 ++----
pkg/hubtest/scenario.go | 8 ++----
6 files changed, 37 insertions(+), 71 deletions(-)
diff --git a/.golangci.yml b/.golangci.yml
index 1ec386183e1..8feb9921175 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,10 +1,6 @@
# https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
linters-settings:
- cyclop:
- # lower this after refactoring
- max-complexity: 45
-
gci:
sections:
- standard
@@ -20,10 +16,6 @@ linters-settings:
# lower this after refactoring
min-complexity: 128
- gocyclo:
- # lower this after refactoring
- min-complexity: 45
-
funlen:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
@@ -133,7 +125,8 @@ linters-settings:
- name: confusing-results
disabled: true
- name: cyclomatic
- disabled: true
+ # lower this after refactoring
+ arguments: [45]
- name: deep-exit
disabled: true
- name: defer
@@ -228,6 +221,13 @@ linters:
- structcheck
- varcheck
+ #
+ # Redundant
+ #
+
+ - gocyclo # revive
+ - cyclop # revive
+
#
# Disabled until fixed for go 1.22
#
@@ -243,7 +243,6 @@ linters:
# - asciicheck # checks that all code identifiers does not have non-ASCII symbols in the name
# - bidichk # Checks for dangerous unicode character sequences
# - bodyclose # checks whether HTTP response body is closed successfully
- # - cyclop # checks function and package cyclomatic complexity
# - decorder # check declaration order and count of types, constants, variables and functions
# - depguard # Go linter that checks if package imports are in a list of acceptable packages
# - dupword # checks for duplicate words in the source code
@@ -259,7 +258,6 @@ linters:
# - gochecksumtype # Run exhaustiveness checks on Go "sum types"
# - gocognit # Computes and checks the cognitive complexity of functions
# - gocritic # Provides diagnostics that check for bugs, performance and style issues.
- # - gocyclo # Computes and checks the cyclomatic complexity of functions
# - goheader # Checks is file header matches to pattern
# - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
# - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
@@ -503,3 +501,7 @@ issues:
- revive
path: cmd/crowdsec-cli/copyfile.go
+ - linters:
+ - revive
+ path: pkg/hubtest/hubtest_item.go
+ text: "cyclomatic: .*RunWithLogFile"
diff --git a/pkg/hubtest/appsecrule.go b/pkg/hubtest/appsecrule.go
index fb4ad78cc18..1c4416c2e9b 100644
--- a/pkg/hubtest/appsecrule.go
+++ b/pkg/hubtest/appsecrule.go
@@ -25,12 +25,8 @@ func (t *HubTestItem) installAppsecRuleItem(item *cwhub.Item) error {
// runtime/appsec-rules/
itemTypeDirDest := fmt.Sprintf("%s/appsec-rules/", t.RuntimePath)
- if err := os.MkdirAll(hubDirAppsecRuleDest, os.ModePerm); err != nil {
- return fmt.Errorf("unable to create folder '%s': %w", hubDirAppsecRuleDest, err)
- }
-
- if err := os.MkdirAll(itemTypeDirDest, os.ModePerm); err != nil {
- return fmt.Errorf("unable to create folder '%s': %w", itemTypeDirDest, err)
+ if err := createDirs([]string{hubDirAppsecRuleDest, itemTypeDirDest}); err != nil {
+ return err
}
// runtime/hub/appsec-rules/crowdsecurity/rule.yaml
diff --git a/pkg/hubtest/hubtest_item.go b/pkg/hubtest/hubtest_item.go
index 4b105777952..5346fb0be50 100644
--- a/pkg/hubtest/hubtest_item.go
+++ b/pkg/hubtest/hubtest_item.go
@@ -380,6 +380,16 @@ func (t *HubTestItem) RunWithNucleiTemplate() error {
return nil
}
+func createDirs(dirs []string) error {
+ for _, dir := range dirs {
+ if err := os.MkdirAll(dir, os.ModePerm); err != nil {
+ return fmt.Errorf("unable to create directory '%s': %w", dir, err)
+ }
+ }
+
+ return nil
+}
+
func (t *HubTestItem) RunWithLogFile() error {
testPath := filepath.Join(t.HubTestPath, t.Name)
if _, err := os.Stat(testPath); os.IsNotExist(err) {
@@ -391,30 +401,15 @@ func (t *HubTestItem) RunWithLogFile() error {
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)
+ // create runtime, data, hub folders
+ if err = createDirs([]string{t.RuntimePath, t.RuntimeDataPath, t.RuntimeHubPath, t.ResultsPath}); err != nil {
+ return 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': %w", 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)
@@ -585,30 +580,15 @@ func (t *HubTestItem) Run() 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)
+ // create runtime, data, hub, result folders
+ if err = createDirs([]string{t.RuntimePath, t.RuntimeDataPath, t.RuntimeHubPath, t.ResultsPath}); err != nil {
+ return 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': %w", 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)
diff --git a/pkg/hubtest/parser.go b/pkg/hubtest/parser.go
index d40301e3015..31ff459ca77 100644
--- a/pkg/hubtest/parser.go
+++ b/pkg/hubtest/parser.go
@@ -23,12 +23,8 @@ func (t *HubTestItem) installParserItem(item *cwhub.Item) error {
// runtime/parsers/s00-raw/
itemTypeDirDest := fmt.Sprintf("%s/parsers/%s/", t.RuntimePath, item.Stage)
- if err := os.MkdirAll(hubDirParserDest, os.ModePerm); err != nil {
- return fmt.Errorf("unable to create folder '%s': %w", hubDirParserDest, err)
- }
-
- if err := os.MkdirAll(itemTypeDirDest, os.ModePerm); err != nil {
- return fmt.Errorf("unable to create folder '%s': %w", itemTypeDirDest, err)
+ if err := createDirs([]string{hubDirParserDest, itemTypeDirDest}); err != nil {
+ return err
}
// runtime/hub/parsers/s00-raw/crowdsecurity/syslog-logs.yaml
diff --git a/pkg/hubtest/postoverflow.go b/pkg/hubtest/postoverflow.go
index 76a67b58b76..65fd0bfbc5d 100644
--- a/pkg/hubtest/postoverflow.go
+++ b/pkg/hubtest/postoverflow.go
@@ -23,12 +23,8 @@ func (t *HubTestItem) installPostoverflowItem(item *cwhub.Item) error {
// runtime/postoverflows/s00-enrich
itemTypeDirDest := fmt.Sprintf("%s/postoverflows/%s/", t.RuntimePath, item.Stage)
- if err := os.MkdirAll(hubDirPostoverflowDest, os.ModePerm); err != nil {
- return fmt.Errorf("unable to create folder '%s': %w", hubDirPostoverflowDest, err)
- }
-
- if err := os.MkdirAll(itemTypeDirDest, os.ModePerm); err != nil {
- return fmt.Errorf("unable to create folder '%s': %w", itemTypeDirDest, err)
+ if err := createDirs([]string{hubDirPostoverflowDest, itemTypeDirDest}); err != nil {
+ return err
}
// runtime/hub/postoverflows/s00-enrich/crowdsecurity/rdns.yaml
diff --git a/pkg/hubtest/scenario.go b/pkg/hubtest/scenario.go
index 35ea465b7c0..7f61e48accf 100644
--- a/pkg/hubtest/scenario.go
+++ b/pkg/hubtest/scenario.go
@@ -22,12 +22,8 @@ func (t *HubTestItem) installScenarioItem(item *cwhub.Item) error {
// runtime/parsers/scenarios/
itemTypeDirDest := fmt.Sprintf("%s/scenarios/", t.RuntimePath)
- if err := os.MkdirAll(hubDirScenarioDest, os.ModePerm); err != nil {
- return fmt.Errorf("unable to create folder '%s': %w", hubDirScenarioDest, err)
- }
-
- if err := os.MkdirAll(itemTypeDirDest, os.ModePerm); err != nil {
- return fmt.Errorf("unable to create folder '%s': %w", itemTypeDirDest, err)
+ if err := createDirs([]string{hubDirScenarioDest, itemTypeDirDest}); err != nil {
+ return err
}
// runtime/hub/scenarios/crowdsecurity/ssh-bf.yaml
From 819fa0ee2271cd665620c45ae4e886ec275f7c8e Mon Sep 17 00:00:00 2001
From: Laurence Jones
-:computer: Console (WebApp)
-:books: Documentation
-:diamond_shape_with_a_dot_inside: Configuration Hub
-:speech_balloon: Discourse (Forum)
-:speech_balloon: Discord (Live Chat)
+
-
-
-:computer: Console (WebApp)
-:books: Documentation
-:diamond_shape_with_a_dot_inside: Configuration Hub
-:speech_balloon: Discourse (Forum)
-:speech_balloon: Discord (Live Chat)
+
-
-