| 1 | package mail |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | |
| 7 | "github.com/carlosframework/platform/internal/dnsrec" |
| 8 | ) |
| 9 | |
| 10 | // The MAIL FROM subdomain carries its region from the FIRST domain ever |
| 11 | // verified. For a custom domain this record lives in the CUSTOMER's zone, |
| 12 | // so it is the one thing we cannot republish for them — and the MX target |
| 13 | // is region-specific, so an unqualified "mail." could never hold a second |
| 14 | // region without ambiguity (spec §7, §8). |
| 15 | func TestMailFromDomainCarriesTheRegion(t *testing.T) { |
| 16 | got := MailFromDomain("harbourmail.bag.oncarlos.com", "eu-west-1") |
| 17 | want := "mail-eu-west-1.harbourmail.bag.oncarlos.com" |
| 18 | if got != want { |
| 19 | t.Fatalf("MailFromDomain = %q, want %q", got, want) |
| 20 | } |
| 21 | if MailFromDomain("x.com", "eu-west-1") == MailFromDomain("x.com", "us-east-1") { |
| 22 | t.Fatal("two regions collided on one MAIL FROM subdomain") |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | // Nothing is EVER written at the app host itself. The edge owns an A record |
| 27 | // there, and an email record sharing that name could fight it — this is a |
| 28 | // permanent invariant, not an implementation detail (spec §7). |
| 29 | func TestNoRecordSitsAtTheDomainApex(t *testing.T) { |
| 30 | const domain = "harbourmail.bag.oncarlos.com" |
| 31 | for _, r := range RecordSet(domain, "eu-west-1", []string{"t1", "t2", "t3"}, "") { |
| 32 | if r.Name == domain { |
| 33 | t.Fatalf("record %s %s sits at the apex — it would fight the edge's A record", r.Type, r.Name) |
| 34 | } |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | func TestRecordSetGolden(t *testing.T) { |
| 39 | got := RecordSet("harbourmail.bag.oncarlos.com", "eu-west-1", |
| 40 | []string{"tok1", "tok2", "tok3"}, "") |
| 41 | want := []dnsrec.Record{ |
| 42 | {Name: "tok1._domainkey.harbourmail.bag.oncarlos.com", Type: dnsrec.TypeCNAME, |
| 43 | Values: []string{"tok1.dkim.amazonses.com"}, TTL: 300}, |
| 44 | {Name: "tok2._domainkey.harbourmail.bag.oncarlos.com", Type: dnsrec.TypeCNAME, |
| 45 | Values: []string{"tok2.dkim.amazonses.com"}, TTL: 300}, |
| 46 | {Name: "tok3._domainkey.harbourmail.bag.oncarlos.com", Type: dnsrec.TypeCNAME, |
| 47 | Values: []string{"tok3.dkim.amazonses.com"}, TTL: 300}, |
| 48 | {Name: "mail-eu-west-1.harbourmail.bag.oncarlos.com", Type: dnsrec.TypeMX, |
| 49 | Values: []string{"10 feedback-smtp.eu-west-1.amazonses.com"}, TTL: 300}, |
| 50 | {Name: "mail-eu-west-1.harbourmail.bag.oncarlos.com", Type: dnsrec.TypeTXT, |
| 51 | Values: []string{"v=spf1 include:amazonses.com ~all"}, TTL: 300}, |
| 52 | {Name: "_dmarc.harbourmail.bag.oncarlos.com", Type: dnsrec.TypeTXT, |
| 53 | Values: []string{"v=DMARC1; p=none; adkim=s; aspf=s"}, TTL: 300}, |
| 54 | } |
| 55 | if len(got) != len(want) { |
| 56 | t.Fatalf("got %d records, want %d:\n%+v", len(got), len(want), got) |
| 57 | } |
| 58 | for i := range want { |
| 59 | if got[i].Name != want[i].Name || got[i].Type != want[i].Type || |
| 60 | got[i].TTL != want[i].TTL || len(got[i].Values) != len(want[i].Values) || |
| 61 | got[i].Values[0] != want[i].Values[0] { |
| 62 | t.Fatalf("record %d:\n got %+v\nwant %+v", i, got[i], want[i]) |
| 63 | } |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | // adkim=s is the TENANT BOUNDARY on a shared registrable domain, not |
| 68 | // hygiene. DMARC's default relaxed alignment compares ORGANIZATIONAL |
| 69 | // domains, so without it tenant bag's DKIM signature authenticates a From |
| 70 | // address at bae.oncarlos.com (spec §7). |
| 71 | func TestDMARCIsStrictlyAligned(t *testing.T) { |
| 72 | var dmarc string |
| 73 | for _, r := range RecordSet("x.bag.oncarlos.com", "eu-west-1", []string{"t"}, "") { |
| 74 | if r.Name == "_dmarc.x.bag.oncarlos.com" { |
| 75 | dmarc = r.Values[0] |
| 76 | } |
| 77 | } |
| 78 | if dmarc == "" { |
| 79 | t.Fatal("no _dmarc record emitted") |
| 80 | } |
| 81 | for _, want := range []string{"adkim=s", "aspf=s"} { |
| 82 | if !contains(dmarc, want) { |
| 83 | t.Fatalf("DMARC %q is missing %q — tenants are not isolated", dmarc, want) |
| 84 | } |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | // rua is CONFIGURATION. Unset means publish no rua at all: a reporting |
| 89 | // address is only honoured by receivers when the destination authorises it |
| 90 | // (RFC 7489 §7.1), so emitting one by default would publish a record that |
| 91 | // looks right and yields nothing. |
| 92 | func TestRUAIsOmittedWhenUnset(t *testing.T) { |
| 93 | for _, r := range RecordSet("x.com", "eu-west-1", []string{"t"}, "") { |
| 94 | if r.Name == "_dmarc.x.com" && contains(r.Values[0], "rua=") { |
| 95 | t.Fatalf("unset rua still emitted: %q", r.Values[0]) |
| 96 | } |
| 97 | } |
| 98 | for _, r := range RecordSet("x.com", "eu-west-1", []string{"t"}, "reports@cribado.report") { |
| 99 | if r.Name == "_dmarc.x.com" { |
| 100 | if !contains(r.Values[0], "rua=mailto:reports@cribado.report") { |
| 101 | t.Fatalf("rua not rendered: %q", r.Values[0]) |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | func contains(h, n string) bool { return len(n) > 0 && len(h) >= len(n) && strings.Contains(h, n) } |
| 108 | |