mirror of
https://github.com/crowdsecurity/crowdsec.git
synced 2025-05-11 20:36:12 +02:00
Merge branch 'master' into remove-dashboard
This commit is contained in:
commit
eca00377af
141 changed files with 1830 additions and 997 deletions
12
.github/workflows/bats-hub.yml
vendored
12
.github/workflows/bats-hub.yml
vendored
|
@ -10,9 +10,6 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
test-file: ["hub-1.bats", "hub-2.bats", "hub-3.bats"]
|
||||
|
||||
name: "Functional tests"
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -46,11 +43,14 @@ jobs:
|
|||
|
||||
- name: "Run hub tests"
|
||||
run: |
|
||||
./test/bin/generate-hub-tests
|
||||
./test/run-tests ./test/dyn-bats/${{ matrix.test-file }} --formatter $(pwd)/test/lib/color-formatter
|
||||
PATH=$(pwd)/test/local/bin:$PATH
|
||||
./test/instance-data load
|
||||
git clone --depth 1 https://github.com/crowdsecurity/hub.git ./hub
|
||||
cd ./hub
|
||||
cscli hubtest run --all --clean --max-jobs 8
|
||||
|
||||
- name: "Collect hub coverage"
|
||||
run: ./test/bin/collect-hub-coverage >> $GITHUB_ENV
|
||||
run: ./test/bin/collect-hub-coverage ./hub >> $GITHUB_ENV
|
||||
|
||||
- name: "Create Parsers badge"
|
||||
uses: schneegans/dynamic-badges-action@v1.7.0
|
||||
|
|
|
@ -46,6 +46,7 @@ linters-settings:
|
|||
gomoddirectives:
|
||||
replace-allow-list:
|
||||
- golang.org/x/time/rate
|
||||
- github.com/corazawaf/coraza/v3
|
||||
|
||||
govet:
|
||||
enable-all: true
|
||||
|
@ -214,7 +215,6 @@ linters-settings:
|
|||
enable-all: true
|
||||
disabled-checks:
|
||||
- paramTypeCombine
|
||||
- httpNoBody
|
||||
- ifElseChain
|
||||
- importShadow
|
||||
- hugeParam
|
||||
|
@ -280,12 +280,10 @@ linters:
|
|||
# Recommended? (requires some work)
|
||||
#
|
||||
|
||||
- containedctx # containedctx is a linter that detects struct contained context.Context field
|
||||
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
|
||||
- ireturn # Accept Interfaces, Return Concrete Types
|
||||
- mnd # An analyzer to detect magic numbers.
|
||||
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
|
||||
- noctx # Finds sending http request without context.Context
|
||||
- unparam # Reports unused function parameters
|
||||
|
||||
#
|
||||
|
@ -503,3 +501,8 @@ issues:
|
|||
- usetesting
|
||||
path: "pkg/apiserver/(.+)_test.go"
|
||||
text: "os.CreateTemp.* could be replaced by os.CreateTemp.*"
|
||||
|
||||
- linters:
|
||||
- containedctx
|
||||
path: "cmd/notification-file/main.go"
|
||||
text: "found a struct that contains a context.Context field"
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -22,6 +22,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/cmd/crowdsec-cli/reload"
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/apiclient"
|
||||
|
@ -45,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()
|
||||
|
@ -191,7 +191,7 @@ After running this command your will need to validate the enrollment in the weba
|
|||
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}
|
||||
|
@ -224,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: `
|
||||
|
@ -277,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
|
||||
}
|
||||
|
@ -300,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,10 +43,10 @@ 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,
|
||||
}
|
||||
|
||||
cmd.AddCommand(cli.newBranchCmd())
|
||||
cmd.AddCommand(cli.newListCmd())
|
||||
cmd.AddCommand(cli.newUpdateCmd())
|
||||
cmd.AddCommand(cli.newUpgradeCmd())
|
||||
|
@ -84,13 +85,35 @@ func (cli *cliHub) List(out io.Writer, hub *cwhub.Hub, all bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli *cliHub) newBranchCmd() *cobra.Command {
|
||||
var all bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "branch",
|
||||
Short: "Show selected hub branch",
|
||||
Long: "Display the hub branch to be used, depending on configuration and crowdsec version",
|
||||
Args: args.NoArgs,
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
branch := require.HubBranch(cmd.Context(), cli.cfg())
|
||||
fmt.Println(branch)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&all, "all", "a", false, "List all available items, including those not installed")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cli *cliHub) newListCmd() *cobra.Command {
|
||||
var all bool
|
||||
|
||||
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 +175,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 +251,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 +299,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()
|
||||
|
|
|
@ -1,31 +1,55 @@
|
|||
package clihubtest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/hubtest"
|
||||
)
|
||||
|
||||
func (cli *cliHubTest) newCleanCmd() *cobra.Command {
|
||||
var all bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "clean",
|
||||
Short: "clean [test_name]",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
for _, testName := range args {
|
||||
test, err := hubPtr.LoadTestItem(testName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load test '%s': %w", testName, err)
|
||||
if !all && len(args) == 0 {
|
||||
return errors.New("please provide test to run or --all flag")
|
||||
}
|
||||
|
||||
fmt.Println("Cleaning test data...")
|
||||
|
||||
tests := []*hubtest.HubTestItem{}
|
||||
|
||||
if all {
|
||||
if err := hubPtr.LoadAllTests(); err != nil {
|
||||
return fmt.Errorf("unable to load all tests: %w", err)
|
||||
}
|
||||
if err := test.Clean(); err != nil {
|
||||
return fmt.Errorf("unable to clean test '%s' env: %w", test.Name, err)
|
||||
|
||||
tests = hubPtr.Tests
|
||||
} else {
|
||||
for _, testName := range args {
|
||||
test, err := hubPtr.LoadTestItem(testName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load test '%s': %w", testName, err)
|
||||
}
|
||||
tests = append(tests, test)
|
||||
}
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test.Clean()
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&all, "all", false, "Run all tests")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -9,16 +9,17 @@ import (
|
|||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/hubtest"
|
||||
)
|
||||
|
||||
// getCoverage returns the coverage and the percentage of tests that passed
|
||||
func getCoverage(show bool, getCoverageFunc func() ([]hubtest.Coverage, error)) ([]hubtest.Coverage, int, error) {
|
||||
func getCoverage(show bool, getCoverageFunc func(string) ([]hubtest.Coverage, error), hubDir string) ([]hubtest.Coverage, int, error) {
|
||||
if !show {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
coverage, err := getCoverageFunc()
|
||||
coverage, err := getCoverageFunc(hubDir)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("while getting coverage: %w", err)
|
||||
}
|
||||
|
@ -45,7 +46,7 @@ func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAp
|
|||
|
||||
// for this one we explicitly don't do for appsec
|
||||
if err := HubTest.LoadAllTests(); err != nil {
|
||||
return fmt.Errorf("unable to load all tests: %+v", err)
|
||||
return fmt.Errorf("unable to load all tests: %w", err)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
@ -57,17 +58,17 @@ func (cli *cliHubTest) coverage(showScenarioCov bool, showParserCov bool, showAp
|
|||
showAppsecCov = true
|
||||
}
|
||||
|
||||
parserCoverage, parserCoveragePercent, err := getCoverage(showParserCov, HubTest.GetParsersCoverage)
|
||||
parserCoverage, parserCoveragePercent, err := getCoverage(showParserCov, HubTest.GetParsersCoverage, cfg.Hub.HubDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scenarioCoverage, scenarioCoveragePercent, err := getCoverage(showScenarioCov, HubTest.GetScenariosCoverage)
|
||||
scenarioCoverage, scenarioCoveragePercent, err := getCoverage(showScenarioCov, HubTest.GetScenariosCoverage, cfg.Hub.HubDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
appsecRuleCoverage, appsecRuleCoveragePercent, err := getCoverage(showAppsecCov, HubTest.GetAppsecCoverage)
|
||||
appsecRuleCoverage, appsecRuleCoveragePercent, err := getCoverage(showAppsecCov, HubTest.GetAppsecCoverage, cfg.Hub.HubDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -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 {
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
package clihubtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/args"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/dumps"
|
||||
)
|
||||
|
||||
func (cli *cliHubTest) explain(testName string, details bool, skipOk bool) error {
|
||||
func (cli *cliHubTest) explain(ctx context.Context, testName string, details bool, skipOk bool) error {
|
||||
test, err := HubTest.LoadTestItem(testName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't load test: %+v", err)
|
||||
return fmt.Errorf("can't load test: %w", err)
|
||||
}
|
||||
|
||||
cfg := cli.cfg()
|
||||
|
@ -19,8 +21,8 @@ func (cli *cliHubTest) explain(testName string, details bool, skipOk bool) error
|
|||
|
||||
err = test.ParserAssert.LoadTest(test.ParserResultFile)
|
||||
if err != nil {
|
||||
if err = test.Run(patternDir); err != nil {
|
||||
return fmt.Errorf("running test '%s' failed: %+v", test.Name, err)
|
||||
if err = test.Run(ctx, patternDir); err != nil {
|
||||
return fmt.Errorf("running test '%s' failed: %w", test.Name, err)
|
||||
}
|
||||
|
||||
if err = test.ParserAssert.LoadTest(test.ParserResultFile); err != nil {
|
||||
|
@ -30,8 +32,8 @@ func (cli *cliHubTest) explain(testName string, details bool, skipOk bool) error
|
|||
|
||||
err = test.ScenarioAssert.LoadTest(test.ScenarioResultFile, test.BucketPourResultFile)
|
||||
if err != nil {
|
||||
if err = test.Run(patternDir); err != nil {
|
||||
return fmt.Errorf("running test '%s' failed: %+v", test.Name, err)
|
||||
if err = test.Run(ctx, patternDir); err != nil {
|
||||
return fmt.Errorf("running test '%s' failed: %w", test.Name, err)
|
||||
}
|
||||
|
||||
if err = test.ScenarioAssert.LoadTest(test.ScenarioResultFile, test.BucketPourResultFile); err != nil {
|
||||
|
@ -58,11 +60,12 @@ 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 {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
for _, testName := range args {
|
||||
if err := cli.explain(testName, details, skipOk); err != nil {
|
||||
if err := cli.explain(ctx, testName, details, skipOk); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -1,34 +1,36 @@
|
|||
package clihubtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/fatih/color"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/emoji"
|
||||
"github.com/crowdsecurity/crowdsec/pkg/hubtest"
|
||||
)
|
||||
|
||||
func (cli *cliHubTest) run(runAll bool, nucleiTargetHost string, appSecHost string, args []string) error {
|
||||
func (cli *cliHubTest) run(ctx context.Context, all bool, nucleiTargetHost string, appSecHost string, args []string, maxJobs uint) error {
|
||||
cfg := cli.cfg()
|
||||
|
||||
if !runAll && len(args) == 0 {
|
||||
if !all && len(args) == 0 {
|
||||
return errors.New("please provide test to run or --all flag")
|
||||
}
|
||||
|
||||
hubPtr.NucleiTargetHost = nucleiTargetHost
|
||||
hubPtr.AppSecHost = appSecHost
|
||||
|
||||
if runAll {
|
||||
if all {
|
||||
if err := hubPtr.LoadAllTests(); err != nil {
|
||||
return fmt.Errorf("unable to load all tests: %+v", err)
|
||||
return fmt.Errorf("unable to load all tests: %w", err)
|
||||
}
|
||||
} else {
|
||||
for _, testName := range args {
|
||||
|
@ -39,23 +41,29 @@ func (cli *cliHubTest) run(runAll bool, nucleiTargetHost string, appSecHost stri
|
|||
}
|
||||
}
|
||||
|
||||
// set timezone to avoid DST issues
|
||||
os.Setenv("TZ", "UTC")
|
||||
|
||||
patternDir := cfg.ConfigPaths.PatternDir
|
||||
|
||||
var eg errgroup.Group
|
||||
|
||||
if isAppsecTest {
|
||||
log.Info("Appsec tests can not run in parallel: setting max_jobs=1")
|
||||
|
||||
maxJobs = 1
|
||||
}
|
||||
|
||||
eg.SetLimit(int(maxJobs))
|
||||
|
||||
for _, test := range hubPtr.Tests {
|
||||
if cfg.Cscli.Output == "human" {
|
||||
log.Infof("Running test '%s'", test.Name)
|
||||
fmt.Printf("Running test '%s'\n", test.Name)
|
||||
}
|
||||
|
||||
err := test.Run(patternDir)
|
||||
if err != nil {
|
||||
log.Errorf("running test '%s' failed: %+v", test.Name, err)
|
||||
}
|
||||
eg.Go(func() error {
|
||||
return test.Run(ctx, patternDir)
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
func printParserFailures(test *hubtest.HubTestItem) {
|
||||
|
@ -101,24 +109,31 @@ func printScenarioFailures(test *hubtest.HubTestItem) {
|
|||
func (cli *cliHubTest) newRunCmd() *cobra.Command {
|
||||
var (
|
||||
noClean bool
|
||||
runAll bool
|
||||
all bool
|
||||
reportSuccess bool
|
||||
forceClean bool
|
||||
nucleiTargetHost string
|
||||
appSecHost string
|
||||
)
|
||||
|
||||
maxJobs := uint(runtime.NumCPU())
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "run [test_name]",
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return cli.run(runAll, nucleiTargetHost, appSecHost, args)
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if all {
|
||||
fmt.Printf("Running all tests (max_jobs: %d)\n", maxJobs)
|
||||
}
|
||||
|
||||
return cli.run(cmd.Context(), all, nucleiTargetHost, appSecHost, args, maxJobs)
|
||||
},
|
||||
PersistentPostRunE: func(_ *cobra.Command, _ []string) error {
|
||||
cfg := cli.cfg()
|
||||
|
||||
success := true
|
||||
testResult := make(map[string]bool)
|
||||
testMap := make(map[string]*hubtest.HubTestItem)
|
||||
for _, test := range hubPtr.Tests {
|
||||
if test.AutoGen && !isAppsecTest {
|
||||
if test.ParserAssert.AutoGenAssert {
|
||||
|
@ -132,22 +147,15 @@ func (cli *cliHubTest) newRunCmd() *cobra.Command {
|
|||
fmt.Println(test.ScenarioAssert.AutoGenAssertData)
|
||||
}
|
||||
if !noClean {
|
||||
if err := test.Clean(); err != nil {
|
||||
return fmt.Errorf("unable to clean test '%s' env: %w", test.Name, err)
|
||||
}
|
||||
test.Clean()
|
||||
}
|
||||
|
||||
return fmt.Errorf("please fill your assert file(s) for test '%s', exiting", test.Name)
|
||||
}
|
||||
testResult[test.Name] = test.Success
|
||||
testMap[test.Name] = test
|
||||
if test.Success {
|
||||
if cfg.Cscli.Output == "human" {
|
||||
log.Infof("Test '%s' passed successfully (%d assertions)\n", test.Name, test.ParserAssert.NbAssert+test.ScenarioAssert.NbAssert)
|
||||
}
|
||||
if !noClean {
|
||||
if err := test.Clean(); err != nil {
|
||||
return fmt.Errorf("unable to clean test '%s' env: %w", test.Name, err)
|
||||
}
|
||||
test.Clean()
|
||||
}
|
||||
} else {
|
||||
success = false
|
||||
|
@ -157,7 +165,7 @@ func (cli *cliHubTest) newRunCmd() *cobra.Command {
|
|||
printScenarioFailures(test)
|
||||
if !forceClean && !noClean {
|
||||
prompt := &survey.Confirm{
|
||||
Message: fmt.Sprintf("\nDo you want to remove runtime folder for test '%s'? (default: Yes)", test.Name),
|
||||
Message: fmt.Sprintf("Do you want to remove runtime and result folder for '%s'?", test.Name),
|
||||
Default: true,
|
||||
}
|
||||
if err := survey.AskOne(prompt, &cleanTestEnv); err != nil {
|
||||
|
@ -167,22 +175,20 @@ func (cli *cliHubTest) newRunCmd() *cobra.Command {
|
|||
}
|
||||
|
||||
if cleanTestEnv || forceClean {
|
||||
if err := test.Clean(); err != nil {
|
||||
return fmt.Errorf("unable to clean test '%s' env: %w", test.Name, err)
|
||||
}
|
||||
test.Clean()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch cfg.Cscli.Output {
|
||||
case "human":
|
||||
hubTestResultTable(color.Output, cfg.Cscli.Color, testResult)
|
||||
hubTestResultTable(color.Output, cfg.Cscli.Color, testMap, reportSuccess)
|
||||
case "json":
|
||||
jsonResult := make(map[string][]string, 0)
|
||||
jsonResult["success"] = make([]string, 0)
|
||||
jsonResult["fail"] = make([]string, 0)
|
||||
for testName, success := range testResult {
|
||||
if success {
|
||||
for testName, test := range testMap {
|
||||
if test.Success {
|
||||
jsonResult["success"] = append(jsonResult["success"], testName)
|
||||
} else {
|
||||
jsonResult["fail"] = append(jsonResult["fail"], testName)
|
||||
|
@ -198,7 +204,11 @@ func (cli *cliHubTest) newRunCmd() *cobra.Command {
|
|||
}
|
||||
|
||||
if !success {
|
||||
return errors.New("some tests failed")
|
||||
if reportSuccess {
|
||||
return errors.New("some tests failed")
|
||||
}
|
||||
|
||||
return errors.New("some tests failed, use --report-success to show them all")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -209,7 +219,9 @@ func (cli *cliHubTest) newRunCmd() *cobra.Command {
|
|||
cmd.Flags().BoolVar(&forceClean, "clean", false, "Clean runtime environment if test fail")
|
||||
cmd.Flags().StringVar(&nucleiTargetHost, "target", hubtest.DefaultNucleiTarget, "Target for AppSec Test")
|
||||
cmd.Flags().StringVar(&appSecHost, "host", hubtest.DefaultAppsecHost, "Address to expose AppSec for hubtest")
|
||||
cmd.Flags().BoolVar(&runAll, "all", false, "Run all tests")
|
||||
cmd.Flags().BoolVar(&all, "all", false, "Run all tests")
|
||||
cmd.Flags().BoolVar(&reportSuccess, "report-success", false, "Report successful tests too (implied with json output)")
|
||||
cmd.Flags().UintVar(&maxJobs, "max-jobs", maxJobs, "Max number of concurrent tests (does not apply to appsec)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package clihubtest
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
|
||||
|
@ -11,22 +12,31 @@ import (
|
|||
"github.com/crowdsecurity/crowdsec/pkg/hubtest"
|
||||
)
|
||||
|
||||
func hubTestResultTable(out io.Writer, wantColor string, testResult map[string]bool) {
|
||||
func hubTestResultTable(out io.Writer, wantColor string, testMap map[string]*hubtest.HubTestItem, reportSuccess bool) {
|
||||
t := cstable.NewLight(out, wantColor)
|
||||
t.SetHeaders("Test", "Result")
|
||||
t.SetHeaders("Test", "Result", "Assertions")
|
||||
t.SetHeaderAlignment(text.AlignLeft)
|
||||
t.SetAlignment(text.AlignLeft)
|
||||
|
||||
for testName, success := range testResult {
|
||||
showTable := reportSuccess
|
||||
|
||||
for testName, test := range testMap {
|
||||
status := emoji.CheckMarkButton
|
||||
if !success {
|
||||
if !test.Success {
|
||||
status = emoji.CrossMark
|
||||
showTable = true
|
||||
}
|
||||
|
||||
t.AddRow(testName, status)
|
||||
if !test.Success || reportSuccess {
|
||||
t.AddRow(testName, status, strconv.Itoa(test.ParserAssert.NbAssert+test.ScenarioAssert.NbAssert))
|
||||
}
|
||||
}
|
||||
|
||||
t.Render()
|
||||
if showTable {
|
||||
t.Render()
|
||||
} else {
|
||||
fmt.Println("All tests passed, use --report-success for more details.")
|
||||
}
|
||||
}
|
||||
|
||||
func hubTestListTable(out io.Writer, wantColor string, tests []*hubtest.HubTestItem) {
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -168,8 +168,9 @@ func (cli *cliRoot) initialize() error {
|
|||
}
|
||||
}
|
||||
|
||||
csConfig.DbConfig.LogLevel = ptr.Of(cli.wantedLogLevel())
|
||||
|
||||
if csConfig.DbConfig != nil {
|
||||
csConfig.DbConfig.LogLevel = ptr.Of(cli.wantedLogLevel())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
63
debian/migrate-hub.sh
vendored
Executable file
63
debian/migrate-hub.sh
vendored
Executable file
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
# This script is provided (only in the source distribution) as an ad-hoc solution
|
||||
# to migrate an installation from the crowdsec package maintained in the debian or ubuntu repositories
|
||||
# to the official crowdsec repository.
|
||||
|
||||
set -eu
|
||||
|
||||
if [ ! -d /var/lib/crowdsec/hub/ ]; then
|
||||
echo "You don't have a hub directory to migrate."
|
||||
echo
|
||||
echo "Use this script only if you upgrade from the crowdsec package included in the debian or ubuntu repositories."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Download everything on the new hub but don't install anything yet
|
||||
|
||||
echo "Downloading Hub content..."
|
||||
|
||||
for itemtype in $(cscli hub types -o raw); do
|
||||
ALL_ITEMS=$(cscli "$itemtype" list -a -o raw | tail +2 | cut -d, -f1)
|
||||
if [ -n "${ALL_ITEMS}" ]; then
|
||||
# shellcheck disable=SC2086
|
||||
cscli "$itemtype" install \
|
||||
$ALL_ITEMS \
|
||||
--download-only -y
|
||||
fi
|
||||
done
|
||||
|
||||
# Fix links
|
||||
|
||||
BASEDIR=/etc/crowdsec/
|
||||
OLD_PATH=/var/lib/crowdsec/hub/
|
||||
NEW_PATH=/etc/crowdsec/hub/
|
||||
|
||||
find "$BASEDIR" -type l 2>/dev/null | while IFS= read -r link
|
||||
do
|
||||
target="$(readlink "$link")" || continue
|
||||
|
||||
case "$target" in
|
||||
"$OLD_PATH"*)
|
||||
suffix="${target#"$OLD_PATH"}"
|
||||
new_target="${NEW_PATH}${suffix}"
|
||||
|
||||
if [ -e "$target" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ ! -e "$new_target" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Update symlink: $link"
|
||||
ln -sf "$new_target" "$link"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# upgrade tainted collections
|
||||
|
||||
cscli hub upgrade --force
|
27
go.mod
27
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
|
||||
|
@ -23,12 +23,11 @@ require (
|
|||
github.com/corazawaf/libinjection-go v0.2.2
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/creack/pty v1.1.21 // indirect
|
||||
github.com/crowdsecurity/coraza/v3 v3.0.0-20250121111732-9b0043b679d7
|
||||
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26
|
||||
github.com/crowdsecurity/go-cs-lib v0.0.16
|
||||
github.com/crowdsecurity/grokky v0.2.2
|
||||
github.com/crowdsecurity/machineid v1.0.2
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/docker v27.3.1+incompatible
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
|
@ -45,7 +44,7 @@ require (
|
|||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/goccy/go-yaml v1.11.0
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0
|
||||
|
@ -97,12 +96,12 @@ require (
|
|||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/mod v0.23.0
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0
|
||||
golang.org/x/text v0.21.0
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/sync v0.12.0
|
||||
golang.org/x/sys v0.31.0
|
||||
golang.org/x/text v0.23.0
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
google.golang.org/grpc v1.67.1
|
||||
google.golang.org/protobuf v1.36.3
|
||||
|
@ -115,6 +114,8 @@ require (
|
|||
|
||||
)
|
||||
|
||||
require github.com/corazawaf/coraza/v3 v3.3.2
|
||||
|
||||
require (
|
||||
ariga.io/atlas v0.31.1-0.20250212144724-069be8033e83 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
|
@ -128,7 +129,6 @@ require (
|
|||
github.com/bytedance/sonic/loader v0.2.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/corazawaf/coraza-coreruleset v0.0.0-20240226094324-415b1017abdc // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.7 // indirect
|
||||
|
@ -162,7 +162,6 @@ require (
|
|||
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgtype v1.14.0 // indirect
|
||||
github.com/jcchavezs/mergefs v0.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
|
@ -186,7 +185,7 @@ require (
|
|||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20240411101913-e07a1f0e8eb4 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.18 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
|
@ -217,7 +216,7 @@ require (
|
|||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
golang.org/x/arch v0.12.0 // indirect
|
||||
golang.org/x/term v0.28.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||
|
@ -233,3 +232,5 @@ require (
|
|||
)
|
||||
|
||||
replace golang.org/x/time/rate => github.com/crowdsecurity/crowdsec/pkg/time/rate v0.0.0
|
||||
|
||||
replace github.com/corazawaf/coraza/v3 => github.com/crowdsecurity/coraza/v3 v3.0.0-20250320231801-749b8bded21a
|
||||
|
|
40
go.sum
40
go.sum
|
@ -93,8 +93,6 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
|||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/corazawaf/coraza-coreruleset v0.0.0-20240226094324-415b1017abdc h1:OlJhrgI3I+FLUCTI3JJW8MoqyM78WbqJjecqMnqG+wc=
|
||||
github.com/corazawaf/coraza-coreruleset v0.0.0-20240226094324-415b1017abdc/go.mod h1:7rsocqNDkTCira5T0M7buoKR2ehh7YZiPkzxRuAgvVU=
|
||||
github.com/corazawaf/coraza/v3 v3.3.2 h1:eG1HPLySTR9lND6y6fPOajubwbuHRF6aXCsCtxyqKTY=
|
||||
github.com/corazawaf/coraza/v3 v3.3.2/go.mod h1:4EqMZkRoil11FnResCT/2JIg61dH+6D7F48VG8SVzuA=
|
||||
github.com/corazawaf/libinjection-go v0.2.2 h1:Chzodvb6+NXh6wew5/yhD0Ggioif9ACrQGR4qjTCs1g=
|
||||
github.com/corazawaf/libinjection-go v0.2.2/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
|
@ -109,8 +107,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
|||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/crowdsecurity/coraza/v3 v3.0.0-20250121111732-9b0043b679d7 h1:nIwAjapWmiQD3W/uAWYE3z+DC5Coy/zTyPBCJ379fAw=
|
||||
github.com/crowdsecurity/coraza/v3 v3.0.0-20250121111732-9b0043b679d7/go.mod h1:A+uciRXu+yhZcHMtM052bSM6vyJsMMU37NJN+tVoGqo=
|
||||
github.com/crowdsecurity/coraza/v3 v3.0.0-20250320231801-749b8bded21a h1:2Nyr+47Y/K68wohQWCrE7jKRIOpp6hJ29XCEQO3FhOw=
|
||||
github.com/crowdsecurity/coraza/v3 v3.0.0-20250320231801-749b8bded21a/go.mod h1:xSaXWOhFMSbrV8qOOfBKAyw3aOqfwaSaOy5BgSF8XlA=
|
||||
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26 h1:r97WNVC30Uen+7WnLs4xDScS/Ex988+id2k6mDf8psU=
|
||||
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26/go.mod h1:zpv7r+7KXwgVUZnUNjyP22zc/D7LKjyoY02weH2RBbk=
|
||||
github.com/crowdsecurity/go-cs-lib v0.0.16 h1:2/htodjwc/sfsv4deX8F/2Fzg1bOI8w3O1/BPSvvsB0=
|
||||
|
@ -121,8 +119,9 @@ github.com/crowdsecurity/machineid v1.0.2 h1:wpkpsUghJF8Khtmn/tg6GxgdhLA1Xflerh5
|
|||
github.com/crowdsecurity/machineid v1.0.2/go.mod h1:XWUSlnS0R0+u/JK5ulidwlbceNT3ZOCKteoVQEn6Luo=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
|
@ -307,8 +306,8 @@ github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx
|
|||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc=
|
||||
github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -584,8 +583,9 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
|
@ -800,8 +800,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
|
|||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
|
@ -833,8 +833,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
|||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -844,8 +844,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -881,8 +881,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
@ -890,8 +890,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
|||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
@ -904,8 +904,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -3,6 +3,8 @@ package appsecacquisition
|
|||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
@ -346,7 +348,7 @@ func TestAppsecRuleMatches(t *testing.T) {
|
|||
input_request: appsec.ParsedRequest{
|
||||
ClientIP: "1.2.3.4",
|
||||
RemoteAddr: "127.0.0.1",
|
||||
Method: "GET",
|
||||
Method: "POST",
|
||||
URI: "/urllll",
|
||||
Headers: http.Header{"Content-Type": []string{"multipart/form-data; boundary=boundary"}},
|
||||
Body: []byte(`
|
||||
|
@ -368,6 +370,11 @@ toto
|
|||
|
||||
require.Len(t, responses, 1)
|
||||
require.True(t, responses[0].InBandInterrupt)
|
||||
|
||||
// Might fail if you have artifacts from previous tests, but good enough 99% of the time
|
||||
tmpFiles, err := filepath.Glob(filepath.Join(os.TempDir(), "crzmp*"))
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, tmpFiles)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/tomb.v2"
|
||||
|
||||
"github.com/crowdsecurity/coraza/v3"
|
||||
corazatypes "github.com/crowdsecurity/coraza/v3/types"
|
||||
"github.com/corazawaf/coraza/v3"
|
||||
corazatypes "github.com/corazawaf/coraza/v3/types"
|
||||
|
||||
// load body processors via init()
|
||||
_ "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/appsec/bodyprocessors"
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -354,6 +355,10 @@ func (r *AppsecRunner) handleRequest(request *appsec.ParsedRequest) {
|
|||
err := r.ProcessInBandRules(request)
|
||||
if err != nil {
|
||||
logger.Errorf("unable to process InBand rules: %s", err)
|
||||
err = request.Tx.Close()
|
||||
if err != nil {
|
||||
logger.Errorf("unable to close inband transaction: %s", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -365,6 +370,11 @@ func (r *AppsecRunner) handleRequest(request *appsec.ParsedRequest) {
|
|||
r.handleInBandInterrupt(request)
|
||||
}
|
||||
|
||||
err = request.Tx.Close()
|
||||
if err != nil {
|
||||
r.logger.Errorf("unable to close inband transaction: %s", err)
|
||||
}
|
||||
|
||||
// send back the result to the HTTP handler for the InBand part
|
||||
request.ResponseChannel <- r.AppsecRuntime.Response
|
||||
|
||||
|
@ -384,6 +394,10 @@ func (r *AppsecRunner) handleRequest(request *appsec.ParsedRequest) {
|
|||
err = r.ProcessOutOfBandRules(request)
|
||||
if err != nil {
|
||||
logger.Errorf("unable to process OutOfBand rules: %s", err)
|
||||
err = request.Tx.Close()
|
||||
if err != nil {
|
||||
logger.Errorf("unable to close outband transaction: %s", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -394,6 +408,10 @@ func (r *AppsecRunner) handleRequest(request *appsec.ParsedRequest) {
|
|||
r.handleOutBandInterrupt(request)
|
||||
}
|
||||
}
|
||||
err = request.Tx.Close()
|
||||
if err != nil {
|
||||
r.logger.Errorf("unable to close outband transaction: %s", err)
|
||||
}
|
||||
// time spent to process inband AND out of band rules
|
||||
globalParsingElapsed := time.Since(startGlobalParsing)
|
||||
AppsecGlobalParsingHistogram.With(prometheus.Labels{"source": request.RemoteAddrNormalized, "appsec_engine": request.AppsecEngine}).Observe(globalParsingElapsed.Seconds())
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/crowdsecurity/coraza/v3/experimental/plugins"
|
||||
"github.com/crowdsecurity/coraza/v3/experimental/plugins/plugintypes"
|
||||
"github.com/corazawaf/coraza/v3/experimental/plugins"
|
||||
"github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes"
|
||||
)
|
||||
|
||||
type rawBodyProcessor struct{}
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
"github.com/wasilibs/go-re2"
|
||||
"github.com/wasilibs/go-re2/experimental"
|
||||
|
||||
"github.com/crowdsecurity/coraza/v3/experimental/plugins"
|
||||
"github.com/crowdsecurity/coraza/v3/experimental/plugins/plugintypes"
|
||||
"github.com/corazawaf/coraza/v3/experimental/plugins"
|
||||
"github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes"
|
||||
)
|
||||
|
||||
type rx struct {
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/crowdsecurity/coraza/v3/collection"
|
||||
"github.com/crowdsecurity/coraza/v3/types/variables"
|
||||
"github.com/corazawaf/coraza/v3/collection"
|
||||
"github.com/corazawaf/coraza/v3/types/variables"
|
||||
"github.com/crowdsecurity/go-cs-lib/ptr"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/alertcontext"
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -254,7 +254,12 @@ basic_auth:
|
|||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
res, err := http.Get(fmt.Sprintf("%s/test", testHTTPServerAddr))
|
||||
ctx := t.Context()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, testHTTPServerAddr + "/test", http.NoBody)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusMethodNotAllowed, res.StatusCode)
|
||||
|
||||
|
@ -265,6 +270,8 @@ basic_auth:
|
|||
}
|
||||
|
||||
func TestStreamingAcquisitionUnknownPath(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
h := &HTTPSource{}
|
||||
_, _, tomb := SetupAndRunHTTPSource(t, h, []byte(`
|
||||
source: http
|
||||
|
@ -277,7 +284,10 @@ basic_auth:
|
|||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
res, err := http.Get(fmt.Sprintf("%s/unknown", testHTTPServerAddr))
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, testHTTPServerAddr + "/unknown", http.NoBody)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusNotFound, res.StatusCode)
|
||||
|
||||
|
@ -303,11 +313,15 @@ basic_auth:
|
|||
|
||||
client := &http.Client{}
|
||||
|
||||
resp, err := http.Post(fmt.Sprintf("%s/test", testHTTPServerAddr), "application/json", strings.NewReader("test"))
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, testHTTPServerAddr + "/test", strings.NewReader("test"))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/test", testHTTPServerAddr), strings.NewReader("test"))
|
||||
req, err = http.NewRequestWithContext(ctx, http.MethodPost, testHTTPServerAddr + "/test", strings.NewReader("test"))
|
||||
require.NoError(t, err)
|
||||
req.SetBasicAuth("test", "WrongPassword")
|
||||
|
||||
|
@ -553,6 +567,8 @@ timeout: 1s`), 0)
|
|||
}
|
||||
|
||||
func TestStreamingAcquisitionTLSHTTPRequest(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
h := &HTTPSource{}
|
||||
_, _, tomb := SetupAndRunHTTPSource(t, h, []byte(`
|
||||
source: http
|
||||
|
@ -566,7 +582,11 @@ tls:
|
|||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
resp, err := http.Post(fmt.Sprintf("%s/test", testHTTPServerAddr), "application/json", strings.NewReader("test"))
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, testHTTPServerAddr + "/test", strings.NewReader("test"))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ type AlertsDeleteOpts struct {
|
|||
func (s *AlertsService) Add(ctx context.Context, alerts models.AddAlertsRequest) (*models.AddAlertsResponse, *Response, error) {
|
||||
u := fmt.Sprintf("%s/alerts", s.client.URLPrefix)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodPost, u, &alerts)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodPost, u, &alerts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ func (s *AlertsService) List(ctx context.Context, opts AlertsListOpts) (*models.
|
|||
URI = fmt.Sprintf("%s?%s", URI, params.Encode())
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodGet, URI, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodGet, URI, nil)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("building request: %w", err)
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ func (s *AlertsService) Delete(ctx context.Context, opts AlertsDeleteOpts) (*mod
|
|||
|
||||
u := fmt.Sprintf("%s/alerts?%s", s.client.URLPrefix, params.Encode())
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodDelete, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodDelete, u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ func (s *AlertsService) Delete(ctx context.Context, opts AlertsDeleteOpts) (*mod
|
|||
func (s *AlertsService) DeleteOne(ctx context.Context, alertID string) (*models.DeleteAlertsResponse, *Response, error) {
|
||||
u := fmt.Sprintf("%s/alerts/%s", s.client.URLPrefix, alertID)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodDelete, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodDelete, u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func (s *AlertsService) DeleteOne(ctx context.Context, alertID string) (*models.
|
|||
func (s *AlertsService) GetByID(ctx context.Context, alertID int) (*models.Alert, *Response, error) {
|
||||
u := fmt.Sprintf("%s/alerts/%d", s.client.URLPrefix, alertID)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
qs "github.com/google/go-querystring/query"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
@ -27,7 +28,7 @@ func (s *AllowlistsService) List(ctx context.Context, opts AllowlistListOpts) (*
|
|||
|
||||
u += "?" + params.Encode()
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -58,7 +59,7 @@ func (s *AllowlistsService) Get(ctx context.Context, name string, opts Allowlist
|
|||
|
||||
log.Debugf("GET %s", u)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -74,9 +75,10 @@ func (s *AllowlistsService) Get(ctx context.Context, name string, opts Allowlist
|
|||
}
|
||||
|
||||
func (s *AllowlistsService) CheckIfAllowlisted(ctx context.Context, value string) (bool, *Response, error) {
|
||||
u := s.client.URLPrefix + "/allowlists/check/" + value
|
||||
escapedValue := url.PathEscape(value)
|
||||
u := s.client.URLPrefix + "/allowlists/check/" + escapedValue
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodHead, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodHead, u, nil)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
@ -92,9 +94,10 @@ func (s *AllowlistsService) CheckIfAllowlisted(ctx context.Context, value string
|
|||
}
|
||||
|
||||
func (s *AllowlistsService) CheckIfAllowlistedWithReason(ctx context.Context, value string) (*models.CheckAllowlistResponse, *Response, error) {
|
||||
u := s.client.URLPrefix + "/allowlists/check/" + value
|
||||
escapedValue := url.PathEscape(value)
|
||||
u := s.client.URLPrefix + "/allowlists/check/" + escapedValue
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ type enrollRequest struct {
|
|||
func (s *AuthService) UnregisterWatcher(ctx context.Context) (*Response, error) {
|
||||
u := fmt.Sprintf("%s/watchers", s.client.URLPrefix)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodDelete, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodDelete, u, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ func (s *AuthService) UnregisterWatcher(ctx context.Context) (*Response, error)
|
|||
func (s *AuthService) RegisterWatcher(ctx context.Context, registration models.WatcherRegistrationRequest) (*Response, error) {
|
||||
u := fmt.Sprintf("%s/watchers", s.client.URLPrefix)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodPost, u, ®istration)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodPost, u, ®istration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ func (s *AuthService) AuthenticateWatcher(ctx context.Context, auth models.Watch
|
|||
|
||||
u := fmt.Sprintf("%s/watchers/login", s.client.URLPrefix)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodPost, u, &auth)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodPost, u, &auth)
|
||||
if err != nil {
|
||||
return authResp, nil, err
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ func (s *AuthService) AuthenticateWatcher(ctx context.Context, auth models.Watch
|
|||
func (s *AuthService) EnrollWatcher(ctx context.Context, enrollKey string, name string, tags []string, overwrite bool) (*Response, error) {
|
||||
u := fmt.Sprintf("%s/watchers/enroll", s.client.URLPrefix)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodPost, u, &enrollRequest{EnrollKey: enrollKey, Name: name, Tags: tags, Overwrite: overwrite})
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodPost, u, &enrollRequest{EnrollKey: enrollKey, Name: name, Tags: tags, Overwrite: overwrite})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -72,10 +72,6 @@ type service struct {
|
|||
}
|
||||
|
||||
func InitLAPIClient(ctx context.Context, apiUrl string, papiUrl string, login string, password string, scenarios []string) error {
|
||||
if lapiClient != nil {
|
||||
return errors.New("client already initialized")
|
||||
}
|
||||
|
||||
apiURL, err := url.Parse(apiUrl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing api url ('%s'): %w", apiURL, err)
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (c *ApiClient) NewRequestWithContext(ctx context.Context, method, url string, body interface{}) (*http.Request, error) {
|
||||
func (c *ApiClient) PrepareRequest(ctx context.Context, method, url string, body interface{}) (*http.Request, error) {
|
||||
if !strings.HasSuffix(c.BaseURL.Path, "/") {
|
||||
return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL)
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ func (s *DecisionsService) List(ctx context.Context, opts DecisionsListOpts) (*m
|
|||
|
||||
u := fmt.Sprintf("%s/decisions?%s", s.client.URLPrefix, params.Encode())
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ func (s *DecisionsService) List(ctx context.Context, opts DecisionsListOpts) (*m
|
|||
}
|
||||
|
||||
func (s *DecisionsService) FetchV2Decisions(ctx context.Context, url string) (*models.DecisionsStreamResponse, *Response, error) {
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func (s *DecisionsService) FetchV3Decisions(ctx context.Context, url string) (*m
|
|||
scenarioDeleted := "deleted"
|
||||
durationDeleted := "1h"
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, nil, 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
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ func (s *DecisionsService) GetStreamV3(ctx context.Context, opts DecisionsStream
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -289,7 +289,7 @@ func (s *DecisionsService) GetStreamV3(ctx context.Context, opts DecisionsStream
|
|||
func (s *DecisionsService) StopStream(ctx context.Context) (*Response, error) {
|
||||
u := fmt.Sprintf("%s/decisions", s.client.URLPrefix)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodDelete, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodDelete, u, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ func (s *DecisionsService) Delete(ctx context.Context, opts DecisionsDeleteOpts)
|
|||
|
||||
u := fmt.Sprintf("%s/decisions?%s", s.client.URLPrefix, params.Encode())
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodDelete, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodDelete, u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -328,7 +328,7 @@ func (s *DecisionsService) Delete(ctx context.Context, opts DecisionsDeleteOpts)
|
|||
func (s *DecisionsService) DeleteOne(ctx context.Context, decisionID string) (*models.DeleteDecisionResponse, *Response, error) {
|
||||
u := fmt.Sprintf("%s/decisions/%s", s.client.URLPrefix, decisionID)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodDelete, u, nil)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodDelete, u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ type DecisionDeleteService service
|
|||
func (d *DecisionDeleteService) Add(ctx context.Context, deletedDecisions *models.DecisionsDeleteRequest) (interface{}, *Response, error) {
|
||||
u := fmt.Sprintf("%s/decisions/delete", d.client.URLPrefix)
|
||||
|
||||
req, err := d.client.NewRequestWithContext(ctx, http.MethodPost, u, &deletedDecisions)
|
||||
req, err := d.client.PrepareRequest(ctx, http.MethodPost, u, &deletedDecisions)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("while building request: %w", err)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ type HeartBeatService service
|
|||
func (h *HeartBeatService) Ping(ctx context.Context) (bool, *Response, error) {
|
||||
u := fmt.Sprintf("%s/heartbeat", h.client.URLPrefix)
|
||||
|
||||
req, err := h.client.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||
req, err := h.client.PrepareRequest(ctx, http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ type MetricsService service
|
|||
func (s *MetricsService) Add(ctx context.Context, metrics *models.Metrics) (interface{}, *Response, error) {
|
||||
u := fmt.Sprintf("%s/metrics/", s.client.URLPrefix)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodPost, u, &metrics)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodPost, u, &metrics)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ type SignalService service
|
|||
func (s *SignalService) Add(ctx context.Context, signals *models.AddSignalsRequest) (interface{}, *Response, error) {
|
||||
u := fmt.Sprintf("%s/signals", s.client.URLPrefix)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodPost, u, &signals)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodPost, u, &signals)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("while building request: %w", err)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ type UsageMetricsService service
|
|||
func (s *UsageMetricsService) Add(ctx context.Context, metrics *models.AllMetrics) (interface{}, *Response, error) {
|
||||
u := fmt.Sprintf("%s/usage-metrics", s.client.URLPrefix)
|
||||
|
||||
req, err := s.client.NewRequestWithContext(ctx, http.MethodPost, u, &metrics)
|
||||
req, err := s.client.PrepareRequest(ctx, http.MethodPost, u, &metrics)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -115,13 +115,35 @@ func TestCheckInAllowlist(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.False(t, resp.Allowlisted)
|
||||
|
||||
// GET request, should return 200 and status in body
|
||||
w = lapi.RecordResponse(t, ctx, http.MethodGet, "/v1/allowlists/check/2.3.4.0%2F24", emptyBody, passwordAuthType)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
resp = models.CheckAllowlistResponse{}
|
||||
|
||||
err = json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.False(t, resp.Allowlisted)
|
||||
|
||||
// HEAD request, should return 200
|
||||
w = lapi.RecordResponse(t, ctx, http.MethodHead, "/v1/allowlists/check/1.2.3.4", emptyBody, passwordAuthType)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
// HEAD request, should return 200
|
||||
w = lapi.RecordResponse(t, ctx, http.MethodHead, "/v1/allowlists/check/1.2.3.0%2F24", emptyBody, passwordAuthType)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
// HEAD request, should return 204
|
||||
w = lapi.RecordResponse(t, ctx, http.MethodHead, "/v1/allowlists/check/2.3.4.5", emptyBody, passwordAuthType)
|
||||
|
||||
require.Equal(t, http.StatusNoContent, w.Code)
|
||||
|
||||
// HEAD request, should return 204
|
||||
w = lapi.RecordResponse(t, ctx, http.MethodHead, "/v1/allowlists/check/2.3.4.5%2F24", emptyBody, passwordAuthType)
|
||||
|
||||
require.Equal(t, http.StatusNoContent, w.Code)
|
||||
}
|
||||
|
|
|
@ -754,7 +754,13 @@ func (a *apic) UpdateAllowlists(ctx context.Context, allowlistsLinks []*modelsca
|
|||
description = *link.Description
|
||||
}
|
||||
|
||||
resp, err := defaultClient.GetClient().Get(*link.URL)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, *link.URL, http.NoBody)
|
||||
if err != nil {
|
||||
log.Errorf("while pulling allowlist: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
resp, err := defaultClient.GetClient().Do(req)
|
||||
if err != nil {
|
||||
log.Errorf("while pulling allowlist: %s", err)
|
||||
continue
|
||||
|
@ -854,6 +860,10 @@ func (a *apic) ApplyApicWhitelists(ctx context.Context, decisions []*models.Deci
|
|||
log.Errorf("while getting allowlists content: %s", err)
|
||||
}
|
||||
|
||||
if a.whitelists != nil && (len(a.whitelists.Cidrs) > 0 || len(a.whitelists.Ips) > 0) {
|
||||
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)
|
||||
|
|
|
@ -98,6 +98,8 @@ func (c *Controller) NewV1() error {
|
|||
c.Router.GET("/health", gin.WrapF(serveHealth()))
|
||||
c.Router.Use(v1.PrometheusMiddleware())
|
||||
c.Router.HandleMethodNotAllowed = true
|
||||
c.Router.UnescapePathValues = true
|
||||
c.Router.UseRawPath = true
|
||||
c.Router.NoRoute(func(ctx *gin.Context) {
|
||||
ctx.AbortWithStatus(http.StatusNotFound)
|
||||
})
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
dbg "github.com/crowdsecurity/coraza/v3/debuglog"
|
||||
dbg "github.com/corazawaf/coraza/v3/debuglog"
|
||||
)
|
||||
|
||||
var DebugRules = map[int]bool{}
|
||||
|
|
|
@ -21,6 +21,8 @@ For those hashes, the value used was the one returned by our code (because we de
|
|||
*/
|
||||
|
||||
func TestJA4H_A(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
request func() *http.Request
|
||||
|
@ -29,7 +31,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.NewRequestWithContext(ctx, http.MethodGet, "http://example.com", http.NoBody)
|
||||
return req
|
||||
},
|
||||
expectedResult: "ge11nn000000",
|
||||
|
@ -37,7 +39,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.NewRequestWithContext(ctx, http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.Header.Set("Accept-Language", "en-US")
|
||||
return req
|
||||
},
|
||||
|
@ -46,7 +48,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.NewRequestWithContext(ctx, 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 +58,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.NewRequestWithContext(ctx, http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.Header.Set("Accept-Language", "aksjdhaslkdhalkjsd")
|
||||
return req
|
||||
},
|
||||
|
@ -65,7 +67,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.NewRequestWithContext(ctx, http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.Header.Set("Accept-Language", ",")
|
||||
return req
|
||||
},
|
||||
|
@ -86,6 +88,9 @@ func TestJA4H_A(t *testing.T) {
|
|||
func TestJA4H_B(t *testing.T) {
|
||||
// This test is only for non-regression
|
||||
// Because go does not keep headers order, we just want to make sure our code always process the headers in the same order
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
request func() *http.Request
|
||||
|
@ -94,7 +99,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.NewRequestWithContext(ctx, http.MethodGet, "http://example.com", http.NoBody)
|
||||
return req
|
||||
},
|
||||
expectedResult: "e3b0c44298fc",
|
||||
|
@ -102,7 +107,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.NewRequestWithContext(ctx, http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.Header.Set("X-Custom-Header", "some value")
|
||||
return req
|
||||
},
|
||||
|
@ -111,7 +116,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.NewRequestWithContext(ctx, 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 +126,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.NewRequestWithContext(ctx, 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 +155,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 +163,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 +172,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 +182,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 +214,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 +222,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 +231,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 +241,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()
|
||||
|
@ -260,6 +265,8 @@ func TestJA4H_D(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestJA4H(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req func() *http.Request
|
||||
|
@ -268,7 +275,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.NewRequestWithContext(ctx, http.MethodGet, "http://example.com", http.NoBody)
|
||||
return req
|
||||
},
|
||||
expectedHash: "ge11nn000000_e3b0c44298fc_000000000000_000000000000",
|
||||
|
@ -276,7 +283,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.NewRequestWithContext(ctx, http.MethodGet, "http://example.com", http.NoBody)
|
||||
req.AddCookie(&http.Cookie{Name: "session", Value: "12345"})
|
||||
return req
|
||||
},
|
||||
|
@ -285,7 +292,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.NewRequestWithContext(ctx, 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,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package appsec
|
||||
|
||||
import (
|
||||
"github.com/crowdsecurity/coraza/v3"
|
||||
"github.com/crowdsecurity/coraza/v3/experimental"
|
||||
"github.com/crowdsecurity/coraza/v3/experimental/plugins/plugintypes"
|
||||
"github.com/crowdsecurity/coraza/v3/types"
|
||||
"github.com/corazawaf/coraza/v3"
|
||||
"github.com/corazawaf/coraza/v3/experimental"
|
||||
"github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes"
|
||||
"github.com/corazawaf/coraza/v3/types"
|
||||
)
|
||||
|
||||
type ExtendedTransaction struct {
|
||||
|
@ -91,3 +91,7 @@ func (t *ExtendedTransaction) MatchedRules() []types.MatchedRule {
|
|||
func (t *ExtendedTransaction) ID() string {
|
||||
return t.Tx.ID()
|
||||
}
|
||||
|
||||
func (t *ExtendedTransaction) Close() error {
|
||||
return t.Tx.Close()
|
||||
}
|
||||
|
|
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue