Cybersecurity-Projects/PROJECTS/intermediate/secrets-scanner/internal/cli/root.go

149 lines
2.9 KiB
Go

// ©AngelaMos | 2026
// root.go
package cli
import (
"fmt"
"os"
"os/signal"
"syscall"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/CarterPerez-dev/portia/internal/config"
"github.com/CarterPerez-dev/portia/internal/ui"
)
var (
cfgFile string
format string
verbose bool
noColor bool
excludes []string
maxSize int64
enableHIBP bool
)
var rootCmd = &cobra.Command{
Use: "portia",
Short: "Secrets scanner for codebases and git repositories",
Long: `Portia scans codebases and git history for leaked secrets,
API keys, passwords, tokens, and private keys.`,
SilenceUsage: true,
SilenceErrors: true,
}
func Execute() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "%s %s\n",
ui.Cross, ui.Red(err.Error()))
os.Exit(1)
}
}
func run() error {
ctx, cancel := signal.NotifyContext(
rootCmd.Context(), os.Interrupt, syscall.SIGTERM,
)
defer cancel()
rootCmd.SetContext(ctx)
return rootCmd.ExecuteContext(ctx)
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(
&cfgFile, "config", "",
"config file (default .portia.toml)",
)
rootCmd.PersistentFlags().StringVarP(
&format, "format", "f", "",
"output format: terminal, json, sarif",
)
rootCmd.PersistentFlags().BoolVarP(
&verbose, "verbose", "v", false,
"verbose output",
)
rootCmd.PersistentFlags().BoolVar(
&noColor, "no-color", false,
"disable colored output",
)
rootCmd.PersistentFlags().StringSliceVarP(
&excludes, "exclude", "e", nil,
"paths to exclude (repeatable)",
)
rootCmd.PersistentFlags().Int64Var(
&maxSize, "max-size", 0,
"max file size in bytes (default 1MB)",
)
rootCmd.PersistentFlags().BoolVar(
&enableHIBP, "hibp", false,
"enable HIBP breach verification",
)
rootCmd.AddCommand(scanCmd)
rootCmd.AddCommand(gitCmd)
rootCmd.AddCommand(initCmd)
rootCmd.AddCommand(configCmd)
rootCmd.AddCommand(pyprojectCmd)
defaultHelp := rootCmd.HelpFunc()
rootCmd.SetHelpFunc(
func(cmd *cobra.Command, args []string) {
if cmd.Root() == cmd {
ui.PrintBannerWithArt()
} else {
ui.PrintBanner()
}
defaultHelp(cmd, args)
},
)
}
var cfg *config.Config
func initConfig() {
if noColor {
color.NoColor = true
}
loaded, err := config.Load(cfgFile)
if err != nil {
fmt.Fprintf(os.Stderr, "%s config error: %s\n",
ui.Warning, err)
}
if loaded != nil {
cfg = loaded
} else {
cfg = &config.Config{}
}
if format == "" && cfg.Output.Format != "" {
format = cfg.Output.Format
}
if format == "" {
format = "terminal"
}
if !verbose && cfg.Output.Verbose {
verbose = true
}
if !noColor && cfg.Output.NoColor {
noColor = true
color.NoColor = true
}
if !enableHIBP && cfg.HIBP.Enabled {
enableHIBP = true
}
if maxSize == 0 && cfg.Scan.MaxFileSize > 0 {
maxSize = cfg.Scan.MaxFileSize
}
if len(excludes) == 0 && len(cfg.Scan.Excludes) > 0 {
excludes = cfg.Scan.Excludes
}
}