feat(01-05): add CLI root command, config package, output table, and settings helpers

- cmd/root.go: Cobra root with all 11 subcommands, viper config loading
- cmd/stubs.go: 8 stub commands for future phases (verify, import, recon, keys, serve, dorks, hook, schedule)
- cmd/scan.go: scan command wiring engine + storage + output with per-installation salt
- cmd/providers.go: providers list/info/stats subcommands
- cmd/config.go: config init/set/get subcommands
- pkg/config/config.go: Config struct with Load() and defaults
- pkg/output/table.go: lipgloss terminal table for PrintFindings
- pkg/storage/settings.go: GetSetting/SetSetting for settings table CRUD
This commit is contained in:
salvacybersec
2026-04-05 12:26:36 +03:00
parent d0396bb384
commit 9da0b68129
8 changed files with 626 additions and 4 deletions

View File

@@ -1,8 +1,76 @@
package cmd
import "os"
import (
"fmt"
"os"
"path/filepath"
// Execute is a stub. The real command tree is built in Plan 05.
func Execute() {
_ = os.Args
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
// rootCmd is the base command when called without any subcommands.
var rootCmd = &cobra.Command{
Use: "keyhunter",
Short: "KeyHunter -- detect leaked LLM API keys across 108+ providers",
Long: `KeyHunter scans files, git history, and internet sources for leaked LLM API keys.
Supports 108+ providers with Aho-Corasick pre-filtering and regex + entropy detection.`,
SilenceUsage: true,
}
// Execute is the entry point called by main.go.
func Execute() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default: ~/.keyhunter.yaml)")
rootCmd.AddCommand(scanCmd)
rootCmd.AddCommand(providersCmd)
rootCmd.AddCommand(configCmd)
// Stub commands for future phases (per CLI-01 requirement of 11 commands)
rootCmd.AddCommand(verifyCmd)
rootCmd.AddCommand(importCmd)
rootCmd.AddCommand(reconCmd)
rootCmd.AddCommand(keysCmd)
rootCmd.AddCommand(serveCmd)
rootCmd.AddCommand(dorksCmd)
rootCmd.AddCommand(hookCmd)
rootCmd.AddCommand(scheduleCmd)
}
func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
home, err := os.UserHomeDir()
if err != nil {
fmt.Fprintln(os.Stderr, "warning: cannot determine home directory:", err)
return
}
viper.SetConfigName(".keyhunter")
viper.SetConfigType("yaml")
viper.AddConfigPath(home)
viper.AddConfigPath(".")
}
viper.SetEnvPrefix("KEYHUNTER")
viper.AutomaticEnv()
// Defaults
viper.SetDefault("scan.workers", 0) // 0 = auto (CPU*8)
viper.SetDefault("database.path", filepath.Join(mustHomeDir(), ".keyhunter", "keyhunter.db"))
// Config file is optional -- ignore if not found
_ = viper.ReadInConfig()
}
func mustHomeDir() string {
h, _ := os.UserHomeDir()
return h
}