From e00fb172ab5aea213d0175b81f02b5eb2e99477a Mon Sep 17 00:00:00 2001 From: salvacybersec Date: Mon, 6 Apr 2026 01:27:25 +0300 Subject: [PATCH] feat(10-09): wire sources.RegisterAll into cmd/recon with viper+env credential lookup --- cmd/recon.go | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/cmd/recon.go b/cmd/recon.go index 5483f09..cdd6fc4 100644 --- a/cmd/recon.go +++ b/cmd/recon.go @@ -3,9 +3,13 @@ package cmd import ( "context" "fmt" + "os" + "github.com/salvacybersec/keyhunter/pkg/providers" "github.com/salvacybersec/keyhunter/pkg/recon" + "github.com/salvacybersec/keyhunter/pkg/recon/sources" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var ( @@ -17,7 +21,7 @@ var ( var reconCmd = &cobra.Command{ Use: "recon", Short: "Run OSINT recon across internet sources", - Long: "Run OSINT recon sweeps across registered sources. Phase 9 ships with an ExampleSource stub; real sources land in Phases 10-16.", + Long: "Run OSINT recon sweeps across registered sources. Phase 10 adds ten code-hosting sources (GitHub/GitLab/Bitbucket/Gist/Codeberg/HuggingFace/Replit/CodeSandbox/Sandboxes/Kaggle). Further phases add pastebins, search engines, etc.", } var reconFullCmd = &cobra.Command{ @@ -56,15 +60,47 @@ var reconListCmd = &cobra.Command{ }, } -// buildReconEngine constructs the recon Engine with all sources registered. -// Phase 9 ships ExampleSource only; Phases 10-16 will add real sources here -// (or via a registration side-effect in their packages). +// buildReconEngine constructs the recon Engine with all registered sources. +// Phase 9 contributes ExampleSource; Phase 10 contributes ten code-hosting +// sources via sources.RegisterAll. Credentials are read from environment +// variables first, then from viper config keys under `recon..*`. +// Sources whose credentials are missing are still registered but Enabled() +// will report false so SweepAll skips them cleanly. func buildReconEngine() *recon.Engine { e := recon.NewEngine() e.Register(recon.ExampleSource{}) + + reg, err := providers.NewRegistry() + if err != nil { + fmt.Fprintf(os.Stderr, "recon: failed to load providers: %v\n", err) + return e + } + + cfg := sources.SourcesConfig{ + Registry: reg, + Limiters: recon.NewLimiterRegistry(), + GitHubToken: firstNonEmpty(os.Getenv("GITHUB_TOKEN"), viper.GetString("recon.github.token")), + GitLabToken: firstNonEmpty(os.Getenv("GITLAB_TOKEN"), viper.GetString("recon.gitlab.token")), + BitbucketToken: firstNonEmpty(os.Getenv("BITBUCKET_TOKEN"), viper.GetString("recon.bitbucket.token")), + BitbucketWorkspace: firstNonEmpty(os.Getenv("BITBUCKET_WORKSPACE"), viper.GetString("recon.bitbucket.workspace")), + CodebergToken: firstNonEmpty(os.Getenv("CODEBERG_TOKEN"), viper.GetString("recon.codeberg.token")), + HuggingFaceToken: firstNonEmpty(os.Getenv("HUGGINGFACE_TOKEN"), viper.GetString("recon.huggingface.token")), + KaggleUser: firstNonEmpty(os.Getenv("KAGGLE_USERNAME"), viper.GetString("recon.kaggle.username")), + KaggleKey: firstNonEmpty(os.Getenv("KAGGLE_KEY"), viper.GetString("recon.kaggle.key")), + } + sources.RegisterAll(e, cfg) return e } +// firstNonEmpty returns a if non-empty, otherwise b. Used to implement the +// env-var → viper-config precedence chain for credential lookup. +func firstNonEmpty(a, b string) string { + if a != "" { + return a + } + return b +} + func init() { reconFullCmd.Flags().BoolVar(&reconStealth, "stealth", false, "enable UA rotation and jitter delays") reconFullCmd.Flags().BoolVar(&reconRespectRobots, "respect-robots", true, "respect robots.txt for web-scraping sources")