diff --git a/cmd/crowdsec-cli/alerts.go b/cmd/crowdsec-cli/clialert/alerts.go similarity index 97% rename from cmd/crowdsec-cli/alerts.go rename to cmd/crowdsec-cli/clialert/alerts.go index 37f9ab435c7..13013153a79 100644 --- a/cmd/crowdsec-cli/alerts.go +++ b/cmd/crowdsec-cli/clialert/alerts.go @@ -1,4 +1,4 @@ -package main +package clialert import ( "context" @@ -24,6 +24,7 @@ import ( "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require" "github.com/crowdsecurity/crowdsec/pkg/apiclient" + "github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/cwversion" "github.com/crowdsecurity/crowdsec/pkg/models" "github.com/crowdsecurity/crowdsec/pkg/types" @@ -183,12 +184,14 @@ func (cli *cliAlerts) displayOneAlert(alert *models.Alert, withDetail bool) erro return nil } +type configGetter func() *csconfig.Config + type cliAlerts struct { client *apiclient.ApiClient cfg configGetter } -func NewCLIAlerts(getconfig configGetter) *cliAlerts { +func New(getconfig configGetter) *cliAlerts { return &cliAlerts{ cfg: getconfig, } @@ -235,8 +238,10 @@ func (cli *cliAlerts) NewCommand() *cobra.Command { } func (cli *cliAlerts) list(alertListFilter apiclient.AlertsListOpts, limit *int, contained *bool, printMachine bool) error { - if err := manageCliDecisionAlerts(alertListFilter.IPEquals, alertListFilter.RangeEquals, - alertListFilter.ScopeEquals, alertListFilter.ValueEquals); err != nil { + var err error + + *alertListFilter.ScopeEquals, err = SanitizeScope(*alertListFilter.ScopeEquals, *alertListFilter.IPEquals, *alertListFilter.RangeEquals) + if err != nil { return err } @@ -378,8 +383,8 @@ func (cli *cliAlerts) delete(alertDeleteFilter apiclient.AlertsDeleteOpts, Activ var err error if !AlertDeleteAll { - if err = manageCliDecisionAlerts(alertDeleteFilter.IPEquals, alertDeleteFilter.RangeEquals, - alertDeleteFilter.ScopeEquals, alertDeleteFilter.ValueEquals); err != nil { + *alertDeleteFilter.ScopeEquals, err = SanitizeScope(*alertDeleteFilter.ScopeEquals, *alertDeleteFilter.IPEquals, *alertDeleteFilter.RangeEquals) + if err != nil { return err } diff --git a/cmd/crowdsec-cli/clialert/sanitize.go b/cmd/crowdsec-cli/clialert/sanitize.go new file mode 100644 index 00000000000..819843f4991 --- /dev/null +++ b/cmd/crowdsec-cli/clialert/sanitize.go @@ -0,0 +1,35 @@ +package clialert + +import ( + "fmt" + "net" + + "github.com/crowdsecurity/crowdsec/pkg/types" +) + +// SanitizeScope validates ip and range and sets the scope accordingly if it's not already set. +// The return value has consistent case. +func SanitizeScope(scope, ip, ipRange string) (string, error) { + if ipRange != "" { + _, _, err := net.ParseCIDR(ipRange) + if err != nil { + return "", fmt.Errorf("%s is not a valid range", ipRange) + } + + if scope == "" { + scope = types.Range + } + } + + if ip != "" { + if net.ParseIP(ip) == nil { + return "", fmt.Errorf("%s is not a valid ip", ip) + } + + if scope == "" { + scope = types.Ip + } + } + + return types.NormalizeScope(scope), nil +} diff --git a/cmd/crowdsec-cli/alerts_table.go b/cmd/crowdsec-cli/clialert/table.go similarity index 99% rename from cmd/crowdsec-cli/alerts_table.go rename to cmd/crowdsec-cli/clialert/table.go index 29383457ced..5dec63ec152 100644 --- a/cmd/crowdsec-cli/alerts_table.go +++ b/cmd/crowdsec-cli/clialert/table.go @@ -1,4 +1,4 @@ -package main +package clialert import ( "fmt" diff --git a/cmd/crowdsec-cli/decisions.go b/cmd/crowdsec-cli/clidecision/decisions.go similarity index 96% rename from cmd/crowdsec-cli/decisions.go rename to cmd/crowdsec-cli/clidecision/decisions.go index d485c90254f..5ecb3fc3304 100644 --- a/cmd/crowdsec-cli/decisions.go +++ b/cmd/crowdsec-cli/clidecision/decisions.go @@ -1,4 +1,4 @@ -package main +package clidecision import ( "context" @@ -17,7 +17,9 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clialert" "github.com/crowdsecurity/crowdsec/pkg/apiclient" + "github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/cwversion" "github.com/crowdsecurity/crowdsec/pkg/models" "github.com/crowdsecurity/crowdsec/pkg/types" @@ -114,12 +116,14 @@ func (cli *cliDecisions) decisionsToTable(alerts *models.GetAlertsResponse, prin return nil } +type configGetter func() *csconfig.Config + type cliDecisions struct { client *apiclient.ApiClient cfg configGetter } -func NewCLIDecisions(cfg configGetter) *cliDecisions { +func New(cfg configGetter) *cliDecisions { return &cliDecisions{ cfg: cfg, } @@ -170,8 +174,9 @@ func (cli *cliDecisions) NewCommand() *cobra.Command { func (cli *cliDecisions) list(filter apiclient.AlertsListOpts, NoSimu *bool, contained *bool, printMachine bool) error { var err error - /*take care of shorthand options*/ - if err = manageCliDecisionAlerts(filter.IPEquals, filter.RangeEquals, filter.ScopeEquals, filter.ValueEquals); err != nil { + + *filter.ScopeEquals, err = clialert.SanitizeScope(*filter.ScopeEquals, *filter.IPEquals, *filter.RangeEquals) + if err != nil { return err } @@ -326,8 +331,10 @@ func (cli *cliDecisions) add(addIP, addRange, addDuration, addValue, addScope, a stopAt := time.Now().UTC().Format(time.RFC3339) createdAt := time.Now().UTC().Format(time.RFC3339) - /*take care of shorthand options*/ - if err := manageCliDecisionAlerts(&addIP, &addRange, &addScope, &addValue); err != nil { + var err error + + addScope, err = clialert.SanitizeScope(addScope, addIP, addRange) + if err != nil { return err } @@ -381,7 +388,7 @@ func (cli *cliDecisions) add(addIP, addRange, addDuration, addValue, addScope, a } alerts = append(alerts, &alert) - _, _, err := cli.client.Alerts.Add(context.Background(), alerts) + _, _, err = cli.client.Alerts.Add(context.Background(), alerts) if err != nil { return err } @@ -435,7 +442,8 @@ func (cli *cliDecisions) delete(delFilter apiclient.DecisionsDeleteOpts, delDeci var err error /*take care of shorthand options*/ - if err = manageCliDecisionAlerts(delFilter.IPEquals, delFilter.RangeEquals, delFilter.ScopeEquals, delFilter.ValueEquals); err != nil { + *delFilter.ScopeEquals, err = clialert.SanitizeScope(*delFilter.ScopeEquals, *delFilter.IPEquals, *delFilter.RangeEquals) + if err != nil { return err } diff --git a/cmd/crowdsec-cli/decisions_import.go b/cmd/crowdsec-cli/clidecision/decisions_import.go similarity index 99% rename from cmd/crowdsec-cli/decisions_import.go rename to cmd/crowdsec-cli/clidecision/decisions_import.go index 338c1b7fb3e..c2e97652f33 100644 --- a/cmd/crowdsec-cli/decisions_import.go +++ b/cmd/crowdsec-cli/clidecision/decisions_import.go @@ -1,4 +1,4 @@ -package main +package clidecision import ( "bufio" diff --git a/cmd/crowdsec-cli/decisions_table.go b/cmd/crowdsec-cli/clidecision/decisions_table.go similarity index 98% rename from cmd/crowdsec-cli/decisions_table.go rename to cmd/crowdsec-cli/clidecision/decisions_table.go index 02952f93b85..90a0ae1176b 100644 --- a/cmd/crowdsec-cli/decisions_table.go +++ b/cmd/crowdsec-cli/clidecision/decisions_table.go @@ -1,4 +1,4 @@ -package main +package clidecision import ( "fmt" diff --git a/cmd/crowdsec-cli/main.go b/cmd/crowdsec-cli/main.go index 6f8e93e463c..01179cf93be 100644 --- a/cmd/crowdsec-cli/main.go +++ b/cmd/crowdsec-cli/main.go @@ -14,9 +14,11 @@ import ( "github.com/crowdsecurity/go-cs-lib/trace" + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clialert" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clibouncer" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clicapi" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cliconsole" + "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clidecision" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cliexplain" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clihub" "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clihubtest" @@ -257,8 +259,8 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall cmd.AddCommand(clihub.New(cli.cfg).NewCommand()) cmd.AddCommand(climetrics.New(cli.cfg).NewCommand()) cmd.AddCommand(NewCLIDashboard(cli.cfg).NewCommand()) - cmd.AddCommand(NewCLIDecisions(cli.cfg).NewCommand()) - cmd.AddCommand(NewCLIAlerts(cli.cfg).NewCommand()) + cmd.AddCommand(clidecision.New(cli.cfg).NewCommand()) + cmd.AddCommand(clialert.New(cli.cfg).NewCommand()) cmd.AddCommand(clisimulation.New(cli.cfg).NewCommand()) cmd.AddCommand(clibouncer.New(cli.cfg).NewCommand()) cmd.AddCommand(climachine.New(cli.cfg).NewCommand()) diff --git a/cmd/crowdsec-cli/utils.go b/cmd/crowdsec-cli/utils.go deleted file mode 100644 index c51140836b8..00000000000 --- a/cmd/crowdsec-cli/utils.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "fmt" - "net" - "strings" - - "github.com/crowdsecurity/crowdsec/pkg/types" -) - -func manageCliDecisionAlerts(ip *string, ipRange *string, scope *string, value *string) error { - /*if a range is provided, change the scope*/ - if *ipRange != "" { - _, _, err := net.ParseCIDR(*ipRange) - if err != nil { - return fmt.Errorf("%s isn't a valid range", *ipRange) - } - } - - if *ip != "" { - ipRepr := net.ParseIP(*ip) - if ipRepr == nil { - return fmt.Errorf("%s isn't a valid ip", *ip) - } - } - - // avoid confusion on scope (ip vs Ip and range vs Range) - switch strings.ToLower(*scope) { - case "ip": - *scope = types.Ip - case "range": - *scope = types.Range - case "country": - *scope = types.Country - case "as": - *scope = types.AS - } - - return nil -} diff --git a/pkg/apiserver/controllers/v1/alerts.go b/pkg/apiserver/controllers/v1/alerts.go index 82dc51d6879..d6ff2e8e144 100644 --- a/pkg/apiserver/controllers/v1/alerts.go +++ b/pkg/apiserver/controllers/v1/alerts.go @@ -6,7 +6,6 @@ import ( "net" "net/http" "strconv" - "strings" "time" "github.com/gin-gonic/gin" @@ -124,21 +123,6 @@ func (c *Controller) sendAlertToPluginChannel(alert *models.Alert, profileID uin } } -func normalizeScope(scope string) string { - switch strings.ToLower(scope) { - case "ip": - return types.Ip - case "range": - return types.Range - case "as": - return types.AS - case "country": - return types.Country - default: - return scope - } -} - // CreateAlert writes the alerts received in the body to the database func (c *Controller) CreateAlert(gctx *gin.Context) { var input models.AddAlertsRequest @@ -160,12 +144,12 @@ func (c *Controller) CreateAlert(gctx *gin.Context) { for _, alert := range input { // normalize scope for alert.Source and decisions if alert.Source.Scope != nil { - *alert.Source.Scope = normalizeScope(*alert.Source.Scope) + *alert.Source.Scope = types.NormalizeScope(*alert.Source.Scope) } for _, decision := range alert.Decisions { if decision.Scope != nil { - *decision.Scope = normalizeScope(*decision.Scope) + *decision.Scope = types.NormalizeScope(*decision.Scope) } } diff --git a/pkg/types/event.go b/pkg/types/event.go index 76a447bdc8c..12ce5c3d0a1 100644 --- a/pkg/types/event.go +++ b/pkg/types/event.go @@ -2,6 +2,7 @@ package types import ( "net" + "strings" "time" "github.com/expr-lang/expr/vm" @@ -143,3 +144,19 @@ func (r RuntimeAlert) GetSources() []string { } return ret } + +func NormalizeScope(scope string) string { + switch strings.ToLower(scope) { + case "ip": + return Ip + case "range": + return Range + case "as": + return AS + case "country": + return Country + default: + return scope + } +} + diff --git a/test/bats/90_decisions.bats b/test/bats/90_decisions.bats index c7ed214ffc9..b892dc84015 100644 --- a/test/bats/90_decisions.bats +++ b/test/bats/90_decisions.bats @@ -108,12 +108,12 @@ teardown() { # invalid json rune -1 cscli decisions import -i - <<<'{"blah":"blah"}' --format json assert_stderr --partial 'Parsing json' - assert_stderr --partial 'json: cannot unmarshal object into Go value of type []main.decisionRaw' + assert_stderr --partial 'json: cannot unmarshal object into Go value of type []clidecision.decisionRaw' # json with extra data rune -1 cscli decisions import -i - <<<'{"values":"1.2.3.4","blah":"blah"}' --format json assert_stderr --partial 'Parsing json' - assert_stderr --partial 'json: cannot unmarshal object into Go value of type []main.decisionRaw' + assert_stderr --partial 'json: cannot unmarshal object into Go value of type []clidecision.decisionRaw' #---------- # CSV