diff --git a/internal/model/record.go b/internal/model/record.go index b419c5f..8aa2d0b 100644 --- a/internal/model/record.go +++ b/internal/model/record.go @@ -54,10 +54,11 @@ func normalizeName(name string) string { // normalizeValue canonicalizes a single RR value for comparison. func normalizeValue(t RecordType, content string) string { + if t == TXT { + return content // byte-exact — case and whitespace are significant (DKIM/SPF/DMARC) + } c := strings.Join(strings.Fields(content), " ") // collapse whitespace switch t { - case TXT: - return c // case-sensitive — keep as is case MX: parts := strings.SplitN(c, " ", 2) if len(parts) == 2 { diff --git a/internal/model/record_test.go b/internal/model/record_test.go index 3e9ff8f..4df2f43 100644 --- a/internal/model/record_test.go +++ b/internal/model/record_test.go @@ -99,6 +99,14 @@ func TestEqualTXTCaseSensitive(t *testing.T) { } } +func TestEqualTXTWhitespaceSignificant(t *testing.T) { + a := Record{Type: TXT, Name: "example.com.", TTL: 60, Values: []string{"v=spf1 a"}} + b := Record{Type: TXT, Name: "example.com.", TTL: 60, Values: []string{"v=spf1 a"}} + if a.Equal(b) { + t.Fatal("TXT records differing only in whitespace count must not be equal (byte-exact comparison)") + } +} + func TestEqualTTLMatters(t *testing.T) { a := Record{Type: A, Name: "example.com.", TTL: 300, Values: []string{"1.2.3.4"}} b := Record{Type: A, Name: "example.com.", TTL: 600, Values: []string{"1.2.3.4"}}