From 86a6bb864b5bd52aba29328b1f58adf3f91010ec Mon Sep 17 00:00:00 2001 From: salvacybersec Date: Mon, 6 Apr 2026 00:47:32 +0300 Subject: [PATCH] feat(09-05): add recon full/list commands and remove stub - cmd/recon.go owns reconCmd with full and list subcommands - Wires pkg/recon.Engine.SweepAll + Dedup with ExampleSource registered - Adds --stealth, --respect-robots (default true), --query flags - Removes reconCmd stub from cmd/stubs.go --- cmd/recon.go | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++ cmd/stubs.go | 6 +---- 2 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 cmd/recon.go diff --git a/cmd/recon.go b/cmd/recon.go new file mode 100644 index 0000000..5483f09 --- /dev/null +++ b/cmd/recon.go @@ -0,0 +1,74 @@ +package cmd + +import ( + "context" + "fmt" + + "github.com/salvacybersec/keyhunter/pkg/recon" + "github.com/spf13/cobra" +) + +var ( + reconStealth bool + reconRespectRobots bool + reconQuery string +) + +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.", +} + +var reconFullCmd = &cobra.Command{ + Use: "full", + Short: "Sweep all enabled sources in parallel and deduplicate findings", + RunE: func(cmd *cobra.Command, args []string) error { + eng := buildReconEngine() + cfg := recon.Config{ + Stealth: reconStealth, + RespectRobots: reconRespectRobots, + Query: reconQuery, + } + ctx := context.Background() + all, err := eng.SweepAll(ctx, cfg) + if err != nil { + return fmt.Errorf("recon sweep: %w", err) + } + deduped := recon.Dedup(all) + fmt.Printf("recon: swept %d sources, %d findings (%d after dedup)\n", len(eng.List()), len(all), len(deduped)) + for _, f := range deduped { + fmt.Printf(" [%s] %s %s %s\n", f.SourceType, f.ProviderName, f.KeyMasked, f.Source) + } + return nil + }, +} + +var reconListCmd = &cobra.Command{ + Use: "list", + Short: "List registered recon sources", + RunE: func(cmd *cobra.Command, args []string) error { + eng := buildReconEngine() + for _, name := range eng.List() { + fmt.Println(name) + } + return nil + }, +} + +// 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). +func buildReconEngine() *recon.Engine { + e := recon.NewEngine() + e.Register(recon.ExampleSource{}) + return e +} + +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") + reconFullCmd.Flags().StringVar(&reconQuery, "query", "", "override query sent to each source") + reconCmd.AddCommand(reconFullCmd) + reconCmd.AddCommand(reconListCmd) +} diff --git a/cmd/stubs.go b/cmd/stubs.go index b3dd58f..e3b2253 100644 --- a/cmd/stubs.go +++ b/cmd/stubs.go @@ -21,11 +21,7 @@ var verifyCmd = &cobra.Command{ RunE: notImplemented("verify", "Phase 5"), } -var reconCmd = &cobra.Command{ - Use: "recon", - Short: "Run OSINT recon across internet sources (Phase 9+)", - RunE: notImplemented("recon", "Phase 9"), -} +// reconCmd is implemented in cmd/recon.go (Phase 9). // keysCmd is implemented in cmd/keys.go (Phase 6).