cscli refact: package 'cliconsole' (#3149)

* cscli refact: package 'cliconsole'

* dry

* lint

* lint
This commit is contained in:
mmetc 2024-08-20 16:20:40 +02:00 committed by GitHub
parent e7b54c68c5
commit 08fdfc4fb0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 90 additions and 106 deletions

View file

@ -17,7 +17,6 @@ linters-settings:
disable:
- reflectvaluecompare
- fieldalignment
- printf
maintidx:
# raise this after refactoring

View file

@ -119,7 +119,7 @@ func (cli *cliCapi) register(capiUserPrefix string, outputFile string) error {
fmt.Println(string(apiConfigDump))
}
log.Warning(ReloadMessage())
log.Warning(reloadMessage)
return nil
}

View file

@ -1,4 +1,4 @@
package main
package cliconsole
import (
"context"
@ -28,13 +28,17 @@ import (
"github.com/crowdsecurity/crowdsec/pkg/types"
)
type configGetter func() *csconfig.Config
type cliConsole struct {
cfg configGetter
cfg func() *csconfig.Config
reloadMessage string
}
func NewCLIConsole(cfg configGetter) *cliConsole {
func New(cfg configGetter, reloadMessage string) *cliConsole {
return &cliConsole{
cfg: cfg,
cfg: cfg,
reloadMessage: reloadMessage,
}
}
@ -221,7 +225,7 @@ Enable given information push to the central API. Allows to empower the console`
log.Infof("%v have been enabled", args)
}
log.Infof(ReloadMessage())
log.Info(cli.reloadMessage)
return nil
},
@ -255,7 +259,7 @@ Disable given information push to the central API.`,
log.Infof("%v have been disabled", args)
}
log.Infof(ReloadMessage())
log.Info(cli.reloadMessage)
return nil
},
@ -348,13 +352,8 @@ func (cli *cliConsole) setConsoleOpts(args []string, wanted bool) error {
switch arg {
case csconfig.CONSOLE_MANAGEMENT:
/*for each flag check if it's already set before setting it*/
if consoleCfg.ConsoleManagement != nil {
if *consoleCfg.ConsoleManagement == wanted {
log.Debugf("%s already set to %t", csconfig.CONSOLE_MANAGEMENT, wanted)
} else {
log.Infof("%s set to %t", csconfig.CONSOLE_MANAGEMENT, wanted)
*consoleCfg.ConsoleManagement = wanted
}
if consoleCfg.ConsoleManagement != nil && *consoleCfg.ConsoleManagement == wanted {
log.Debugf("%s already set to %t", csconfig.CONSOLE_MANAGEMENT, wanted)
} else {
log.Infof("%s set to %t", csconfig.CONSOLE_MANAGEMENT, wanted)
consoleCfg.ConsoleManagement = ptr.Of(wanted)
@ -386,52 +385,32 @@ func (cli *cliConsole) setConsoleOpts(args []string, wanted bool) error {
}
case csconfig.SEND_CUSTOM_SCENARIOS:
/*for each flag check if it's already set before setting it*/
if consoleCfg.ShareCustomScenarios != nil {
if *consoleCfg.ShareCustomScenarios == wanted {
log.Debugf("%s already set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
} else {
log.Infof("%s set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
*consoleCfg.ShareCustomScenarios = wanted
}
if consoleCfg.ShareCustomScenarios != nil && *consoleCfg.ShareCustomScenarios == wanted {
log.Debugf("%s already set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
} else {
log.Infof("%s set to %t", csconfig.SEND_CUSTOM_SCENARIOS, wanted)
consoleCfg.ShareCustomScenarios = ptr.Of(wanted)
}
case csconfig.SEND_TAINTED_SCENARIOS:
/*for each flag check if it's already set before setting it*/
if consoleCfg.ShareTaintedScenarios != nil {
if *consoleCfg.ShareTaintedScenarios == wanted {
log.Debugf("%s already set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
} else {
log.Infof("%s set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
*consoleCfg.ShareTaintedScenarios = wanted
}
if consoleCfg.ShareTaintedScenarios != nil && *consoleCfg.ShareTaintedScenarios == wanted {
log.Debugf("%s already set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
} else {
log.Infof("%s set to %t", csconfig.SEND_TAINTED_SCENARIOS, wanted)
consoleCfg.ShareTaintedScenarios = ptr.Of(wanted)
}
case csconfig.SEND_MANUAL_SCENARIOS:
/*for each flag check if it's already set before setting it*/
if consoleCfg.ShareManualDecisions != nil {
if *consoleCfg.ShareManualDecisions == wanted {
log.Debugf("%s already set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
} else {
log.Infof("%s set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
*consoleCfg.ShareManualDecisions = wanted
}
if consoleCfg.ShareManualDecisions != nil && *consoleCfg.ShareManualDecisions == wanted {
log.Debugf("%s already set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
} else {
log.Infof("%s set to %t", csconfig.SEND_MANUAL_SCENARIOS, wanted)
consoleCfg.ShareManualDecisions = ptr.Of(wanted)
}
case csconfig.SEND_CONTEXT:
/*for each flag check if it's already set before setting it*/
if consoleCfg.ShareContext != nil {
if *consoleCfg.ShareContext == wanted {
log.Debugf("%s already set to %t", csconfig.SEND_CONTEXT, wanted)
} else {
log.Infof("%s set to %t", csconfig.SEND_CONTEXT, wanted)
*consoleCfg.ShareContext = wanted
}
if consoleCfg.ShareContext != nil && *consoleCfg.ShareContext == wanted {
log.Debugf("%s already set to %t", csconfig.SEND_CONTEXT, wanted)
} else {
log.Infof("%s set to %t", csconfig.SEND_CONTEXT, wanted)
consoleCfg.ShareContext = ptr.Of(wanted)

View file

@ -1,4 +1,4 @@
package main
package cliconsole
import (
"io"

View file

@ -129,7 +129,7 @@ func (*statBouncer) Description() (string, string) {
func logWarningOnce(warningsLogged map[string]bool, msg string) {
if _, ok := warningsLogged[msg]; !ok {
log.Warningf(msg)
log.Warning(msg)
warningsLogged[msg] = true
}

View file

@ -78,7 +78,7 @@ func (cli cliItem) install(ctx context.Context, args []string, downloadOnly bool
return errors.New(msg)
}
log.Errorf(msg)
log.Error(msg)
continue
}
@ -92,7 +92,7 @@ func (cli cliItem) install(ctx context.Context, args []string, downloadOnly bool
}
}
log.Infof(ReloadMessage())
log.Info(reloadMessage)
return nil
}
@ -175,7 +175,7 @@ func (cli cliItem) remove(args []string, purge bool, force bool, all bool) error
log.Infof("Removed %d %s", removed, cli.name)
if removed > 0 {
log.Infof(ReloadMessage())
log.Info(reloadMessage)
}
return nil
@ -217,7 +217,7 @@ func (cli cliItem) remove(args []string, purge bool, force bool, all bool) error
log.Infof("Removed %d %s", removed, cli.name)
if removed > 0 {
log.Infof(ReloadMessage())
log.Info(reloadMessage)
}
return nil
@ -283,7 +283,7 @@ func (cli cliItem) upgrade(ctx context.Context, args []string, force bool, all b
log.Infof("Updated %d %s", updated, cli.name)
if updated > 0 {
log.Infof(ReloadMessage())
log.Info(reloadMessage)
}
return nil
@ -314,7 +314,7 @@ func (cli cliItem) upgrade(ctx context.Context, args []string, force bool, all b
}
if updated > 0 {
log.Infof(ReloadMessage())
log.Info(reloadMessage)
}
return nil

View file

@ -161,7 +161,7 @@ func (cli *cliLapi) register(apiURL string, outputFile string, machine string) e
fmt.Printf("%s\n", string(apiConfigDump))
}
log.Warning(ReloadMessage())
log.Warning(reloadMessage)
return nil
}

View file

@ -14,8 +14,8 @@ import (
"github.com/crowdsecurity/go-cs-lib/trace"
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cliconsole"
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/climetrics"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/fflag"
)
@ -262,7 +262,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
cmd.AddCommand(NewCLICapi(cli.cfg).NewCommand())
cmd.AddCommand(NewCLILapi(cli.cfg).NewCommand())
cmd.AddCommand(NewCompletionCmd())
cmd.AddCommand(NewCLIConsole(cli.cfg).NewCommand())
cmd.AddCommand(cliconsole.New(cli.cfg, reloadMessage).NewCommand())
cmd.AddCommand(NewCLIExplain(cli.cfg).NewCommand())
cmd.AddCommand(NewCLIHubTest(cli.cfg).NewCommand())
cmd.AddCommand(NewCLINotifications(cli.cfg).NewCommand())

View file

@ -1,23 +0,0 @@
package main
import (
"fmt"
"runtime"
)
// ReloadMessage returns a description of the task required to reload
// the crowdsec configuration, according to the operating system.
func ReloadMessage() string {
var msg string
switch runtime.GOOS {
case "windows":
msg = "Please restart the crowdsec service"
case "freebsd":
msg = `Run 'sudo service crowdsec reload'`
default:
msg = `Run 'sudo systemctl reload crowdsec'`
}
return fmt.Sprintf("%s for the new configuration to be effective.", msg)
}

View file

@ -0,0 +1,6 @@
//go:build !windows && !freebsd && !linux
package main
// generic message since we don't know the platform
const reloadMessage = "Please reload the crowdsec process for the new configuration to be effective."

View file

@ -0,0 +1,4 @@
package main
// actually sudo is not that popular on freebsd, but this will do
const reloadMessage = "Run 'sudo service crowdsec reload' for the new configuration to be effective."

View file

@ -0,0 +1,4 @@
package main
// assume systemd, although gentoo and others may differ
const reloadMessage = "Run 'sudo systemctl reload crowdsec' for the new configuration to be effective."

View file

@ -0,0 +1,3 @@
package main
const reloadMessage = "Please restart the crowdsec service for the new configuration to be effective."

View file

@ -44,7 +44,7 @@ cscli simulation disable crowdsecurity/ssh-bf`,
},
PersistentPostRun: func(cmd *cobra.Command, _ []string) {
if cmd.Name() != "status" {
log.Infof(ReloadMessage())
log.Info(reloadMessage)
}
},
}

View file

@ -40,14 +40,16 @@ func appendMeta(meta models.Meta, key string, value string) models.Meta {
Key: key,
Value: value,
})
return meta
}
func AppsecEventGeneration(inEvt types.Event) (*types.Event, error) {
//if the request didnd't trigger inband rules, we don't want to generate an event to LAPI/CAPI
// if the request didnd't trigger inband rules, we don't want to generate an event to LAPI/CAPI
if !inEvt.Appsec.HasInBandMatches {
return nil, nil
}
evt := types.Event{}
evt.Type = types.APPSEC
evt.Process = true
@ -105,7 +107,6 @@ func AppsecEventGeneration(inEvt types.Event) (*types.Event, error) {
evtRule.Meta = make(models.Meta, 0)
for _, key := range appsecMetaKeys {
if tmpAppsecContext[key] == nil {
tmpAppsecContext[key] = make([]string, 0)
}
@ -113,18 +114,21 @@ func AppsecEventGeneration(inEvt types.Event) (*types.Event, error) {
switch value := matched_rule[key].(type) {
case string:
evtRule.Meta = appendMeta(evtRule.Meta, key, value)
if value != "" && !slices.Contains(tmpAppsecContext[key], value) {
tmpAppsecContext[key] = append(tmpAppsecContext[key], value)
}
case int:
val := strconv.Itoa(value)
evtRule.Meta = appendMeta(evtRule.Meta, key, val)
if val != "" && !slices.Contains(tmpAppsecContext[key], val) {
tmpAppsecContext[key] = append(tmpAppsecContext[key], val)
}
case []string:
for _, v := range value {
evtRule.Meta = appendMeta(evtRule.Meta, key, v)
if v != "" && !slices.Contains(tmpAppsecContext[key], v) {
tmpAppsecContext[key] = append(tmpAppsecContext[key], v)
}
@ -133,20 +137,21 @@ func AppsecEventGeneration(inEvt types.Event) (*types.Event, error) {
for _, v := range value {
val := strconv.Itoa(v)
evtRule.Meta = appendMeta(evtRule.Meta, key, val)
if val != "" && !slices.Contains(tmpAppsecContext[key], val) {
tmpAppsecContext[key] = append(tmpAppsecContext[key], val)
}
}
default:
val := fmt.Sprintf("%v", value)
evtRule.Meta = appendMeta(evtRule.Meta, key, val)
if val != "" && !slices.Contains(tmpAppsecContext[key], val) {
tmpAppsecContext[key] = append(tmpAppsecContext[key], val)
}
}
}
alert.Events = append(alert.Events, &evtRule)
}
@ -159,7 +164,7 @@ func AppsecEventGeneration(inEvt types.Event) (*types.Event, error) {
valueStr, err := alertcontext.TruncateContext(values, alertcontext.MaxContextValueLen)
if err != nil {
log.Warningf(err.Error())
log.Warning(err.Error())
}
meta := models.MetaItems0{
@ -185,15 +190,16 @@ func AppsecEventGeneration(inEvt types.Event) (*types.Event, error) {
alert.StopAt = ptr.Of(time.Now().UTC().Format(time.RFC3339))
evt.Overflow.APIAlerts = []models.Alert{alert}
evt.Overflow.Alert = &alert
return &evt, nil
}
func EventFromRequest(r *appsec.ParsedRequest, labels map[string]string) (types.Event, error) {
evt := types.Event{}
//we might want to change this based on in-band vs out-of-band ?
// we might want to change this based on in-band vs out-of-band ?
evt.Type = types.LOG
evt.ExpectMode = types.LIVE
//def needs fixing
// def needs fixing
evt.Stage = "s00-raw"
evt.Parsed = map[string]string{
"source_ip": r.ClientIP,
@ -203,19 +209,19 @@ func EventFromRequest(r *appsec.ParsedRequest, labels map[string]string) (types.
"req_uuid": r.Tx.ID(),
"source": "crowdsec-appsec",
"remediation_cmpt_ip": r.RemoteAddrNormalized,
//TBD:
//http_status
//user_agent
// TBD:
// http_status
// user_agent
}
evt.Line = types.Line{
Time: time.Now(),
//should we add some info like listen addr/port/path ?
// should we add some info like listen addr/port/path ?
Labels: labels,
Process: true,
Module: "appsec",
Src: "appsec",
Raw: "dummy-appsec-data", //we discard empty Line.Raw items :)
Raw: "dummy-appsec-data", // we discard empty Line.Raw items :)
}
evt.Appsec = types.AppsecEvent{}
@ -247,29 +253,29 @@ func LogAppsecEvent(evt *types.Event, logger *log.Entry) {
"target_uri": req,
}).Debugf("%s triggered non-blocking rules on %s (%d rules) [%v]", evt.Parsed["source_ip"], req, len(evt.Appsec.MatchedRules), evt.Appsec.GetRuleIDs())
}
}
func (r *AppsecRunner) AccumulateTxToEvent(evt *types.Event, req *appsec.ParsedRequest) error {
if evt == nil {
//an error was already emitted, let's not spam the logs
// an error was already emitted, let's not spam the logs
return nil
}
if !req.Tx.IsInterrupted() {
//if the phase didn't generate an interruption, we don't have anything to add to the event
// if the phase didn't generate an interruption, we don't have anything to add to the event
return nil
}
//if one interruption was generated, event is good for processing :)
// if one interruption was generated, event is good for processing :)
evt.Process = true
if evt.Meta == nil {
evt.Meta = map[string]string{}
}
if evt.Parsed == nil {
evt.Parsed = map[string]string{}
}
if req.IsInBand {
evt.Meta["appsec_interrupted"] = "true"
evt.Meta["appsec_action"] = req.Tx.Interruption().Action
@ -290,9 +296,11 @@ func (r *AppsecRunner) AccumulateTxToEvent(evt *types.Event, req *appsec.ParsedR
if variable.Key() != "" {
key += "." + variable.Key()
}
if variable.Value() == "" {
continue
}
for _, collectionToKeep := range r.AppsecRuntime.CompiledVariablesTracking {
match := collectionToKeep.MatchString(key)
if match {
@ -303,6 +311,7 @@ func (r *AppsecRunner) AccumulateTxToEvent(evt *types.Event, req *appsec.ParsedR
}
}
}
return true
})
@ -325,11 +334,12 @@ func (r *AppsecRunner) AccumulateTxToEvent(evt *types.Event, req *appsec.ParsedR
ruleNameProm := fmt.Sprintf("%d", rule.Rule().ID())
if details, ok := appsec.AppsecRulesDetails[rule.Rule().ID()]; ok {
//Only set them for custom rules, not for rules written in seclang
// Only set them for custom rules, not for rules written in seclang
name = details.Name
version = details.Version
hash = details.Hash
ruleNameProm = details.Name
r.logger.Debugf("custom rule for event, setting name: %s, version: %s, hash: %s", name, version, hash)
} else {
name = fmt.Sprintf("native_rule:%d", rule.Rule().ID())
@ -338,12 +348,15 @@ func (r *AppsecRunner) AccumulateTxToEvent(evt *types.Event, req *appsec.ParsedR
AppsecRuleHits.With(prometheus.Labels{"rule_name": ruleNameProm, "type": kind, "source": req.RemoteAddrNormalized, "appsec_engine": req.AppsecEngine}).Inc()
matchedZones := make([]string, 0)
for _, matchData := range rule.MatchedDatas() {
zone := matchData.Variable().Name()
varName := matchData.Key()
if varName != "" {
zone += "." + varName
}
matchedZones = append(matchedZones, zone)
}

View file

@ -385,7 +385,6 @@ func (f *FileSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) er
}
filink, err := os.Lstat(file)
if err != nil {
f.logger.Errorf("Could not lstat() new file %s, ignoring it : %s", file, err)
continue
@ -578,7 +577,7 @@ func (f *FileSource) tailFile(out chan types.Event, t *tomb.Tomb, tail *tail.Tai
errMsg = fmt.Sprintf(errMsg+" : %s", err)
}
logger.Warningf(errMsg)
logger.Warning(errMsg)
return nil
case line := <-tail.Lines:
@ -629,8 +628,8 @@ func (f *FileSource) readFile(filename string, out chan types.Event, t *tomb.Tom
var scanner *bufio.Scanner
logger := f.logger.WithField("oneshot", filename)
fd, err := os.Open(filename)
fd, err := os.Open(filename)
if err != nil {
return fmt.Errorf("failed opening %s: %w", filename, err)
}

View file

@ -32,7 +32,7 @@ func ValidateContextExpr(key string, expressions []string) error {
for _, expression := range expressions {
_, err := expr.Compile(expression, exprhelpers.GetExprOptions(map[string]interface{}{"evt": &types.Event{}})...)
if err != nil {
return fmt.Errorf("compilation of '%s' failed: %v", expression, err)
return fmt.Errorf("compilation of '%s' failed: %w", expression, err)
}
}
@ -74,7 +74,7 @@ func NewAlertContext(contextToSend map[string][]string, valueLength int) error {
for _, value := range values {
valueCompiled, err := expr.Compile(value, exprhelpers.GetExprOptions(map[string]interface{}{"evt": &types.Event{}})...)
if err != nil {
return fmt.Errorf("compilation of '%s' context value failed: %v", value, err)
return fmt.Errorf("compilation of '%s' context value failed: %w", value, err)
}
alertContext.ContextToSendCompiled[key] = append(alertContext.ContextToSendCompiled[key], valueCompiled)
@ -133,7 +133,7 @@ func EventToContext(events []types.Event) (models.Meta, []error) {
output, err := expr.Run(value, map[string]interface{}{"evt": evt})
if err != nil {
errors = append(errors, fmt.Errorf("failed to get value for %s : %v", key, err))
errors = append(errors, fmt.Errorf("failed to get value for %s: %w", key, err))
continue
}
@ -143,7 +143,7 @@ func EventToContext(events []types.Event) (models.Meta, []error) {
case int:
val = strconv.Itoa(out)
default:
errors = append(errors, fmt.Errorf("unexpected return type for %s : %T", key, output))
errors = append(errors, fmt.Errorf("unexpected return type for %s: %T", key, output))
continue
}
@ -161,7 +161,7 @@ func EventToContext(events []types.Event) (models.Meta, []error) {
valueStr, err := TruncateContext(values, alertContext.ContextValueLen)
if err != nil {
log.Warningf(err.Error())
log.Warning(err.Error())
}
meta := models.MetaItems0{

View file

@ -161,7 +161,7 @@ func TestWatcherAuth(t *testing.T) {
bodyBytes, err := io.ReadAll(resp.Response.Body)
require.NoError(t, err)
log.Printf(string(bodyBytes))
log.Print(string(bodyBytes))
t.Fatalf("The AuthenticateWatcher function should have returned an error for the response code %d", errorCodeToTest)
}

View file

@ -95,7 +95,7 @@ func (c *LongPollClient) poll() error {
logger.Errorf("failed to read response body: %s", err)
return err
}
logger.Errorf(string(bodyContent))
logger.Error(string(bodyContent))
return errUnauthorized
}
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)