package sources import ( "context" "net/http" "net/http/httptest" "testing" "time" "github.com/salvacybersec/keyhunter/pkg/providers" "github.com/salvacybersec/keyhunter/pkg/recon" ) func gcsTestServer() *httptest.Server { mux := http.NewServeMux() mux.HandleFunc("/testprov-keys/", func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodHead { w.WriteHeader(http.StatusOK) return } w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"items":[ {"name":".env"}, {"name":"config.yaml"}, {"name":"readme.md"}, {"name":"secrets.toml"} ]}`)) }) mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) }) return httptest.NewServer(mux) } func TestGCSScanner_Sweep(t *testing.T) { srv := gcsTestServer() defer srv.Close() src := &GCSScanner{ Registry: cloudTestRegistry(), BaseURL: srv.URL + "/%s/", client: NewClient(), } out := make(chan recon.Finding, 32) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := src.Sweep(ctx, "", out); err != nil { t.Fatalf("Sweep error: %v", err) } close(out) var findings []recon.Finding for f := range out { findings = append(findings, f) } // .env, config.yaml, secrets.toml match; readme.md does not. if len(findings) != 3 { t.Fatalf("expected 3 findings, got %d: %+v", len(findings), findings) } for _, f := range findings { if f.SourceType != "recon:gcs" { t.Errorf("unexpected SourceType: %s", f.SourceType) } if f.Confidence != "medium" { t.Errorf("unexpected Confidence: %s", f.Confidence) } } } func TestGCSScanner_EmptyRegistry(t *testing.T) { src := &GCSScanner{ Registry: providers.NewRegistryFromProviders(nil), Limiters: recon.NewLimiterRegistry(), client: NewClient(), } out := make(chan recon.Finding, 4) if err := src.Sweep(context.Background(), "", out); err != nil { t.Fatalf("Sweep error: %v", err) } close(out) if len(out) != 0 { t.Fatal("expected 0 findings") } } func TestGCSScanner_CtxCancelled(t *testing.T) { srv := gcsTestServer() defer srv.Close() src := &GCSScanner{ Registry: cloudTestRegistry(), BaseURL: srv.URL + "/%s/", client: NewClient(), } ctx, cancel := context.WithCancel(context.Background()) cancel() out := make(chan recon.Finding, 4) if err := src.Sweep(ctx, "", out); err == nil { t.Fatal("expected ctx error") } } func TestGCSScanner_EnabledAndMeta(t *testing.T) { g := &GCSScanner{} if g.Name() != "gcs" { t.Fatalf("unexpected name: %s", g.Name()) } if !g.Enabled(recon.Config{}) { t.Fatal("expected Enabled=true") } if g.RespectsRobots() { t.Fatal("expected RespectsRobots=false") } if g.Burst() != 3 { t.Fatal("expected Burst=3") } }