Skip to content

Commit 8c7a080

Browse files
committed
🐛(dns) relax DNS record whitespace checks
1 parent b63d347 commit 8c7a080

File tree

2 files changed

+69
-17
lines changed

2 files changed

+69
-17
lines changed

src/backend/core/services/dns/check.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@
22
DNS checking functionality for mail domains.
33
"""
44

5+
import re
56
from typing import Dict, List
67

78
from core.models import MailDomain
89

910
import dns.resolver
1011

1112

13+
def normalize_txt_value(value: str) -> str:
14+
"""
15+
Normalize a TXT record value.
16+
"""
17+
return re.sub(r"\s*\;\s*", ";", value.strip('"'))
18+
19+
1220
def check_single_record(
1321
maildomain: MailDomain, expected_record: Dict[str, any]
1422
) -> Dict[str, any]:
@@ -46,7 +54,10 @@ def check_single_record(
4654
answer.to_text().strip('"').replace('" "', "") for answer in answers
4755
]
4856
else:
49-
found_values = [answer.to_text().strip('"') for answer in answers]
57+
found_values = [
58+
normalize_txt_value(answer.to_text()) for answer in answers
59+
]
60+
expected_value = normalize_txt_value(expected_value)
5061
else:
5162
# For other record types, try to resolve them as-is
5263
answers = dns.resolver.resolve(query_name, record_type)

src/backend/core/tests/dns/test_check.py

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -213,31 +213,72 @@ def test_check_dns_records_multiple_records(self, maildomain_factory):
213213
"target": "@",
214214
"value": "v=spf1 include:_spf.example.com -all",
215215
},
216+
{
217+
"type": "TXT",
218+
"target": "_dmarc",
219+
"value": "v=DMARC1; p=reject; adkim=s; aspf=s;",
220+
},
221+
{
222+
"type": "TXT",
223+
"target": "_dmarc_stripped",
224+
"value": "v=DMARC1;p=reject;adkim=s;aspf=s; ",
225+
},
226+
{
227+
"type": "TXT",
228+
"target": "_dmarc_missing",
229+
"value": "v=DMARC1;p=reject;adkim=s;aspf=s; ",
230+
},
216231
]
217232

218233
with patch("core.services.dns.check.dns.resolver.resolve") as mock_resolve:
219-
# Mock responses for both records
220-
mock_mx_answer = MagicMock()
221-
mock_mx_answer.preference = 10
222-
mock_mx_answer.exchange = "mx1.example.com"
223234

224-
mock_txt_answer = MagicMock()
225-
mock_txt_answer.to_text.return_value = (
226-
'"v=spf1 include:_spf.example.com -all"'
227-
)
228-
229-
mock_resolve.side_effect = [
230-
[mock_mx_answer], # MX record response
231-
[mock_txt_answer], # TXT record response
232-
]
235+
def resolve_side_effect(name, record_type):
236+
if name == "_dmarc_missing.example.com":
237+
raise NoAnswer()
238+
239+
if record_type == "MX":
240+
mock_mx_answer = MagicMock()
241+
mock_mx_answer.preference = 10
242+
mock_mx_answer.exchange = "mx1.example.com"
243+
return [mock_mx_answer]
244+
245+
if record_type == "TXT" and name == "@.example.com":
246+
mock_txt_answer = MagicMock()
247+
mock_txt_answer.to_text.return_value = (
248+
'"v=spf1 include:_spf.example.com -all"'
249+
)
250+
garbage = MagicMock()
251+
garbage.to_text.return_value = "some-garbage"
252+
return [garbage, mock_txt_answer, garbage]
253+
254+
if (
255+
record_type == "TXT"
256+
and name == "_dmarc.example.com"
257+
or name == "_dmarc_stripped.example.com"
258+
):
259+
mock_txt_dmarc_answer = MagicMock()
260+
mock_txt_dmarc_answer.to_text.return_value = (
261+
'"v=DMARC1; p=reject; adkim=s; aspf=s;"'
262+
)
263+
return [mock_txt_dmarc_answer]
264+
265+
return []
266+
267+
mock_resolve.side_effect = resolve_side_effect
233268

234269
results = check_dns_records(maildomain)
235270

236-
assert len(results) == 2
271+
assert len(results) == 5
237272
assert results[0]["type"] == "MX"
238-
assert results[0]["_check"]["status"] == "correct"
273+
assert results[0]["_check"]["status"] == "correct", results[0]
239274
assert results[1]["type"] == "TXT"
240-
assert results[1]["_check"]["status"] == "correct"
275+
assert results[1]["_check"]["status"] == "correct", results[1]
276+
assert results[2]["type"] == "TXT"
277+
assert results[2]["_check"]["status"] == "correct", results[2]
278+
assert results[3]["type"] == "TXT"
279+
assert results[3]["_check"]["status"] == "correct", results[3]
280+
assert results[4]["type"] == "TXT"
281+
assert results[4]["_check"]["status"] == "missing"
241282

242283
def test_check_dns_records_mixed_status(self, maildomain_factory):
243284
"""Test checking DNS records with mixed status (correct, incorrect, missing)."""

0 commit comments

Comments
 (0)