diff --git a/cmd/crowdsec-cli/hubappsec.go b/cmd/crowdsec-cli/hubappsec.go index ff41ad5f9..7ee578edc 100644 --- a/cmd/crowdsec-cli/hubappsec.go +++ b/cmd/crowdsec-cli/hubappsec.go @@ -13,8 +13,9 @@ import ( "github.com/crowdsecurity/crowdsec/pkg/cwhub" ) -func NewCLIAppsecConfig() *cliItem { +func NewCLIAppsecConfig(cfg configGetter) *cliItem { return &cliItem{ + cfg: cfg, name: cwhub.APPSEC_CONFIGS, singular: "appsec-config", oneOrMore: "appsec-config(s)", @@ -46,7 +47,7 @@ cscli appsec-configs list crowdsecurity/vpatch`, } } -func NewCLIAppsecRule() *cliItem { +func NewCLIAppsecRule(cfg configGetter) *cliItem { inspectDetail := func(item *cwhub.Item) error { // Only show the converted rules in human mode if csConfig.Cscli.Output != "human" { @@ -57,11 +58,11 @@ func NewCLIAppsecRule() *cliItem { yamlContent, err := os.ReadFile(item.State.LocalPath) if err != nil { - return fmt.Errorf("unable to read file %s : %s", item.State.LocalPath, err) + return fmt.Errorf("unable to read file %s: %w", item.State.LocalPath, err) } if err := yaml.Unmarshal(yamlContent, &appsecRule); err != nil { - return fmt.Errorf("unable to unmarshal yaml file %s : %s", item.State.LocalPath, err) + return fmt.Errorf("unable to unmarshal yaml file %s: %w", item.State.LocalPath, err) } for _, ruleType := range appsec_rule.SupportedTypes() { @@ -70,7 +71,7 @@ func NewCLIAppsecRule() *cliItem { for _, rule := range appsecRule.Rules { convertedRule, _, err := rule.Convert(ruleType, appsecRule.Name) if err != nil { - return fmt.Errorf("unable to convert rule %s : %s", rule.Name, err) + return fmt.Errorf("unable to convert rule %s: %w", rule.Name, err) } fmt.Println(convertedRule) @@ -88,6 +89,7 @@ func NewCLIAppsecRule() *cliItem { } return &cliItem{ + cfg: cfg, name: "appsec-rules", singular: "appsec-rule", oneOrMore: "appsec-rule(s)", diff --git a/cmd/crowdsec-cli/hubcollection.go b/cmd/crowdsec-cli/hubcollection.go index dee9a0b9e..655b36eb1 100644 --- a/cmd/crowdsec-cli/hubcollection.go +++ b/cmd/crowdsec-cli/hubcollection.go @@ -4,8 +4,9 @@ import ( "github.com/crowdsecurity/crowdsec/pkg/cwhub" ) -func NewCLICollection() *cliItem { +func NewCLICollection(cfg configGetter) *cliItem { return &cliItem{ + cfg: cfg, name: cwhub.COLLECTIONS, singular: "collection", oneOrMore: "collection(s)", diff --git a/cmd/crowdsec-cli/hubcontext.go b/cmd/crowdsec-cli/hubcontext.go index 630dbb2f7..2a7773273 100644 --- a/cmd/crowdsec-cli/hubcontext.go +++ b/cmd/crowdsec-cli/hubcontext.go @@ -4,8 +4,9 @@ import ( "github.com/crowdsecurity/crowdsec/pkg/cwhub" ) -func NewCLIContext() *cliItem { +func NewCLIContext(cfg configGetter) *cliItem { return &cliItem{ + cfg: cfg, name: cwhub.CONTEXTS, singular: "context", oneOrMore: "context(s)", diff --git a/cmd/crowdsec-cli/hubparser.go b/cmd/crowdsec-cli/hubparser.go index 0b224c8a7..cc856cbed 100644 --- a/cmd/crowdsec-cli/hubparser.go +++ b/cmd/crowdsec-cli/hubparser.go @@ -4,8 +4,9 @@ import ( "github.com/crowdsecurity/crowdsec/pkg/cwhub" ) -func NewCLIParser() *cliItem { +func NewCLIParser(cfg configGetter) *cliItem { return &cliItem{ + cfg: cfg, name: cwhub.PARSERS, singular: "parser", oneOrMore: "parser(s)", diff --git a/cmd/crowdsec-cli/hubpostoverflow.go b/cmd/crowdsec-cli/hubpostoverflow.go index 908ccbea0..3fd45fd11 100644 --- a/cmd/crowdsec-cli/hubpostoverflow.go +++ b/cmd/crowdsec-cli/hubpostoverflow.go @@ -4,8 +4,9 @@ import ( "github.com/crowdsecurity/crowdsec/pkg/cwhub" ) -func NewCLIPostOverflow() *cliItem { +func NewCLIPostOverflow(cfg configGetter) *cliItem { return &cliItem{ + cfg: cfg, name: cwhub.POSTOVERFLOWS, singular: "postoverflow", oneOrMore: "postoverflow(s)", diff --git a/cmd/crowdsec-cli/hubscenario.go b/cmd/crowdsec-cli/hubscenario.go index 1de2182bf..4434b9a2c 100644 --- a/cmd/crowdsec-cli/hubscenario.go +++ b/cmd/crowdsec-cli/hubscenario.go @@ -4,8 +4,9 @@ import ( "github.com/crowdsecurity/crowdsec/pkg/cwhub" ) -func NewCLIScenario() *cliItem { +func NewCLIScenario(cfg configGetter) *cliItem { return &cliItem{ + cfg: cfg, name: cwhub.SCENARIOS, singular: "scenario", oneOrMore: "scenario(s)", diff --git a/cmd/crowdsec-cli/hubtest.go b/cmd/crowdsec-cli/hubtest.go index d6ed45600..51735ce19 100644 --- a/cmd/crowdsec-cli/hubtest.go +++ b/cmd/crowdsec-cli/hubtest.go @@ -135,6 +135,7 @@ cscli hubtest create my-scenario-test --parsers crowdsecurity/nginx --scenarios // create empty nuclei template file nucleiFileName := fmt.Sprintf("%s.yaml", testName) nucleiFilePath := filepath.Join(testPath, nucleiFileName) + nucleiFile, err := os.OpenFile(nucleiFilePath, os.O_RDWR|os.O_CREATE, 0755) if err != nil { return err @@ -405,7 +406,7 @@ func (cli *cliHubTest) NewRunCmd() *cobra.Command { } func (cli *cliHubTest) NewCleanCmd() *cobra.Command { - var cmd = &cobra.Command{ + cmd := &cobra.Command{ Use: "clean", Short: "clean [test_name]", Args: cobra.MinimumNArgs(1), diff --git a/cmd/crowdsec-cli/item_metrics.go b/cmd/crowdsec-cli/item_metrics.go index e6f27ae5d..b571fb1c5 100644 --- a/cmd/crowdsec-cli/item_metrics.go +++ b/cmd/crowdsec-cli/item_metrics.go @@ -37,6 +37,7 @@ func ShowMetrics(hubItem *cwhub.Item) error { appsecMetricsTable(color.Output, hubItem.Name, metrics) default: // no metrics for this item type } + return nil } @@ -49,21 +50,27 @@ func GetParserMetric(url string, itemName string) map[string]map[string]int { if !strings.HasPrefix(fam.Name, "cs_") { continue } + log.Tracef("round %d", idx) + for _, m := range fam.Metrics { metric, ok := m.(prom2json.Metric) if !ok { log.Debugf("failed to convert metric to prom2json.Metric") continue } + name, ok := metric.Labels["name"] if !ok { log.Debugf("no name in Metric %v", metric.Labels) } + if name != itemName { continue } + source, ok := metric.Labels["source"] + if !ok { log.Debugf("no source in Metric %v", metric.Labels) } else { @@ -71,12 +78,15 @@ func GetParserMetric(url string, itemName string) map[string]map[string]int { source = srctype + ":" + source } } + value := m.(prom2json.Metric).Value + fval, err := strconv.ParseFloat(value, 32) if err != nil { log.Errorf("Unexpected int value %s : %s", value, err) continue } + ival := int(fval) switch fam.Name { @@ -119,6 +129,7 @@ func GetParserMetric(url string, itemName string) map[string]map[string]int { } } } + return stats } @@ -136,26 +147,34 @@ func GetScenarioMetric(url string, itemName string) map[string]int { if !strings.HasPrefix(fam.Name, "cs_") { continue } + log.Tracef("round %d", idx) + for _, m := range fam.Metrics { metric, ok := m.(prom2json.Metric) if !ok { log.Debugf("failed to convert metric to prom2json.Metric") continue } + name, ok := metric.Labels["name"] + if !ok { log.Debugf("no name in Metric %v", metric.Labels) } + if name != itemName { continue } + value := m.(prom2json.Metric).Value + fval, err := strconv.ParseFloat(value, 32) if err != nil { log.Errorf("Unexpected int value %s : %s", value, err) continue } + ival := int(fval) switch fam.Name { @@ -174,6 +193,7 @@ func GetScenarioMetric(url string, itemName string) map[string]int { } } } + return stats } @@ -188,17 +208,22 @@ func GetAppsecRuleMetric(url string, itemName string) map[string]int { if !strings.HasPrefix(fam.Name, "cs_") { continue } + log.Tracef("round %d", idx) + for _, m := range fam.Metrics { metric, ok := m.(prom2json.Metric) if !ok { log.Debugf("failed to convert metric to prom2json.Metric") continue } + name, ok := metric.Labels["rule_name"] + if !ok { log.Debugf("no rule_name in Metric %v", metric.Labels) } + if name != itemName { continue } @@ -209,11 +234,13 @@ func GetAppsecRuleMetric(url string, itemName string) map[string]int { } value := m.(prom2json.Metric).Value + fval, err := strconv.ParseFloat(value, 32) if err != nil { log.Errorf("Unexpected int value %s : %s", value, err) continue } + ival := int(fval) switch fam.Name { @@ -231,6 +258,7 @@ func GetAppsecRuleMetric(url string, itemName string) map[string]int { } } } + return stats } @@ -247,6 +275,7 @@ func GetPrometheusMetric(url string) []*prom2json.Family { go func() { defer trace.CatchPanic("crowdsec/GetPrometheusMetric") + err := prom2json.FetchMetricFamilies(url, mfChan, transport) if err != nil { log.Fatalf("failed to fetch prometheus metrics : %v", err) @@ -257,6 +286,7 @@ func GetPrometheusMetric(url string) []*prom2json.Family { for mf := range mfChan { result = append(result, prom2json.NewFamily(mf)) } + log.Debugf("Finished reading prometheus output, %d entries", len(result)) return result diff --git a/cmd/crowdsec-cli/itemcli.go b/cmd/crowdsec-cli/itemcli.go index 4f3dc40ae..c2614068f 100644 --- a/cmd/crowdsec-cli/itemcli.go +++ b/cmd/crowdsec-cli/itemcli.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "os" "strings" @@ -28,6 +29,7 @@ type cliHelp struct { } type cliItem struct { + cfg configGetter name string // plural, as used in the hub index singular string oneOrMore string // parenthetical pluralizaion: "parser(s)" @@ -61,7 +63,9 @@ func (cli cliItem) NewCommand() *cobra.Command { } func (cli cliItem) install(args []string, downloadOnly bool, force bool, ignoreError bool) error { - hub, err := require.Hub(csConfig, require.RemoteHub(csConfig), log.StandardLogger()) + cfg := cli.cfg() + + hub, err := require.Hub(cfg, require.RemoteHub(cfg), log.StandardLogger()) if err != nil { return err } @@ -71,7 +75,7 @@ func (cli cliItem) install(args []string, downloadOnly bool, force bool, ignoreE if item == nil { msg := suggestNearestMessage(hub, cli.name, name) if !ignoreError { - return fmt.Errorf(msg) + return errors.New(msg) } log.Errorf(msg) @@ -107,10 +111,10 @@ func (cli cliItem) newInstallCmd() *cobra.Command { Example: cli.installHelp.example, Args: cobra.MinimumNArgs(1), DisableAutoGenTag: true, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return compAllItems(cli.name, args, toComplete) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { return cli.install(args, downloadOnly, force, ignoreError) }, } @@ -137,7 +141,7 @@ func istalledParentNames(item *cwhub.Item) []string { } func (cli cliItem) remove(args []string, purge bool, force bool, all bool) error { - hub, err := require.Hub(csConfig, nil, log.StandardLogger()) + hub, err := require.Hub(cli.cfg(), nil, log.StandardLogger()) if err != nil { return err } @@ -163,6 +167,7 @@ func (cli cliItem) remove(args []string, purge bool, force bool, all bool) error if didRemove { log.Infof("Removed %s", item.Name) + removed++ } } @@ -204,6 +209,7 @@ func (cli cliItem) remove(args []string, purge bool, force bool, all bool) error if didRemove { log.Infof("Removed %s", item.Name) + removed++ } } @@ -231,10 +237,10 @@ func (cli cliItem) newRemoveCmd() *cobra.Command { Example: cli.removeHelp.example, Aliases: []string{"delete"}, DisableAutoGenTag: true, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return compInstalledItems(cli.name, args, toComplete) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { return cli.remove(args, purge, force, all) }, } @@ -248,7 +254,9 @@ func (cli cliItem) newRemoveCmd() *cobra.Command { } func (cli cliItem) upgrade(args []string, force bool, all bool) error { - hub, err := require.Hub(csConfig, require.RemoteHub(csConfig), log.StandardLogger()) + cfg := cli.cfg() + + hub, err := require.Hub(cfg, require.RemoteHub(cfg), log.StandardLogger()) if err != nil { return err } @@ -300,6 +308,7 @@ func (cli cliItem) upgrade(args []string, force bool, all bool) error { if didUpdate { log.Infof("Updated %s", item.Name) + updated++ } } @@ -323,10 +332,10 @@ func (cli cliItem) newUpgradeCmd() *cobra.Command { Long: coalesce.String(cli.upgradeHelp.long, fmt.Sprintf("Fetch and upgrade one or more %s from the hub", cli.name)), Example: cli.upgradeHelp.example, DisableAutoGenTag: true, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return compInstalledItems(cli.name, args, toComplete) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { return cli.upgrade(args, force, all) }, } @@ -339,21 +348,23 @@ func (cli cliItem) newUpgradeCmd() *cobra.Command { } func (cli cliItem) inspect(args []string, url string, diff bool, rev bool, noMetrics bool) error { + cfg := cli.cfg() + if rev && !diff { - return fmt.Errorf("--rev can only be used with --diff") + return errors.New("--rev can only be used with --diff") } if url != "" { - csConfig.Cscli.PrometheusUrl = url + cfg.Cscli.PrometheusUrl = url } remote := (*cwhub.RemoteHubCfg)(nil) if diff { - remote = require.RemoteHub(csConfig) + remote = require.RemoteHub(cfg) } - hub, err := require.Hub(csConfig, remote, log.StandardLogger()) + hub, err := require.Hub(cfg, remote, log.StandardLogger()) if err != nil { return err } @@ -399,10 +410,10 @@ func (cli cliItem) newInspectCmd() *cobra.Command { Example: cli.inspectHelp.example, Args: cobra.MinimumNArgs(1), DisableAutoGenTag: true, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return compInstalledItems(cli.name, args, toComplete) }, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { return cli.inspect(args, url, diff, rev, noMetrics) }, } @@ -417,7 +428,7 @@ func (cli cliItem) newInspectCmd() *cobra.Command { } func (cli cliItem) list(args []string, all bool) error { - hub, err := require.Hub(csConfig, nil, log.StandardLogger()) + hub, err := require.Hub(cli.cfg(), nil, log.StandardLogger()) if err != nil { return err } @@ -526,6 +537,7 @@ func (cli cliItem) whyTainted(hub *cwhub.Hub, item *cwhub.Item, reverse bool) st // hack: avoid message "item is tainted by itself" continue } + ret = append(ret, fmt.Sprintf("# %s is tainted by %s", sub.FQName(), taintList)) } } diff --git a/cmd/crowdsec-cli/items.go b/cmd/crowdsec-cli/items.go index ea6d8a256..b8c83809d 100644 --- a/cmd/crowdsec-cli/items.go +++ b/cmd/crowdsec-cli/items.go @@ -116,7 +116,7 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item } if err := csvwriter.Write(header); err != nil { - return fmt.Errorf("failed to write header: %s", err) + return fmt.Errorf("failed to write header: %w", err) } for _, itemType := range itemTypes { @@ -132,7 +132,7 @@ func listItems(out io.Writer, itemTypes []string, items map[string][]*cwhub.Item } if err := csvwriter.Write(row); err != nil { - return fmt.Errorf("failed to write raw output: %s", err) + return fmt.Errorf("failed to write raw output: %w", err) } } } @@ -150,12 +150,12 @@ func inspectItem(item *cwhub.Item, showMetrics bool) error { enc.SetIndent(2) if err := enc.Encode(item); err != nil { - return fmt.Errorf("unable to encode item: %s", err) + return fmt.Errorf("unable to encode item: %w", err) } case "json": b, err := json.MarshalIndent(*item, "", " ") if err != nil { - return fmt.Errorf("unable to marshal item: %s", err) + return fmt.Errorf("unable to marshal item: %w", err) } fmt.Print(string(b)) diff --git a/cmd/crowdsec-cli/lapi.go b/cmd/crowdsec-cli/lapi.go index 13a9d8d7e..51f372cc2 100644 --- a/cmd/crowdsec-cli/lapi.go +++ b/cmd/crowdsec-cli/lapi.go @@ -116,7 +116,6 @@ func (cli *cliLapi) register(apiURL string, outputFile string, machine string) e URL: apiurl, VersionPrefix: LAPIURLPrefix, }, nil) - if err != nil { return fmt.Errorf("api client register: %w", err) } @@ -585,7 +584,7 @@ func detectNode(node parser.Node, parserCTX parser.UnixParserCtx) []string { } func detectSubNode(node parser.Node, parserCTX parser.UnixParserCtx) []string { - var ret = make([]string, 0) + ret := make([]string, 0) for _, subnode := range node.LeavesNodes { if subnode.Grok.RunTimeRegexp != nil { diff --git a/cmd/crowdsec-cli/main.go b/cmd/crowdsec-cli/main.go index 9e721f1fa..0705faa40 100644 --- a/cmd/crowdsec-cli/main.go +++ b/cmd/crowdsec-cli/main.go @@ -260,13 +260,13 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall cmd.AddCommand(NewCLINotifications(cli.cfg).NewCommand()) cmd.AddCommand(NewCLISupport().NewCommand()) cmd.AddCommand(NewCLIPapi(cli.cfg).NewCommand()) - cmd.AddCommand(NewCLICollection().NewCommand()) - cmd.AddCommand(NewCLIParser().NewCommand()) - cmd.AddCommand(NewCLIScenario().NewCommand()) - cmd.AddCommand(NewCLIPostOverflow().NewCommand()) - cmd.AddCommand(NewCLIContext().NewCommand()) - cmd.AddCommand(NewCLIAppsecConfig().NewCommand()) - cmd.AddCommand(NewCLIAppsecRule().NewCommand()) + cmd.AddCommand(NewCLICollection(cli.cfg).NewCommand()) + cmd.AddCommand(NewCLIParser(cli.cfg).NewCommand()) + cmd.AddCommand(NewCLIScenario(cli.cfg).NewCommand()) + cmd.AddCommand(NewCLIPostOverflow(cli.cfg).NewCommand()) + cmd.AddCommand(NewCLIContext(cli.cfg).NewCommand()) + cmd.AddCommand(NewCLIAppsecConfig(cli.cfg).NewCommand()) + cmd.AddCommand(NewCLIAppsecRule(cli.cfg).NewCommand()) if fflag.CscliSetup.IsEnabled() { cmd.AddCommand(NewSetupCmd())