panel: show the SPF and DMARC records the domain page expects
The domain page generated and displayed the DKIM record but said only "also configure SPF and DMARC for the domain (see the documentation)". The concrete example existed — buried in the check's remediation text, and only visible once the check had already failed. Show both records up front, host and value with a Copy button, the way the DKIM record is shown, plus the two things that actually bite: a domain may carry only one SPF record (add the mechanism to the existing one rather than publishing a second), and p=none is safe to publish immediately. The SPF value names the addresses SELFPOST_HOSTNAME resolves to, taken from the hostname check the page already runs, and falls back to an "a:" mechanism when it does not resolve. New dnscheck.SPFExample/DMARCExample are the single source for both the page and the checks' advice, so the two cannot drift into recommending different records; dnscheck.Query gains Hostname for the fallback. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
package dnscheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"codeberg.org/mix/selfpost/internal/health"
|
||||
)
|
||||
|
||||
func TestSPFExample(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
hostname string
|
||||
ips []string
|
||||
want string
|
||||
}{
|
||||
{"ipv4", "mail.example.com", []string{"203.0.113.10"}, "v=spf1 ip4:203.0.113.10 -all"},
|
||||
{"both families", "mail.example.com", []string{"203.0.113.10", "2001:db8::1"},
|
||||
"v=spf1 ip4:203.0.113.10 ip6:2001:db8::1 -all"},
|
||||
// The hostname does not resolve, so there is no address to name; an "a:"
|
||||
// mechanism still gives the operator a publishable record.
|
||||
{"no addresses", "mail.example.com", nil, "v=spf1 a:mail.example.com -all"},
|
||||
{"unparsable addresses", "mail.example.com", []string{"not-an-ip"}, "v=spf1 a:mail.example.com -all"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
if got := SPFExample(c.hostname, c.ips); got != c.want {
|
||||
t.Errorf("SPFExample = %q, want %q", got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// The record the panel shows and the one a failed check suggests must be the
|
||||
// same string, or the operator is told two different things on one page.
|
||||
func TestMissingRecordChecksSuggestTheShownExample(t *testing.T) {
|
||||
f := &fakeResolver{}
|
||||
c := newTestChecker(f)
|
||||
|
||||
spf := c.checkSPF(context.Background(), Query{
|
||||
Name: "example.com",
|
||||
Hostname: "mail.example.com",
|
||||
ServerIPs: []string{"203.0.113.10"},
|
||||
})
|
||||
if spf.Status != health.StatusError {
|
||||
t.Fatalf("SPF status = %q, want error (%s)", spf.Status, spf.Detail)
|
||||
}
|
||||
if want := SPFExample("mail.example.com", []string{"203.0.113.10"}); !strings.Contains(spf.Detail, want) {
|
||||
t.Errorf("SPF advice %q does not suggest %q", spf.Detail, want)
|
||||
}
|
||||
|
||||
dmarc := c.checkDMARC(context.Background(), "example.com")
|
||||
if dmarc.Status != health.StatusWarn {
|
||||
t.Fatalf("DMARC status = %q, want warn (%s)", dmarc.Status, dmarc.Detail)
|
||||
}
|
||||
if want := DMARCExample("example.com"); !strings.Contains(dmarc.Detail, want) {
|
||||
t.Errorf("DMARC advice %q does not suggest %q", dmarc.Detail, want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user