mirror of
https://github.com/crowdsecurity/crowdsec.git
synced 2025-05-11 12:25:53 +02:00
Merge branch 'master' into releases/1.6.x
This commit is contained in:
commit
bc8a255f89
94 changed files with 1235 additions and 595 deletions
|
@ -214,7 +214,6 @@ linters-settings:
|
|||
enable-all: true
|
||||
disabled-checks:
|
||||
- paramTypeCombine
|
||||
- httpNoBody
|
||||
- ifElseChain
|
||||
- importShadow
|
||||
- hugeParam
|
||||
|
|
|
@ -21,7 +21,7 @@ stages:
|
|||
- task: GoTool@0
|
||||
displayName: "Install Go"
|
||||
inputs:
|
||||
version: '1.24.0'
|
||||
version: '1.24.1'
|
||||
|
||||
- pwsh: |
|
||||
choco install -y make
|
||||
|
|
|
@ -6,22 +6,49 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// MinimumNArgs is a drop-in replacement for cobra.MinimumNArgs that prints the usage in case of wrong number of arguments, but not other errors.
|
||||
func MinimumNArgs(n int) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < n {
|
||||
cmd.Help() //nolint:errcheck
|
||||
_ = cmd.Help()
|
||||
fmt.Fprintln(cmd.OutOrStdout(), "")
|
||||
return fmt.Errorf("requires at least %d arg(s), only received %d", n, len(args))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MaximumNArgs is a drop-in replacement for cobra.MaximumNArgs that prints the usage in case of wrong number of arguments, but not other errors.
|
||||
func MaximumNArgs(n int) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > n {
|
||||
_ = cmd.Help()
|
||||
fmt.Fprintln(cmd.OutOrStdout(), "")
|
||||
return fmt.Errorf("accepts at most %d arg(s), received %d", n, len(args))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ExactArgs is a drop-in replacement for cobra.ExactArgs that prints the usage in case of wrong number of arguments, but not other errors.
|
||||
func ExactArgs(n int) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != n {
|
||||
cmd.Help() //nolint:errcheck
|
||||
_ = cmd.Help()
|
||||
fmt.Fprintln(cmd.OutOrStdout(), "")
|
||||
return fmt.Errorf("accepts %d arg(s), received %d", n, len(args))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NoArgs is a drop-in replacement for cobra.NoArgs that prints the usage in case of wrong number of arguments, but not other errors.
|
||||
func NoArgs(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
_ = cmd.Help()
|
||||
fmt.Fprintln(cmd.OutOrStdout(), "")
|
||||
return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"github.com/crowdsecurity/go-cs-lib/maptools"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
|
||||
|
@ -200,7 +201,6 @@ func (cli *cliAlerts) NewCommand() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "alerts [action]",
|
||||
Short: "Manage alerts",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
Aliases: []string{"alert"},
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
|
@ -352,6 +352,7 @@ cscli alerts list --origin lists
|
|||
cscli alerts list -s crowdsecurity/ssh-bf
|
||||
cscli alerts list --type ban`,
|
||||
Long: `List alerts with optional filters`,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cli.list(cmd.Context(), alertListFilter, limit, contained, printMachine)
|
||||
|
@ -465,7 +466,7 @@ cscli alerts delete --range 1.2.3.0/24
|
|||
cscli alerts delete -s crowdsecurity/ssh-bf"`,
|
||||
DisableAutoGenTag: true,
|
||||
Aliases: []string{"remove"},
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
PreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
if deleteAll {
|
||||
return nil
|
||||
|
@ -545,12 +546,9 @@ func (cli *cliAlerts) newInspectCmd() *cobra.Command {
|
|||
Use: `inspect "alert_id"`,
|
||||
Short: `Show info about an alert`,
|
||||
Example: `cscli alerts inspect 123`,
|
||||
Args: args.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
_ = cmd.Help()
|
||||
return errors.New("missing alert_id")
|
||||
}
|
||||
return cli.inspect(cmd.Context(), details, args...)
|
||||
},
|
||||
}
|
||||
|
@ -572,6 +570,7 @@ func (cli *cliAlerts) newFlushCmd() *cobra.Command {
|
|||
Short: `Flush alerts
|
||||
/!\ This command can be used only on the same machine than the local API`,
|
||||
Example: `cscli alerts flush --max-items 1000 --max-age 7d`,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
|
|
@ -228,7 +228,6 @@ func (cli *cliAllowLists) NewCommand() *cobra.Command {
|
|||
Use: "allowlists [action]",
|
||||
Short: "Manage centralized allowlists",
|
||||
Aliases: []string{"allowlist"},
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
|
@ -294,7 +293,7 @@ func (cli *cliAllowLists) newListCmd() *cobra.Command {
|
|||
Use: "list",
|
||||
Example: `cscli allowlists list`,
|
||||
Short: "List all allowlists",
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
if err := cfg.LoadAPIClient(); err != nil {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
)
|
||||
|
@ -56,7 +57,7 @@ func (cli *cliBouncers) newAddCmd() *cobra.Command {
|
|||
Short: "add a single bouncer to the database",
|
||||
Example: `cscli bouncers add MyBouncerName
|
||||
cscli bouncers add MyBouncerName --key <random-key>`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cli.add(cmd.Context(), args[0], key)
|
||||
|
|
|
@ -34,7 +34,6 @@ func (cli *cliBouncers) NewCommand() *cobra.Command {
|
|||
Long: `To list/add/delete/prune bouncers.
|
||||
Note: This command requires database direct access, so is intended to be run on Local API/master.
|
||||
`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Aliases: []string{"bouncer"},
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
)
|
||||
|
@ -83,7 +84,7 @@ func (cli *cliBouncers) newDeleteCmd() *cobra.Command {
|
|||
Use: "delete MyBouncerName",
|
||||
Short: "delete bouncer(s) from the database",
|
||||
Example: `cscli bouncers delete "bouncer1" "bouncer2"`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Args: args.MinimumNArgs(1),
|
||||
Aliases: []string{"remove"},
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgsFunction: cli.validBouncerID,
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clientinfo"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
|
||||
|
@ -78,7 +79,7 @@ func (cli *cliBouncers) newInspectCmd() *cobra.Command {
|
|||
Use: "inspect [bouncer_name]",
|
||||
Short: "inspect a bouncer by name",
|
||||
Example: `cscli bouncers inspect "bouncer1"`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgsFunction: cli.validBouncerID,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
|
||||
|
@ -105,7 +106,7 @@ func (cli *cliBouncers) newListCmd() *cobra.Command {
|
|||
Use: "list",
|
||||
Short: "list all bouncers within the database",
|
||||
Example: `cscli bouncers list`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cli.List(cmd.Context(), color.Output, cli.db)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/ask"
|
||||
)
|
||||
|
||||
|
@ -68,7 +69,7 @@ func (cli *cliBouncers) newPruneCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "prune",
|
||||
Short: "prune multiple bouncers from the database",
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
Example: `cscli bouncers prune -d 45m
|
||||
cscli bouncers prune -d 45m --force`,
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/idgen"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/reload"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
|
@ -40,7 +41,6 @@ func (cli *cliCapi) NewCommand() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "capi [action]",
|
||||
Short: "Manage interaction with Central API (CAPI)",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
@ -139,7 +139,7 @@ func (cli *cliCapi) newRegisterCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "register",
|
||||
Short: "Register to Central API (CAPI)",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cli.register(cmd.Context(), capiUserPrefix, outputFile)
|
||||
|
@ -260,7 +260,7 @@ func (cli *cliCapi) newStatusCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Check status with the Central API (CAPI)",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
hub, err := require.Hub(cli.cfg(), nil)
|
||||
|
|
|
@ -24,7 +24,6 @@ func (cli *cliConfig) NewCommand(mergedConfigGetter mergedConfigGetter) *cobra.C
|
|||
cmd := &cobra.Command{
|
||||
Use: "config [command]",
|
||||
Short: "Allows to view current config",
|
||||
Args: cobra.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/fflag"
|
||||
)
|
||||
|
@ -121,7 +122,7 @@ func (cli *cliConfig) newFeatureFlagsCmd() *cobra.Command {
|
|||
Use: "feature-flags",
|
||||
Short: "Displays feature flag status",
|
||||
Long: `Displays the supported feature flags and their current status.`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.featureFlags(showRetired)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||
)
|
||||
|
@ -235,7 +236,7 @@ func (cli *cliConfig) newShowCmd() *cobra.Command {
|
|||
Use: "show",
|
||||
Short: "Displays current config",
|
||||
Long: `Displays the current cli configuration.`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
if err := cli.cfg().LoadAPIClient(); err != nil {
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
)
|
||||
|
||||
func (cli *cliConfig) showYAML(mergedConfig string) error {
|
||||
|
@ -15,7 +17,7 @@ func (cli *cliConfig) newShowYAMLCmd(mergedConfigGetter mergedConfigGetter) *cob
|
|||
cmd := &cobra.Command{
|
||||
Use: "show-yaml",
|
||||
Short: "Displays merged config.yaml + config.yaml.local",
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.showYAML(mergedConfigGetter())
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
|
@ -19,7 +20,9 @@ import (
|
|||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/crowdsecurity/go-cs-lib/ptr"
|
||||
"github.com/crowdsecurity/go-cs-lib/slicetools"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/reload"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
|
||||
|
@ -43,7 +46,6 @@ func (cli *cliConsole) NewCommand() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "console [action]",
|
||||
Short: "Manage interaction with Crowdsec console (https://app.crowdsec.net)",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
@ -75,45 +77,6 @@ func (cli *cliConsole) enroll(ctx context.Context, key string, name string, over
|
|||
return fmt.Errorf("could not parse CAPI URL: %w", err)
|
||||
}
|
||||
|
||||
enableOpts := []string{csconfig.SEND_MANUAL_SCENARIOS, csconfig.SEND_TAINTED_SCENARIOS}
|
||||
|
||||
if len(opts) != 0 {
|
||||
for _, opt := range opts {
|
||||
valid := false
|
||||
|
||||
if opt == "all" {
|
||||
enableOpts = csconfig.CONSOLE_CONFIGS
|
||||
break
|
||||
}
|
||||
|
||||
for _, availableOpt := range csconfig.CONSOLE_CONFIGS {
|
||||
if opt != availableOpt {
|
||||
continue
|
||||
}
|
||||
|
||||
valid = true
|
||||
enable := true
|
||||
|
||||
for _, enabledOpt := range enableOpts {
|
||||
if opt == enabledOpt {
|
||||
enable = false
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if enable {
|
||||
enableOpts = append(enableOpts, opt)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return fmt.Errorf("option %s doesn't exist", opt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hub, err := require.Hub(cfg, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -137,11 +100,11 @@ func (cli *cliConsole) enroll(ctx context.Context, key string, name string, over
|
|||
return nil
|
||||
}
|
||||
|
||||
if err := cli.setConsoleOpts(enableOpts, true); err != nil {
|
||||
if err := cli.setConsoleOpts(opts, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, opt := range enableOpts {
|
||||
for _, opt := range opts {
|
||||
log.Infof("Enabled %s : %s", opt, csconfig.CONSOLE_CONFIGS_HELP[opt])
|
||||
}
|
||||
|
||||
|
@ -151,11 +114,67 @@ func (cli *cliConsole) enroll(ctx context.Context, key string, name string, over
|
|||
return nil
|
||||
}
|
||||
|
||||
func optionFilterEnable(opts []string, enableOpts []string) ([]string, error) {
|
||||
if len(enableOpts) == 0 {
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
for _, opt := range enableOpts {
|
||||
if opt == "all" {
|
||||
opts = append(opts, csconfig.CONSOLE_CONFIGS...)
|
||||
// keep validating the rest of the option names
|
||||
continue
|
||||
}
|
||||
|
||||
if !slices.Contains(csconfig.CONSOLE_CONFIGS, opt) {
|
||||
return nil, fmt.Errorf("option %s doesn't exist", opt)
|
||||
}
|
||||
|
||||
opts = append(opts, opt)
|
||||
}
|
||||
|
||||
opts = slicetools.Deduplicate(opts)
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func optionFilterDisable(opts []string, disableOpts []string) ([]string, error) {
|
||||
if len(disableOpts) == 0 {
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
for _, opt := range disableOpts {
|
||||
if opt == "all" {
|
||||
opts = []string{}
|
||||
// keep validating the rest of the option names
|
||||
continue
|
||||
}
|
||||
|
||||
if !slices.Contains(csconfig.CONSOLE_CONFIGS, opt) {
|
||||
return nil, fmt.Errorf("option %s doesn't exist", opt)
|
||||
}
|
||||
|
||||
// discard all elements == opt
|
||||
|
||||
j := 0
|
||||
for _, o := range opts {
|
||||
if o != opt {
|
||||
opts[j] = o
|
||||
j++
|
||||
}
|
||||
}
|
||||
opts = opts[:j]
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func (cli *cliConsole) newEnrollCmd() *cobra.Command {
|
||||
name := ""
|
||||
overwrite := false
|
||||
tags := []string{}
|
||||
opts := []string{}
|
||||
enableOpts := []string{}
|
||||
disableOpts := []string{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "enroll [enroll-key]",
|
||||
|
@ -168,12 +187,25 @@ After running this command your will need to validate the enrollment in the weba
|
|||
Example: fmt.Sprintf(`cscli console enroll YOUR-ENROLL-KEY
|
||||
cscli console enroll --name [instance_name] YOUR-ENROLL-KEY
|
||||
cscli console enroll --name [instance_name] --tags [tag_1] --tags [tag_2] YOUR-ENROLL-KEY
|
||||
cscli console enroll --enable context,manual YOUR-ENROLL-KEY
|
||||
cscli console enroll --enable console_management YOUR-ENROLL-KEY
|
||||
cscli console enroll --disable context YOUR-ENROLL-KEY
|
||||
|
||||
valid options are : %s,all (see 'cscli console status' for details)`, strings.Join(csconfig.CONSOLE_CONFIGS, ",")),
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts := []string{csconfig.SEND_MANUAL_SCENARIOS, csconfig.SEND_TAINTED_SCENARIOS, csconfig.SEND_CONTEXT}
|
||||
|
||||
opts, err := optionFilterEnable(opts, enableOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts, err = optionFilterDisable(opts, disableOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cli.enroll(cmd.Context(), args[0], name, overwrite, tags, opts)
|
||||
},
|
||||
}
|
||||
|
@ -182,7 +214,8 @@ After running this command your will need to validate the enrollment in the weba
|
|||
flags.StringVarP(&name, "name", "n", "", "Name to display in the console")
|
||||
flags.BoolVarP(&overwrite, "overwrite", "", false, "Force enroll the instance")
|
||||
flags.StringSliceVarP(&tags, "tags", "t", tags, "Tags to display in the console")
|
||||
flags.StringSliceVarP(&opts, "enable", "e", opts, "Enable console options")
|
||||
flags.StringSliceVarP(&enableOpts, "enable", "e", enableOpts, "Enable console options")
|
||||
flags.StringSliceVarP(&disableOpts, "disable", "d", disableOpts, "Disable console options")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -191,7 +224,7 @@ func (cli *cliConsole) newEnableCmd() *cobra.Command {
|
|||
var enableAll bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "enable [option]",
|
||||
Use: "enable [option]...",
|
||||
Short: "Enable a console option",
|
||||
Example: "sudo cscli console enable tainted",
|
||||
Long: `
|
||||
|
@ -244,6 +277,9 @@ Disable given information push to the central API.`,
|
|||
}
|
||||
log.Infof("All features have been disabled")
|
||||
} else {
|
||||
if len(args) == 0 {
|
||||
return errors.New("you must specify at least one feature to disable")
|
||||
}
|
||||
if err := cli.setConsoleOpts(args, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -267,6 +303,7 @@ func (cli *cliConsole) newStatusCmd() *cobra.Command {
|
|||
Use: "status",
|
||||
Short: "Shows status of the console options",
|
||||
Example: `sudo cscli console status`,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clialert"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
|
@ -136,7 +137,6 @@ func (cli *cliDecisions) NewCommand() *cobra.Command {
|
|||
Example: `cscli decisions [action] [filter]`,
|
||||
Aliases: []string{"decision"},
|
||||
/*TBD example*/
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
@ -290,7 +290,7 @@ cscli decisions list -r 1.2.3.0/24
|
|||
cscli decisions list -s crowdsecurity/ssh-bf
|
||||
cscli decisions list --origin lists --scenario list_name
|
||||
`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cli.list(cmd.Context(), filter, NoSimu, contained, printMachine)
|
||||
|
@ -427,7 +427,7 @@ cscli decisions add --ip 1.2.3.4 --duration 24h --type captcha
|
|||
cscli decisions add --scope username --value foobar
|
||||
`,
|
||||
/*TBD : fix long and example*/
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cli.add(cmd.Context(), addIP, addRange, addDuration, addValue, addScope, addReason, addType, bypassAllowlist)
|
||||
|
@ -532,6 +532,7 @@ func (cli *cliDecisions) newDeleteCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "delete [options]",
|
||||
Short: "Delete decisions",
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
Aliases: []string{"remove"},
|
||||
Example: `cscli decisions delete -r 1.2.3.0/24
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/crowdsecurity/go-cs-lib/ptr"
|
||||
"github.com/crowdsecurity/go-cs-lib/slicetools"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
)
|
||||
|
@ -216,7 +217,7 @@ func (cli *cliDecisions) newImportCmd() *cobra.Command {
|
|||
Long: "expected format:\n" +
|
||||
"csv : any of duration,reason,scope,type,value, with a header line\n" +
|
||||
"json :" + "`{" + `"duration": "24h", "reason": "my_scenario", "scope": "ip", "type": "ban", "value": "x.y.z.z"` + "}`",
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
Example: `decisions.csv:
|
||||
duration,scope,value
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/dumps"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/hubtest"
|
||||
|
@ -80,7 +81,7 @@ cscli explain --log "Sep 19 18:33:22 scw-d95986 sshd[24347]: pam_unix(sshd:auth)
|
|||
cscli explain --dsn "file://myfile.log" --type nginx
|
||||
tail -n 5 myfile.log | cscli explain --type nginx -f -
|
||||
`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.run()
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/reload"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
|
@ -42,7 +43,6 @@ The Hub is managed by cscli, to get the latest hub files from [Crowdsec Hub](htt
|
|||
Example: `cscli hub list
|
||||
cscli hub update
|
||||
cscli hub upgrade`,
|
||||
Args: cobra.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ func (cli *cliHub) newListCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "list [-a]",
|
||||
Short: "List all installed configurations",
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
hub, err := require.Hub(cli.cfg(), log.StandardLogger())
|
||||
|
@ -152,7 +152,7 @@ cscli hub update
|
|||
|
||||
# Download a 4x bigger version with all item contents (effectively pre-caching item downloads, but not data files).
|
||||
cscli hub update --with-content`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
if cmd.Flags().Changed("with-content") {
|
||||
|
@ -228,7 +228,7 @@ cscli hub upgrade --force
|
|||
# Prompt for confirmation if running in an interactive terminal; otherwise, the option is ignored.
|
||||
cscli hub upgrade --interactive
|
||||
cscli hub upgrade -i`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cli.upgrade(cmd.Context(), interactive, dryRun, force)
|
||||
|
@ -276,7 +276,7 @@ func (cli *cliHub) newTypesCmd() *cobra.Command {
|
|||
Long: `
|
||||
List the types of supported hub items.
|
||||
`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.types()
|
||||
|
|
|
@ -4,13 +4,15 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
)
|
||||
|
||||
func (cli *cliHubTest) newCleanCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "clean",
|
||||
Short: "clean [test_name]",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Args: args.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
for _, testName := range args {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/hubtest"
|
||||
)
|
||||
|
||||
|
@ -151,6 +152,7 @@ func (cli *cliHubTest) newCoverageCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "coverage",
|
||||
Short: "coverage",
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.coverage(showScenarioCov, showParserCov, showAppsecCov, showOnlyPercent)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/hubtest"
|
||||
)
|
||||
|
||||
|
@ -30,7 +31,7 @@ func (cli *cliHubTest) newCreateCmd() *cobra.Command {
|
|||
Example: `cscli hubtest create my-awesome-test --type syslog
|
||||
cscli hubtest create my-nginx-custom-test --type nginx
|
||||
cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios crowdsecurity/http-probing`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
testName := args[0]
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
)
|
||||
|
||||
func (cli *cliHubTest) newEvalCmd() *cobra.Command {
|
||||
|
@ -11,8 +13,8 @@ func (cli *cliHubTest) newEvalCmd() *cobra.Command {
|
|||
|
||||
cmd := &cobra.Command{
|
||||
Use: "eval",
|
||||
Short: "eval [test_name]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "eval [test_name]...",
|
||||
Args: args.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
for _, testName := range args {
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/dumps"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
)
|
||||
|
||||
func (cli *cliHubTest) explain(testName string, details bool, skipOk bool) error {
|
||||
|
@ -58,7 +60,7 @@ func (cli *cliHubTest) newExplainCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "explain",
|
||||
Short: "explain [test_name]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
for _, testName := range args {
|
||||
|
|
|
@ -39,7 +39,6 @@ func (cli *cliHubTest) NewCommand() *cobra.Command {
|
|||
Use: "hubtest",
|
||||
Short: "Run functional tests on hub configurations",
|
||||
Long: "Run functional tests on hub configurations (parsers, scenarios, collections...)",
|
||||
Args: cobra.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
var err error
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/hubtest"
|
||||
)
|
||||
|
||||
|
@ -14,7 +15,7 @@ func (cli *cliHubTest) newInfoCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "info",
|
||||
Short: "info [test_name]",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Args: args.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
for _, testName := range args {
|
||||
|
|
|
@ -7,12 +7,15 @@ import (
|
|||
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
)
|
||||
|
||||
func (cli *cliHubTest) newListCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "list",
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
|
||||
)
|
||||
|
@ -171,7 +172,7 @@ func (cli cliItem) newInspectCmd() *cobra.Command {
|
|||
Short: cmp.Or(cli.inspectHelp.short, "Inspect given "+cli.oneOrMore),
|
||||
Long: cmp.Or(cli.inspectHelp.long, "Inspect the state of one or more "+cli.name),
|
||||
Example: cli.inspectHelp.example,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Args: args.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compInstalledItems(cli.name, args, toComplete, cli.cfg)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/reload"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
|
||||
|
@ -129,7 +130,7 @@ func (cli cliItem) newInstallCmd() *cobra.Command {
|
|||
Short: cmp.Or(cli.installHelp.short, "Install given "+cli.oneOrMore),
|
||||
Long: cmp.Or(cli.installHelp.long, fmt.Sprintf("Fetch and install one or more %s from the hub", cli.name)),
|
||||
Example: cli.installHelp.example,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Args: args.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compAllItems(cli.name, args, toComplete, cli.cfg)
|
||||
|
|
|
@ -46,7 +46,6 @@ func (cli cliItem) NewCommand() *cobra.Command {
|
|||
Short: cmp.Or(cli.help.short, "Manage hub "+cli.name),
|
||||
Long: cli.help.long,
|
||||
Example: cli.help.example,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Aliases: []string{cli.singular},
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/alertcontext"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||
|
@ -57,6 +58,7 @@ func (cli *cliLapi) newContextAddCmd() *cobra.Command {
|
|||
cscli lapi context add --key file_source --value evt.Line.Src
|
||||
cscli lapi context add --value evt.Meta.source_ip --value evt.Meta.target_user
|
||||
`,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
hub, err := require.Hub(cli.cfg(), nil)
|
||||
|
@ -98,6 +100,7 @@ func (cli *cliLapi) newContextStatusCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "List context to send with alerts",
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
|
|
@ -24,7 +24,6 @@ func (cli *cliLapi) NewCommand() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "lapi [action]",
|
||||
Short: "Manage interaction with Local API (LAPI)",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
if err := cli.cfg().LoadAPIClient(); err != nil {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/idgen"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/reload"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
|
||||
|
@ -107,7 +108,7 @@ func (cli *cliLapi) newRegisterCmd() *cobra.Command {
|
|||
Short: "Register a machine to Local API (LAPI)",
|
||||
Long: `Register your machine to the Local API (LAPI).
|
||||
Keep in mind the machine needs to be validated by an administrator on LAPI side to be effective.`,
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cli.register(cmd.Context(), apiURL, outputFile, machine, token)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/go-openapi/strfmt"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
|
@ -99,7 +100,7 @@ func (cli *cliLapi) newStatusCmd() *cobra.Command {
|
|||
cmdLapiStatus := &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Check authentication to Local API (LAPI)",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
hub, err := require.Hub(cli.cfg(), nil)
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/idgen"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
|
@ -134,6 +135,7 @@ func (cli *cliMachines) newAddCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "add a single machine to the database",
|
||||
Args: args.MaximumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
Long: `Register a new machine in the database. cscli should be on the same machine as LAPI.`,
|
||||
Example: `cscli machines add --auto
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database"
|
||||
)
|
||||
|
||||
|
@ -36,7 +37,7 @@ func (cli *cliMachines) newDeleteCmd() *cobra.Command {
|
|||
Use: "delete [machine_name]...",
|
||||
Short: "delete machine(s) by name",
|
||||
Example: `cscli machines delete "machine1" "machine2"`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Args: args.MinimumNArgs(1),
|
||||
Aliases: []string{"remove"},
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgsFunction: cli.validMachineID,
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clientinfo"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/cwhub"
|
||||
|
@ -156,7 +157,7 @@ func (cli *cliMachines) newInspectCmd() *cobra.Command {
|
|||
Use: "inspect [machine_name]",
|
||||
Short: "inspect a machine by name",
|
||||
Example: `cscli machines inspect "machine1"`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgsFunction: cli.validMachineID,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clientinfo"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database"
|
||||
|
@ -125,7 +126,7 @@ func (cli *cliMachines) newListCmd() *cobra.Command {
|
|||
Short: "list all machines in the database",
|
||||
Long: `list all machines in the database with their status and last heartbeat`,
|
||||
Example: `cscli machines list`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cli.List(cmd.Context(), color.Output, cli.db)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/ask"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
|
||||
)
|
||||
|
@ -80,7 +81,7 @@ func (cli *cliMachines) newPruneCmd() *cobra.Command {
|
|||
Example: `cscli machines prune
|
||||
cscli machines prune --duration 1h
|
||||
cscli machines prune --not-validated-only --force`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cli.prune(cmd.Context(), duration, notValidOnly, force)
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
)
|
||||
|
||||
func (cli *cliMachines) validate(ctx context.Context, machineID string) error {
|
||||
|
@ -24,7 +26,7 @@ func (cli *cliMachines) newValidateCmd() *cobra.Command {
|
|||
Short: "validate a machine to access the local API",
|
||||
Long: `validate a machine to access the local API.`,
|
||||
Example: `cscli machines validate "machine_name"`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cli.validate(cmd.Context(), args[0])
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/crowdsecurity/go-cs-lib/maptools"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable"
|
||||
)
|
||||
|
||||
|
@ -83,7 +84,7 @@ func (cli *cliMetrics) newListCmd() *cobra.Command {
|
|||
Use: "list",
|
||||
Short: "List available types of metrics.",
|
||||
Long: `List available types of metrics.`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cli.list()
|
||||
|
|
|
@ -36,7 +36,6 @@ cscli metrics --url http://lapi.local:6060/metrics show acquisition parsers
|
|||
|
||||
# List available metric types
|
||||
cscli metrics list`,
|
||||
Args: cobra.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cli.show(cmd.Context(), nil, url, noUnit)
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"github.com/crowdsecurity/go-cs-lib/ptr"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
|
@ -56,7 +57,6 @@ func (cli *cliNotifications) NewCommand() *cobra.Command {
|
|||
Use: "notifications [action]",
|
||||
Short: "Helper for notification plugin configuration",
|
||||
Long: "To list/inspect/test notification template",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Aliases: []string{"notifications", "notification"},
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
|
@ -158,7 +158,7 @@ func (cli *cliNotifications) newListCmd() *cobra.Command {
|
|||
Short: "list notifications plugins",
|
||||
Long: `list notifications plugins and their status (active or not)`,
|
||||
Example: `cscli notifications list`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
@ -207,7 +207,7 @@ func (cli *cliNotifications) newInspectCmd() *cobra.Command {
|
|||
Short: "Inspect notifications plugin",
|
||||
Long: `Inspect notifications plugin and show configuration`,
|
||||
Example: `cscli notifications inspect <plugin_name>`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
ValidArgsFunction: cli.notificationConfigFilter,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
|
@ -272,7 +272,7 @@ func (cli *cliNotifications) newTestCmd() *cobra.Command {
|
|||
Short: "send a generic test alert to notification plugin",
|
||||
Long: `send a generic test alert to a notification plugin even if it is not active in profiles`,
|
||||
Example: `cscli notifications test [plugin_name]`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgsFunction: cli.notificationConfigFilter,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
@ -367,7 +367,7 @@ cscli notifications reinject <alert_id>
|
|||
cscli notifications reinject <alert_id> -a '{"remediation": false,"scenario":"notification/test"}'
|
||||
cscli notifications reinject <alert_id> -a '{"remediation": true,"scenario":"notification/test"}'
|
||||
`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
"github.com/crowdsecurity/go-cs-lib/ptr"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/apiserver"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
|
@ -35,7 +36,6 @@ func (cli *cliPapi) NewCommand() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "papi [action]",
|
||||
Short: "Manage interaction with Polling API (PAPI)",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
@ -100,7 +100,7 @@ func (cli *cliPapi) newStatusCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Get status of the Polling API",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
@ -155,7 +155,7 @@ func (cli *cliPapi) newSyncCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "sync",
|
||||
Short: "Sync with the Polling API, pulling all non-expired orders for the instance",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/setup"
|
||||
|
@ -35,7 +36,6 @@ func (cli *cliSetup) NewCommand() *cobra.Command {
|
|||
Use: "setup",
|
||||
Short: "Tools to configure crowdsec",
|
||||
Long: "Manage hub configuration and service detection",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,7 @@ func (cli *cliSetup) newDetectCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "detect",
|
||||
Short: "detect running services, generate a setup file",
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return cli.detect(f)
|
||||
|
@ -102,7 +103,7 @@ func (cli *cliSetup) newInstallHubCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "install-hub [setup_file] [flags]",
|
||||
Short: "install items from a setup file",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cli.install(cmd.Context(), interactive, dryRun, args[0])
|
||||
|
@ -123,7 +124,7 @@ func (cli *cliSetup) newDataSourcesCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "datasources [setup_file] [flags]",
|
||||
Short: "generate datasource (acquisition) configuration from a setup file",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cli.dataSources(args[0], toDir)
|
||||
|
@ -140,7 +141,7 @@ func (cli *cliSetup) newValidateCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "validate [setup_file]",
|
||||
Short: "validate a setup file",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: args.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cli.validate(args[0])
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/reload"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
|
@ -174,6 +175,7 @@ func (cli *cliSimulation) newStatusCmd() *cobra.Command {
|
|||
Use: "status",
|
||||
Short: "Show simulation mode status",
|
||||
Example: `cscli simulation status`,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
cli.status()
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
"github.com/crowdsecurity/go-cs-lib/trace"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clibouncer"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clicapi"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/clihub"
|
||||
|
@ -112,7 +113,7 @@ func (cli *cliSupport) dumpMetrics(ctx context.Context, db *database.Client, zw
|
|||
return fmt.Errorf("could not format prometheus metrics: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, cfg.Cscli.PrometheusUrl, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, cfg.Cscli.PrometheusUrl, http.NoBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create request to prometheus endpoint: %w", err)
|
||||
}
|
||||
|
@ -327,7 +328,7 @@ func (cli *cliSupport) dumpPprof(ctx context.Context, zw *zip.Writer, prometheus
|
|||
),
|
||||
endpoint,
|
||||
),
|
||||
nil,
|
||||
http.NoBody,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create request to pprof endpoint: %w", err)
|
||||
|
@ -409,7 +410,6 @@ func (cli *cliSupport) NewCommand() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "support [action]",
|
||||
Short: "Provide commands to help during support",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
}
|
||||
|
||||
|
@ -625,7 +625,7 @@ func (cli *cliSupport) NewDumpCmd() *cobra.Command {
|
|||
Example: `cscli support dump
|
||||
cscli support dump -f /tmp/crowdsec-support.zip
|
||||
`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
output := cli.cfg().Cscli.Output
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
)
|
||||
|
||||
func NewCompletionCmd() *cobra.Command {
|
||||
|
@ -67,7 +69,7 @@ func NewCompletionCmd() *cobra.Command {
|
|||
DisableFlagsInUseLine: true,
|
||||
DisableAutoGenTag: true,
|
||||
ValidArgs: []string{"bash", "zsh", "powershell", "fish"},
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
||||
Args: cobra.MatchAll(args.ExactArgs(1), cobra.OnlyValidArgs),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
switch args[0] {
|
||||
case "bash":
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
"github.com/crowdsecurity/go-cs-lib/version"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/idgen"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/metabase"
|
||||
|
@ -64,7 +65,6 @@ func (cli *cliDashboard) NewCommand() *cobra.Command {
|
|||
Long: `Install/Start/Stop/Remove a metabase container exposing dashboard and metrics.
|
||||
Note: This command requires database direct access, so is intended to be run on Local API/master.
|
||||
`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
Example: `
|
||||
cscli dashboard setup
|
||||
|
@ -130,7 +130,7 @@ func (cli *cliDashboard) newSetupCmd() *cobra.Command {
|
|||
Use: "setup",
|
||||
Short: "Setup a metabase container.",
|
||||
Long: `Perform a metabase docker setup, download standard dashboards, create a fresh user and start the container`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
Example: `
|
||||
cscli dashboard setup
|
||||
|
@ -206,7 +206,7 @@ func (cli *cliDashboard) newStartCmd() *cobra.Command {
|
|||
Use: "start",
|
||||
Short: "Start the metabase container.",
|
||||
Long: `Stats the metabase container using docker.`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
mb, err := metabase.NewMetabase(metabaseConfigPath, metabaseContainerID)
|
||||
|
@ -237,7 +237,7 @@ func (cli *cliDashboard) newStopCmd() *cobra.Command {
|
|||
Use: "stop",
|
||||
Short: "Stops the metabase container.",
|
||||
Long: `Stops the metabase container using docker.`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
if err := metabase.StopContainer(metabaseContainerID); err != nil {
|
||||
|
@ -254,7 +254,7 @@ func (cli *cliDashboard) newShowPasswordCmd() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "show-password",
|
||||
Short: "displays password of metabase.",
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
m := metabase.Metabase{}
|
||||
|
@ -277,7 +277,7 @@ func (cli *cliDashboard) newRemoveCmd() *cobra.Command {
|
|||
Use: "remove",
|
||||
Short: "removes the metabase container.",
|
||||
Long: `removes the metabase container using docker.`,
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
Example: `
|
||||
cscli dashboard remove
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra/doc"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
)
|
||||
|
||||
type cliDoc struct{}
|
||||
|
@ -23,7 +25,7 @@ func (cli cliDoc) NewCommand(rootCmd *cobra.Command) *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "doc",
|
||||
Short: "Generate the documentation related to cscli commands. Target directory must exist.",
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
Hidden: true,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
|
|
|
@ -25,7 +25,7 @@ func lookupLatest(ctx context.Context) (string, error) {
|
|||
|
||||
url := "https://version.crowdsec.net/latest"
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to create request for %s: %w", url, err)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/cwversion"
|
||||
)
|
||||
|
||||
|
@ -18,7 +19,7 @@ func (cliVersion) NewCommand() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Display version",
|
||||
Args: cobra.NoArgs,
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
_, _ = os.Stdout.WriteString(cwversion.FullString())
|
||||
|
|
|
@ -186,9 +186,10 @@ func (f *Flags) Parse() {
|
|||
flag.Parse()
|
||||
}
|
||||
|
||||
func newLogLevel(curLevelPtr *log.Level, f *Flags) *log.Level {
|
||||
func newLogLevel(curLevelPtr *log.Level, f *Flags) (*log.Level, bool) {
|
||||
// mother of all defaults
|
||||
ret := log.InfoLevel
|
||||
logLevelViaFlag := true
|
||||
|
||||
// keep if already set
|
||||
if curLevelPtr != nil {
|
||||
|
@ -210,14 +211,16 @@ func newLogLevel(curLevelPtr *log.Level, f *Flags) *log.Level {
|
|||
case f.LogLevelFatal:
|
||||
ret = log.FatalLevel
|
||||
default:
|
||||
// We set logLevelViaFlag to false in default cause no flag was provided
|
||||
logLevelViaFlag = false
|
||||
}
|
||||
|
||||
if curLevelPtr != nil && ret == *curLevelPtr {
|
||||
// avoid returning a new ptr to the same value
|
||||
return curLevelPtr
|
||||
return curLevelPtr, logLevelViaFlag
|
||||
}
|
||||
|
||||
return &ret
|
||||
return &ret, logLevelViaFlag
|
||||
}
|
||||
|
||||
// LoadConfig returns a configuration parsed from configuration file
|
||||
|
@ -230,8 +233,8 @@ func LoadConfig(configFile string, disableAgent bool, disableAPI bool, quiet boo
|
|||
if err := trace.Init(filepath.Join(cConfig.ConfigPaths.DataDir, "trace")); err != nil {
|
||||
return nil, fmt.Errorf("while setting up trace directory: %w", err)
|
||||
}
|
||||
|
||||
cConfig.Common.LogLevel = newLogLevel(cConfig.Common.LogLevel, flags)
|
||||
var logLevelViaFlag bool
|
||||
cConfig.Common.LogLevel, logLevelViaFlag = newLogLevel(cConfig.Common.LogLevel, flags)
|
||||
|
||||
if dumpFolder != "" {
|
||||
parser.ParseDump = true
|
||||
|
@ -250,7 +253,7 @@ func LoadConfig(configFile string, disableAgent bool, disableAPI bool, quiet boo
|
|||
cConfig.Common.LogDir, *cConfig.Common.LogLevel,
|
||||
cConfig.Common.LogMaxSize, cConfig.Common.LogMaxFiles,
|
||||
cConfig.Common.LogMaxAge, cConfig.Common.LogFormat, cConfig.Common.CompressLogs,
|
||||
cConfig.Common.ForceColorLogs); err != nil {
|
||||
cConfig.Common.ForceColorLogs, logLevelViaFlag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
2
go.mod
2
go.mod
|
@ -1,6 +1,6 @@
|
|||
module github.com/crowdsecurity/crowdsec
|
||||
|
||||
go 1.24.0
|
||||
go 1.24.1
|
||||
|
||||
require (
|
||||
entgo.io/ent v0.14.2
|
||||
|
|
|
@ -92,14 +92,10 @@ func registerDataSource(dataSourceType string, dsGetter func() DataSource) {
|
|||
// setupLogger creates a logger for the datasource to use at runtime.
|
||||
func setupLogger(source, name string, level *log.Level) (*log.Entry, error) {
|
||||
clog := log.New()
|
||||
if err := types.ConfigureLogger(clog); err != nil {
|
||||
if err := types.ConfigureLogger(clog, level); err != nil {
|
||||
return nil, fmt.Errorf("while configuring datasource logger: %w", err)
|
||||
}
|
||||
|
||||
if level != nil {
|
||||
clog.SetLevel(*level)
|
||||
}
|
||||
|
||||
fields := log.Fields{
|
||||
"type": source,
|
||||
}
|
||||
|
|
|
@ -2,9 +2,12 @@ package appsecacquisition
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -64,6 +67,7 @@ type AppsecSource struct {
|
|||
AuthCache AuthCache
|
||||
AppsecRunners []AppsecRunner // one for each go-routine
|
||||
appsecAllowlistClient *allowlists.AppsecAllowlist
|
||||
lapiCACertPool *x509.CertPool
|
||||
}
|
||||
|
||||
// Struct to handle cache of authentication
|
||||
|
@ -158,6 +162,28 @@ func (w *AppsecSource) GetAggregMetrics() []prometheus.Collector {
|
|||
return []prometheus.Collector{AppsecReqCounter, AppsecBlockCounter, AppsecRuleHits, AppsecOutbandParsingHistogram, AppsecInbandParsingHistogram, AppsecGlobalParsingHistogram}
|
||||
}
|
||||
|
||||
func loadCertPool(caCertPath string, logger log.FieldLogger) (*x509.CertPool, error) {
|
||||
caCertPool, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
logger.Warnf("Error loading system CA certificates: %s", err)
|
||||
}
|
||||
|
||||
if caCertPool == nil {
|
||||
caCertPool = x509.NewCertPool()
|
||||
}
|
||||
|
||||
if caCertPath != "" {
|
||||
caCert, err := os.ReadFile(caCertPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while opening cert file: %w", err)
|
||||
}
|
||||
|
||||
caCertPool.AppendCertsFromPEM(caCert)
|
||||
}
|
||||
|
||||
return caCertPool, nil
|
||||
}
|
||||
|
||||
func (w *AppsecSource) Configure(yamlConfig []byte, logger *log.Entry, metricsLevel int) error {
|
||||
err := w.UnmarshalConfig(yamlConfig)
|
||||
if err != nil {
|
||||
|
@ -241,8 +267,7 @@ func (w *AppsecSource) Configure(yamlConfig []byte, logger *log.Entry, metricsLe
|
|||
appsecAllowlistsClient: w.appsecAllowlistClient,
|
||||
}
|
||||
|
||||
err := runner.Init(appsecCfg.GetDataDir())
|
||||
if err != nil {
|
||||
if err = runner.Init(appsecCfg.GetDataDir()); err != nil {
|
||||
return fmt.Errorf("unable to initialize runner: %w", err)
|
||||
}
|
||||
|
||||
|
@ -254,6 +279,19 @@ func (w *AppsecSource) Configure(yamlConfig []byte, logger *log.Entry, metricsLe
|
|||
// We don´t use the wrapper provided by coraza because we want to fully control what happens when a rule match to send the information in crowdsec
|
||||
w.mux.HandleFunc(w.config.Path, w.appsecHandler)
|
||||
|
||||
csConfig := csconfig.GetConfig()
|
||||
|
||||
caCertPath := ""
|
||||
|
||||
if csConfig.API.Client != nil && csConfig.API.Client.Credentials != nil {
|
||||
caCertPath = csConfig.API.Client.Credentials.CACertPath
|
||||
}
|
||||
|
||||
w.lapiCACertPool, err = loadCertPool(caCertPath, w.logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load LAPI CA cert pool: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -273,6 +311,103 @@ func (w *AppsecSource) OneShotAcquisition(_ context.Context, _ chan types.Event,
|
|||
return errors.New("AppSec datasource does not support command line acquisition")
|
||||
}
|
||||
|
||||
func (w *AppsecSource) listenAndServe(ctx context.Context, t *tomb.Tomb) error {
|
||||
defer trace.CatchPanic("crowdsec/acquis/appsec/listenAndServe")
|
||||
|
||||
w.logger.Infof("%d appsec runner to start", len(w.AppsecRunners))
|
||||
|
||||
serverError := make(chan error, 2)
|
||||
|
||||
startServer := func(listener net.Listener, canTLS bool) {
|
||||
var err error
|
||||
|
||||
if canTLS && (w.config.CertFilePath != "" || w.config.KeyFilePath != "") {
|
||||
if w.config.KeyFilePath == "" {
|
||||
serverError <- errors.New("missing TLS key file")
|
||||
return
|
||||
}
|
||||
|
||||
if w.config.CertFilePath == "" {
|
||||
serverError <- errors.New("missing TLS cert file")
|
||||
return
|
||||
}
|
||||
|
||||
err = w.server.ServeTLS(listener, w.config.CertFilePath, w.config.KeyFilePath)
|
||||
} else {
|
||||
err = w.server.Serve(listener)
|
||||
}
|
||||
|
||||
switch {
|
||||
case errors.Is(err, http.ErrServerClosed):
|
||||
break
|
||||
case err != nil:
|
||||
serverError <- err
|
||||
}
|
||||
}
|
||||
|
||||
// Starting Unix socket listener
|
||||
go func(socket string) {
|
||||
if socket == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.Remove(w.config.ListenSocket); err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
w.logger.Errorf("can't remove socket %s: %s", socket, err)
|
||||
}
|
||||
}
|
||||
|
||||
w.logger.Infof("creating unix socket %s", socket)
|
||||
|
||||
listener, err := net.Listen("unix", socket)
|
||||
if err != nil {
|
||||
serverError <- fmt.Errorf("appsec server failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
w.logger.Infof("Appsec listening on Unix socket %s", socket)
|
||||
startServer(listener, false)
|
||||
}(w.config.ListenSocket)
|
||||
|
||||
// Starting TCP listener
|
||||
go func(url string) {
|
||||
if url == "" {
|
||||
return
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", url)
|
||||
if err != nil {
|
||||
serverError <- fmt.Errorf("listening on %s: %w", url, err)
|
||||
}
|
||||
|
||||
w.logger.Infof("Appsec listening on %s", url)
|
||||
startServer(listener, true)
|
||||
}(w.config.ListenAddr)
|
||||
|
||||
select {
|
||||
case err := <-serverError:
|
||||
return err
|
||||
case <-t.Dying():
|
||||
w.logger.Info("Shutting down Appsec server")
|
||||
// xx let's clean up the appsec runners :)
|
||||
appsec.AppsecRulesDetails = make(map[int]appsec.RulesDetails)
|
||||
|
||||
if err := w.server.Shutdown(ctx); err != nil {
|
||||
w.logger.Errorf("Error shutting down Appsec server: %s", err.Error())
|
||||
}
|
||||
|
||||
if w.config.ListenSocket != "" {
|
||||
if err := os.Remove(w.config.ListenSocket); err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
w.logger.Errorf("can't remove socket %s: %s", w.config.ListenSocket, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *AppsecSource) StreamingAcquisition(ctx context.Context, out chan types.Event, t *tomb.Tomb) error {
|
||||
w.outChan = out
|
||||
|
||||
|
@ -285,13 +420,12 @@ func (w *AppsecSource) StreamingAcquisition(ctx context.Context, out chan types.
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch allowlists: %w", err)
|
||||
}
|
||||
|
||||
w.appsecAllowlistClient.StartRefresh(ctx, t)
|
||||
|
||||
t.Go(func() error {
|
||||
defer trace.CatchPanic("crowdsec/acquis/appsec/live")
|
||||
|
||||
w.logger.Infof("%d appsec runner to start", len(w.AppsecRunners))
|
||||
|
||||
for _, runner := range w.AppsecRunners {
|
||||
runner.outChan = out
|
||||
|
||||
|
@ -301,60 +435,7 @@ func (w *AppsecSource) StreamingAcquisition(ctx context.Context, out chan types.
|
|||
})
|
||||
}
|
||||
|
||||
t.Go(func() error {
|
||||
if w.config.ListenSocket != "" {
|
||||
w.logger.Infof("creating unix socket %s", w.config.ListenSocket)
|
||||
_ = os.RemoveAll(w.config.ListenSocket)
|
||||
|
||||
listener, err := net.Listen("unix", w.config.ListenSocket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("appsec server failed: %w", err)
|
||||
}
|
||||
|
||||
defer listener.Close()
|
||||
|
||||
if w.config.CertFilePath != "" && w.config.KeyFilePath != "" {
|
||||
err = w.server.ServeTLS(listener, w.config.CertFilePath, w.config.KeyFilePath)
|
||||
} else {
|
||||
err = w.server.Serve(listener)
|
||||
}
|
||||
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
return fmt.Errorf("appsec server failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
t.Go(func() error {
|
||||
var err error
|
||||
|
||||
if w.config.ListenAddr != "" {
|
||||
w.logger.Infof("creating TCP server on %s", w.config.ListenAddr)
|
||||
|
||||
if w.config.CertFilePath != "" && w.config.KeyFilePath != "" {
|
||||
err = w.server.ListenAndServeTLS(w.config.CertFilePath, w.config.KeyFilePath)
|
||||
} else {
|
||||
err = w.server.ListenAndServe()
|
||||
}
|
||||
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
return fmt.Errorf("appsec server failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
<-t.Dying()
|
||||
w.logger.Info("Shutting down Appsec server")
|
||||
// xx let's clean up the appsec runners :)
|
||||
appsec.AppsecRulesDetails = make(map[int]appsec.RulesDetails)
|
||||
|
||||
if err := w.server.Shutdown(ctx); err != nil {
|
||||
w.logger.Errorf("Error shutting down Appsec server: %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
return w.listenAndServe(ctx, t)
|
||||
})
|
||||
|
||||
return nil
|
||||
|
@ -373,21 +454,29 @@ func (w *AppsecSource) Dump() interface{} {
|
|||
}
|
||||
|
||||
func (w *AppsecSource) IsAuth(ctx context.Context, apiKey string) bool {
|
||||
client := &http.Client{
|
||||
Timeout: 200 * time.Millisecond,
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodHead, w.lapiURL, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodHead, w.lapiURL, http.NoBody)
|
||||
if err != nil {
|
||||
log.Errorf("Error creating request: %s", err)
|
||||
w.logger.Errorf("Error creating request: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
req.Header.Add("X-Api-Key", apiKey)
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 200 * time.Millisecond,
|
||||
}
|
||||
|
||||
if w.lapiCACertPool != nil {
|
||||
client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: w.lapiCACertPool,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Errorf("Error performing request: %s", err)
|
||||
w.logger.Errorf("Error performing request: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -332,6 +332,67 @@ func TestAppsecOnMatchHooks(t *testing.T) {
|
|||
require.Equal(t, appsec.CaptchaRemediation, responses[0].Action)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_match: SendAlert() with out-of-band rule",
|
||||
expected_load_ok: true,
|
||||
outofband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
DefaultRemediation: appsec.AllowRemediation,
|
||||
on_match: []appsec.Hook{
|
||||
{Filter: "IsInBand == false", Apply: []string{"SendAlert()"}},
|
||||
},
|
||||
input_request: appsec.ParsedRequest{
|
||||
ClientIP: "1.2.3.4",
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Equal(t, appsec.AllowRemediation, appsecResponse.Action)
|
||||
require.Equal(t, http.StatusOK, appsecResponse.HTTPStatus)
|
||||
require.Equal(t, http.StatusOK, statusCode)
|
||||
// We have both an event an overflow
|
||||
require.Len(t, events, 2)
|
||||
require.Equal(t, types.LOG, events[0].Type)
|
||||
require.Equal(t, types.APPSEC, events[1].Type)
|
||||
require.Nil(t, events[0].Overflow.Alert)
|
||||
require.NotNil(t, events[1].Overflow.Alert)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on_match: no alert with default config",
|
||||
expected_load_ok: true,
|
||||
outofband_rules: []appsec_rule.CustomRule{
|
||||
{
|
||||
Name: "rule42",
|
||||
Zones: []string{"ARGS"},
|
||||
Variables: []string{"foo"},
|
||||
Match: appsec_rule.Match{Type: "regex", Value: "^toto"},
|
||||
Transform: []string{"lowercase"},
|
||||
},
|
||||
},
|
||||
on_match: []appsec.Hook{},
|
||||
input_request: appsec.ParsedRequest{
|
||||
RemoteAddr: "1.2.3.4",
|
||||
Method: "GET",
|
||||
URI: "/urllll",
|
||||
Args: url.Values{"foo": []string{"toto"}},
|
||||
},
|
||||
output_asserts: func(events []types.Event, responses []appsec.AppsecTempResponse, appsecResponse appsec.BodyResponse, statusCode int) {
|
||||
require.Len(t, events, 1)
|
||||
require.Equal(t, types.LOG, events[0].Type)
|
||||
require.Len(t, responses, 1)
|
||||
require.Equal(t, appsec.AllowRemediation, responses[0].Action)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
|
|
@ -286,7 +286,6 @@ func (r *AppsecRunner) handleInBandInterrupt(request *appsec.ParsedRequest) {
|
|||
r.outChan <- *appsecOvlfw
|
||||
}
|
||||
}
|
||||
|
||||
// Should the in band match trigger an event ?
|
||||
if r.AppsecRuntime.Response.SendEvent {
|
||||
r.outChan <- evt
|
||||
|
@ -332,7 +331,9 @@ func (r *AppsecRunner) handleOutBandInterrupt(request *appsec.ParsedRequest) {
|
|||
r.logger.Errorf("unable to generate appsec event : %s", err)
|
||||
return
|
||||
}
|
||||
r.outChan <- *appsecOvlfw
|
||||
if appsecOvlfw != nil {
|
||||
r.outChan <- *appsecOvlfw
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,8 +60,8 @@ func AppsecEventGenerationGeoIPEnrich(src *models.Source) error {
|
|||
}
|
||||
|
||||
func AppsecEventGeneration(inEvt types.Event, request *http.Request) (*types.Event, error) {
|
||||
// if the request didnd't trigger inband rules, we don't want to generate an event to LAPI/CAPI
|
||||
if !inEvt.Appsec.HasInBandMatches {
|
||||
// if the request didn't trigger inband rules or out-of-band rules, we don't want to generate an event to LAPI/CAPI
|
||||
if !inEvt.Appsec.HasInBandMatches && !inEvt.Appsec.HasOutBandMatches {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -302,7 +302,7 @@ func (lc *LokiClient) QueryRange(ctx context.Context, infinite bool) chan *LokiQ
|
|||
|
||||
// Create a wrapper for http.Get to be able to set headers and auth
|
||||
func (lc *LokiClient) Get(ctx context.Context, url string) (*http.Response, error) {
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -370,7 +370,7 @@ func (lc *VLClient) QueryRange(ctx context.Context, infinite bool) chan *Log {
|
|||
}
|
||||
|
||||
func (lc *VLClient) Get(ctx context.Context, url string) (*http.Response, error) {
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ func ValidateContextExpr(key string, expressions []string) error {
|
|||
|
||||
func NewAlertContext(contextToSend map[string][]string, valueLength int) error {
|
||||
clog := log.New()
|
||||
if err := types.ConfigureLogger(clog); err != nil {
|
||||
if err := types.ConfigureLogger(clog, nil); err != nil {
|
||||
return fmt.Errorf("couldn't create logger for alert context: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ func (s *DecisionsService) GetDecisionsFromBlocklist(ctx context.Context, blockl
|
|||
|
||||
client := http.Client{}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, *blocklist.URL, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, *blocklist.URL, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
|
|
@ -854,6 +854,10 @@ func (a *apic) ApplyApicWhitelists(ctx context.Context, decisions []*models.Deci
|
|||
log.Errorf("while getting allowlists content: %s", err)
|
||||
}
|
||||
|
||||
if a.whitelists != nil {
|
||||
log.Warn("capi_whitelists_path is deprecated, please use centralized allowlists instead. See https://docs.crowdsec.net/docs/next/local_api/centralized_allowlists.")
|
||||
}
|
||||
|
||||
if (a.whitelists == nil || len(a.whitelists.Cidrs) == 0 && len(a.whitelists.Ips) == 0) && len(allowlisted_ips) == 0 && len(allowlisted_cidrs) == 0 {
|
||||
return decisions
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -118,14 +119,10 @@ func CustomRecoveryWithWriter() gin.HandlerFunc {
|
|||
func newGinLogger(config *csconfig.LocalApiServerCfg) (*log.Logger, string, error) {
|
||||
clog := log.New()
|
||||
|
||||
if err := types.ConfigureLogger(clog); err != nil {
|
||||
if err := types.ConfigureLogger(clog, config.LogLevel); err != nil {
|
||||
return nil, "", fmt.Errorf("while configuring gin logger: %w", err)
|
||||
}
|
||||
|
||||
if config.LogLevel != nil {
|
||||
clog.SetLevel(*config.LogLevel)
|
||||
}
|
||||
|
||||
if config.LogMedia != "file" {
|
||||
return clog, "", nil
|
||||
}
|
||||
|
@ -411,15 +408,11 @@ func (s *APIServer) Run(apiReady chan bool) error {
|
|||
// it also updates the URL field with the actual address the server is listening on
|
||||
// it's meant to be run in a separate goroutine
|
||||
func (s *APIServer) listenAndServeLAPI(apiReady chan bool) error {
|
||||
var (
|
||||
tcpListener net.Listener
|
||||
unixListener net.Listener
|
||||
err error
|
||||
serverError = make(chan error, 2)
|
||||
listenerClosed = make(chan struct{})
|
||||
)
|
||||
serverError := make(chan error, 2)
|
||||
|
||||
startServer := func(listener net.Listener, canTLS bool) {
|
||||
var err error
|
||||
|
||||
if canTLS && s.TLS != nil && (s.TLS.CertFilePath != "" || s.TLS.KeyFilePath != "") {
|
||||
if s.TLS.KeyFilePath == "" {
|
||||
serverError <- errors.New("missing TLS key file")
|
||||
|
@ -445,38 +438,42 @@ func (s *APIServer) listenAndServeLAPI(apiReady chan bool) error {
|
|||
}
|
||||
|
||||
// Starting TCP listener
|
||||
go func() {
|
||||
if s.URL == "" {
|
||||
go func(url string) {
|
||||
if url == "" {
|
||||
return
|
||||
}
|
||||
|
||||
tcpListener, err = net.Listen("tcp", s.URL)
|
||||
listener, err := net.Listen("tcp", url)
|
||||
if err != nil {
|
||||
serverError <- fmt.Errorf("listening on %s: %w", s.URL, err)
|
||||
serverError <- fmt.Errorf("listening on %s: %w", url, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("CrowdSec Local API listening on %s", s.URL)
|
||||
startServer(tcpListener, true)
|
||||
}()
|
||||
log.Infof("CrowdSec Local API listening on %s", url)
|
||||
startServer(listener, true)
|
||||
}(s.URL)
|
||||
|
||||
// Starting Unix socket listener
|
||||
go func() {
|
||||
if s.UnixSocket == "" {
|
||||
go func(socket string) {
|
||||
if socket == "" {
|
||||
return
|
||||
}
|
||||
|
||||
_ = os.RemoveAll(s.UnixSocket)
|
||||
if err := os.Remove(socket); err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
log.Errorf("can't remove socket %s: %s", socket, err)
|
||||
}
|
||||
}
|
||||
|
||||
unixListener, err = net.Listen("unix", s.UnixSocket)
|
||||
listener, err := net.Listen("unix", socket)
|
||||
if err != nil {
|
||||
serverError <- fmt.Errorf("while creating unix listener: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("CrowdSec Local API listening on Unix socket %s", s.UnixSocket)
|
||||
startServer(unixListener, false)
|
||||
}()
|
||||
log.Infof("CrowdSec Local API listening on Unix socket %s", socket)
|
||||
startServer(listener, false)
|
||||
}(s.UnixSocket)
|
||||
|
||||
apiReady <- true
|
||||
|
||||
|
@ -493,10 +490,12 @@ func (s *APIServer) listenAndServeLAPI(apiReady chan bool) error {
|
|||
log.Errorf("while shutting down http server: %v", err)
|
||||
}
|
||||
|
||||
close(listenerClosed)
|
||||
case <-listenerClosed:
|
||||
if s.UnixSocket != "" {
|
||||
_ = os.RemoveAll(s.UnixSocket)
|
||||
if err := os.Remove(s.UnixSocket); err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
log.Errorf("can't remove socket %s: %s", s.UnixSocket, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -337,7 +337,7 @@ func TestUnknownPath(t *testing.T) {
|
|||
router, _ := NewAPITest(t, ctx)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "/test", nil)
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "/test", http.NoBody)
|
||||
req.Header.Set("User-Agent", UserAgent)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
|
@ -388,7 +388,7 @@ func TestLoggingDebugToFileConfig(t *testing.T) {
|
|||
cfg.LogLevel = ptr.Of(log.DebugLevel)
|
||||
|
||||
// Configure logging
|
||||
err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel, cfg.LogMaxSize, cfg.LogMaxFiles, cfg.LogMaxAge, cfg.LogFormat, cfg.CompressLogs, false)
|
||||
err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel, cfg.LogMaxSize, cfg.LogMaxFiles, cfg.LogMaxAge, cfg.LogFormat, cfg.CompressLogs, false, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
api, err := NewServer(ctx, &cfg)
|
||||
|
@ -396,7 +396,7 @@ func TestLoggingDebugToFileConfig(t *testing.T) {
|
|||
require.NotNil(t, api)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "/test42", nil)
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "/test42", http.NoBody)
|
||||
req.Header.Set("User-Agent", UserAgent)
|
||||
api.router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
|
@ -440,7 +440,7 @@ func TestLoggingErrorToFileConfig(t *testing.T) {
|
|||
cfg.LogLevel = ptr.Of(log.ErrorLevel)
|
||||
|
||||
// Configure logging
|
||||
err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel, cfg.LogMaxSize, cfg.LogMaxFiles, cfg.LogMaxAge, cfg.LogFormat, cfg.CompressLogs, false)
|
||||
err := types.SetDefaultLoggerConfig(cfg.LogMedia, cfg.LogDir, *cfg.LogLevel, cfg.LogMaxSize, cfg.LogMaxFiles, cfg.LogMaxAge, cfg.LogFormat, cfg.CompressLogs, false, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
api, err := NewServer(ctx, &cfg)
|
||||
|
@ -448,7 +448,7 @@ func TestLoggingErrorToFileConfig(t *testing.T) {
|
|||
require.NotNil(t, api)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "/test42", nil)
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "/test42", http.NoBody)
|
||||
req.Header.Set("User-Agent", UserAgent)
|
||||
api.router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
|
|
|
@ -83,12 +83,10 @@ type PapiPermCheckSuccess struct {
|
|||
|
||||
func NewPAPI(apic *apic, dbClient *database.Client, consoleConfig *csconfig.ConsoleConfig, logLevel log.Level) (*Papi, error) {
|
||||
logger := log.New()
|
||||
if err := types.ConfigureLogger(logger); err != nil {
|
||||
if err := types.ConfigureLogger(logger, &logLevel); err != nil {
|
||||
return &Papi{}, fmt.Errorf("creating papi logger: %w", err)
|
||||
}
|
||||
|
||||
logger.SetLevel(logLevel)
|
||||
|
||||
papiUrl := *apic.apiClient.PapiURL
|
||||
papiUrl.Path = fmt.Sprintf("%s%s", types.PAPIVersion, types.PAPIPollUrl)
|
||||
|
||||
|
@ -160,7 +158,7 @@ func (p *Papi) GetPermissions(ctx context.Context) (PapiPermCheckSuccess, error)
|
|||
httpClient := p.apiClient.GetClient()
|
||||
papiCheckUrl := fmt.Sprintf("%s%s%s", p.URL, types.PAPIVersion, types.PAPIPermissionsUrl)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, papiCheckUrl, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, papiCheckUrl, http.NoBody)
|
||||
if err != nil {
|
||||
return PapiPermCheckSuccess{}, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
|
|
@ -62,6 +62,8 @@ func (a *AppsecAllowlist) FetchAllowlists(ctx context.Context) error {
|
|||
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
prevIPsLen := len(a.ips)
|
||||
prevRangesLen := len(a.ranges)
|
||||
a.ranges = []rangeAllowlist{}
|
||||
a.ips = []ipAllowlist{}
|
||||
|
||||
|
@ -93,7 +95,7 @@ func (a *AppsecAllowlist) FetchAllowlists(ctx context.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
if len(a.ips) != 0 || len(a.ranges) != 0 {
|
||||
if (len(a.ips) != 0 || len(a.ranges) != 0) && (prevIPsLen != len(a.ips) || prevRangesLen != len(a.ranges)) {
|
||||
a.logger.Infof("fetched %d IPs and %d ranges", len(a.ips), len(a.ranges))
|
||||
}
|
||||
a.logger.Debugf("fetched %d IPs and %d ranges", len(a.ips), len(a.ranges))
|
||||
|
|
|
@ -29,7 +29,7 @@ func TestJA4H_A(t *testing.T) {
|
|||
{
|
||||
name: "basic GET request - HTTP1.1 - no accept-language header",
|
||||
request: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
return req
|
||||
},
|
||||
expectedResult: "ge11nn000000",
|
||||
|
@ -37,7 +37,7 @@ func TestJA4H_A(t *testing.T) {
|
|||
{
|
||||
name: "basic GET request - HTTP1.1 - with accept-language header",
|
||||
request: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.Header.Set("Accept-Language", "en-US")
|
||||
return req
|
||||
},
|
||||
|
@ -46,7 +46,7 @@ func TestJA4H_A(t *testing.T) {
|
|||
{
|
||||
name: "basic POST request - HTTP1.1 - no accept-language header - cookies - referer",
|
||||
request: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodPost, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodPost, "http://example.com", http.NoBody)
|
||||
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
|
||||
req.Header.Set("Referer", "http://example.com")
|
||||
return req
|
||||
|
@ -56,7 +56,7 @@ func TestJA4H_A(t *testing.T) {
|
|||
{
|
||||
name: "bad accept-language header",
|
||||
request: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.Header.Set("Accept-Language", "aksjdhaslkdhalkjsd")
|
||||
return req
|
||||
},
|
||||
|
@ -65,7 +65,7 @@ func TestJA4H_A(t *testing.T) {
|
|||
{
|
||||
name: "bad accept-language header 2",
|
||||
request: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.Header.Set("Accept-Language", ",")
|
||||
return req
|
||||
},
|
||||
|
@ -94,7 +94,7 @@ func TestJA4H_B(t *testing.T) {
|
|||
{
|
||||
name: "no headers",
|
||||
request: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
return req
|
||||
},
|
||||
expectedResult: "e3b0c44298fc",
|
||||
|
@ -102,7 +102,7 @@ func TestJA4H_B(t *testing.T) {
|
|||
{
|
||||
name: "header with arbitrary content",
|
||||
request: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.Header.Set("X-Custom-Header", "some value")
|
||||
return req
|
||||
},
|
||||
|
@ -111,7 +111,7 @@ func TestJA4H_B(t *testing.T) {
|
|||
{
|
||||
name: "header with multiple headers",
|
||||
request: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.Header.Set("X-Custom-Header", "some value")
|
||||
req.Header.Set("Authorization", "Bearer token")
|
||||
return req
|
||||
|
@ -121,7 +121,7 @@ func TestJA4H_B(t *testing.T) {
|
|||
{
|
||||
name: "curl-like request",
|
||||
request: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://localhost", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://localhost", http.NoBody)
|
||||
req.Header.Set("Host", "localhost")
|
||||
req.Header.Set("User-Agent", "curl/8.12.1")
|
||||
req.Header.Set("Accept", "*/*")
|
||||
|
@ -150,7 +150,7 @@ func TestJA4H_C(t *testing.T) {
|
|||
{
|
||||
name: "no cookies",
|
||||
cookies: func() []*http.Cookie {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
return req.Cookies()
|
||||
},
|
||||
expectedResult: "000000000000",
|
||||
|
@ -158,7 +158,7 @@ func TestJA4H_C(t *testing.T) {
|
|||
{
|
||||
name: "one cookie",
|
||||
cookies: func() []*http.Cookie {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
|
||||
return req.Cookies()
|
||||
},
|
||||
|
@ -167,7 +167,7 @@ func TestJA4H_C(t *testing.T) {
|
|||
{
|
||||
name: "duplicate cookies",
|
||||
cookies: func() []*http.Cookie {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
|
||||
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar2"})
|
||||
return req.Cookies()
|
||||
|
@ -177,7 +177,7 @@ func TestJA4H_C(t *testing.T) {
|
|||
{
|
||||
name: "multiple cookies",
|
||||
cookies: func() []*http.Cookie {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
|
||||
req.AddCookie(&http.Cookie{Name: "bar", Value: "foo"})
|
||||
cookies := req.Cookies()
|
||||
|
@ -209,7 +209,7 @@ func TestJA4H_D(t *testing.T) {
|
|||
{
|
||||
name: "no cookies",
|
||||
cookies: func() []*http.Cookie {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
return req.Cookies()
|
||||
},
|
||||
expectedResult: "000000000000",
|
||||
|
@ -217,7 +217,7 @@ func TestJA4H_D(t *testing.T) {
|
|||
{
|
||||
name: "one cookie",
|
||||
cookies: func() []*http.Cookie {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
|
||||
return req.Cookies()
|
||||
},
|
||||
|
@ -226,7 +226,7 @@ func TestJA4H_D(t *testing.T) {
|
|||
{
|
||||
name: "duplicate cookies",
|
||||
cookies: func() []*http.Cookie {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
|
||||
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar2"})
|
||||
return req.Cookies()
|
||||
|
@ -236,7 +236,7 @@ func TestJA4H_D(t *testing.T) {
|
|||
{
|
||||
name: "multiple cookies",
|
||||
cookies: func() []*http.Cookie {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
|
||||
req.AddCookie(&http.Cookie{Name: "bar", Value: "foo"})
|
||||
cookies := req.Cookies()
|
||||
|
@ -268,7 +268,7 @@ func TestJA4H(t *testing.T) {
|
|||
{
|
||||
name: "Basic GET - No cookies",
|
||||
req: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
return req
|
||||
},
|
||||
expectedHash: "ge11nn000000_e3b0c44298fc_000000000000_000000000000",
|
||||
|
@ -276,7 +276,7 @@ func TestJA4H(t *testing.T) {
|
|||
{
|
||||
name: "Basic GET - With cookies",
|
||||
req: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.AddCookie(&http.Cookie{Name: "session", Value: "12345"})
|
||||
return req
|
||||
},
|
||||
|
@ -285,7 +285,7 @@ func TestJA4H(t *testing.T) {
|
|||
{
|
||||
name: "Basic GET - Multiple cookies",
|
||||
req: func() *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
req, _ := http.NewRequest(http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
|
||||
req.AddCookie(&http.Cookie{Name: "baz", Value: "qux"})
|
||||
return req
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
package appsec
|
||||
|
||||
// This file is mostly stolen from net/url package, but with some modifications to allow less strict parsing of query strings
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parseQuery and parseQuery are copied net/url package, but allow semicolon in values
|
||||
func ParseQuery(query string) url.Values {
|
||||
m := make(url.Values)
|
||||
parseQuery(m, query)
|
||||
return m
|
||||
}
|
||||
|
||||
func parseQuery(m url.Values, query string) {
|
||||
for query != "" {
|
||||
var key string
|
||||
key, query, _ = strings.Cut(query, "&")
|
||||
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
key, value, _ := strings.Cut(key, "=")
|
||||
//for now we'll just ignore the errors, but ideally we want to fire some "internal" rules when we see invalid query strings
|
||||
key = unescape(key)
|
||||
value = unescape(value)
|
||||
m[key] = append(m[key], value)
|
||||
}
|
||||
}
|
||||
|
||||
func hexDigitToByte(digit byte) (byte, bool) {
|
||||
switch {
|
||||
case digit >= '0' && digit <= '9':
|
||||
return digit - '0', true
|
||||
case digit >= 'a' && digit <= 'f':
|
||||
return digit - 'a' + 10, true
|
||||
case digit >= 'A' && digit <= 'F':
|
||||
return digit - 'A' + 10, true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func unescape(input string) string {
|
||||
ilen := len(input)
|
||||
res := strings.Builder{}
|
||||
res.Grow(ilen)
|
||||
for i := 0; i < ilen; i++ {
|
||||
ci := input[i]
|
||||
if ci == '+' {
|
||||
res.WriteByte(' ')
|
||||
continue
|
||||
}
|
||||
if ci == '%' {
|
||||
if i+2 >= ilen {
|
||||
res.WriteByte(ci)
|
||||
continue
|
||||
}
|
||||
hi, ok := hexDigitToByte(input[i+1])
|
||||
if !ok {
|
||||
res.WriteByte(ci)
|
||||
continue
|
||||
}
|
||||
lo, ok := hexDigitToByte(input[i+2])
|
||||
if !ok {
|
||||
res.WriteByte(ci)
|
||||
continue
|
||||
}
|
||||
res.WriteByte(hi<<4 | lo)
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
res.WriteByte(ci)
|
||||
}
|
||||
return res.String()
|
||||
}
|
|
@ -1,207 +0,0 @@
|
|||
package appsec
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
query string
|
||||
expected url.Values
|
||||
}{
|
||||
{
|
||||
name: "Simple query",
|
||||
query: "foo=bar",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple values",
|
||||
query: "foo=bar&foo=baz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty value",
|
||||
query: "foo=",
|
||||
expected: url.Values{
|
||||
"foo": []string{""},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty key",
|
||||
query: "=bar",
|
||||
expected: url.Values{
|
||||
"": []string{"bar"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty query",
|
||||
query: "",
|
||||
expected: url.Values{},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys",
|
||||
query: "foo=bar&baz=qux",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
"baz": []string{"qux"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value",
|
||||
query: "foo=bar&baz=qux&quux=",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values and escaped characters",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz", "bar baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz", "bar baz", "bar;baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon and ampersand",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz&foo=bar%26baz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz", "bar baz", "bar;baz", "bar&baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon and ampersand and equals",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz&foo=bar%26baz&foo=bar%3Dbaz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz", "bar baz", "bar;baz", "bar&baz", "bar=baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon and ampersand and equals and question mark",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz&foo=bar%26baz&foo=bar%3Dbaz&foo=bar%3Fbaz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz", "bar baz", "bar;baz", "bar&baz", "bar=baz", "bar?baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keys with escaped characters",
|
||||
query: "foo=ba;r&baz=qu;;x&quux=x\\&ww&xx=qu?uz&",
|
||||
expected: url.Values{
|
||||
"foo": []string{"ba;r"},
|
||||
"baz": []string{"qu;;x"},
|
||||
"quux": []string{"x\\"},
|
||||
"ww": []string{""},
|
||||
"xx": []string{"qu?uz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hexadecimal characters",
|
||||
query: "foo=bar%20baz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar baz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hexadecimal characters upper and lower case",
|
||||
query: "foo=Ba%42%42&bar=w%2f%2F",
|
||||
expected: url.Values{
|
||||
"foo": []string{"BaBB"},
|
||||
"bar": []string{"w//"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hexadecimal characters with invalid characters",
|
||||
query: "foo=bar%20baz%2",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar baz%2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hexadecimal characters with invalid hex characters",
|
||||
query: "foo=bar%xx",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar%xx"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hexadecimal characters with invalid 2nd hex character",
|
||||
query: "foo=bar%2x",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar%2x"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "url +",
|
||||
query: "foo=bar+x",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar x"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "url &&",
|
||||
query: "foo=bar&&lol=bur",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
"lol": []string{"bur"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
res := ParseQuery(test.query)
|
||||
if !reflect.DeepEqual(res, test.expected) {
|
||||
t.Fatalf("unexpected result: %v", res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import (
|
|||
"os"
|
||||
"regexp"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
@ -332,7 +333,7 @@ func NewParsedRequestFromRequest(r *http.Request, logger *log.Entry) (ParsedRequ
|
|||
} else {
|
||||
r.Proto = "HTTP/" + string(major) + "." + string(minor)
|
||||
}
|
||||
} else {
|
||||
} else if httpVersion != "" {
|
||||
logger.Warnf("Invalid value %s for HTTP version header", httpVersion)
|
||||
}
|
||||
|
||||
|
@ -396,7 +397,7 @@ func NewParsedRequestFromRequest(r *http.Request, logger *log.Entry) (ParsedRequ
|
|||
URL: parsedURL,
|
||||
Proto: r.Proto,
|
||||
Body: body,
|
||||
Args: ParseQuery(parsedURL.RawQuery),
|
||||
Args: exprhelpers.ParseQuery(parsedURL.RawQuery),
|
||||
TransferEncoding: r.TransferEncoding,
|
||||
ResponseChannel: make(chan AppsecTempResponse),
|
||||
RemoteAddrNormalized: remoteAddrNormalized,
|
||||
|
|
3
pkg/cache/cache.go
vendored
3
pkg/cache/cache.go
vendored
|
@ -59,11 +59,10 @@ func CacheInit(cfg CacheCfg) error {
|
|||
|
||||
clog := log.New()
|
||||
|
||||
if err := types.ConfigureLogger(clog); err != nil {
|
||||
if err := types.ConfigureLogger(clog, cfg.LogLevel); err != nil {
|
||||
return fmt.Errorf("while creating cache logger: %w", err)
|
||||
}
|
||||
|
||||
clog.SetLevel(*cfg.LogLevel)
|
||||
cfg.Logger = clog.WithField("cache", cfg.Name)
|
||||
|
||||
tmpCache := gcache.New(cfg.Size)
|
||||
|
|
|
@ -473,6 +473,8 @@ func (c *LocalApiServerCfg) LoadCapiWhitelists() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
log.Warn("capi_whitelists_path is deprecated, please use centralized allowlists instead. See https://docs.crowdsec.net/docs/next/local_api/centralized_allowlists.")
|
||||
|
||||
fd, err := os.Open(c.CapiWhitelistsPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while opening capi whitelist file: %w", err)
|
||||
|
|
|
@ -26,7 +26,10 @@ type DatabaseCfg struct {
|
|||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
DbName string `yaml:"db_name"`
|
||||
Sslmode string `yaml:"sslmode"`
|
||||
SSLMode string `yaml:"sslmode"`
|
||||
SSLCACert string `yaml:"ssl_ca_cert"`
|
||||
SSLClientCert string `yaml:"ssl_client_cert"`
|
||||
SSLClientKey string `yaml:"ssl_client_key"`
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
DbPath string `yaml:"db_path"`
|
||||
|
@ -136,14 +139,34 @@ func (d *DatabaseCfg) ConnectionString() string {
|
|||
connString = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=True", d.User, d.Password, d.Host, d.Port, d.DbName)
|
||||
}
|
||||
|
||||
if d.Sslmode != "" {
|
||||
connString = fmt.Sprintf("%s&tls=%s", connString, d.Sslmode)
|
||||
if d.SSLMode != "" {
|
||||
connString = fmt.Sprintf("%s&tls=%s", connString, d.SSLMode)
|
||||
}
|
||||
|
||||
if d.SSLCACert != "" {
|
||||
connString = fmt.Sprintf("%s&tls-ca=%s", connString, d.SSLCACert)
|
||||
}
|
||||
|
||||
if d.SSLClientCert != "" && d.SSLClientKey != "" {
|
||||
connString = fmt.Sprintf("%s&tls-cert=%s&tls-key=%s", connString, d.SSLClientCert, d.SSLClientKey)
|
||||
}
|
||||
case "postgres", "postgresql", "pgx":
|
||||
if d.isSocketConfig() {
|
||||
connString = fmt.Sprintf("host=%s user=%s dbname=%s password=%s", d.DbPath, d.User, d.DbName, d.Password)
|
||||
} else {
|
||||
connString = fmt.Sprintf("host=%s port=%d user=%s dbname=%s password=%s sslmode=%s", d.Host, d.Port, d.User, d.DbName, d.Password, d.Sslmode)
|
||||
connString = fmt.Sprintf("host=%s port=%d user=%s dbname=%s password=%s", d.Host, d.Port, d.User, d.DbName, d.Password)
|
||||
}
|
||||
|
||||
if d.SSLMode != "" {
|
||||
connString = fmt.Sprintf("%s sslmode=%s", connString, d.SSLMode)
|
||||
}
|
||||
|
||||
if d.SSLCACert != "" {
|
||||
connString = fmt.Sprintf("%s sslrootcert=%s", connString, d.SSLCACert)
|
||||
}
|
||||
|
||||
if d.SSLClientCert != "" && d.SSLClientKey != "" {
|
||||
connString = fmt.Sprintf("%s sslcert=%s sslkey=%s", connString, d.SSLClientCert, d.SSLClientKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/crowdsecurity/go-cs-lib/csstring"
|
||||
"github.com/crowdsecurity/go-cs-lib/ptr"
|
||||
"github.com/crowdsecurity/go-cs-lib/slicetools"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
|
@ -321,13 +322,12 @@ func (pb *PluginBroker) loadNotificationPlugin(name string, binaryPath string) (
|
|||
pb.pluginMap[name] = &NotifierPlugin{}
|
||||
l := log.New()
|
||||
|
||||
err = types.ConfigureLogger(l)
|
||||
err = types.ConfigureLogger(l, ptr.Of(log.TraceLevel))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// We set the highest level to permit plugins to set their own log level
|
||||
// without that, crowdsec log level is controlling plugins level
|
||||
l.SetLevel(log.TraceLevel)
|
||||
logger := NewHCLogAdapter(l, "")
|
||||
c := plugin.NewClient(&plugin.ClientConfig{
|
||||
HandshakeConfig: handshake,
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/crowdsecurity/go-cs-lib/ptr"
|
||||
)
|
||||
|
||||
type Runtime struct {
|
||||
|
@ -34,11 +35,10 @@ func NewProfile(profilesCfg []*csconfig.ProfileCfg) ([]*Runtime, error) {
|
|||
runtime := &Runtime{}
|
||||
|
||||
xlog := log.New()
|
||||
if err := types.ConfigureLogger(xlog); err != nil {
|
||||
if err := types.ConfigureLogger(xlog, ptr.Of(log.InfoLevel)); err != nil {
|
||||
return nil, fmt.Errorf("while configuring profiles-specific logger: %w", err)
|
||||
}
|
||||
|
||||
xlog.SetLevel(log.InfoLevel)
|
||||
runtime.Logger = xlog.WithFields(log.Fields{
|
||||
"type": "profile",
|
||||
"name": profile.Name,
|
||||
|
|
|
@ -42,8 +42,7 @@ func (c *CrowdsecCTIClient) doRequest(ctx context.Context, method string, endpoi
|
|||
url += fmt.Sprintf("%s=%s&", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, url, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, method, url, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -52,14 +52,10 @@ func NewClient(ctx context.Context, config *csconfig.DatabaseCfg) (*Client, erro
|
|||
}
|
||||
/*The logger that will be used by db operations*/
|
||||
clog := log.New()
|
||||
if err := types.ConfigureLogger(clog); err != nil {
|
||||
if err := types.ConfigureLogger(clog, config.LogLevel); err != nil {
|
||||
return nil, fmt.Errorf("while configuring db logger: %w", err)
|
||||
}
|
||||
|
||||
if config.LogLevel != nil {
|
||||
clog.SetLevel(*config.LogLevel)
|
||||
}
|
||||
|
||||
entLogger := clog.WithField("context", "ent")
|
||||
entOpt := ent.Log(entLogger.Debug)
|
||||
|
||||
|
|
|
@ -44,12 +44,9 @@ func InitCrowdsecCTI(key *string, ttl *time.Duration, size *int, logLevel *log.L
|
|||
*ttl = 5 * time.Minute
|
||||
}
|
||||
clog := log.New()
|
||||
if err := types.ConfigureLogger(clog); err != nil {
|
||||
if err := types.ConfigureLogger(clog, logLevel); err != nil {
|
||||
return fmt.Errorf("while configuring datasource logger: %w", err)
|
||||
}
|
||||
if logLevel != nil {
|
||||
clog.SetLevel(*logLevel)
|
||||
}
|
||||
subLogger := clog.WithField("type", "crowdsec-cti")
|
||||
CrowdsecCTIInitCache(*size, *ttl)
|
||||
ctiClient = cticlient.NewCrowdsecCTIClient(cticlient.WithAPIKey(CTIApiKey), cticlient.WithLogger(subLogger))
|
||||
|
|
|
@ -3,6 +3,7 @@ package exprhelpers
|
|||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/oschwald/geoip2-golang"
|
||||
|
@ -151,6 +152,20 @@ var exprFuncs = []exprCustomFunc{
|
|||
new(func(string) map[string][]string),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ParseQuery",
|
||||
function: ExprWrapParseQuery,
|
||||
signature: []interface{}{
|
||||
new(func(string) url.Values),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ExtractQueryParam",
|
||||
function: ExprWrapExtractQueryParam,
|
||||
signature: []interface{}{
|
||||
new(func(string, string) []string),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "PathUnescape",
|
||||
function: PathUnescape,
|
||||
|
|
|
@ -2,6 +2,7 @@ package exprhelpers
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -160,6 +161,68 @@ func TestMatch(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// just to verify that the function is available, real tests are in TestExtractQueryParam
|
||||
func TestExtractQueryParamExpr(t *testing.T) {
|
||||
err := Init(nil)
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
env map[string]interface{}
|
||||
code string
|
||||
result []string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "ExtractQueryParam() test: basic test",
|
||||
env: map[string]interface{}{
|
||||
"query": "/foo?a=1&b=2",
|
||||
},
|
||||
code: "ExtractQueryParam(query, 'a')",
|
||||
result: []string{"1"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
program, err := expr.Compile(test.code, GetExprOptions(test.env)...)
|
||||
require.NoError(t, err)
|
||||
output, err := expr.Run(program, test.env)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.result, output)
|
||||
log.Printf("test '%s' : OK", test.name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// just to verify that the function is available, real tests are in TestParseQuery
|
||||
func TestParseQueryInExpr(t *testing.T) {
|
||||
err := Init(nil)
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
env map[string]interface{}
|
||||
code string
|
||||
result url.Values
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "ParseQuery() test: basic test",
|
||||
env: map[string]interface{}{
|
||||
"query": "a=1&b=2",
|
||||
"ParseQuery": ParseQuery,
|
||||
},
|
||||
code: "ParseQuery(query)",
|
||||
result: url.Values{"a": {"1"}, "b": {"2"}},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
program, err := expr.Compile(test.code, GetExprOptions(test.env)...)
|
||||
require.NoError(t, err)
|
||||
output, err := expr.Run(program, test.env)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.result, output)
|
||||
log.Printf("test '%s' : OK", test.name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistanceHelper(t *testing.T) {
|
||||
err := Init(nil)
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -2,6 +2,8 @@ package exprhelpers
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/appsec/ja4h"
|
||||
)
|
||||
|
@ -11,3 +13,112 @@ func JA4H(params ...any) (any, error) {
|
|||
req := params[0].(*http.Request)
|
||||
return ja4h.JA4H(req), nil
|
||||
}
|
||||
|
||||
// just a expr wrapper for ParseQuery
|
||||
func ExprWrapParseQuery(params ...any) (any, error) {
|
||||
query := params[0].(string)
|
||||
return ParseQuery(query), nil
|
||||
}
|
||||
|
||||
// parseQuery and parseQuery are copied net/url package, but allow semicolon in values
|
||||
func ParseQuery(query string) url.Values {
|
||||
m := make(url.Values)
|
||||
ParseQueryIntoValues(m, query)
|
||||
return m
|
||||
}
|
||||
|
||||
func ParseQueryIntoValues(m url.Values, query string) {
|
||||
for query != "" {
|
||||
var key string
|
||||
key, query, _ = strings.Cut(query, "&")
|
||||
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
key, value, _ := strings.Cut(key, "=")
|
||||
//for now we'll just ignore the errors, but ideally we want to fire some "internal" rules when we see invalid query strings
|
||||
key = unescape(key)
|
||||
value = unescape(value)
|
||||
m[key] = append(m[key], value)
|
||||
}
|
||||
}
|
||||
|
||||
func hexDigitToByte(digit byte) (byte, bool) {
|
||||
switch {
|
||||
case digit >= '0' && digit <= '9':
|
||||
return digit - '0', true
|
||||
case digit >= 'a' && digit <= 'f':
|
||||
return digit - 'a' + 10, true
|
||||
case digit >= 'A' && digit <= 'F':
|
||||
return digit - 'A' + 10, true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func unescape(input string) string {
|
||||
ilen := len(input)
|
||||
res := strings.Builder{}
|
||||
res.Grow(ilen)
|
||||
for i := 0; i < ilen; i++ {
|
||||
ci := input[i]
|
||||
if ci == '+' {
|
||||
res.WriteByte(' ')
|
||||
continue
|
||||
}
|
||||
if ci == '%' {
|
||||
if i+2 >= ilen {
|
||||
res.WriteByte(ci)
|
||||
continue
|
||||
}
|
||||
hi, ok := hexDigitToByte(input[i+1])
|
||||
if !ok {
|
||||
res.WriteByte(ci)
|
||||
continue
|
||||
}
|
||||
lo, ok := hexDigitToByte(input[i+2])
|
||||
if !ok {
|
||||
res.WriteByte(ci)
|
||||
continue
|
||||
}
|
||||
res.WriteByte(hi<<4 | lo)
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
res.WriteByte(ci)
|
||||
}
|
||||
return res.String()
|
||||
}
|
||||
|
||||
// just a expr wrapper for ExtractQueryParam
|
||||
func ExprWrapExtractQueryParam(params ...any) (any, error) {
|
||||
uri := params[0].(string)
|
||||
param := params[1].(string)
|
||||
return ExtractQueryParam(uri, param), nil
|
||||
}
|
||||
|
||||
// ExtractQueryParam extracts values for a given query parameter from a raw URI string.
|
||||
func ExtractQueryParam(uri, param string) []string {
|
||||
// Find the first occurrence of "?"
|
||||
idx := strings.Index(uri, "?")
|
||||
if idx == -1 {
|
||||
// No query string present
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Extract the query string part
|
||||
queryString := uri[idx+1:]
|
||||
|
||||
// Parse the query string using a function that supports both `&` and `;`
|
||||
values := ParseQuery(queryString)
|
||||
|
||||
if values == nil {
|
||||
// No query string present
|
||||
return []string{}
|
||||
}
|
||||
// Retrieve the values for the specified parameter
|
||||
if _, ok := values[param]; !ok {
|
||||
return []string{}
|
||||
}
|
||||
return values[param]
|
||||
}
|
||||
|
|
|
@ -2,11 +2,275 @@ package exprhelpers
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
query string
|
||||
expected url.Values
|
||||
}{
|
||||
{
|
||||
name: "Full URI",
|
||||
query: "/foobar/toto?ab=cd&ef=gh",
|
||||
expected: url.Values{
|
||||
"/foobar/toto?ab": []string{"cd"},
|
||||
"ef": []string{"gh"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Simple query",
|
||||
query: "foo=bar",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple values",
|
||||
query: "foo=bar&foo=baz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty value",
|
||||
query: "foo=",
|
||||
expected: url.Values{
|
||||
"foo": []string{""},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty key",
|
||||
query: "=bar",
|
||||
expected: url.Values{
|
||||
"": []string{"bar"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty query",
|
||||
query: "",
|
||||
expected: url.Values{},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys",
|
||||
query: "foo=bar&baz=qux",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
"baz": []string{"qux"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value",
|
||||
query: "foo=bar&baz=qux&quux=",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values and escaped characters",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz", "bar baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz", "bar baz", "bar;baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon and ampersand",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz&foo=bar%26baz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz", "bar baz", "bar;baz", "bar&baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon and ampersand and equals",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz&foo=bar%26baz&foo=bar%3Dbaz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz", "bar baz", "bar;baz", "bar&baz", "bar=baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple keys with empty value and empty key and multiple values and escaped characters and semicolon and ampersand and equals and question mark",
|
||||
query: "foo=bar&baz=qux&quux=&=quuz&foo=baz&foo=bar%20baz&foo=bar%3Bbaz&foo=bar%26baz&foo=bar%3Dbaz&foo=bar%3Fbaz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar", "baz", "bar baz", "bar;baz", "bar&baz", "bar=baz", "bar?baz"},
|
||||
"baz": []string{"qux"},
|
||||
"quux": []string{""},
|
||||
"": []string{"quuz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keys with escaped characters",
|
||||
query: "foo=ba;r&baz=qu;;x&quux=x\\&ww&xx=qu?uz&",
|
||||
expected: url.Values{
|
||||
"foo": []string{"ba;r"},
|
||||
"baz": []string{"qu;;x"},
|
||||
"quux": []string{"x\\"},
|
||||
"ww": []string{""},
|
||||
"xx": []string{"qu?uz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hexadecimal characters",
|
||||
query: "foo=bar%20baz",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar baz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hexadecimal characters upper and lower case",
|
||||
query: "foo=Ba%42%42&bar=w%2f%2F",
|
||||
expected: url.Values{
|
||||
"foo": []string{"BaBB"},
|
||||
"bar": []string{"w//"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hexadecimal characters with invalid characters",
|
||||
query: "foo=bar%20baz%2",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar baz%2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hexadecimal characters with invalid hex characters",
|
||||
query: "foo=bar%xx",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar%xx"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hexadecimal characters with invalid 2nd hex character",
|
||||
query: "foo=bar%2x",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar%2x"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "url +",
|
||||
query: "foo=bar+x",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar x"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "url &&",
|
||||
query: "foo=bar&&lol=bur",
|
||||
expected: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
"lol": []string{"bur"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
res := ParseQuery(test.query)
|
||||
if !reflect.DeepEqual(res, test.expected) {
|
||||
t.Fatalf("unexpected result: %v", res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractQueryParam(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
query string
|
||||
param string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "Simple uri",
|
||||
query: "/foobar/toto?ab=cd&ef=gh",
|
||||
param: "ab",
|
||||
expected: []string{"cd"},
|
||||
},
|
||||
{
|
||||
name: "Simple uri, repeating param",
|
||||
query: "/foobar?foo=bar&foo=baz",
|
||||
param: "foo",
|
||||
expected: []string{"bar", "baz"},
|
||||
},
|
||||
{
|
||||
name: "Simple uri with semicolon",
|
||||
query: "/foobar/toto?ab=cd;ef=gh",
|
||||
param: "ab",
|
||||
expected: []string{"cd;ef=gh"},
|
||||
},
|
||||
{
|
||||
name: "Simple query no uri",
|
||||
query: "foo=bar",
|
||||
param: "foo",
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
name: "No QS",
|
||||
query: "/foobar",
|
||||
param: "foo",
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
name: "missing param",
|
||||
query: "/foobar/toto?ab=cd&ef=gh",
|
||||
param: "baz",
|
||||
expected: []string{},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
res := ExtractQueryParam(test.query, test.param)
|
||||
if !reflect.DeepEqual(res, test.expected) {
|
||||
t.Fatalf("unexpected result: %v", res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJA4H(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
|
@ -50,7 +314,7 @@ func TestJA4H(t *testing.T) {
|
|||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req, err := http.NewRequest(test.method, test.url, nil)
|
||||
req, err := http.NewRequest(test.method, test.url, http.NoBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create request: %s", err)
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/crowdsecurity/crowdsec/pkg/cwversion/constraint"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/types"
|
||||
"github.com/crowdsecurity/go-cs-lib/ptr"
|
||||
)
|
||||
|
||||
// BucketFactory struct holds all fields for any bucket configuration. This is to have a
|
||||
|
@ -345,11 +346,10 @@ func LoadBucket(bucketFactory *BucketFactory, tomb *tomb.Tomb) error {
|
|||
|
||||
if bucketFactory.Debug {
|
||||
clog := log.New()
|
||||
if err = types.ConfigureLogger(clog); err != nil {
|
||||
if err = types.ConfigureLogger(clog, ptr.Of(log.DebugLevel)); err != nil {
|
||||
return fmt.Errorf("while creating bucket-specific logger: %w", err)
|
||||
}
|
||||
|
||||
clog.SetLevel(log.DebugLevel)
|
||||
bucketFactory.logger = clog.WithFields(log.Fields{
|
||||
"cfg": bucketFactory.BucketName,
|
||||
"name": bucketFactory.Name,
|
||||
|
|
|
@ -60,7 +60,7 @@ func (c *LongPollClient) doQuery(ctx context.Context) (*http.Response, error) {
|
|||
|
||||
logger.Debugf("Query parameters: %s", c.url.RawQuery)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.url.String(), nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.url.String(), http.NoBody)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to create request: %s", err)
|
||||
return nil, err
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/crowdsecurity/go-cs-lib/ptr"
|
||||
"github.com/crowdsecurity/grokky"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/cache"
|
||||
|
@ -462,11 +463,10 @@ func (n *Node) compile(pctx *UnixParserCtx, ectx EnricherCtx) error {
|
|||
that will be used only for processing this node ;) */
|
||||
if n.Debug {
|
||||
clog := log.New()
|
||||
if err = types.ConfigureLogger(clog); err != nil {
|
||||
if err = types.ConfigureLogger(clog, ptr.Of(log.DebugLevel)); err != nil {
|
||||
return fmt.Errorf("while creating bucket-specific logger: %w", err)
|
||||
}
|
||||
|
||||
clog.SetLevel(log.DebugLevel)
|
||||
n.Logger = clog.WithField("id", n.rn)
|
||||
n.Logger.Infof("%s has debug enabled", n.Name)
|
||||
} else {
|
||||
|
|
|
@ -11,12 +11,13 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
logFormatter log.Formatter
|
||||
LogOutput *lumberjack.Logger // io.Writer
|
||||
logLevel log.Level
|
||||
logFormatter log.Formatter
|
||||
LogOutput *lumberjack.Logger // io.Writer
|
||||
logLevel log.Level
|
||||
logLevelViaFlag bool
|
||||
)
|
||||
|
||||
func SetDefaultLoggerConfig(cfgMode string, cfgFolder string, cfgLevel log.Level, maxSize int, maxFiles int, maxAge int, format string, compress *bool, forceColors bool) error {
|
||||
func SetDefaultLoggerConfig(cfgMode string, cfgFolder string, cfgLevel log.Level, maxSize int, maxFiles int, maxAge int, format string, compress *bool, forceColors bool, levelViaFlag bool) error {
|
||||
if format == "" {
|
||||
format = "text"
|
||||
}
|
||||
|
@ -68,13 +69,14 @@ func SetDefaultLoggerConfig(cfgMode string, cfgFolder string, cfgLevel log.Level
|
|||
}
|
||||
|
||||
logLevel = cfgLevel
|
||||
logLevelViaFlag = levelViaFlag
|
||||
log.SetLevel(logLevel)
|
||||
log.SetFormatter(logFormatter)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ConfigureLogger(clog *log.Logger) error {
|
||||
func ConfigureLogger(clog *log.Logger, level *log.Level) error {
|
||||
/*Configure logs*/
|
||||
if LogOutput != nil {
|
||||
clog.SetOutput(LogOutput)
|
||||
|
@ -86,6 +88,10 @@ func ConfigureLogger(clog *log.Logger) error {
|
|||
|
||||
clog.SetLevel(logLevel)
|
||||
|
||||
if level != nil && !logLevelViaFlag {
|
||||
clog.SetLevel(*level)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -101,6 +101,24 @@ teardown() {
|
|||
assert_stderr --regexp 'FATAL.* you must run at least the API Server or crowdsec$'
|
||||
}
|
||||
|
||||
@test "crowdsec - pass log level flag to apiserver" {
|
||||
LOCAL_API_CREDENTIALS=$(config_get '.api.client.credentials_path')
|
||||
config_set "$LOCAL_API_CREDENTIALS" '.password="badpassword"'
|
||||
|
||||
config_set '.common.log_media="stdout"'
|
||||
rune -1 "$CROWDSEC"
|
||||
|
||||
# info
|
||||
assert_stderr --partial "/v1/watchers/login"
|
||||
# fatal
|
||||
assert_stderr --partial "incorrect Username or Password"
|
||||
|
||||
config_set '.common.log_media="stdout"'
|
||||
rune -1 "$CROWDSEC" -error
|
||||
|
||||
refute_stderr --partial "/v1/watchers/login"
|
||||
}
|
||||
|
||||
@test "CS_LAPI_SECRET not strong enough" {
|
||||
CS_LAPI_SECRET=foo rune -1 wait-for "$CROWDSEC"
|
||||
assert_stderr --partial "api server init: unable to run local API: controller init: CS_LAPI_SECRET not strong enough"
|
||||
|
|
|
@ -66,10 +66,10 @@ teardown() {
|
|||
|
||||
@test "cscli alerts inspect" {
|
||||
rune -1 cscli alerts inspect
|
||||
assert_stderr --partial 'missing alert_id'
|
||||
assert_stderr 'Error: requires at least 1 arg(s), only received 0'
|
||||
|
||||
rune -0 cscli decisions add -i 10.20.30.40 -t ban
|
||||
rune -0 cscli alerts list -o raw <(output)
|
||||
rune -0 cscli alerts list -o raw
|
||||
rune -0 grep 10.20.30.40 <(output)
|
||||
rune -0 cut -d, -f1 <(output)
|
||||
ALERT_ID="$output"
|
||||
|
|
174
test/bats/appsec.bats
Normal file
174
test/bats/appsec.bats
Normal file
|
@ -0,0 +1,174 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
set -u
|
||||
|
||||
setup_file() {
|
||||
load "../lib/setup_file.sh"
|
||||
CONFIG_DIR=$(dirname "$CONFIG_YAML")
|
||||
export CONFIG_DIR
|
||||
|
||||
ACQUIS_DIR=$(config_get '.crowdsec_service.acquisition_dir')
|
||||
export ACQUIS_DIR
|
||||
}
|
||||
|
||||
teardown_file() {
|
||||
load "../lib/teardown_file.sh"
|
||||
}
|
||||
|
||||
setup() {
|
||||
load "../lib/setup.sh"
|
||||
./instance-data load
|
||||
mkdir -p "$ACQUIS_DIR"
|
||||
}
|
||||
|
||||
teardown() {
|
||||
./instance-crowdsec stop
|
||||
}
|
||||
|
||||
#----------
|
||||
|
||||
@test "invalid configuration" {
|
||||
config_set '.common.log_media="stdout"'
|
||||
|
||||
cat > "$ACQUIS_DIR"/appsec.yaml <<-EOT
|
||||
source: appsec
|
||||
EOT
|
||||
|
||||
rune -1 wait-for "$CROWDSEC"
|
||||
assert_stderr --partial "crowdsec init: while loading acquisition config: missing labels in $ACQUIS_DIR/appsec.yaml (position 0)"
|
||||
|
||||
cat > "$ACQUIS_DIR"/appsec.yaml <<-EOT
|
||||
source: appsec
|
||||
labels:
|
||||
type: appsec
|
||||
EOT
|
||||
|
||||
rune -1 wait-for "$CROWDSEC"
|
||||
assert_stderr --partial "crowdsec init: while loading acquisition config: while configuring datasource of type appsec from $ACQUIS_DIR/appsec.yaml (position 0): unable to parse appsec configuration: appsec_config or appsec_config_path must be set"
|
||||
}
|
||||
|
||||
@test "appsec allow and ban" {
|
||||
config_set '.common.log_media="stdout"'
|
||||
|
||||
rune -0 cscli collections install crowdsecurity/appsec-virtual-patching
|
||||
rune -0 cscli collections install crowdsecurity/appsec-generic-rules
|
||||
|
||||
socket="$BATS_TEST_TMPDIR"/sock
|
||||
|
||||
cat > "$ACQUIS_DIR"/appsec.yaml <<-EOT
|
||||
source: appsec
|
||||
listen_socket: $socket
|
||||
labels:
|
||||
type: appsec
|
||||
appsec_config: crowdsecurity/appsec-default
|
||||
EOT
|
||||
|
||||
rune -0 wait-for \
|
||||
--err "Appsec Runner ready to process event" \
|
||||
"$CROWDSEC"
|
||||
|
||||
assert_stderr --partial "loading inband rule crowdsecurity/base-config"
|
||||
assert_stderr --partial "loading inband rule crowdsecurity/vpatch-*"
|
||||
assert_stderr --partial "loading inband rule crowdsecurity/generic-*"
|
||||
assert_stderr --partial "Created 1 appsec runners"
|
||||
assert_stderr --partial "Appsec Runner ready to process event"
|
||||
|
||||
./instance-crowdsec start
|
||||
|
||||
rune -0 cscli bouncers add appsecbouncer --key appkey
|
||||
|
||||
# appsec will perform a HEAD request to validate.
|
||||
# If it fails, check downstream with:
|
||||
#
|
||||
# lapisocket=$(config_get '.api.server.listen_socket')
|
||||
# rune -0 curl -sS --fail-with-body --unix-socket "$lapisocket" -H "X-Api-Key: appkey" "http://fakehost/v1/decisions/stream"
|
||||
# assert_json '{deleted:null,new:null}'
|
||||
|
||||
rune -0 curl -sS --fail-with-body --unix-socket "$socket" \
|
||||
-H "x-crowdsec-appsec-api-key: appkey" \
|
||||
-H "x-crowdsec-appsec-ip: 1.2.3.4" \
|
||||
-H 'x-crowdsec-appsec-uri: /' \
|
||||
-H 'x-crowdsec-appsec-host: foo.com' \
|
||||
-H 'x-crowdsec-appsec-verb: GET' \
|
||||
'http://fakehost'
|
||||
|
||||
assert_json '{action:"allow",http_status:200}'
|
||||
|
||||
rune -22 curl -sS --fail-with-body --unix-socket "$socket" \
|
||||
-H "x-crowdsec-appsec-api-key: appkey" \
|
||||
-H "x-crowdsec-appsec-ip: 1.2.3.4" \
|
||||
-H 'x-crowdsec-appsec-uri: /.env' \
|
||||
-H 'x-crowdsec-appsec-host: foo.com' \
|
||||
-H 'x-crowdsec-appsec-verb: GET' \
|
||||
'http://fakehost'
|
||||
|
||||
assert_json '{action:"ban",http_status:403}'
|
||||
}
|
||||
|
||||
@test "TLS connection to lapi, own CA" {
|
||||
tmpdir="$BATS_FILE_TMPDIR"
|
||||
|
||||
CFDIR="$BATS_TEST_DIRNAME/testdata/cfssl"
|
||||
|
||||
# Root CA
|
||||
cfssl gencert -loglevel 2 \
|
||||
--initca "$CFDIR/ca_root.json" \
|
||||
| cfssljson --bare "$tmpdir/root"
|
||||
|
||||
# Intermediate CA
|
||||
cfssl gencert -loglevel 2 \
|
||||
--initca "$CFDIR/ca_intermediate.json" \
|
||||
| cfssljson --bare "$tmpdir/inter"
|
||||
|
||||
cfssl sign -loglevel 2 \
|
||||
-ca "$tmpdir/root.pem" -ca-key "$tmpdir/root-key.pem" \
|
||||
-config "$CFDIR/profiles.json" -profile intermediate_ca "$tmpdir/inter.csr" \
|
||||
| cfssljson --bare "$tmpdir/inter"
|
||||
|
||||
# Server cert for crowdsec with the intermediate
|
||||
cfssl gencert -loglevel 2 \
|
||||
-ca "$tmpdir/inter.pem" -ca-key "$tmpdir/inter-key.pem" \
|
||||
-config "$CFDIR/profiles.json" -profile=server "$CFDIR/server.json" \
|
||||
| cfssljson --bare "$tmpdir/server"
|
||||
|
||||
cat "$tmpdir/root.pem" "$tmpdir/inter.pem" > "$tmpdir/bundle.pem"
|
||||
|
||||
export tmpdir
|
||||
config_set '
|
||||
.api.server.tls.cert_file=strenv(tmpdir) + "/server.pem" |
|
||||
.api.server.tls.key_file=strenv(tmpdir) + "/server-key.pem" |
|
||||
.api.server.tls.ca_cert_path=strenv(tmpdir) + "/inter.pem"
|
||||
'
|
||||
|
||||
rune -0 cscli collections install crowdsecurity/appsec-virtual-patching
|
||||
rune -0 cscli collections install crowdsecurity/appsec-generic-rules
|
||||
|
||||
socket="$BATS_TEST_TMPDIR"/sock
|
||||
|
||||
cat > "$ACQUIS_DIR"/appsec.yaml <<-EOT
|
||||
source: appsec
|
||||
listen_socket: $socket
|
||||
labels:
|
||||
type: appsec
|
||||
appsec_config: crowdsecurity/appsec-default
|
||||
EOT
|
||||
|
||||
config_set "$CONFIG_DIR/local_api_credentials.yaml" '
|
||||
.url="https://127.0.0.1:8080" |
|
||||
.ca_cert_path=strenv(tmpdir) + "/bundle.pem"
|
||||
'
|
||||
|
||||
./instance-crowdsec start
|
||||
|
||||
rune -0 cscli bouncers add appsecbouncer --key appkey
|
||||
|
||||
rune -0 curl -sS --fail-with-body --unix-socket "$socket" \
|
||||
-H "x-crowdsec-appsec-api-key: appkey" \
|
||||
-H "x-crowdsec-appsec-ip: 1.2.3.4" \
|
||||
-H 'x-crowdsec-appsec-uri: /' \
|
||||
-H 'x-crowdsec-appsec-host: foo.com' \
|
||||
-H 'x-crowdsec-appsec-verb: GET' \
|
||||
'http://fakehost'
|
||||
|
||||
assert_json '{action:"allow",http_status:200}'
|
||||
}
|
|
@ -47,13 +47,13 @@ get_latest_version() {
|
|||
|
||||
@test "cscli <hubtype> install (no argument)" {
|
||||
rune -1 cscli parsers install
|
||||
refute_output
|
||||
assert_output --partial 'Usage:'
|
||||
assert_stderr --partial 'requires at least 1 arg(s), only received 0'
|
||||
}
|
||||
|
||||
@test "cscli <hubtype> install (aliased)" {
|
||||
rune -1 cscli parser install
|
||||
refute_output
|
||||
assert_output --partial 'Usage:'
|
||||
assert_stderr --partial 'requires at least 1 arg(s), only received 0'
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue