test(13-04): add integration test handlers for all 12 Phase 13 sources (40 total)

- Add httptest mux handlers for npm, pypi, crates, rubygems, maven, nuget, goproxy, packagist, dockerhub, k8s, terraform, helm
- Register all 12 Phase 13 sources with BaseURL prefix routing
- Update expected source types and count assertions from 28 to 40
This commit is contained in:
salvacybersec
2026-04-06 13:03:27 +03:00
parent c16f5feaee
commit 9b005e78bb

View File

@@ -15,10 +15,11 @@ import (
// TestIntegration_AllSources_SweepAll spins up a single multiplexed httptest // TestIntegration_AllSources_SweepAll spins up a single multiplexed httptest
// server that serves canned fixtures for every Phase 10 code-hosting source, // server that serves canned fixtures for every Phase 10 code-hosting source,
// Phase 11 search engine / paste site source, Phase 12 IoT scanner, and // Phase 11 search engine / paste site source, Phase 12 IoT scanner / cloud
// Phase 12 cloud storage source, registers the sources (with BaseURL overrides // storage source, and Phase 13 package registry / container / IaC source,
// pointing at the test server) onto a fresh recon.Engine, runs SweepAll, and // registers the sources (with BaseURL overrides pointing at the test server)
// asserts at least one Finding was emitted per SourceType across all 28 sources. // onto a fresh recon.Engine, runs SweepAll, and asserts at least one Finding
// was emitted per SourceType across all 40 sources.
// //
// RegisterAll cannot be used directly because it wires production URLs; the // RegisterAll cannot be used directly because it wires production URLs; the
// test exercises the same code paths by constructing each source identically // test exercises the same code paths by constructing each source identically
@@ -239,6 +240,78 @@ func TestIntegration_AllSources_SweepAll(t *testing.T) {
</EnumerationResults>`)) </EnumerationResults>`))
}) })
// ---- Phase 13: npm /-/v1/search (prefix /npm) ----
mux.HandleFunc("/npm/-/v1/search", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"objects":[{"package":{"name":"leak-pkg","links":{"npm":"https://npmjs.com/package/leak-pkg"}}}]}`))
})
// ---- Phase 13: pypi /search/ (prefix /pypi) ----
mux.HandleFunc("/pypi/search/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
_, _ = w.Write([]byte(`<html><body><a href="/project/leaked-pkg/">leaked-pkg</a></body></html>`))
})
// ---- Phase 13: crates /api/v1/crates (prefix /crates) ----
mux.HandleFunc("/crates/api/v1/crates", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"crates":[{"id":"leaked-crate","name":"leaked-crate","repository":"https://github.com/example/leaked-crate"}]}`))
})
// ---- Phase 13: rubygems /api/v1/search.json (prefix /rubygems) ----
mux.HandleFunc("/rubygems/api/v1/search.json", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`[{"name":"leaked-gem","project_uri":"https://rubygems.org/gems/leaked-gem"}]`))
})
// ---- Phase 13: maven /solrsearch/select (prefix /maven) ----
mux.HandleFunc("/maven/solrsearch/select", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"response":{"numFound":1,"docs":[{"g":"com.leak","a":"sdk","latestVersion":"1.0"}]}}`))
})
// ---- Phase 13: nuget /query (prefix /nuget) ----
mux.HandleFunc("/nuget/query", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"data":[{"id":"LeakedPkg","version":"1.0","projectUrl":"https://nuget.org/packages/LeakedPkg"}]}`))
})
// ---- Phase 13: goproxy /search (prefix /goproxy) ----
mux.HandleFunc("/goproxy/search", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
_, _ = w.Write([]byte(`<html><body><a href="/github.com/leak/module">module</a></body></html>`))
})
// ---- Phase 13: packagist /search.json (prefix /packagist) ----
mux.HandleFunc("/packagist/search.json", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"results":[{"name":"vendor/leaked","url":"https://packagist.org/packages/vendor/leaked"}]}`))
})
// ---- Phase 13: dockerhub /v2/search/repositories/ (prefix /dockerhub) ----
mux.HandleFunc("/dockerhub/v2/search/repositories/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"results":[{"repo_name":"user/leaked-image","description":"leaked"}]}`))
})
// ---- Phase 13: k8s /api/v1/packages/search (prefix /k8s) ----
mux.HandleFunc("/k8s/api/v1/packages/search", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"packages":[{"package_id":"pkg-1","name":"leaked-operator","normalized_name":"leaked-operator","repository":{"name":"community","kind":6}}]}`))
})
// ---- Phase 13: terraform /v1/modules (prefix /terraform) ----
mux.HandleFunc("/terraform/v1/modules", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"modules":[{"id":"hashicorp/leaked/aws","namespace":"hashicorp","name":"leaked","provider":"aws"}]}`))
})
// ---- Phase 13: helm /api/v1/packages/search (prefix /helm) ----
mux.HandleFunc("/helm/api/v1/packages/search", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"packages":[{"package_id":"chart-1","name":"leaked-chart","normalized_name":"leaked-chart","repository":{"name":"bitnami","kind":0}}]}`))
})
srv := httptest.NewServer(mux) srv := httptest.NewServer(mux)
defer srv.Close() defer srv.Close()
@@ -447,9 +520,39 @@ func TestIntegration_AllSources_SweepAll(t *testing.T) {
client: NewClient(), client: NewClient(),
}) })
// Sanity: all 28 sources registered. // --- Phase 13: Package registry sources ---
if n := len(eng.List()); n != 28 {
t.Fatalf("expected 28 sources on engine, got %d: %v", n, eng.List()) // npm
eng.Register(&NpmSource{BaseURL: srv.URL + "/npm", Registry: reg, Limiters: lim, Client: NewClient()})
// pypi
eng.Register(&PyPISource{BaseURL: srv.URL + "/pypi", Registry: reg, Limiters: lim, Client: NewClient()})
// crates
eng.Register(&CratesIOSource{BaseURL: srv.URL + "/crates", Registry: reg, Limiters: lim, Client: NewClient()})
// rubygems
eng.Register(&RubyGemsSource{BaseURL: srv.URL + "/rubygems", Registry: reg, Limiters: lim, Client: NewClient()})
// maven
eng.Register(&MavenSource{BaseURL: srv.URL + "/maven", Registry: reg, Limiters: lim, Client: NewClient()})
// nuget
eng.Register(&NuGetSource{BaseURL: srv.URL + "/nuget", Registry: reg, Limiters: lim, Client: NewClient()})
// goproxy
eng.Register(&GoProxySource{BaseURL: srv.URL + "/goproxy", Registry: reg, Limiters: lim, Client: NewClient()})
// packagist
eng.Register(&PackagistSource{BaseURL: srv.URL + "/packagist", Registry: reg, Limiters: lim, Client: NewClient()})
// --- Phase 13: Container & IaC sources ---
// dockerhub
eng.Register(&DockerHubSource{BaseURL: srv.URL + "/dockerhub", Registry: reg, Limiters: lim, Client: NewClient()})
// k8s
eng.Register(&KubernetesSource{BaseURL: srv.URL + "/k8s", Registry: reg, Limiters: lim, Client: NewClient()})
// terraform
eng.Register(&TerraformSource{BaseURL: srv.URL + "/terraform", Registry: reg, Limiters: lim, Client: NewClient()})
// helm
eng.Register(&HelmSource{BaseURL: srv.URL + "/helm", Registry: reg, Limiters: lim, Client: NewClient()})
// Sanity: all 40 sources registered.
if n := len(eng.List()); n != 40 {
t.Fatalf("expected 40 sources on engine, got %d: %v", n, eng.List())
} }
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
@@ -499,6 +602,20 @@ func TestIntegration_AllSources_SweepAll(t *testing.T) {
"recon:gcs", "recon:gcs",
"recon:azureblob", "recon:azureblob",
"recon:spaces", "recon:spaces",
// Phase 13: Package registries
"recon:npm",
"recon:pypi",
"recon:crates",
"recon:rubygems",
"recon:maven",
"recon:nuget",
"recon:goproxy",
"recon:packagist",
// Phase 13: Container & IaC
"recon:dockerhub",
"recon:k8s",
"recon:terraform",
"recon:helm",
} }
for _, st := range wantTypes { for _, st := range wantTypes {
if byType[st] == 0 { if byType[st] == 0 {