package server import ( "encoding/json" "strings" "testing" ) func TestExposureAPI(t *testing.T) { e := newAPIEnv(t) e.do("POST", "/zones", e.token, map[string]any{"name": "example.com", "records": []map[string]any{{"name": "ns1", "type": "A", "data": "192.0.2.1"}}}, 201) e.do("POST", "/views", e.token, map[string]any{"name": "internal", "match_clients": []string{"192.168.1.0/24", "localhost"}, "adopt_zones": true}, 201) ext := e.do("POST", "/views", e.token, map[string]any{"name": "external", "match_clients": []string{"any"}}, 201)["object"].(map[string]any)["id"] type report struct { Scopes []struct { View string `json:"view"` Reason string `json:"reason"` RateLimit string `json:"rate_limit"` Public bool `json:"public"` Findings []struct { Code, Severity, Fix string } } Server []struct{ Code, Fix string } } get := func() report { b, status := e.req("GET", "/exposure", e.token, nil) if status != 200 { t.Fatalf("exposure = %d %s", status, b) } var r report json.Unmarshal(b, &r) return r } r := get() if len(r.Scopes) != 2 || r.Scopes[0].View != "internal" || r.Scopes[0].Public || !r.Scopes[1].Public { t.Fatalf("scopes = %+v", r.Scopes) } var codes []string for _, f := range r.Scopes[1].Findings { codes = append(codes, f.Code) if f.Code == "exposure_rate_limit" && !strings.Contains(f.Fix, "log-only yes") { t.Errorf("rate limit fix = %q", f.Fix) } } if !strings.Contains(strings.Join(codes, " "), "exposure_rate_limit") || len(r.Server) < 2 { t.Errorf("findings = %v, server = %+v", codes, r.Server) } e.do("POST", "/exposure/fix", e.token, map[string]any{"view": "external", "code": "exposure_rate_limit"}, 201) if r := get(); r.Scopes[1].RateLimit != "log-only" { t.Errorf("rate limit = %q", r.Scopes[1].RateLimit) } if _, status := e.req("POST", "/exposure/fix", e.token, map[string]any{"view": "external", "code": "exposure_open_notify"}); status != 400 { t.Errorf("fix without an automatic fix = %d", status) } // Opening a public view up needs confirming. b, status := e.req("POST", "/options", e.token, map[string]any{"scope": "view", "scope_id": ext, "name": "allow-recursion", "value": "any"}) if status != 409 || !strings.Contains(string(b), "confirm_risk") { t.Errorf("open resolver = %d %s", status, b) } e.do("POST", "/exposure/ack", e.token, map[string]any{"view": "external", "code": "exposure_minimal_any", "reason": "testing"}, 201) for _, f := range get().Scopes[1].Findings { if f.Code == "exposure_minimal_any" && f.Severity != "info" { t.Errorf("acknowledged finding = %+v", f) } } }