summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Sapiro2017-01-01 19:22:07 -0800
committerMark Sapiro2017-01-01 19:22:07 -0800
commitd233dd419fabd4b42d5b83b920ec7ee54fee57da (patch)
treeee58e594431e792b4caf229349c32a275123513f
parent6b06535a572f0457844ef262f133a3b04145ef3f (diff)
downloadmailman-d233dd419fabd4b42d5b83b920ec7ee54fee57da.tar.gz
mailman-d233dd419fabd4b42d5b83b920ec7ee54fee57da.tar.zst
mailman-d233dd419fabd4b42d5b83b920ec7ee54fee57da.zip
-rw-r--r--src/mailman/rules/dmarc.py6
-rw-r--r--src/mailman/rules/tests/test_dmarc.py187
2 files changed, 177 insertions, 16 deletions
diff --git a/src/mailman/rules/dmarc.py b/src/mailman/rules/dmarc.py
index 43bbfc609..92db64ccd 100644
--- a/src/mailman/rules/dmarc.py
+++ b/src/mailman/rules/dmarc.py
@@ -149,10 +149,8 @@ def _DMARCProhibited(mlist, email, dmarc_domain, org=False):
seen.add(cnames[item])
want_names.add(cnames[item])
want_names.discard(item)
- if len(want_names) != 1:
- elog.error(
- """multiple DMARC entries in results for %s,
- processing each to be strict""",
+ assert len(want_names) == 1, """\
+ Error in CNAME processing for {}; want_names != 1.""".format(
dmarc_domain)
for name in want_names:
if name not in results_by_name:
diff --git a/src/mailman/rules/tests/test_dmarc.py b/src/mailman/rules/tests/test_dmarc.py
index 640dd5cda..8d2aa7061 100644
--- a/src/mailman/rules/tests/test_dmarc.py
+++ b/src/mailman/rules/tests/test_dmarc.py
@@ -20,7 +20,7 @@ organizational domain tests."""
from contextlib import ExitStack
from dns.exception import DNSException
-from dns.rdatatype import TXT
+from dns.rdatatype import CNAME, TXT
from dns.resolver import NXDOMAIN, NoAnswer
from mailman.app.lifecycle import create_list
from mailman.interfaces.mailinglist import DMARCMitigateAction
@@ -37,7 +37,12 @@ from urllib.error import URLError
@public
-def get_dns_resolver():
+def get_dns_resolver(
+ rtype=TXT,
+ rdata=b'v=DMARC1; p=reject;',
+ rmult=False,
+ cmult=False,
+ cloop=False):
"""Create a dns.resolver.Resolver mock.
This is used to return a predictable response to a _dmarc query. It
@@ -48,28 +53,64 @@ def get_dns_resolver():
"""
class Name:
# mock answer.name
- def __init__(self):
- pass
+ def __init__(self, name='_dmarc.example.biz.'):
+ self.name = name
def to_text(self):
- return '_dmarc.example.biz.'
+ return self.name
class Item:
# mock answer.items
- def __init__(self):
- self.strings = [b'v=DMARC1; p=reject;']
+ def __init__(self, d=rdata, n='_dmarc.example.com.'):
+ self.strings = [d]
+ # for CNAMES
+ self.target = Name(n)
class Ans_e:
# mock answer element
- def __init__(self):
- self.rdtype = TXT
- self.items = [Item()]
- self.name = Name()
+ def __init__(
+ self,
+ typ=rtype,
+ d=rdata,
+ t='_dmarc.example.com.',
+ n='_dmarc.example.biz.'):
+ self.rdtype = typ
+ self.items = [Item(d, t)]
+ self.name = Name(n)
class Answer:
# mock answer
def __init__(self):
- self.answer = [Ans_e()]
+ if cloop:
+ self.answer = [
+ Ans_e(
+ typ=CNAME,
+ n='_dmarc.example.biz.',
+ t='_dmarc.example.org.'
+ ),
+ Ans_e(
+ typ=CNAME,
+ n='_dmarc.example.org.',
+ t='_dmarc.example.biz.'
+ ),
+ ]
+ elif cmult:
+ self.answer = [
+ Ans_e(
+ typ=CNAME,
+ n='_dmarc.example.biz.',
+ t='_dmarc.example.net.'
+ ),
+ Ans_e(
+ typ=CNAME,
+ n='_dmarc.example.net.',
+ t='_dmarc.example.com.'
+ ),
+ ]
+ elif rmult:
+ self.answer = [Ans_e(), Ans_e(d=b'v=DMARC1; p=none;')]
+ else:
+ self.answer = [Ans_e()]
class Resolver:
# mock dns.resolver.Resolver class.
@@ -190,3 +231,125 @@ To: ant@example.com
'DNSException: Unable to query DMARC policy for '
'anne@example.info (_dmarc.example.info). '
'Abstract base class shared by all dnspython exceptions.\n')
+
+ def test_cname_return(self):
+ mlist = create_list('ant@example.com')
+ # Use action reject. The rule only hits on reject and discard.
+ mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
+ msg = mfs("""\
+From: anne@example.biz
+To: ant@example.com
+
+""")
+ mark = LogFileMark('mailman.error')
+ rule = dmarc.DMARCMitigation()
+ with get_dns_resolver(rtype=CNAME), get_org_data():
+ self.assertFalse(rule.check(mlist, msg, {}))
+ line = mark.readline()
+ self.assertEqual(line, '')
+
+ def test_domain_with_subdomain_policy(self):
+ mlist = create_list('ant@example.com')
+ # Use action reject. The rule only hits on reject and discard.
+ mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
+ msg = mfs("""\
+From: anne@example.biz
+To: ant@example.com
+
+""")
+ rule = dmarc.DMARCMitigation()
+ with get_dns_resolver(
+ rdata=b'v=DMARC1; sp=quarantine;'), get_org_data():
+ self.assertFalse(rule.check(mlist, msg, {}))
+
+ def test_org_domain_with_subdomain_policy(self):
+ mlist = create_list('ant@example.com')
+ # Use action reject. The rule only hits on reject and discard.
+ mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
+ msg = mfs("""\
+From: anne@sub.domain.example.biz
+To: ant@example.com
+
+""")
+ rule = dmarc.DMARCMitigation()
+ with get_dns_resolver(
+ rdata=b'v=DMARC1; sp=quarantine;'), get_org_data():
+ self.assertTrue(rule.check(mlist, msg, {}))
+
+ def test_wrong_dmarc_version(self):
+ mlist = create_list('ant@example.com')
+ # Use action reject. The rule only hits on reject and discard.
+ mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
+ msg = mfs("""\
+From: anne@example.biz
+To: ant@example.com
+
+""")
+ rule = dmarc.DMARCMitigation()
+ with get_dns_resolver(
+ rdata=b'v=DMARC01; p=reject;'), get_org_data():
+ self.assertFalse(rule.check(mlist, msg, {}))
+
+ def test_multiple_records(self):
+ mlist = create_list('ant@example.com')
+ # Use action reject. The rule only hits on reject and discard.
+ mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
+ msg = mfs("""\
+From: anne@example.biz
+To: ant@example.com
+
+""")
+ mark = LogFileMark('mailman.error')
+ rule = dmarc.DMARCMitigation()
+ with get_dns_resolver(rmult=True), get_org_data():
+ self.assertTrue(rule.check(mlist, msg, {}))
+ line = mark.readline()
+ self.assertEqual(
+ line[-68:],
+ 'RRset of TXT records for _dmarc.example.biz has 2 v=DMARC1 '
+ 'entries;\n')
+
+ def test_multiple_cnames(self):
+ mlist = create_list('ant@example.com')
+ # Use action reject. The rule only hits on reject and discard.
+ mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
+ msg = mfs("""\
+From: anne@example.biz
+To: ant@example.com
+
+""")
+ mark = LogFileMark('mailman.error')
+ rule = dmarc.DMARCMitigation()
+ with get_dns_resolver(cmult=True), get_org_data():
+ self.assertFalse(rule.check(mlist, msg, {}))
+ line = mark.readline()
+ self.assertEqual(line, '')
+
+ def test_looping_cnames(self):
+ mlist = create_list('ant@example.com')
+ # Use action reject. The rule only hits on reject and discard.
+ mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
+ msg = mfs("""\
+From: anne@example.biz
+To: ant@example.com
+
+""")
+ mark = LogFileMark('mailman.error')
+ rule = dmarc.DMARCMitigation()
+ with get_dns_resolver(cloop=True), get_org_data():
+ self.assertFalse(rule.check(mlist, msg, {}))
+ line = mark.readline()
+ self.assertEqual(line, '')
+
+ def test_no_policy(self):
+ mlist = create_list('ant@example.com')
+ # Use action reject. The rule only hits on reject and discard.
+ mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
+ msg = mfs("""\
+From: anne@example.biz
+To: ant@example.com
+
+""")
+ rule = dmarc.DMARCMitigation()
+ with get_dns_resolver(rdata=b'v=DMARC1; pct=100;'), get_org_data():
+ self.assertFalse(rule.check(mlist, msg, {}))