mirror of
https://github.com/crowdsecurity/crowdsec.git
synced 2025-05-11 12:25:53 +02:00
Merge branch 'master' into login/cache
This commit is contained in:
commit
8bf2f2873d
16 changed files with 318 additions and 240 deletions
|
@ -184,7 +184,7 @@ linters:
|
||||||
|
|
||||||
maintidx:
|
maintidx:
|
||||||
# raise this after refactoring
|
# raise this after refactoring
|
||||||
under: 15
|
under: 18
|
||||||
|
|
||||||
misspell:
|
misspell:
|
||||||
locale: US
|
locale: US
|
||||||
|
@ -210,7 +210,7 @@ linters:
|
||||||
- name: cognitive-complexity
|
- name: cognitive-complexity
|
||||||
arguments:
|
arguments:
|
||||||
# lower this after refactoring
|
# lower this after refactoring
|
||||||
- 119
|
- 113
|
||||||
- name: comment-spacings
|
- name: comment-spacings
|
||||||
disabled: true
|
disabled: true
|
||||||
- name: confusing-results
|
- name: confusing-results
|
||||||
|
@ -235,8 +235,8 @@ linters:
|
||||||
- name: function-length
|
- name: function-length
|
||||||
arguments:
|
arguments:
|
||||||
# lower this after refactoring
|
# lower this after refactoring
|
||||||
- 111
|
- 87
|
||||||
- 238
|
- 198
|
||||||
- name: get-return
|
- name: get-return
|
||||||
disabled: true
|
disabled: true
|
||||||
- name: increment-decrement
|
- name: increment-decrement
|
||||||
|
@ -294,9 +294,7 @@ linters:
|
||||||
- -ST1003
|
- -ST1003
|
||||||
- -ST1005
|
- -ST1005
|
||||||
- -ST1012
|
- -ST1012
|
||||||
- -ST1022
|
|
||||||
- -QF1003
|
- -QF1003
|
||||||
- -QF1008
|
|
||||||
- -QF1012
|
- -QF1012
|
||||||
|
|
||||||
wsl:
|
wsl:
|
||||||
|
|
|
@ -104,15 +104,15 @@ func (cli *cliAlerts) alertsToTable(alerts *models.GetAlertsResponse, printMachi
|
||||||
if *alerts == nil {
|
if *alerts == nil {
|
||||||
// avoid returning "null" in json
|
// avoid returning "null" in json
|
||||||
// could be cleaner if we used slice of alerts directly
|
// could be cleaner if we used slice of alerts directly
|
||||||
fmt.Println("[]")
|
fmt.Fprintln(os.Stdout, "[]")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
x, _ := json.MarshalIndent(alerts, "", " ")
|
x, _ := json.MarshalIndent(alerts, "", " ")
|
||||||
fmt.Print(string(x))
|
fmt.Fprint(os.Stdout, string(x))
|
||||||
case "human":
|
case "human":
|
||||||
if len(*alerts) == 0 {
|
if len(*alerts) == 0 {
|
||||||
fmt.Println("No active alerts")
|
fmt.Fprintln(os.Stdout, "No active alerts")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ func (cli *cliAlerts) displayOneAlert(alert *models.Alert, withDetail bool) erro
|
||||||
alertDecisionsTable(color.Output, cfg.Cscli.Color, alert)
|
alertDecisionsTable(color.Output, cfg.Cscli.Color, alert)
|
||||||
|
|
||||||
if len(alert.Meta) > 0 {
|
if len(alert.Meta) > 0 {
|
||||||
fmt.Printf("\n - Context :\n")
|
fmt.Fprintf(os.Stdout, "\n - Context :\n")
|
||||||
sort.Slice(alert.Meta, func(i, j int) bool {
|
sort.Slice(alert.Meta, func(i, j int) bool {
|
||||||
return alert.Meta[i].Key < alert.Meta[j].Key
|
return alert.Meta[i].Key < alert.Meta[j].Key
|
||||||
})
|
})
|
||||||
|
@ -183,7 +183,7 @@ func (cli *cliAlerts) displayOneAlert(alert *models.Alert, withDetail bool) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
if withDetail {
|
if withDetail {
|
||||||
fmt.Printf("\n - Events :\n")
|
fmt.Fprintf(os.Stdout, "\n - Events :\n")
|
||||||
|
|
||||||
for _, event := range alert.Events {
|
for _, event := range alert.Events {
|
||||||
alertEventTable(color.Output, cfg.Cscli.Color, event)
|
alertEventTable(color.Output, cfg.Cscli.Color, event)
|
||||||
|
@ -240,7 +240,7 @@ func (cli *cliAlerts) NewCommand() *cobra.Command {
|
||||||
func (cli *cliAlerts) list(ctx context.Context, alertListFilter apiclient.AlertsListOpts, limit *int, contained *bool, printMachine bool) error {
|
func (cli *cliAlerts) list(ctx context.Context, alertListFilter apiclient.AlertsListOpts, limit *int, contained *bool, printMachine bool) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
*alertListFilter.ScopeEquals, err = SanitizeScope(*alertListFilter.ScopeEquals, *alertListFilter.IPEquals, *alertListFilter.RangeEquals)
|
alertListFilter.ScopeEquals, err = SanitizeScope(alertListFilter.ScopeEquals, alertListFilter.IPEquals, alertListFilter.RangeEquals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -253,34 +253,6 @@ func (cli *cliAlerts) list(ctx context.Context, alertListFilter apiclient.Alerts
|
||||||
*alertListFilter.Limit = 0
|
*alertListFilter.Limit = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if *alertListFilter.TypeEquals == "" {
|
|
||||||
alertListFilter.TypeEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *alertListFilter.ScopeEquals == "" {
|
|
||||||
alertListFilter.ScopeEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *alertListFilter.ValueEquals == "" {
|
|
||||||
alertListFilter.ValueEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *alertListFilter.ScenarioEquals == "" {
|
|
||||||
alertListFilter.ScenarioEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *alertListFilter.IPEquals == "" {
|
|
||||||
alertListFilter.IPEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *alertListFilter.RangeEquals == "" {
|
|
||||||
alertListFilter.RangeEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *alertListFilter.OriginEquals == "" {
|
|
||||||
alertListFilter.OriginEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if contained != nil && *contained {
|
if contained != nil && *contained {
|
||||||
alertListFilter.Contains = new(bool)
|
alertListFilter.Contains = new(bool)
|
||||||
}
|
}
|
||||||
|
@ -299,16 +271,16 @@ func (cli *cliAlerts) list(ctx context.Context, alertListFilter apiclient.Alerts
|
||||||
|
|
||||||
func (cli *cliAlerts) newListCmd() *cobra.Command {
|
func (cli *cliAlerts) newListCmd() *cobra.Command {
|
||||||
alertListFilter := apiclient.AlertsListOpts{
|
alertListFilter := apiclient.AlertsListOpts{
|
||||||
ScopeEquals: new(string),
|
ScopeEquals: "",
|
||||||
ValueEquals: new(string),
|
ValueEquals: "",
|
||||||
ScenarioEquals: new(string),
|
ScenarioEquals: "",
|
||||||
IPEquals: new(string),
|
IPEquals: "",
|
||||||
RangeEquals: new(string),
|
RangeEquals: "",
|
||||||
Since: cstime.DurationWithDays(0),
|
Since: cstime.DurationWithDays(0),
|
||||||
Until: cstime.DurationWithDays(0),
|
Until: cstime.DurationWithDays(0),
|
||||||
TypeEquals: new(string),
|
TypeEquals: "",
|
||||||
IncludeCAPI: new(bool),
|
IncludeCAPI: new(bool),
|
||||||
OriginEquals: new(string),
|
OriginEquals: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
limit := new(int)
|
limit := new(int)
|
||||||
|
@ -338,13 +310,13 @@ cscli alerts list --type ban`,
|
||||||
flags.BoolVarP(alertListFilter.IncludeCAPI, "all", "a", false, "Include decisions from Central API")
|
flags.BoolVarP(alertListFilter.IncludeCAPI, "all", "a", false, "Include decisions from Central API")
|
||||||
flags.Var(&alertListFilter.Until, "until", "restrict to alerts older than until (ie. 4h, 30d)")
|
flags.Var(&alertListFilter.Until, "until", "restrict to alerts older than until (ie. 4h, 30d)")
|
||||||
flags.Var(&alertListFilter.Since, "since", "restrict to alerts newer than since (ie. 4h, 30d)")
|
flags.Var(&alertListFilter.Since, "since", "restrict to alerts newer than since (ie. 4h, 30d)")
|
||||||
flags.StringVarP(alertListFilter.IPEquals, "ip", "i", "", "restrict to alerts from this source ip (shorthand for --scope ip --value <IP>)")
|
flags.StringVarP(&alertListFilter.IPEquals, "ip", "i", "", "restrict to alerts from this source ip (shorthand for --scope ip --value <IP>)")
|
||||||
flags.StringVarP(alertListFilter.ScenarioEquals, "scenario", "s", "", "the scenario (ie. crowdsecurity/ssh-bf)")
|
flags.StringVarP(&alertListFilter.ScenarioEquals, "scenario", "s", "", "the scenario (ie. crowdsecurity/ssh-bf)")
|
||||||
flags.StringVarP(alertListFilter.RangeEquals, "range", "r", "", "restrict to alerts from this range (shorthand for --scope range --value <RANGE/X>)")
|
flags.StringVarP(&alertListFilter.RangeEquals, "range", "r", "", "restrict to alerts from this range (shorthand for --scope range --value <RANGE/X>)")
|
||||||
flags.StringVar(alertListFilter.TypeEquals, "type", "", "restrict to alerts with given decision type (ie. ban, captcha)")
|
flags.StringVar(&alertListFilter.TypeEquals, "type", "", "restrict to alerts with given decision type (ie. ban, captcha)")
|
||||||
flags.StringVar(alertListFilter.ScopeEquals, "scope", "", "restrict to alerts of this scope (ie. ip,range)")
|
flags.StringVar(&alertListFilter.ScopeEquals, "scope", "", "restrict to alerts of this scope (ie. ip,range)")
|
||||||
flags.StringVarP(alertListFilter.ValueEquals, "value", "v", "", "the value to match for in the specified scope")
|
flags.StringVarP(&alertListFilter.ValueEquals, "value", "v", "", "the value to match for in the specified scope")
|
||||||
flags.StringVar(alertListFilter.OriginEquals, "origin", "", fmt.Sprintf("the value to match for the specified origin (%s ...)", strings.Join(types.GetOrigins(), ",")))
|
flags.StringVar(&alertListFilter.OriginEquals, "origin", "", fmt.Sprintf("the value to match for the specified origin (%s ...)", strings.Join(types.GetOrigins(), ",")))
|
||||||
flags.BoolVar(contained, "contained", false, "query decisions contained by range")
|
flags.BoolVar(contained, "contained", false, "query decisions contained by range")
|
||||||
flags.BoolVarP(&printMachine, "machine", "m", false, "print machines that sent alerts")
|
flags.BoolVarP(&printMachine, "machine", "m", false, "print machines that sent alerts")
|
||||||
flags.IntVarP(limit, "limit", "l", 50, "limit size of alerts list table (0 to view all alerts)")
|
flags.IntVarP(limit, "limit", "l", 50, "limit size of alerts list table (0 to view all alerts)")
|
||||||
|
@ -356,7 +328,7 @@ func (cli *cliAlerts) delete(ctx context.Context, delFilter apiclient.AlertsDele
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if !deleteAll {
|
if !deleteAll {
|
||||||
*delFilter.ScopeEquals, err = SanitizeScope(*delFilter.ScopeEquals, *delFilter.IPEquals, *delFilter.RangeEquals)
|
delFilter.ScopeEquals, err = SanitizeScope(delFilter.ScopeEquals, delFilter.IPEquals, delFilter.RangeEquals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -365,26 +337,6 @@ func (cli *cliAlerts) delete(ctx context.Context, delFilter apiclient.AlertsDele
|
||||||
delFilter.ActiveDecisionEquals = activeDecision
|
delFilter.ActiveDecisionEquals = activeDecision
|
||||||
}
|
}
|
||||||
|
|
||||||
if *delFilter.ScopeEquals == "" {
|
|
||||||
delFilter.ScopeEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *delFilter.ValueEquals == "" {
|
|
||||||
delFilter.ValueEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *delFilter.ScenarioEquals == "" {
|
|
||||||
delFilter.ScenarioEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *delFilter.IPEquals == "" {
|
|
||||||
delFilter.IPEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *delFilter.RangeEquals == "" {
|
|
||||||
delFilter.RangeEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if contained != nil && *contained {
|
if contained != nil && *contained {
|
||||||
delFilter.Contains = new(bool)
|
delFilter.Contains = new(bool)
|
||||||
}
|
}
|
||||||
|
@ -422,11 +374,11 @@ func (cli *cliAlerts) newDeleteCmd() *cobra.Command {
|
||||||
)
|
)
|
||||||
|
|
||||||
delFilter := apiclient.AlertsDeleteOpts{
|
delFilter := apiclient.AlertsDeleteOpts{
|
||||||
ScopeEquals: new(string),
|
ScopeEquals: "",
|
||||||
ValueEquals: new(string),
|
ValueEquals: "",
|
||||||
ScenarioEquals: new(string),
|
ScenarioEquals: "",
|
||||||
IPEquals: new(string),
|
IPEquals: "",
|
||||||
RangeEquals: new(string),
|
RangeEquals: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
contained := new(bool)
|
contained := new(bool)
|
||||||
|
@ -445,9 +397,9 @@ cscli alerts delete -s crowdsecurity/ssh-bf"`,
|
||||||
if deleteAll {
|
if deleteAll {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if *delFilter.ScopeEquals == "" && *delFilter.ValueEquals == "" &&
|
if delFilter.ScopeEquals == "" && delFilter.ValueEquals == "" &&
|
||||||
*delFilter.ScenarioEquals == "" && *delFilter.IPEquals == "" &&
|
delFilter.ScenarioEquals == "" && delFilter.IPEquals == "" &&
|
||||||
*delFilter.RangeEquals == "" && delAlertByID == "" {
|
delFilter.RangeEquals == "" && delAlertByID == "" {
|
||||||
_ = cmd.Usage()
|
_ = cmd.Usage()
|
||||||
return errors.New("at least one filter or --all must be specified")
|
return errors.New("at least one filter or --all must be specified")
|
||||||
}
|
}
|
||||||
|
@ -461,11 +413,11 @@ cscli alerts delete -s crowdsecurity/ssh-bf"`,
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
flags.SortFlags = false
|
flags.SortFlags = false
|
||||||
flags.StringVar(delFilter.ScopeEquals, "scope", "", "the scope (ie. ip,range)")
|
flags.StringVar(&delFilter.ScopeEquals, "scope", "", "the scope (ie. ip,range)")
|
||||||
flags.StringVarP(delFilter.ValueEquals, "value", "v", "", "the value to match for in the specified scope")
|
flags.StringVarP(&delFilter.ValueEquals, "value", "v", "", "the value to match for in the specified scope")
|
||||||
flags.StringVarP(delFilter.ScenarioEquals, "scenario", "s", "", "the scenario (ie. crowdsecurity/ssh-bf)")
|
flags.StringVarP(&delFilter.ScenarioEquals, "scenario", "s", "", "the scenario (ie. crowdsecurity/ssh-bf)")
|
||||||
flags.StringVarP(delFilter.IPEquals, "ip", "i", "", "Source ip (shorthand for --scope ip --value <IP>)")
|
flags.StringVarP(&delFilter.IPEquals, "ip", "i", "", "Source ip (shorthand for --scope ip --value <IP>)")
|
||||||
flags.StringVarP(delFilter.RangeEquals, "range", "r", "", "Range source ip (shorthand for --scope range --value <RANGE>)")
|
flags.StringVarP(&delFilter.RangeEquals, "range", "r", "", "Range source ip (shorthand for --scope range --value <RANGE>)")
|
||||||
flags.StringVar(&delAlertByID, "id", "", "alert ID")
|
flags.StringVar(&delAlertByID, "id", "", "alert ID")
|
||||||
flags.BoolVarP(&deleteAll, "all", "a", false, "delete all alerts")
|
flags.BoolVarP(&deleteAll, "all", "a", false, "delete all alerts")
|
||||||
flags.BoolVar(contained, "contained", false, "query decisions contained by range")
|
flags.BoolVar(contained, "contained", false, "query decisions contained by range")
|
||||||
|
@ -499,14 +451,14 @@ func (cli *cliAlerts) inspect(ctx context.Context, details bool, alertIDs ...str
|
||||||
return fmt.Errorf("unable to serialize alert with id %s: %w", alertID, err)
|
return fmt.Errorf("unable to serialize alert with id %s: %w", alertID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s\n", string(data))
|
fmt.Fprintln(os.Stdout, string(data))
|
||||||
case "raw":
|
case "raw":
|
||||||
data, err := yaml.Marshal(alert)
|
data, err := yaml.Marshal(alert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to serialize alert with id %s: %w", alertID, err)
|
return fmt.Errorf("unable to serialize alert with id %s: %w", alertID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(string(data))
|
fmt.Fprintln(os.Stdout, string(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -536,7 +488,7 @@ func (cli *cliAlerts) newInspectCmd() *cobra.Command {
|
||||||
func (cli *cliAlerts) newFlushCmd() *cobra.Command {
|
func (cli *cliAlerts) newFlushCmd() *cobra.Command {
|
||||||
var maxItems int
|
var maxItems int
|
||||||
|
|
||||||
maxAge := cstime.DurationWithDays(7*24*time.Hour)
|
maxAge := cstime.DurationWithDays(7 * 24 * time.Hour)
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: `flush`,
|
Use: `flush`,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -283,7 +284,7 @@ func (cli *cliAllowLists) create(ctx context.Context, db *database.Client, name
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("allowlist '%s' created successfully\n", name)
|
fmt.Fprintf(os.Stdout, "allowlist '%s' created successfully\n", name)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -392,7 +393,7 @@ func (cli *cliAllowLists) delete(ctx context.Context, db *database.Client, name
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("allowlist '%s' deleted successfully\n", name)
|
fmt.Fprintf(os.Stdout, "allowlist '%s' deleted successfully\n", name)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -475,7 +476,7 @@ func (cli *cliAllowLists) add(ctx context.Context, db *database.Client, name str
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(toAdd) == 0 {
|
if len(toAdd) == 0 {
|
||||||
fmt.Println("no new values for allowlist")
|
fmt.Fprintln(os.Stdout, "no new values for allowlist")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,7 +486,15 @@ func (cli *cliAllowLists) add(ctx context.Context, db *database.Client, name str
|
||||||
}
|
}
|
||||||
|
|
||||||
if added > 0 {
|
if added > 0 {
|
||||||
fmt.Printf("added %d values to allowlist %s\n", added, name)
|
fmt.Fprintf(os.Stdout, "added %d values to allowlist %s\n", added, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
deleted, err := db.ApplyAllowlistsToExistingDecisions(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to apply allowlists to existing decisions: %w", err)
|
||||||
|
}
|
||||||
|
if deleted > 0 {
|
||||||
|
fmt.Printf("%d decisions deleted by allowlists\n", deleted)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -614,7 +623,7 @@ func (cli *cliAllowLists) remove(ctx context.Context, db *database.Client, name
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(toRemove) == 0 {
|
if len(toRemove) == 0 {
|
||||||
fmt.Println("no value to remove from allowlist")
|
fmt.Fprintln(os.Stdout, "no value to remove from allowlist")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,7 +633,7 @@ func (cli *cliAllowLists) remove(ctx context.Context, db *database.Client, name
|
||||||
}
|
}
|
||||||
|
|
||||||
if deleted > 0 {
|
if deleted > 0 {
|
||||||
fmt.Printf("removed %d values from allowlist %s", deleted, name)
|
fmt.Fprintf(os.Stdout, "removed %d values from allowlist %s", deleted, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -103,22 +103,22 @@ func (cli *cliDecisions) decisionsToTable(alerts *models.GetAlertsResponse, prin
|
||||||
if *alerts == nil {
|
if *alerts == nil {
|
||||||
// avoid returning "null" in `json"
|
// avoid returning "null" in `json"
|
||||||
// could be cleaner if we used slice of alerts directly
|
// could be cleaner if we used slice of alerts directly
|
||||||
fmt.Println("[]")
|
fmt.Fprintln(os.Stdout, "[]")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
x, _ := json.MarshalIndent(alerts, "", " ")
|
x, _ := json.MarshalIndent(alerts, "", " ")
|
||||||
fmt.Printf("%s", string(x))
|
fmt.Fprintln(os.Stdout, string(x))
|
||||||
case "human":
|
case "human":
|
||||||
if len(*alerts) == 0 {
|
if len(*alerts) == 0 {
|
||||||
fmt.Println("No active decisions")
|
fmt.Fprintln(os.Stdout, "No active decisions")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cli.decisionsTable(color.Output, alerts, printMachine)
|
cli.decisionsTable(color.Output, alerts, printMachine)
|
||||||
|
|
||||||
if skipped > 0 {
|
if skipped > 0 {
|
||||||
fmt.Printf("%d duplicated entries skipped\n", skipped)
|
fmt.Fprintf(os.Stdout, "%d duplicated entries skipped\n", skipped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ func (cli *cliDecisions) NewCommand() *cobra.Command {
|
||||||
func (cli *cliDecisions) list(ctx context.Context, filter apiclient.AlertsListOpts, noSimu *bool, contained *bool, printMachine bool) error {
|
func (cli *cliDecisions) list(ctx context.Context, filter apiclient.AlertsListOpts, noSimu *bool, contained *bool, printMachine bool) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
*filter.ScopeEquals, err = clialert.SanitizeScope(*filter.ScopeEquals, *filter.IPEquals, *filter.RangeEquals)
|
filter.ScopeEquals, err = clialert.SanitizeScope(filter.ScopeEquals, filter.IPEquals, filter.RangeEquals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -193,34 +193,6 @@ func (cli *cliDecisions) list(ctx context.Context, filter apiclient.AlertsListOp
|
||||||
*filter.Limit = 0
|
*filter.Limit = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if *filter.TypeEquals == "" {
|
|
||||||
filter.TypeEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *filter.ValueEquals == "" {
|
|
||||||
filter.ValueEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *filter.ScopeEquals == "" {
|
|
||||||
filter.ScopeEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *filter.ScenarioEquals == "" {
|
|
||||||
filter.ScenarioEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *filter.IPEquals == "" {
|
|
||||||
filter.IPEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *filter.RangeEquals == "" {
|
|
||||||
filter.RangeEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *filter.OriginEquals == "" {
|
|
||||||
filter.OriginEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if contained != nil && *contained {
|
if contained != nil && *contained {
|
||||||
filter.Contains = new(bool)
|
filter.Contains = new(bool)
|
||||||
}
|
}
|
||||||
|
@ -240,15 +212,15 @@ func (cli *cliDecisions) list(ctx context.Context, filter apiclient.AlertsListOp
|
||||||
|
|
||||||
func (cli *cliDecisions) newListCmd() *cobra.Command {
|
func (cli *cliDecisions) newListCmd() *cobra.Command {
|
||||||
filter := apiclient.AlertsListOpts{
|
filter := apiclient.AlertsListOpts{
|
||||||
ValueEquals: new(string),
|
ValueEquals: "",
|
||||||
ScopeEquals: new(string),
|
ScopeEquals: "",
|
||||||
ScenarioEquals: new(string),
|
ScenarioEquals: "",
|
||||||
OriginEquals: new(string),
|
OriginEquals: "",
|
||||||
IPEquals: new(string),
|
IPEquals: "",
|
||||||
RangeEquals: new(string),
|
RangeEquals: "",
|
||||||
Since: cstime.DurationWithDays(0),
|
Since: cstime.DurationWithDays(0),
|
||||||
Until: cstime.DurationWithDays(0),
|
Until: cstime.DurationWithDays(0),
|
||||||
TypeEquals: new(string),
|
TypeEquals: "",
|
||||||
IncludeCAPI: new(bool),
|
IncludeCAPI: new(bool),
|
||||||
Limit: new(int),
|
Limit: new(int),
|
||||||
}
|
}
|
||||||
|
@ -278,13 +250,13 @@ cscli decisions list --origin lists --scenario list_name
|
||||||
flags.BoolVarP(filter.IncludeCAPI, "all", "a", false, "Include decisions from Central API")
|
flags.BoolVarP(filter.IncludeCAPI, "all", "a", false, "Include decisions from Central API")
|
||||||
flags.Var(&filter.Since, "since", "restrict to alerts newer than since (ie. 4h, 30d)")
|
flags.Var(&filter.Since, "since", "restrict to alerts newer than since (ie. 4h, 30d)")
|
||||||
flags.Var(&filter.Until, "until", "restrict to alerts older than until (ie. 4h, 30d)")
|
flags.Var(&filter.Until, "until", "restrict to alerts older than until (ie. 4h, 30d)")
|
||||||
flags.StringVarP(filter.TypeEquals, "type", "t", "", "restrict to this decision type (ie. ban,captcha)")
|
flags.StringVarP(&filter.TypeEquals, "type", "t", "", "restrict to this decision type (ie. ban,captcha)")
|
||||||
flags.StringVar(filter.ScopeEquals, "scope", "", "restrict to this scope (ie. ip,range,session)")
|
flags.StringVar(&filter.ScopeEquals, "scope", "", "restrict to this scope (ie. ip,range,session)")
|
||||||
flags.StringVar(filter.OriginEquals, "origin", "", fmt.Sprintf("the value to match for the specified origin (%s ...)", strings.Join(types.GetOrigins(), ",")))
|
flags.StringVar(&filter.OriginEquals, "origin", "", fmt.Sprintf("the value to match for the specified origin (%s ...)", strings.Join(types.GetOrigins(), ",")))
|
||||||
flags.StringVarP(filter.ValueEquals, "value", "v", "", "restrict to this value (ie. 1.2.3.4,userName)")
|
flags.StringVarP(&filter.ValueEquals, "value", "v", "", "restrict to this value (ie. 1.2.3.4,userName)")
|
||||||
flags.StringVarP(filter.ScenarioEquals, "scenario", "s", "", "restrict to this scenario (ie. crowdsecurity/ssh-bf)")
|
flags.StringVarP(&filter.ScenarioEquals, "scenario", "s", "", "restrict to this scenario (ie. crowdsecurity/ssh-bf)")
|
||||||
flags.StringVarP(filter.IPEquals, "ip", "i", "", "restrict to alerts from this source ip (shorthand for --scope ip --value <IP>)")
|
flags.StringVarP(&filter.IPEquals, "ip", "i", "", "restrict to alerts from this source ip (shorthand for --scope ip --value <IP>)")
|
||||||
flags.StringVarP(filter.RangeEquals, "range", "r", "", "restrict to alerts from this source range (shorthand for --scope range --value <RANGE>)")
|
flags.StringVarP(&filter.RangeEquals, "range", "r", "", "restrict to alerts from this source range (shorthand for --scope range --value <RANGE>)")
|
||||||
flags.IntVarP(filter.Limit, "limit", "l", 100, "number of alerts to get (use 0 to remove the limit)")
|
flags.IntVarP(filter.Limit, "limit", "l", 100, "number of alerts to get (use 0 to remove the limit)")
|
||||||
flags.BoolVar(NoSimu, "no-simu", false, "exclude decisions in simulation mode")
|
flags.BoolVar(NoSimu, "no-simu", false, "exclude decisions in simulation mode")
|
||||||
flags.BoolVarP(&printMachine, "machine", "m", false, "print machines that triggered decisions")
|
flags.BoolVarP(&printMachine, "machine", "m", false, "print machines that triggered decisions")
|
||||||
|
@ -428,39 +400,11 @@ func (cli *cliDecisions) delete(ctx context.Context, delFilter apiclient.Decisio
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
/*take care of shorthand options*/
|
/*take care of shorthand options*/
|
||||||
*delFilter.ScopeEquals, err = clialert.SanitizeScope(*delFilter.ScopeEquals, *delFilter.IPEquals, *delFilter.RangeEquals)
|
delFilter.ScopeEquals, err = clialert.SanitizeScope(delFilter.ScopeEquals, delFilter.IPEquals, delFilter.RangeEquals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if *delFilter.ScopeEquals == "" {
|
|
||||||
delFilter.ScopeEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *delFilter.OriginEquals == "" {
|
|
||||||
delFilter.OriginEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *delFilter.ValueEquals == "" {
|
|
||||||
delFilter.ValueEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *delFilter.ScenarioEquals == "" {
|
|
||||||
delFilter.ScenarioEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *delFilter.TypeEquals == "" {
|
|
||||||
delFilter.TypeEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *delFilter.IPEquals == "" {
|
|
||||||
delFilter.IPEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if *delFilter.RangeEquals == "" {
|
|
||||||
delFilter.RangeEquals = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if contained != nil && *contained {
|
if contained != nil && *contained {
|
||||||
delFilter.Contains = new(bool)
|
delFilter.Contains = new(bool)
|
||||||
}
|
}
|
||||||
|
@ -490,13 +434,13 @@ func (cli *cliDecisions) delete(ctx context.Context, delFilter apiclient.Decisio
|
||||||
|
|
||||||
func (cli *cliDecisions) newDeleteCmd() *cobra.Command {
|
func (cli *cliDecisions) newDeleteCmd() *cobra.Command {
|
||||||
delFilter := apiclient.DecisionsDeleteOpts{
|
delFilter := apiclient.DecisionsDeleteOpts{
|
||||||
ScopeEquals: new(string),
|
ScopeEquals: "",
|
||||||
ValueEquals: new(string),
|
ValueEquals: "",
|
||||||
TypeEquals: new(string),
|
TypeEquals: "",
|
||||||
IPEquals: new(string),
|
IPEquals: "",
|
||||||
RangeEquals: new(string),
|
RangeEquals: "",
|
||||||
ScenarioEquals: new(string),
|
ScenarioEquals: "",
|
||||||
OriginEquals: new(string),
|
OriginEquals: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
var delDecisionID string
|
var delDecisionID string
|
||||||
|
@ -522,10 +466,10 @@ cscli decisions delete --origin lists --scenario list_name
|
||||||
if delDecisionAll {
|
if delDecisionAll {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if *delFilter.ScopeEquals == "" && *delFilter.ValueEquals == "" &&
|
if delFilter.ScopeEquals == "" && delFilter.ValueEquals == "" &&
|
||||||
*delFilter.TypeEquals == "" && *delFilter.IPEquals == "" &&
|
delFilter.TypeEquals == "" && delFilter.IPEquals == "" &&
|
||||||
*delFilter.RangeEquals == "" && *delFilter.ScenarioEquals == "" &&
|
delFilter.RangeEquals == "" && delFilter.ScenarioEquals == "" &&
|
||||||
*delFilter.OriginEquals == "" && delDecisionID == "" {
|
delFilter.OriginEquals == "" && delDecisionID == "" {
|
||||||
_ = cmd.Usage()
|
_ = cmd.Usage()
|
||||||
return errors.New("at least one filter or --all must be specified")
|
return errors.New("at least one filter or --all must be specified")
|
||||||
}
|
}
|
||||||
|
@ -539,12 +483,12 @@ cscli decisions delete --origin lists --scenario list_name
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
flags.SortFlags = false
|
flags.SortFlags = false
|
||||||
flags.StringVarP(delFilter.IPEquals, "ip", "i", "", "Source ip (shorthand for --scope ip --value <IP>)")
|
flags.StringVarP(&delFilter.IPEquals, "ip", "i", "", "Source ip (shorthand for --scope ip --value <IP>)")
|
||||||
flags.StringVarP(delFilter.RangeEquals, "range", "r", "", "Range source ip (shorthand for --scope range --value <RANGE>)")
|
flags.StringVarP(&delFilter.RangeEquals, "range", "r", "", "Range source ip (shorthand for --scope range --value <RANGE>)")
|
||||||
flags.StringVarP(delFilter.TypeEquals, "type", "t", "", "the decision type (ie. ban,captcha)")
|
flags.StringVarP(&delFilter.TypeEquals, "type", "t", "", "the decision type (ie. ban,captcha)")
|
||||||
flags.StringVarP(delFilter.ValueEquals, "value", "v", "", "the value to match for in the specified scope")
|
flags.StringVarP(&delFilter.ValueEquals, "value", "v", "", "the value to match for in the specified scope")
|
||||||
flags.StringVarP(delFilter.ScenarioEquals, "scenario", "s", "", "the scenario name (ie. crowdsecurity/ssh-bf)")
|
flags.StringVarP(&delFilter.ScenarioEquals, "scenario", "s", "", "the scenario name (ie. crowdsecurity/ssh-bf)")
|
||||||
flags.StringVar(delFilter.OriginEquals, "origin", "", fmt.Sprintf("the value to match for the specified origin (%s ...)", strings.Join(types.GetOrigins(), ",")))
|
flags.StringVar(&delFilter.OriginEquals, "origin", "", fmt.Sprintf("the value to match for the specified origin (%s ...)", strings.Join(types.GetOrigins(), ",")))
|
||||||
|
|
||||||
flags.StringVar(&delDecisionID, "id", "", "decision id")
|
flags.StringVar(&delDecisionID, "id", "", "decision id")
|
||||||
flags.BoolVar(&delDecisionAll, "all", false, "delete all decisions")
|
flags.BoolVar(&delDecisionAll, "all", false, "delete all decisions")
|
||||||
|
|
|
@ -15,14 +15,14 @@ import (
|
||||||
type AlertsService service
|
type AlertsService service
|
||||||
|
|
||||||
type AlertsListOpts struct {
|
type AlertsListOpts struct {
|
||||||
ScopeEquals *string `url:"scope,omitempty"`
|
ScopeEquals string `url:"scope,omitempty"`
|
||||||
ValueEquals *string `url:"value,omitempty"`
|
ValueEquals string `url:"value,omitempty"`
|
||||||
ScenarioEquals *string `url:"scenario,omitempty"`
|
ScenarioEquals string `url:"scenario,omitempty"`
|
||||||
IPEquals *string `url:"ip,omitempty"`
|
IPEquals string `url:"ip,omitempty"`
|
||||||
RangeEquals *string `url:"range,omitempty"`
|
RangeEquals string `url:"range,omitempty"`
|
||||||
OriginEquals *string `url:"origin,omitempty"`
|
OriginEquals string `url:"origin,omitempty"`
|
||||||
Since cstime.DurationWithDays `url:"since,omitempty"`
|
Since cstime.DurationWithDays `url:"since,omitempty"`
|
||||||
TypeEquals *string `url:"decision_type,omitempty"`
|
TypeEquals string `url:"decision_type,omitempty"`
|
||||||
Until cstime.DurationWithDays `url:"until,omitempty"`
|
Until cstime.DurationWithDays `url:"until,omitempty"`
|
||||||
IncludeSimulated *bool `url:"simulated,omitempty"`
|
IncludeSimulated *bool `url:"simulated,omitempty"`
|
||||||
ActiveDecisionEquals *bool `url:"has_active_decision,omitempty"`
|
ActiveDecisionEquals *bool `url:"has_active_decision,omitempty"`
|
||||||
|
@ -33,16 +33,16 @@ type AlertsListOpts struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AlertsDeleteOpts struct {
|
type AlertsDeleteOpts struct {
|
||||||
ScopeEquals *string `url:"scope,omitempty"`
|
ScopeEquals string `url:"scope,omitempty"`
|
||||||
ValueEquals *string `url:"value,omitempty"`
|
ValueEquals string `url:"value,omitempty"`
|
||||||
ScenarioEquals *string `url:"scenario,omitempty"`
|
ScenarioEquals string `url:"scenario,omitempty"`
|
||||||
IPEquals *string `url:"ip,omitempty"`
|
IPEquals string `url:"ip,omitempty"`
|
||||||
RangeEquals *string `url:"range,omitempty"`
|
RangeEquals string `url:"range,omitempty"`
|
||||||
Since cstime.DurationWithDays `url:"since,omitempty"`
|
Since cstime.DurationWithDays `url:"since,omitempty"`
|
||||||
Until cstime.DurationWithDays `url:"until,omitempty"`
|
Until cstime.DurationWithDays `url:"until,omitempty"`
|
||||||
OriginEquals *string `url:"origin,omitempty"`
|
OriginEquals string `url:"origin,omitempty"`
|
||||||
ActiveDecisionEquals *bool `url:"has_active_decision,omitempty"`
|
ActiveDecisionEquals *bool `url:"has_active_decision,omitempty"`
|
||||||
SourceEquals *string `url:"alert_source,omitempty"`
|
SourceEquals string `url:"alert_source,omitempty"`
|
||||||
Contains *bool `url:"contains,omitempty"`
|
Contains *bool `url:"contains,omitempty"`
|
||||||
Limit *int `url:"limit,omitempty"`
|
Limit *int `url:"limit,omitempty"`
|
||||||
ListOpts
|
ListOpts
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
|
|
||||||
func TestAlertsListAsMachine(t *testing.T) {
|
func TestAlertsListAsMachine(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
|
|
||||||
mux, urlx, teardown := setup()
|
mux, urlx, teardown := setup()
|
||||||
|
@ -189,7 +190,7 @@ func TestAlertsListAsMachine(t *testing.T) {
|
||||||
assert.Equal(t, expected, *alerts)
|
assert.Equal(t, expected, *alerts)
|
||||||
|
|
||||||
// this one doesn't
|
// this one doesn't
|
||||||
filter := AlertsListOpts{IPEquals: ptr.Of("1.2.3.4")}
|
filter := AlertsListOpts{IPEquals: "1.2.3.4"}
|
||||||
|
|
||||||
alerts, resp, err = client.Alerts.List(ctx, filter)
|
alerts, resp, err = client.Alerts.List(ctx, filter)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -199,6 +200,7 @@ func TestAlertsListAsMachine(t *testing.T) {
|
||||||
|
|
||||||
func TestAlertsGetAsMachine(t *testing.T) {
|
func TestAlertsGetAsMachine(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
|
|
||||||
mux, urlx, teardown := setup()
|
mux, urlx, teardown := setup()
|
||||||
|
@ -367,6 +369,7 @@ func TestAlertsGetAsMachine(t *testing.T) {
|
||||||
|
|
||||||
func TestAlertsCreateAsMachine(t *testing.T) {
|
func TestAlertsCreateAsMachine(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
|
|
||||||
mux, urlx, teardown := setup()
|
mux, urlx, teardown := setup()
|
||||||
|
@ -410,6 +413,7 @@ func TestAlertsCreateAsMachine(t *testing.T) {
|
||||||
|
|
||||||
func TestAlertsDeleteAsMachine(t *testing.T) {
|
func TestAlertsDeleteAsMachine(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
|
|
||||||
mux, urlx, teardown := setup()
|
mux, urlx, teardown := setup()
|
||||||
|
@ -442,7 +446,7 @@ func TestAlertsDeleteAsMachine(t *testing.T) {
|
||||||
|
|
||||||
defer teardown()
|
defer teardown()
|
||||||
|
|
||||||
alert := AlertsDeleteOpts{IPEquals: ptr.Of("1.2.3.4")}
|
alert := AlertsDeleteOpts{IPEquals: "1.2.3.4"}
|
||||||
alerts, resp, err := client.Alerts.Delete(ctx, alert)
|
alerts, resp, err := client.Alerts.Delete(ctx, alert)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,11 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/crowdsecurity/go-cs-lib/cstest"
|
"github.com/crowdsecurity/go-cs-lib/cstest"
|
||||||
"github.com/crowdsecurity/go-cs-lib/ptr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestApiAuth(t *testing.T) {
|
func TestApiAuth(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
log.SetLevel(log.TraceLevel)
|
log.SetLevel(log.TraceLevel)
|
||||||
|
|
||||||
mux, urlx, teardown := setup()
|
mux, urlx, teardown := setup()
|
||||||
|
@ -40,7 +40,7 @@ func TestApiAuth(t *testing.T) {
|
||||||
|
|
||||||
defer teardown()
|
defer teardown()
|
||||||
|
|
||||||
//ok no answer
|
// ok no answer
|
||||||
auth := &APIKeyTransport{
|
auth := &APIKeyTransport{
|
||||||
APIKey: "ixu",
|
APIKey: "ixu",
|
||||||
}
|
}
|
||||||
|
@ -48,12 +48,12 @@ func TestApiAuth(t *testing.T) {
|
||||||
newcli, err := NewDefaultClient(apiURL, "v1", "toto", auth.Client())
|
newcli, err := NewDefaultClient(apiURL, "v1", "toto", auth.Client())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
alert := DecisionsListOpts{IPEquals: ptr.Of("1.2.3.4")}
|
alert := DecisionsListOpts{IPEquals: "1.2.3.4"}
|
||||||
_, resp, err := newcli.Decisions.List(ctx, alert)
|
_, resp, err := newcli.Decisions.List(ctx, alert)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, http.StatusOK, resp.Response.StatusCode)
|
assert.Equal(t, http.StatusOK, resp.Response.StatusCode)
|
||||||
|
|
||||||
//ko bad token
|
// ko bad token
|
||||||
auth = &APIKeyTransport{
|
auth = &APIKeyTransport{
|
||||||
APIKey: "bad",
|
APIKey: "bad",
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func TestApiAuth(t *testing.T) {
|
||||||
|
|
||||||
cstest.RequireErrorMessage(t, err, "API error: access forbidden")
|
cstest.RequireErrorMessage(t, err, "API error: access forbidden")
|
||||||
|
|
||||||
//ko empty token
|
// ko empty token
|
||||||
auth = &APIKeyTransport{}
|
auth = &APIKeyTransport{}
|
||||||
|
|
||||||
newcli, err = NewDefaultClient(apiURL, "v1", "toto", auth.Client())
|
newcli, err = NewDefaultClient(apiURL, "v1", "toto", auth.Client())
|
||||||
|
|
|
@ -20,12 +20,12 @@ import (
|
||||||
type DecisionsService service
|
type DecisionsService service
|
||||||
|
|
||||||
type DecisionsListOpts struct {
|
type DecisionsListOpts struct {
|
||||||
ScopeEquals *string `url:"scope,omitempty"`
|
ScopeEquals string `url:"scope,omitempty"`
|
||||||
ValueEquals *string `url:"value,omitempty"`
|
ValueEquals string `url:"value,omitempty"`
|
||||||
TypeEquals *string `url:"type,omitempty"`
|
TypeEquals string `url:"type,omitempty"`
|
||||||
IPEquals *string `url:"ip,omitempty"`
|
IPEquals string `url:"ip,omitempty"`
|
||||||
RangeEquals *string `url:"range,omitempty"`
|
RangeEquals string `url:"range,omitempty"`
|
||||||
Contains *bool `url:"contains,omitempty"`
|
Contains *bool `url:"contains,omitempty"`
|
||||||
ListOpts
|
ListOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,15 +60,15 @@ func (o *DecisionsStreamOpts) addQueryParamsToURL(url string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DecisionsDeleteOpts struct {
|
type DecisionsDeleteOpts struct {
|
||||||
ScopeEquals *string `url:"scope,omitempty"`
|
ScopeEquals string `url:"scope,omitempty"`
|
||||||
ValueEquals *string `url:"value,omitempty"`
|
ValueEquals string `url:"value,omitempty"`
|
||||||
TypeEquals *string `url:"type,omitempty"`
|
TypeEquals string `url:"type,omitempty"`
|
||||||
IPEquals *string `url:"ip,omitempty"`
|
IPEquals string `url:"ip,omitempty"`
|
||||||
RangeEquals *string `url:"range,omitempty"`
|
RangeEquals string `url:"range,omitempty"`
|
||||||
Contains *bool `url:"contains,omitempty"`
|
Contains *bool `url:"contains,omitempty"`
|
||||||
OriginEquals *string `url:"origin,omitempty"`
|
OriginEquals string `url:"origin,omitempty"`
|
||||||
//
|
//
|
||||||
ScenarioEquals *string `url:"scenario,omitempty"`
|
ScenarioEquals string `url:"scenario,omitempty"`
|
||||||
ListOpts
|
ListOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
|
|
||||||
func TestDecisionsList(t *testing.T) {
|
func TestDecisionsList(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
|
|
||||||
mux, urlx, teardown := setup()
|
mux, urlx, teardown := setup()
|
||||||
|
@ -64,15 +65,13 @@ func TestDecisionsList(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OK decisions
|
// OK decisions
|
||||||
decisionsFilter := DecisionsListOpts{IPEquals: ptr.Of("1.2.3.4")}
|
decisions, resp, err := newcli.Decisions.List(ctx, DecisionsListOpts{IPEquals: "1.2.3.4"})
|
||||||
decisions, resp, err := newcli.Decisions.List(ctx, decisionsFilter)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, http.StatusOK, resp.Response.StatusCode)
|
assert.Equal(t, http.StatusOK, resp.Response.StatusCode)
|
||||||
assert.Equal(t, *expected, *decisions)
|
assert.Equal(t, *expected, *decisions)
|
||||||
|
|
||||||
// Empty return
|
// Empty return
|
||||||
decisionsFilter = DecisionsListOpts{IPEquals: ptr.Of("1.2.3.5")}
|
decisions, resp, err = newcli.Decisions.List(ctx, DecisionsListOpts{IPEquals: "1.2.3.5"})
|
||||||
decisions, resp, err = newcli.Decisions.List(ctx, decisionsFilter)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, http.StatusOK, resp.Response.StatusCode)
|
assert.Equal(t, http.StatusOK, resp.Response.StatusCode)
|
||||||
assert.Empty(t, *decisions)
|
assert.Empty(t, *decisions)
|
||||||
|
@ -80,6 +79,7 @@ func TestDecisionsList(t *testing.T) {
|
||||||
|
|
||||||
func TestDecisionsStream(t *testing.T) {
|
func TestDecisionsStream(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
|
|
||||||
mux, urlx, teardown := setup()
|
mux, urlx, teardown := setup()
|
||||||
|
@ -156,6 +156,7 @@ func TestDecisionsStream(t *testing.T) {
|
||||||
|
|
||||||
func TestDecisionsStreamV3Compatibility(t *testing.T) {
|
func TestDecisionsStreamV3Compatibility(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
|
|
||||||
mux, urlx, teardown := setupWithPrefix("v3")
|
mux, urlx, teardown := setupWithPrefix("v3")
|
||||||
|
@ -224,6 +225,7 @@ func TestDecisionsStreamV3Compatibility(t *testing.T) {
|
||||||
|
|
||||||
func TestDecisionsStreamV3(t *testing.T) {
|
func TestDecisionsStreamV3(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
|
|
||||||
mux, urlx, teardown := setupWithPrefix("v3")
|
mux, urlx, teardown := setupWithPrefix("v3")
|
||||||
|
@ -297,6 +299,7 @@ func TestDecisionsStreamV3(t *testing.T) {
|
||||||
|
|
||||||
func TestDecisionsFromBlocklist(t *testing.T) {
|
func TestDecisionsFromBlocklist(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
|
|
||||||
mux, urlx, teardown := setupWithPrefix("v3")
|
mux, urlx, teardown := setupWithPrefix("v3")
|
||||||
|
@ -429,10 +432,7 @@ func TestDeleteDecisions(t *testing.T) {
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
filters := DecisionsDeleteOpts{IPEquals: new(string)}
|
deleted, _, err := client.Decisions.Delete(ctx, DecisionsDeleteOpts{IPEquals: "1.2.3.4"})
|
||||||
*filters.IPEquals = "1.2.3.4"
|
|
||||||
|
|
||||||
deleted, _, err := client.Decisions.Delete(ctx, filters)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, "1", deleted.NbDeleted)
|
assert.Equal(t, "1", deleted.NbDeleted)
|
||||||
}
|
}
|
||||||
|
|
|
@ -661,6 +661,8 @@ func fillAlertsWithDecisions(alerts []*models.Alert, decisions []*models.Decisio
|
||||||
func (a *apic) PullTop(ctx context.Context, forcePull bool) error {
|
func (a *apic) PullTop(ctx context.Context, forcePull bool) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
hasPulledAllowlists := false
|
||||||
|
|
||||||
// A mutex with TryLock would be a bit simpler
|
// A mutex with TryLock would be a bit simpler
|
||||||
// But go does not guarantee that TryLock will be able to acquire the lock even if it is available
|
// But go does not guarantee that TryLock will be able to acquire the lock even if it is available
|
||||||
select {
|
select {
|
||||||
|
@ -722,7 +724,7 @@ func (a *apic) PullTop(ctx context.Context, forcePull bool) error {
|
||||||
// process deleted decisions
|
// process deleted decisions
|
||||||
nbDeleted, err := a.HandleDeletedDecisionsV3(ctx, data.Deleted, deleteCounters)
|
nbDeleted, err := a.HandleDeletedDecisionsV3(ctx, data.Deleted, deleteCounters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Errorf("could not delete decisions from CAPI: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("capi/community-blocklist : %d explicit deletions", nbDeleted)
|
log.Printf("capi/community-blocklist : %d explicit deletions", nbDeleted)
|
||||||
|
@ -730,8 +732,9 @@ func (a *apic) PullTop(ctx context.Context, forcePull bool) error {
|
||||||
// Update allowlists before processing decisions
|
// Update allowlists before processing decisions
|
||||||
if data.Links != nil {
|
if data.Links != nil {
|
||||||
if len(data.Links.Allowlists) > 0 {
|
if len(data.Links.Allowlists) > 0 {
|
||||||
|
hasPulledAllowlists = true
|
||||||
if err := a.UpdateAllowlists(ctx, data.Links.Allowlists, forcePull); err != nil {
|
if err := a.UpdateAllowlists(ctx, data.Links.Allowlists, forcePull); err != nil {
|
||||||
return fmt.Errorf("while updating allowlists: %w", err)
|
log.Errorf("could not update allowlists from CAPI: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -748,7 +751,7 @@ func (a *apic) PullTop(ctx context.Context, forcePull bool) error {
|
||||||
|
|
||||||
err = a.SaveAlerts(ctx, alertsFromCapi, addCounters, deleteCounters)
|
err = a.SaveAlerts(ctx, alertsFromCapi, addCounters, deleteCounters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("while saving alerts: %w", err)
|
log.Errorf("could not save alert for CAPI pull: %s", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if a.pullCommunity {
|
if a.pullCommunity {
|
||||||
|
@ -762,11 +765,21 @@ func (a *apic) PullTop(ctx context.Context, forcePull bool) error {
|
||||||
if data.Links != nil {
|
if data.Links != nil {
|
||||||
if len(data.Links.Blocklists) > 0 {
|
if len(data.Links.Blocklists) > 0 {
|
||||||
if err := a.UpdateBlocklists(ctx, data.Links.Blocklists, addCounters, forcePull); err != nil {
|
if err := a.UpdateBlocklists(ctx, data.Links.Blocklists, addCounters, forcePull); err != nil {
|
||||||
return fmt.Errorf("while updating blocklists: %w", err)
|
log.Errorf("could not update blocklists from CAPI: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hasPulledAllowlists {
|
||||||
|
deleted, err := a.dbClient.ApplyAllowlistsToExistingDecisions(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("could not apply allowlists to existing decisions: %s", err)
|
||||||
|
}
|
||||||
|
if deleted > 0 {
|
||||||
|
log.Infof("deleted %d decisions from allowlists", deleted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,6 +264,14 @@ func ManagementCmd(message *Message, p *Papi, sync bool) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to force pull operation: %w", err)
|
return fmt.Errorf("failed to force pull operation: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleted, err := p.DBClient.ApplyAllowlistsToExistingDecisions(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("could not apply allowlists to existing decisions: %s", err)
|
||||||
|
}
|
||||||
|
if deleted > 0 {
|
||||||
|
log.Infof("deleted %d decisions from allowlists", deleted)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case "allowlist_unsubscribe":
|
case "allowlist_unsubscribe":
|
||||||
data, err := json.Marshal(message.Data)
|
data, err := json.Marshal(message.Data)
|
||||||
|
|
|
@ -12,6 +12,8 @@ import (
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
|
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/database/ent/allowlist"
|
"github.com/crowdsecurity/crowdsec/pkg/database/ent/allowlist"
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/database/ent/allowlistitem"
|
"github.com/crowdsecurity/crowdsec/pkg/database/ent/allowlistitem"
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/database/ent/decision"
|
||||||
|
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||||
)
|
)
|
||||||
|
@ -389,3 +391,96 @@ func (c *Client) GetAllowlistsContentForAPIC(ctx context.Context) ([]net.IP, []*
|
||||||
|
|
||||||
return ips, nets, nil
|
return ips, nets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) ApplyAllowlistsToExistingDecisions(ctx context.Context) (int, error) {
|
||||||
|
// Soft delete (set expiration to now) all decisions that matches any allowlist
|
||||||
|
|
||||||
|
totalCount := 0
|
||||||
|
|
||||||
|
// Get all non-expired allowlist items
|
||||||
|
// We will match them one by one against all decisions
|
||||||
|
allowlistItems, err := c.Ent.AllowListItem.Query().
|
||||||
|
Where(
|
||||||
|
allowlistitem.Or(
|
||||||
|
allowlistitem.ExpiresAtGTE(time.Now().UTC()),
|
||||||
|
allowlistitem.ExpiresAtIsNil(),
|
||||||
|
),
|
||||||
|
).All(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("unable to get allowlist items: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().UTC()
|
||||||
|
|
||||||
|
for _, item := range allowlistItems {
|
||||||
|
updateQuery := c.Ent.Decision.Update().SetUntil(now).Where(decision.UntilGTE(now))
|
||||||
|
switch item.IPSize {
|
||||||
|
case 4:
|
||||||
|
updateQuery = updateQuery.Where(
|
||||||
|
decision.And(
|
||||||
|
decision.IPSizeEQ(4),
|
||||||
|
decision.Or(
|
||||||
|
decision.And(
|
||||||
|
decision.StartIPLTE(item.StartIP),
|
||||||
|
decision.EndIPGTE(item.EndIP),
|
||||||
|
),
|
||||||
|
decision.And(
|
||||||
|
decision.StartIPGTE(item.StartIP),
|
||||||
|
decision.EndIPLTE(item.EndIP),
|
||||||
|
),
|
||||||
|
)))
|
||||||
|
case 16:
|
||||||
|
updateQuery = updateQuery.Where(
|
||||||
|
decision.And(
|
||||||
|
decision.IPSizeEQ(16),
|
||||||
|
decision.Or(
|
||||||
|
decision.And(
|
||||||
|
decision.Or(
|
||||||
|
decision.StartIPLT(item.StartIP),
|
||||||
|
decision.And(
|
||||||
|
decision.StartIPEQ(item.StartIP),
|
||||||
|
decision.StartSuffixLTE(item.StartSuffix),
|
||||||
|
)),
|
||||||
|
decision.Or(
|
||||||
|
decision.EndIPGT(item.EndIP),
|
||||||
|
decision.And(
|
||||||
|
decision.EndIPEQ(item.EndIP),
|
||||||
|
decision.EndSuffixGTE(item.EndSuffix),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
decision.And(
|
||||||
|
decision.Or(
|
||||||
|
decision.StartIPGT(item.StartIP),
|
||||||
|
decision.And(
|
||||||
|
decision.StartIPEQ(item.StartIP),
|
||||||
|
decision.StartSuffixGTE(item.StartSuffix),
|
||||||
|
)),
|
||||||
|
decision.Or(
|
||||||
|
decision.EndIPLT(item.EndIP),
|
||||||
|
decision.And(
|
||||||
|
decision.EndIPEQ(item.EndIP),
|
||||||
|
decision.EndSuffixLTE(item.EndSuffix),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
// This should never happen
|
||||||
|
// But better safe than sorry and just skip it instead of expiring all decisions
|
||||||
|
c.Log.Errorf("unexpected IP size %d for allowlist item %s", item.IPSize, item.Value)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Update the decisions
|
||||||
|
count, err := updateQuery.Save(ctx)
|
||||||
|
if err != nil {
|
||||||
|
c.Log.Errorf("unable to expire existing decisions: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
totalCount += count
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalCount, nil
|
||||||
|
}
|
||||||
|
|
|
@ -246,3 +246,58 @@ teardown() {
|
||||||
rune -0 jq 'del(.created_at) | del(.updated_at) | del(.items.[].created_at) | del(.items.[].expiration)' <(output)
|
rune -0 jq 'del(.created_at) | del(.updated_at) | del(.items.[].created_at) | del(.items.[].expiration)' <(output)
|
||||||
assert_json '{"description":"a foo","items":[],"name":"foo"}'
|
assert_json '{"description":"a foo","items":[],"name":"foo"}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "allowlists expire active decisions" {
|
||||||
|
rune -0 cscli decisions add -i 1.2.3.4
|
||||||
|
rune -0 cscli decisions add -r 2.3.4.0/24
|
||||||
|
rune -0 cscli decisions add -i 5.4.3.42
|
||||||
|
rune -0 cscli decisions add -r 6.5.4.0/24
|
||||||
|
rune -0 cscli decisions add -r 10.0.0.0/23
|
||||||
|
|
||||||
|
rune -0 cscli decisions list -o json
|
||||||
|
rune -0 jq -r 'sort_by(.decisions[].value) | .[].decisions[0].value' <(output)
|
||||||
|
assert_output - <<-EOT
|
||||||
|
1.2.3.4
|
||||||
|
10.0.0.0/23
|
||||||
|
2.3.4.0/24
|
||||||
|
5.4.3.42
|
||||||
|
6.5.4.0/24
|
||||||
|
EOT
|
||||||
|
|
||||||
|
rune -0 cscli allowlists create foo -d "foo"
|
||||||
|
|
||||||
|
# add an allowlist that matches exactly
|
||||||
|
rune -0 cscli allowlists add foo 1.2.3.4
|
||||||
|
if is_db_mysql; then sleep 2; fi
|
||||||
|
# it should not be here anymore
|
||||||
|
rune -0 cscli decisions list -o json
|
||||||
|
rune -0 jq -e 'any(.[].decisions[]; .value == "1.2.3.4") | not' <(output)
|
||||||
|
|
||||||
|
# allowlist an IP belonging to a range
|
||||||
|
rune -0 cscli allowlist add foo 2.3.4.42
|
||||||
|
if is_db_mysql; then sleep 2; fi
|
||||||
|
rune -0 cscli decisions list -o json
|
||||||
|
rune -0 jq -e 'any(.[].decisions[]; .value == "2.3.4.0/24") | not' <(output)
|
||||||
|
|
||||||
|
# allowlist a range with an active decision inside
|
||||||
|
rune -0 cscli allowlist add foo 5.4.3.0/24
|
||||||
|
if is_db_mysql; then sleep 2; fi
|
||||||
|
rune -0 cscli decisions list -o json
|
||||||
|
rune -0 jq -e 'any(.[].decisions[]; .value == "5.4.3.42") | not' <(output)
|
||||||
|
|
||||||
|
# allowlist a range inside a range for which we have a decision
|
||||||
|
rune -0 cscli allowlist add foo 6.5.4.0/25
|
||||||
|
if is_db_mysql; then sleep 2; fi
|
||||||
|
rune -0 cscli decisions list -o json
|
||||||
|
rune -0 jq -e 'any(.[].decisions[]; .value == "6.5.4.0/24") | not' <(output)
|
||||||
|
|
||||||
|
# allowlist a range bigger than a range for which we have a decision
|
||||||
|
rune -0 cscli allowlist add foo 10.0.0.0/24
|
||||||
|
if is_db_mysql; then sleep 2; fi
|
||||||
|
rune -0 cscli decisions list -o json
|
||||||
|
rune -0 jq -e 'any(.[].decisions[]; .value == "10.0.0.0/24") | not' <(output)
|
||||||
|
|
||||||
|
# sanity check no more active decisions
|
||||||
|
rune -0 cscli decisions list -o json
|
||||||
|
assert_json []
|
||||||
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ load_init_data() {
|
||||||
|
|
||||||
dump_backend="$(cat "${LOCAL_INIT_DIR}/.backend")"
|
dump_backend="$(cat "${LOCAL_INIT_DIR}/.backend")"
|
||||||
if [[ "${DB_BACKEND}" != "${dump_backend}" ]]; then
|
if [[ "${DB_BACKEND}" != "${dump_backend}" ]]; then
|
||||||
die "Can't run with backend '${DB_BACKEND}' because the test data was built with '${dump_backend}'"
|
die "Can't run with backend '${DB_BACKEND}' because 'make bats-fixture' was ran with '${dump_backend}'"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
remove_init_data
|
remove_init_data
|
||||||
|
|
|
@ -168,7 +168,7 @@ load_init_data() {
|
||||||
|
|
||||||
dump_backend="$(cat "${LOCAL_INIT_DIR}/.backend")"
|
dump_backend="$(cat "${LOCAL_INIT_DIR}/.backend")"
|
||||||
if [[ "${DB_BACKEND}" != "${dump_backend}" ]]; then
|
if [[ "${DB_BACKEND}" != "${dump_backend}" ]]; then
|
||||||
die "Can't run with backend '${DB_BACKEND}' because the test data was built with '${dump_backend}'"
|
die "Can't run with backend '${DB_BACKEND}' because 'make bats-fixture' was ran with '${dump_backend}'"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
remove_init_data
|
remove_init_data
|
||||||
|
|
|
@ -26,7 +26,7 @@ fi
|
||||||
|
|
||||||
dump_backend="$(cat "$LOCAL_INIT_DIR/.backend")"
|
dump_backend="$(cat "$LOCAL_INIT_DIR/.backend")"
|
||||||
if [[ "$DB_BACKEND" != "$dump_backend" ]]; then
|
if [[ "$DB_BACKEND" != "$dump_backend" ]]; then
|
||||||
die "Can't run with backend '$DB_BACKEND' because the test data was build with '$dump_backend'"
|
die "Can't run with backend '$DB_BACKEND' because 'make bats-fixture' was ran with '$dump_backend'"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $# -ge 1 ]]; then
|
if [[ $# -ge 1 ]]; then
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue