149 lines
2.9 KiB
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
|
|
}
|
|
}
|