From d252bd1772fd52789d9162822f456bd1ef7723fd Mon Sep 17 00:00:00 2001 From: Joey Wilhelm Date: Tue, 7 Nov 2023 08:54:59 -0700 Subject: [PATCH 1/3] fix: Remove global cmd objects and init() methods --- cmd/root.go | 78 +++++++++++++++++++++++-------------------- cmd/scan.go | 20 +++++------ config/config.go | 13 +++++--- config/config_test.go | 2 ++ 4 files changed, 61 insertions(+), 52 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 2d5e42c..d4616d5 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,55 +2,61 @@ package cmd import ( "github.com/rs/zerolog" + "github.com/underdog-tech/vulnbot/config" "github.com/underdog-tech/vulnbot/logger" "github.com/spf13/cobra" "github.com/spf13/viper" ) -// rootCmd represents the base command when called without any subcommands -var rootCmd = &cobra.Command{ - Use: "vulnbot", - Short: "Vulnbot: Your all-in-one security alert manager.", - Long: `Vulnbot is a comprehensive security alert manager designed to keep your code safe from vulnerabilities. - -It is a versatile bot that can seamlessly integrate with multiple data sources, such as GitHub, and soon Phylum, -Vulnbot empowers developers and security teams to efficiently manage and respond to security threats.`, - PersistentPreRun: func(cmd *cobra.Command, args []string) { - quiet, _ := cmd.Flags().GetBool("quiet") - verbosity, _ := cmd.Flags().GetCount("verbose") - - if quiet { - logger.SetLogLevel(zerolog.Disabled) - } else if verbosity > 0 { - if verbosity > 3 { - verbosity = 3 +func NewRootCommand() *cobra.Command { + rootCmd := &cobra.Command{ + Use: "vulnbot", + Short: "Vulnbot: Your all-in-one security alert manager.", + Long: `Vulnbot is a comprehensive security alert manager designed to keep your code safe from vulnerabilities. + + It is a versatile bot that can seamlessly integrate with multiple data sources, such as GitHub, and soon Phylum, + Vulnbot empowers developers and security teams to efficiently manage and respond to security threats.`, + PersistentPreRun: func(cmd *cobra.Command, args []string) { + quiet, _ := cmd.Flags().GetBool("quiet") + verbosity, _ := cmd.Flags().GetCount("verbose") + + if quiet { + logger.SetLogLevel(zerolog.Disabled) + } else if verbosity > 0 { + if verbosity > 3 { + verbosity = 3 + } + logLevel := logger.DEFAULT_LOG_LEVEL - zerolog.Level(verbosity) + logger.SetLogLevel(logLevel) + } else { + logger.SetLogLevel(logger.DEFAULT_LOG_LEVEL) } - logLevel := logger.DEFAULT_LOG_LEVEL - zerolog.Level(verbosity) - logger.SetLogLevel(logLevel) - } else { - logger.SetLogLevel(logger.DEFAULT_LOG_LEVEL) - } - }, + }, + } + // Add all subcommand(s) + rootCmd.AddCommand(NewScanCommand()) + + // Set up flags for the command(s) + pflags := rootCmd.PersistentFlags() + pflags.StringP("config", "c", "config.toml", "Config file path.") + pflags.StringSliceP("reporters", "r", []string{"slack", "console"}, "Specify a list of reporters for reporting vulnerabilities.") + pflags.BoolP("quiet", "q", false, "Suppress all console output. (Mutually exclusive with 'verbose'.)") + pflags.CountP("verbose", "v", "More verbose output. Specifying multiple times increases verbosity. (Mutually exclusive with 'quiet'.)") + rootCmd.MarkFlagsMutuallyExclusive("verbose", "quiet") + + // Set up Viper config + _ = viper.BindPFlags(pflags) + config.SetConfigDefaults() + + return rootCmd } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { log := logger.Get() - err := rootCmd.Execute() - if err != nil { + if err := NewRootCommand().Execute(); err != nil { log.Fatal().Err(err).Msg("Failed to execute command.") } } - -func init() { - persistent := rootCmd.PersistentFlags() - persistent.StringP("config", "c", "config.toml", "Config file path.") - persistent.StringSliceP("reporters", "r", []string{"slack", "console"}, "Specify a list of reporters for reporting vulnerabilities.") - persistent.BoolP("quiet", "q", false, "Suppress all console output. (Mutually exclusive with 'verbose'.)") - persistent.CountP("verbose", "v", "More verbose output. Specifying multiple times increases verbosity. (Mutually exclusive with 'quiet'.)") - - _ = viper.BindPFlags(persistent) - rootCmd.MarkFlagsMutuallyExclusive("verbose", "quiet") -} diff --git a/cmd/scan.go b/cmd/scan.go index b995f70..8fe7722 100644 --- a/cmd/scan.go +++ b/cmd/scan.go @@ -6,15 +6,13 @@ import ( "github.com/spf13/cobra" ) -// scanCmd represents the scan command -var scanCmd = &cobra.Command{ - Use: "scan", - Short: "", - Long: ``, - Run: internal.Scan, - Aliases: []string{"s", "scan"}, -} - -func init() { - rootCmd.AddCommand(scanCmd) +// NewScanCommand returns a Cobra command for running scans +func NewScanCommand() *cobra.Command { + return &cobra.Command{ + Use: "scan", + Short: "", + Long: ``, + Run: internal.Scan, + Aliases: []string{"s", "scan"}, + } } diff --git a/config/config.go b/config/config.go index 1c55b5e..e72a32c 100644 --- a/config/config.go +++ b/config/config.go @@ -39,18 +39,21 @@ func fileExists(fname string) bool { return true } -func GetUserConfig(configFile string) (Config, error) { - log := logger.Get() - - userCfg := Config{} +func SetConfigDefaults() { + cfg := Config{} // Use reflection to register all config fields in Viper to set up defaults - cfgFields := reflect.ValueOf(userCfg) + cfgFields := reflect.ValueOf(cfg) cfgType := cfgFields.Type() for i := 0; i < cfgFields.NumField(); i++ { viper.SetDefault(cfgType.Field(i).Name, cfgFields.Field(i).Interface()) } +} + +func GetUserConfig(configFile string) (Config, error) { + log := logger.Get() + userCfg := Config{} // Load the main config file if !fileExists(configFile) { diff --git a/config/config_test.go b/config/config_test.go index 774a57d..f409385 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -80,6 +80,8 @@ func TestGetUserConfigFromFile(t *testing.T) { } func TestGetUserConfigFromEnv(t *testing.T) { + config.SetConfigDefaults() + t.Setenv("VULNBOT_REPORTERS", "slack") t.Setenv("VULNBOT_GITHUB_ORG", "hitchhikers") // This should override the config file From 9ca2dd400d607c96e4f910b6f247e6b8cc3dbad6 Mon Sep 17 00:00:00 2001 From: Joey Wilhelm Date: Tue, 7 Nov 2023 09:09:08 -0700 Subject: [PATCH 2/3] chore: Add a small test to make sure NewRootCommand works --- cmd/root_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 cmd/root_test.go diff --git a/cmd/root_test.go b/cmd/root_test.go new file mode 100644 index 0000000..07fa55e --- /dev/null +++ b/cmd/root_test.go @@ -0,0 +1,18 @@ +package cmd_test + +import ( + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/underdog-tech/vulnbot/cmd" +) + +func TestNewRootCommand(t *testing.T) { + // Mostly just ensure nothing errors out. + c := cmd.NewRootCommand() + + assert.IsType(t, &cobra.Command{}, c) + assert.Equal(t, "vulnbot", c.Use) + assert.True(t, c.HasAvailableSubCommands()) +} From 1861acef8a6229a6d9b5c7af8eb97b1929468b8f Mon Sep 17 00:00:00 2001 From: Joey Wilhelm Date: Tue, 7 Nov 2023 09:10:51 -0700 Subject: [PATCH 3/3] chore: Add gochecknoinits linter --- .golangci.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.golangci.toml b/.golangci.toml index a76b2a8..92969b2 100644 --- a/.golangci.toml +++ b/.golangci.toml @@ -1,2 +1,10 @@ [linters] -enable = ["cyclop", "gocognit", "gosec", "nilnil", "prealloc", "zerologlint"] +enable = [ + "cyclop", + "gochecknoinits", + "gocognit", + "gosec", + "nilnil", + "prealloc", + "zerologlint", +]