package httppresentation import ( "context" "encoding/json" "errors" "net" "net/http" "net/http/httptest" "os" "strings" "testing" "time" "lambdamail/protocols/internal/domain/bimi" ) // memoryBimiStore is the store without a database. type memoryBimiStore struct { logos map[string]*BimiLogo } func (m *memoryBimiStore) Save(_ context.Context, domain string, svg []byte, vmcURL string) (string, error) { held := m.logos[domain] logo := &BimiLogo{SVG: svg, ETag: "etag-" + domain, VmcURL: vmcURL} if held != nil { // Replacing the logo keeps a held certificate, as the real store does logo.VmcPEM = held.VmcPEM } m.logos[domain] = logo return logo.ETag, nil } func (m *memoryBimiStore) Load(_ context.Context, domain string) (*BimiLogo, error) { return m.logos[domain], nil } func (m *memoryBimiStore) Delete(_ context.Context, domain string) error { delete(m.logos, domain) return nil } func (m *memoryBimiStore) SaveCertificate(_ context.Context, domain string, pem []byte) (bool, error) { logo := m.logos[domain] if logo == nil { return false, nil } logo.VmcPEM = pem return true, nil } func (m *memoryBimiStore) DeleteCertificate(_ context.Context, domain string) error { if logo := m.logos[domain]; logo != nil { logo.VmcPEM = nil } return nil } type fakeBimiDNS map[string][]string func (f fakeBimiDNS) LookupTXT(_ context.Context, name string) ([]string, error) { if records, ok := f[name]; ok { return records, nil } return nil, errors.New("no such host") } func fixture(t *testing.T, name string) []byte { t.Helper() data, err := os.ReadFile("../../domain/bimi/testdata/" + name) if err != nil { t.Fatal(err) } return data } // The certificates in the fixtures are valid on this date. var bimiTestNow = time.Date(2026, 10, 16, 12, 0, 0, 0, time.UTC) func newBimiRouter(store *memoryBimiStore) *Router { r := &Router{mux: http.NewServeMux()} r.bimi = &bimiAPI{ store: store, sessions: testVerifier(interopSecret), defaultDomain: "ebay.com", mailHost: "mail.ebay.com", now: func() time.Time { return bimiTestNow }, dns: fakeBimiDNS{}, fetch: func(context.Context, string) (bimi.Response, error) { return bimi.Response{}, errors.New("offline") }, } r.mux.HandleFunc("/.well-known/bimi/default.svg", r.handleBimiLogo) r.mux.HandleFunc("/.well-known/bimi/vmc.pem", r.handleBimiCertificate) r.mux.HandleFunc("/.well-known/bimi/", r.handleBimiPerDomain) r.mux.HandleFunc("/api/v1/admin/bimi", r.handleBimiAdmin) r.mux.HandleFunc("/api/v1/admin/bimi/certificate", r.handleBimiCertificateAdmin) r.mux.HandleFunc("/api/v1/admin/bimi/check", r.handleBimiCheck) return r } func bimiCall(r *Router, method, target, body string, authed bool) *httptest.ResponseRecorder { req := httptest.NewRequest(method, target, strings.NewReader(body)) if authed { req.Header.Set("Authorization", "Bearer "+adminSurfaceToken2()) } rec := httptest.NewRecorder() r.mux.ServeHTTP(rec, req) return rec } func jsonBody(t *testing.T, rec *httptest.ResponseRecorder) map[string]any { t.Helper() var out map[string]any if err := json.Unmarshal(rec.Body.Bytes(), &out); err != nil { t.Fatalf("not JSON (%d): %s", rec.Code, rec.Body) } return out } func putLogo(t *testing.T, r *Router, svg []byte) *httptest.ResponseRecorder { t.Helper() payload, _ := json.Marshal(map[string]string{"svg": string(svg)}) return bimiCall(r, http.MethodPut, "/api/v1/admin/bimi?domain=ebay.com", string(payload), true) } func putCertificate(r *Router, pem []byte) *httptest.ResponseRecorder { payload, _ := json.Marshal(map[string]string{"pem": string(pem)}) return bimiCall(r, http.MethodPut, "/api/v1/admin/bimi/certificate?domain=ebay.com", string(payload), true) } func TestBimiAdminRoutesNeedAnAdminSession(t *testing.T) { r := newBimiRouter(&memoryBimiStore{logos: map[string]*BimiLogo{}}) for _, target := range []string{ "/api/v1/admin/bimi?domain=ebay.com", "/api/v1/admin/bimi/certificate?domain=ebay.com", "/api/v1/admin/bimi/check?domain=ebay.com", } { for _, method := range []string{http.MethodGet, http.MethodPut, http.MethodDelete} { if rec := bimiCall(r, method, target, "{}", false); rec.Code != http.StatusUnauthorized { t.Errorf("%s %s without a session answered %d", method, target, rec.Code) } } } if rec := bimiCall(r, http.MethodGet, "/api/v1/admin/bimi/check", "", true); rec.Code != http.StatusBadRequest { t.Errorf("no domain answered %d", rec.Code) } } func TestBimiCertificateNeedsALogoFirst(t *testing.T) { r := newBimiRouter(&memoryBimiStore{logos: map[string]*BimiLogo{}}) rec := putCertificate(r, fixture(t, "ebay.pem")) if rec.Code != http.StatusConflict { t.Fatalf("answered %d: %s", rec.Code, rec.Body) } } func TestBimiAcceptsTheRealCertificateAndServesIt(t *testing.T) { store := &memoryBimiStore{logos: map[string]*BimiLogo{}} r := newBimiRouter(store) if rec := putLogo(t, r, fixture(t, "ebay.svg")); rec.Code != http.StatusOK { t.Fatalf("the real logo was refused: %s", rec.Body) } if rec := bimiCall(r, http.MethodGet, "/.well-known/bimi/vmc.pem", "", false); rec.Code != http.StatusNotFound { t.Errorf("before upload the public URL answered %d, want 404", rec.Code) } rec := putCertificate(r, fixture(t, "ebay.pem")) if rec.Code != http.StatusOK { t.Fatalf("the real certificate was refused: %s", rec.Body) } cert := jsonBody(t, rec)["certificate"].(map[string]any) if cert["valid"] != true || cert["matches_logo"] != true || cert["kind"] != "vmc" { t.Errorf("certificate report = %v", cert) } // The public document: no session, the PEM, cacheable, and conditional public := bimiCall(r, http.MethodGet, "/.well-known/bimi/vmc.pem", "", false) if public.Code != http.StatusOK || !strings.Contains(public.Body.String(), "BEGIN CERTIFICATE") { t.Fatalf("public answered %d", public.Code) } if got := public.Header().Get("Content-Type"); got != "application/x-pem-file" { t.Errorf("content type = %q", got) } if public.Header().Get("X-Content-Type-Options") != "nosniff" || !strings.Contains(public.Header().Get("Cache-Control"), "public") { t.Errorf("headers = %v", public.Header()) } conditional := httptest.NewRequest(http.MethodGet, "/.well-known/bimi/vmc.pem", nil) conditional.Header.Set("If-None-Match", public.Header().Get("ETag")) again := httptest.NewRecorder() r.mux.ServeHTTP(again, conditional) if again.Code != http.StatusNotModified { t.Errorf("conditional request answered %d", again.Code) } // The console reads it back, with the URLs the DNS record should carry got := jsonBody(t, bimiCall(r, http.MethodGet, "/api/v1/admin/bimi?domain=ebay.com", "", true)) if got["logo_url"] != "https://mail.ebay.com/.well-known/bimi/ebay.com/default.svg" || got["certificate_url"] != "https://mail.ebay.com/.well-known/bimi/ebay.com/vmc.pem" { t.Errorf("urls = %v / %v", got["logo_url"], got["certificate_url"]) } if got["certificate"].(map[string]any)["present"] != true { t.Errorf("certificate = %v", got["certificate"]) } // Removing it leaves the logo if rec := bimiCall(r, http.MethodDelete, "/api/v1/admin/bimi/certificate?domain=ebay.com", "", true); rec.Code != http.StatusOK { t.Fatalf("delete answered %d", rec.Code) } if rec := bimiCall(r, http.MethodGet, "/.well-known/bimi/vmc.pem", "", false); rec.Code != http.StatusNotFound { t.Errorf("after delete the public URL answered %d", rec.Code) } if rec := bimiCall(r, http.MethodGet, "/.well-known/bimi/default.svg", "", false); rec.Code != http.StatusOK { t.Errorf("the logo disappeared with the certificate: %d", rec.Code) } } func TestBimiRefusesACertificateForAnotherLogo(t *testing.T) { store := &memoryBimiStore{logos: map[string]*BimiLogo{}} r := newBimiRouter(store) // CNN's logo is valid SVG Tiny PS but is not the one eBay's certificate holds if rec := putLogo(t, r, fixture(t, "cnn.svg")); rec.Code != http.StatusOK { t.Fatalf("logo refused: %s", rec.Body) } rec := putCertificate(r, fixture(t, "ebay.pem")) if rec.Code != http.StatusBadRequest { t.Fatalf("answered %d: %s", rec.Code, rec.Body) } violations, _ := jsonBody(t, rec)["violations"].([]any) if len(violations) == 0 || !strings.Contains(violations[0].(string), "logoMismatch") { t.Errorf("violations = %v", violations) } if len(store.logos["ebay.com"].VmcPEM) != 0 { t.Error("a certificate that does not match was stored") } } func TestBimiNeverStoresAPrivateKey(t *testing.T) { store := &memoryBimiStore{logos: map[string]*BimiLogo{}} r := newBimiRouter(store) putLogo(t, r, fixture(t, "ebay.svg")) key := "-----BEGIN PRIVATE KEY-----\nQUJD\n-----END PRIVATE KEY-----\n" rec := putCertificate(r, append(fixture(t, "ebay.pem"), key...)) if rec.Code != http.StatusBadRequest { t.Fatalf("answered %d", rec.Code) } if len(store.logos["ebay.com"].VmcPEM) != 0 { t.Error("a private key was stored and would be served publicly") } if strings.Contains(rec.Body.String(), "QUJD") { t.Error("the refusal echoed the key") } } func TestBimiCertificateBodyMustBeJSON(t *testing.T) { r := newBimiRouter(&memoryBimiStore{logos: map[string]*BimiLogo{"ebay.com": {SVG: fixture(t, "ebay.svg")}}}) if rec := bimiCall(r, http.MethodPut, "/api/v1/admin/bimi/certificate?domain=ebay.com", "-----BEGIN CERTIFICATE-----", true); rec.Code != http.StatusBadRequest { t.Errorf("answered %d", rec.Code) } } func TestBimiReplacingTheLogoWarnsAboutTheHeldCertificate(t *testing.T) { store := &memoryBimiStore{logos: map[string]*BimiLogo{}} r := newBimiRouter(store) putLogo(t, r, fixture(t, "ebay.svg")) putCertificate(r, fixture(t, "ebay.pem")) // Another valid logo: the certificate no longer vouches for what is served rec := putLogo(t, r, fixture(t, "cnn.svg")) if rec.Code != http.StatusOK || jsonBody(t, rec)["certificate_mismatch"] != true { t.Fatalf("no warning: %d %s", rec.Code, rec.Body) } cert := jsonBody(t, bimiCall(r, http.MethodGet, "/api/v1/admin/bimi?domain=ebay.com", "", true))["certificate"].(map[string]any) if cert["valid"] != false || cert["matches_logo"] != false { t.Errorf("the console should show the certificate as no longer matching: %v", cert) } } func TestBimiCheckReportsEachStep(t *testing.T) { store := &memoryBimiStore{logos: map[string]*BimiLogo{}} r := newBimiRouter(store) putLogo(t, r, fixture(t, "ebay.svg")) rec := bimiCall(r, http.MethodGet, "/api/v1/admin/bimi/check?domain=ebay.com", "", true) body := jsonBody(t, rec) if rec.Code != http.StatusOK || body["ready"] != false { t.Fatalf("with nothing published it must not be ready: %d %v", rec.Code, body) } ids := map[string]string{} for _, c := range body["checks"].([]any) { m := c.(map[string]any) ids[m["id"].(string)] = m["status"].(string) } if ids["logo"] != "ok" || ids["dmarc"] != "fail" || ids["record"] != "fail" { t.Errorf("checks = %v", ids) } none := bimiCall(r, http.MethodGet, "/api/v1/admin/bimi/check?domain=unknown.test", "", true) if jsonBody(t, none)["configured"] != false { t.Errorf("a domain with no logo: %s", none.Body) } } func TestPublicAddress(t *testing.T) { for address, want := range map[string]bool{ "8.8.8.8": true, "1.1.1.1": true, "2001:4860::8888": true, "127.0.0.1": false, "10.0.0.5": false, "172.16.3.4": false, "192.168.1.1": false, "169.254.169.254": false, "100.64.0.1": false, "0.0.0.0": false, "::1": false, "fe80::1": false, "fc00::1": false, "224.0.0.1": false, } { if got := publicAddress(net.ParseIP(address)); got != want { t.Errorf("publicAddress(%s) = %v, want %v", address, got, want) } } } // The fetcher is what keeps the console's checks from reaching this server's // own network. It refuses a literal private address before any request leaves. func TestFetcherRefusesPrivateAndPlainHTTP(t *testing.T) { fetch := newBimiFetcher() for _, url := range []string{"http://example.test/logo.svg", "https://127.0.0.1/logo.svg", "https://169.254.169.254/latest/meta-data/", "https://[::1]/x"} { if _, err := fetch(context.Background(), url); err == nil { t.Errorf("%s was fetched", url) } } } // publishRecorder collects the domains the console asked to have published. type publishRecorder struct{ domains chan string } func newPublishRecorder() *publishRecorder { return &publishRecorder{domains: make(chan string, 8)} } func (p *publishRecorder) record(domain string) { p.domains <- domain } func (p *publishRecorder) expect(t *testing.T, what string) { t.Helper() select { case got := <-p.domains: if got != "ebay.com" { t.Errorf("%s published %q", what, got) } case <-time.After(2 * time.Second): t.Fatalf("%s did not ask for the DNS record to be published", what) } } func (p *publishRecorder) expectNothing(t *testing.T, what string) { t.Helper() select { case got := <-p.domains: t.Fatalf("%s published %q though it changed nothing", what, got) case <-time.After(150 * time.Millisecond): } } // The record in the zone follows the picture: every change to the logo or the // certificate asks for the DNS to be reconciled, and nothing else does. func TestBimiChangesAreHandedToThePublisher(t *testing.T) { store := &memoryBimiStore{logos: map[string]*BimiLogo{}} r := newBimiRouter(store) rec := newPublishRecorder() r.bimi.publish = rec.record putLogo(t, r, fixture(t, "ebay.svg")) rec.expect(t, "saving the logo") putCertificate(r, fixture(t, "ebay.pem")) rec.expect(t, "saving the certificate") // Reads and refused uploads change nothing and publish nothing bimiCall(r, http.MethodGet, "/api/v1/admin/bimi?domain=ebay.com", "", true) putCertificate(r, []byte("-----BEGIN PRIVATE KEY-----\nQUJD\n-----END PRIVATE KEY-----\n")) putLogo(t, r, []byte("")) rec.expectNothing(t, "a read or a refused upload") bimiCall(r, http.MethodDelete, "/api/v1/admin/bimi/certificate?domain=ebay.com", "", true) rec.expect(t, "removing the certificate") bimiCall(r, http.MethodDelete, "/api/v1/admin/bimi?domain=ebay.com", "", true) rec.expect(t, "removing the logo") } func TestBimiWithoutAPublisherStillWorks(t *testing.T) { r := newBimiRouter(&memoryBimiStore{logos: map[string]*BimiLogo{}}) resp := jsonBody(t, putLogo(t, r, fixture(t, "ebay.svg"))) if resp["ok"] != true || resp["dns_publishing"] != false { t.Fatalf("%v", resp) } } // The check expects what the reconciler writes: with a certificate that no // longer matches the logo that is a record without a=, and that is correct. func TestBimiCheckExpectsWhatTheReconcilerPublishes(t *testing.T) { store := &memoryBimiStore{logos: map[string]*BimiLogo{}} r := newBimiRouter(store) putLogo(t, r, fixture(t, "cnn.svg")) store.logos["ebay.com"].VmcPEM = fixture(t, "ebay.pem") // a certificate for another picture r.bimi.dns = fakeBimiDNS{ "_dmarc.ebay.com": {"v=DMARC1; p=reject"}, "default._bimi.ebay.com": {"v=BIMI1; l=https://mail.ebay.com/.well-known/bimi/ebay.com/default.svg;"}, } body := jsonBody(t, bimiCall(r, http.MethodGet, "/api/v1/admin/bimi/check?domain=ebay.com", "", true)) status := map[string]string{} detail := map[string]string{} for _, c := range body["checks"].([]any) { m := c.(map[string]any) status[m["id"].(string)] = m["status"].(string) detail[m["id"].(string)] = m["detail"].(string) } if status["record"] != "ok" { t.Errorf("the record without a= is what the reconciler writes here, but the check said %s: %s", status["record"], detail["record"]) } if status["certificate"] != "fail" || !strings.Contains(detail["certificate"], "NOT published") { t.Errorf("the certificate check must say it is held back: %s %s", status["certificate"], detail["certificate"]) } } // The console shows the record the reconciler writes, taken from the server // rather than put together again in the browser. func TestBimiGetReportsTheRecordItWouldPublish(t *testing.T) { store := &memoryBimiStore{logos: map[string]*BimiLogo{}} r := newBimiRouter(store) putLogo(t, r, fixture(t, "ebay.svg")) read := func() map[string]any { return jsonBody(t, bimiCall(r, http.MethodGet, "/api/v1/admin/bimi?domain=ebay.com", "", true)) } got := read() if got["dns_record"] != "v=BIMI1; l=https://mail.ebay.com/.well-known/bimi/ebay.com/default.svg;" || got["certificate_withheld"] != "" { t.Fatalf("logo only: %v", got) } putCertificate(r, fixture(t, "ebay.pem")) if got := read(); got["dns_record"] != "v=BIMI1; l=https://mail.ebay.com/.well-known/bimi/ebay.com/default.svg; a=https://mail.ebay.com/.well-known/bimi/ebay.com/vmc.pem;" { t.Fatalf("with its certificate: %v", got["dns_record"]) } // The logo is replaced: the certificate is held but no longer advertised putLogo(t, r, fixture(t, "cnn.svg")) got = read() if got["certificate_withheld"] != "logoMismatch" || strings.Contains(got["dns_record"].(string), "a=") { t.Fatalf("after replacing the logo: %v / %v", got["dns_record"], got["certificate_withheld"]) } } // The bug this pins: every domain's record named the deployment's own mail host, // and the host chose the domain, so only the primary domain's logo was ever // found. A second domain's logo has to be served under its own name. func TestBimiServesEachDomainsLogoUnderItsOwnName(t *testing.T) { store := &memoryBimiStore{logos: map[string]*BimiLogo{ "ebay.com": {SVG: fixture(t, "ebay.svg"), ETag: "e1"}, "cnn.com": {SVG: fixture(t, "cnn.svg"), ETag: "c1", VmcPEM: fixture(t, "ebay.pem")}, }} r := newBimiRouter(store) // its default domain, and the host these requests arrive on, is ebay.com logo := bimiCall(r, http.MethodGet, "/.well-known/bimi/cnn.com/default.svg", "", false) if logo.Code != http.StatusOK || logo.Body.String() != string(fixture(t, "cnn.svg")) { t.Fatalf("the second domain's logo: %d", logo.Code) } if got := logo.Header().Get("Content-Type"); got != "image/svg+xml" { t.Errorf("content type = %q", got) } if rec := bimiCall(r, http.MethodGet, "/.well-known/bimi/cnn.com/vmc.pem", "", false); rec.Code != http.StatusOK || !strings.Contains(rec.Body.String(), "BEGIN CERTIFICATE") { t.Errorf("the second domain's certificate: %d", rec.Code) } // Case does not matter in a host name if rec := bimiCall(r, http.MethodGet, "/.well-known/bimi/CNN.com/default.svg", "", false); rec.Code != http.StatusOK { t.Errorf("upper case: %d", rec.Code) } // The legacy path still answers with the host's own domain if rec := bimiCall(r, http.MethodGet, "/.well-known/bimi/default.svg", "", false); rec.Code != http.StatusOK || rec.Body.String() != string(fixture(t, "ebay.svg")) { t.Errorf("legacy path: %d", rec.Code) } } func TestBimiPerDomainPathsRefuseWhatIsNotADomainLogo(t *testing.T) { store := &memoryBimiStore{logos: map[string]*BimiLogo{"ebay.com": {SVG: fixture(t, "ebay.svg"), ETag: "e1"}}} r := newBimiRouter(store) for _, path := range []string{ "/.well-known/bimi/nobody.test/default.svg", // a domain with no logo "/.well-known/bimi/ebay.com/other.svg", // not a document we serve "/.well-known/bimi/ebay.com/", // no document "/.well-known/bimi/ebay.com/x/default.svg", // extra segment "/.well-known/bimi/ebay.com/default.svg/x", "/.well-known/bimi/-bad.com/default.svg", // not a host name "/.well-known/bimi/a_b.com/default.svg", "/.well-known/bimi/localhost/default.svg", // a single label is not a mail domain "/.well-known/bimi/%2e%2e/default.svg", } { if rec := bimiCall(r, http.MethodGet, path, "", false); rec.Code != http.StatusNotFound { t.Errorf("%s answered %d, want 404", path, rec.Code) } } if rec := bimiCall(r, http.MethodPost, "/.well-known/bimi/ebay.com/default.svg", "", false); rec.Code != http.StatusMethodNotAllowed { t.Errorf("POST answered %d", rec.Code) } } // The wiring bug: the publisher was handed over before the BIMI API existed and // was dropped. Either order has to work. func TestBimiPublisherSurvivesTheOrderItIsWiredIn(t *testing.T) { store := &memoryBimiStore{logos: map[string]*BimiLogo{}} publisher := func(string) {} before := &Router{mux: http.NewServeMux()} before.SetBimiPublisher(publisher) before.SetBimiAPI(store, "secret", "ebay.com", "mail.ebay.com") if before.bimi.publish == nil { t.Error("a publisher set before the API was lost") } after := &Router{mux: http.NewServeMux()} after.SetBimiAPI(store, "secret", "ebay.com", "mail.ebay.com") after.SetBimiPublisher(publisher) if after.bimi.publish == nil { t.Error("a publisher set after the API was lost") } none := &Router{mux: http.NewServeMux()} none.SetBimiAPI(store, "secret", "ebay.com", "mail.ebay.com") if none.bimi.publish != nil { t.Error("with no DNS provider there is nothing to publish to") } }