diff --git a/cmd/recon.go b/cmd/recon.go index 4b6f2d6..4e82483 100644 --- a/cmd/recon.go +++ b/cmd/recon.go @@ -26,7 +26,7 @@ var ( var reconCmd = &cobra.Command{ Use: "recon", Short: "Run OSINT recon across internet sources", - 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.", + 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). Phase 11 adds search engine dorking (Google/Bing/DuckDuckGo/Yandex/Brave) and paste site scanning (Pastebin/GistPaste/PasteSites).", } var reconFullCmd = &cobra.Command{ @@ -153,6 +153,12 @@ func buildReconEngine() *recon.Engine { 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")), + GoogleAPIKey: firstNonEmpty(os.Getenv("GOOGLE_API_KEY"), viper.GetString("recon.google.api_key")), + GoogleCX: firstNonEmpty(os.Getenv("GOOGLE_CX"), viper.GetString("recon.google.cx")), + BingAPIKey: firstNonEmpty(os.Getenv("BING_API_KEY"), viper.GetString("recon.bing.api_key")), + YandexUser: firstNonEmpty(os.Getenv("YANDEX_USER"), viper.GetString("recon.yandex.user")), + YandexAPIKey: firstNonEmpty(os.Getenv("YANDEX_API_KEY"), viper.GetString("recon.yandex.api_key")), + BraveAPIKey: firstNonEmpty(os.Getenv("BRAVE_API_KEY"), viper.GetString("recon.brave.api_key")), } sources.RegisterAll(e, cfg) return e diff --git a/pkg/recon/sources/register.go b/pkg/recon/sources/register.go index e63f19b..b9b2c50 100644 --- a/pkg/recon/sources/register.go +++ b/pkg/recon/sources/register.go @@ -28,20 +28,32 @@ type SourcesConfig struct { KaggleUser string KaggleKey string + // Google Custom Search API key and search engine ID (CX). + GoogleAPIKey string + GoogleCX string + // Bing Web Search API subscription key. + BingAPIKey string + // Yandex XML Search user and API key. + YandexUser string + YandexAPIKey string + // Brave Search API subscription token. + BraveAPIKey string + // Registry drives query generation for every source via BuildQueries. Registry *providers.Registry // Limiters is the shared per-source rate-limiter registry. Limiters *recon.LimiterRegistry } -// RegisterAll registers every Phase 10 code-hosting source on engine. +// RegisterAll registers every Phase 10 code-hosting and Phase 11 search +// engine / paste site source on engine (18 sources total). // -// All ten sources are registered unconditionally so that cmd/recon.go can -// surface the full catalog via `keyhunter recon list` regardless of which -// credentials are configured. Sources without required credentials return -// Enabled()==false so SweepAll skips them without erroring. +// All sources are registered unconditionally so that cmd/recon.go can surface +// the full catalog via `keyhunter recon list` regardless of which credentials +// are configured. Sources without required credentials return Enabled()==false +// so SweepAll skips them without erroring. // -// A nil engine is treated as a no-op (not an error) — callers in broken init +// A nil engine is treated as a no-op (not an error) -- callers in broken init // paths shouldn't panic. func RegisterAll(engine *recon.Engine, cfg SourcesConfig) { if engine == nil { @@ -95,4 +107,46 @@ func RegisterAll(engine *recon.Engine, cfg SourcesConfig) { Registry: reg, Limiters: lim, }) + + // Phase 11: Search engine dorking sources. + engine.Register(&GoogleDorkSource{ + APIKey: cfg.GoogleAPIKey, + CX: cfg.GoogleCX, + Registry: reg, + Limiters: lim, + }) + engine.Register(&BingDorkSource{ + APIKey: cfg.BingAPIKey, + Registry: reg, + Limiters: lim, + }) + engine.Register(&DuckDuckGoSource{ + Registry: reg, + Limiters: lim, + }) + engine.Register(&YandexSource{ + User: cfg.YandexUser, + APIKey: cfg.YandexAPIKey, + Registry: reg, + Limiters: lim, + }) + engine.Register(&BraveSource{ + APIKey: cfg.BraveAPIKey, + Registry: reg, + Limiters: lim, + }) + + // Phase 11: Paste site sources. + engine.Register(&PastebinSource{ + Registry: reg, + Limiters: lim, + }) + engine.Register(&GistPasteSource{ + Registry: reg, + Limiters: lim, + }) + engine.Register(&PasteSitesSource{ + Registry: reg, + Limiters: lim, + }) } diff --git a/pkg/recon/sources/register_test.go b/pkg/recon/sources/register_test.go index 0f8baa5..b1bbd44 100644 --- a/pkg/recon/sources/register_test.go +++ b/pkg/recon/sources/register_test.go @@ -16,9 +16,9 @@ func registerTestRegistry() *providers.Registry { }) } -// TestRegisterAll_WiresAllTenSources asserts that RegisterAll registers every -// Phase 10 code-hosting source by its stable name on a fresh engine. -func TestRegisterAll_WiresAllTenSources(t *testing.T) { +// TestRegisterAll_WiresAllEighteenSources asserts that RegisterAll registers +// every Phase 10 + Phase 11 source by its stable name on a fresh engine. +func TestRegisterAll_WiresAllEighteenSources(t *testing.T) { eng := recon.NewEngine() cfg := SourcesConfig{ Registry: registerTestRegistry(), @@ -28,16 +28,24 @@ func TestRegisterAll_WiresAllTenSources(t *testing.T) { got := eng.List() want := []string{ + "bing", "bitbucket", + "brave", "codeberg", "codesandbox", + "duckduckgo", "gist", + "gistpaste", "github", "gitlab", + "google", "huggingface", "kaggle", + "pastebin", + "pastesites", "replit", "sandboxes", + "yandex", } if !reflect.DeepEqual(got, want) { t.Fatalf("RegisterAll names mismatch\n got: %v\nwant: %v", got, want) @@ -55,8 +63,8 @@ func TestRegisterAll_MissingCredsStillRegistered(t *testing.T) { Limiters: recon.NewLimiterRegistry(), }) - if n := len(eng.List()); n != 10 { - t.Fatalf("expected 10 sources registered, got %d: %v", n, eng.List()) + if n := len(eng.List()); n != 18 { + t.Fatalf("expected 18 sources registered, got %d: %v", n, eng.List()) } // SweepAll with an empty config should filter out cred-gated sources