package bimi import ( "context" "errors" "strings" "testing" ) type fakeDNS map[string][]string func (f fakeDNS) LookupTXT(_ context.Context, name string) ([]string, error) { records, ok := f[name] if !ok { return nil, errors.New("no such host") } return records, nil } func fakeFetch(files map[string]Response) Fetcher { return func(_ context.Context, url string) (Response, error) { resp, ok := files[url] if !ok { return Response{}, errors.New("connection refused") } return resp, nil } } func byID(checks []Check) map[string]Check { out := map[string]Check{} for _, c := range checks { out[c.ID] = c } return out } const ( logoURL = "https://mail.ebay.com/.well-known/bimi/default.svg" vmcURL = "https://mail.ebay.com/.well-known/bimi/vmc.pem" ) // goodSVG is a logo the validator accepts. const goodSVG = `Example` func TestRunChecksAllGoodWithTheRealCertificate(t *testing.T) { svg := read(t, "ebay.svg") pemData := read(t, "ebay.pem") in := Inputs{Domain: "ebay.com", LogoURL: logoURL, CertificateURL: vmcURL, SVG: svg, CertificatePEM: pemData, Now: fixtureNow} dns := fakeDNS{ "_dmarc.ebay.com": {"v=DMARC1; p=reject; pct=100; rua=mailto:x@ebay.com"}, "default._bimi.ebay.com": {"v=BIMI1; l=" + logoURL + "; a=" + vmcURL + ";"}, } fetch := fakeFetch(map[string]Response{ logoURL: {Status: 200, ContentType: "image/svg+xml", Body: svg}, vmcURL: {Status: 200, ContentType: "application/x-pem-file", Body: pemData}, }) got := byID(RunChecks(context.Background(), in, dns, fetch)) for _, id := range []string{"logo", "dmarc", "record", "logoUrl", "certificate", "certificateUrl"} { if got[id].Status != StatusOK { t.Errorf("%s = %s (%s)", id, got[id].Status, got[id].Detail) } } } func TestRunChecksDMARC(t *testing.T) { cases := []struct { record string want Status }{ {"v=DMARC1; p=none", StatusFail}, {"v=DMARC1; p=quarantine", StatusOK}, {"v=DMARC1; p=reject; pct=100", StatusOK}, {"v=DMARC1; p=reject; pct=50", StatusWarn}, {"v=spf1 -all", StatusFail}, } for _, c := range cases { got := checkDMARC(context.Background(), "example.test", fakeDNS{"_dmarc.example.test": {c.record}}) if got.Status != c.want { t.Errorf("%q: %s (%s), want %s", c.record, got.Status, got.Detail, c.want) } } if got := checkDMARC(context.Background(), "example.test", fakeDNS{}); got.Status != StatusFail { t.Errorf("missing record: %s", got.Status) } } func TestRunChecksRecordMismatchesAreSpelledOut(t *testing.T) { in := Inputs{Domain: "example.test", LogoURL: "https://mail.example.test/.well-known/bimi/default.svg", CertificateURL: "https://mail.example.test/.well-known/bimi/vmc.pem"} missing := checkRecord(context.Background(), in, fakeDNS{}) if missing.Status != StatusFail || !strings.Contains(missing.Detail, "default._bimi.example.test") { t.Errorf("missing: %+v", missing) } wrong := checkRecord(context.Background(), in, fakeDNS{"default._bimi.example.test": {"v=BIMI1; l=https://elsewhere.test/logo.svg;"}}) if wrong.Status != StatusFail || !strings.Contains(wrong.Detail, "l=") || !strings.Contains(wrong.Detail, "a=") { t.Errorf("wrong: %+v", wrong) } // Without a certificate here, an a= in DNS is a fault too: it points nowhere noCert := Inputs{Domain: "example.test", LogoURL: in.LogoURL} stray := checkRecord(context.Background(), noCert, fakeDNS{"default._bimi.example.test": {"v=BIMI1; l=" + in.LogoURL + "; a=https://old.test/x.pem"}}) if stray.Status != StatusFail { t.Errorf("stray a=: %+v", stray) } } func TestRunChecksLogoURL(t *testing.T) { in := Inputs{Domain: "example.test", LogoURL: logoURL, SVG: []byte(goodSVG)} cases := map[string]struct { resp Response err bool want Status }{ "ok": {Response{Status: 200, ContentType: "image/svg+xml; charset=utf-8", Body: []byte(goodSVG)}, false, StatusOK}, "not found": {Response{Status: 404}, false, StatusFail}, "wrong type": {Response{Status: 200, ContentType: "text/html", Body: []byte(goodSVG)}, false, StatusFail}, "different bytes": {Response{Status: 200, ContentType: "image/svg+xml", Body: []byte("")}, false, StatusFail}, "unreachable": {Response{}, true, StatusFail}, } for name, c := range cases { files := map[string]Response{} if !c.err { files[logoURL] = c.resp } if got := checkLogoURL(context.Background(), in, fakeFetch(files)); got.Status != c.want { t.Errorf("%s: %s (%s), want %s", name, got.Status, got.Detail, c.want) } } } func TestRunChecksCertificate(t *testing.T) { svg := read(t, "ebay.svg") pemData := read(t, "ebay.pem") base := Inputs{Domain: "ebay.com", LogoURL: logoURL, SVG: svg, Now: fixtureNow} t.Run("none is a warning, with the reason", func(t *testing.T) { got := byID(checkCertificate(context.Background(), base, fakeFetch(nil)))["certificate"] if got.Status != StatusWarn || !strings.Contains(got.Detail, "Gmail") { t.Errorf("%+v", got) } }) t.Run("hosted but the public URL serves something else", func(t *testing.T) { in := base in.CertificatePEM, in.CertificateURL = pemData, vmcURL got := byID(checkCertificate(context.Background(), in, fakeFetch(map[string]Response{vmcURL: {Status: 200, Body: []byte("other")}}))) if got["certificate"].Status != StatusOK || got["certificateUrl"].Status != StatusFail { t.Errorf("%+v", got) } }) t.Run("hosted for a logo that changed since", func(t *testing.T) { in := base in.CertificatePEM, in.CertificateURL, in.SVG = pemData, vmcURL, []byte(goodSVG) got := byID(checkCertificate(context.Background(), in, fakeFetch(map[string]Response{vmcURL: {Status: 200, Body: pemData}}))) if got["certificate"].Status != StatusFail || !strings.Contains(got["certificate"].Detail, "logoMismatch") { t.Errorf("%+v", got["certificate"]) } }) t.Run("kept elsewhere, fetched and inspected", func(t *testing.T) { in := base in.ExternalCertificateURL = "https://vmc.example.test/x.pem" got := byID(checkCertificate(context.Background(), in, fakeFetch(map[string]Response{in.ExternalCertificateURL: {Status: 200, Body: pemData}}))) if got["certificate"].Status != StatusOK { t.Errorf("%+v", got["certificate"]) } bad := byID(checkCertificate(context.Background(), in, fakeFetch(nil))) if bad["certificate"].Status != StatusFail { t.Errorf("unreachable external certificate: %+v", bad["certificate"]) } }) }