From b50972f38debbe499695156c68c6ee8091340dec Mon Sep 17 00:00:00 2001 From: Vassiliy Yegorov Date: Fri, 3 Jul 2026 12:57:24 +0700 Subject: [PATCH] =?UTF-8?q?fix(model):=20TXT=20=D1=81=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BD=D0=B8=D0=B2=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=B1=D0=B0?= =?UTF-8?q?=D0=B9=D1=82-=D1=82=D0=BE=D1=87=D0=BD=D0=BE=20(=D0=B1=D0=B5?= =?UTF-8?q?=D0=B7=20=D1=81=D1=85=D0=BB=D0=BE=D0=BF=D1=8B=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B1=D0=B5=D0=BB=D0=BE=D0=B2?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/model/record.go | 5 +++-- internal/model/record_test.go | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) 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"}}