================
DMARC mitigation
================

This rule only matches in order to jump to the moderation chain to reject or
discard the message.  The rule looks at the list's ``dmarc_mitigate_action``
and if it is other than ``no_mitigation``, it checks the domain of the
``From:`` address for a DMARC policy.  Depending on various settings, reject
or discard the message, or just flag it for the dmarc handler to apply DMARC
mitigations to the message.

    >>> from mailman.app.lifecycle import create_list
    >>> mlist = create_list('ant@example.com')
    >>> from mailman.config import config    
    >>> rule = config.rules['dmarc-mitigation']
    >>> print(rule.name)
    dmarc-mitigation

First we set up a mock to return predictable responses to DNS lookups.  This
returns ``p=reject`` for the ``example.biz`` domain and not for any others.

    >>> from mailman.rules.tests.test_dmarc import get_dns_resolver
    >>> ignore = cleanups.enter_context(get_dns_resolver())

Use test data for the organizational domain suffixes.

    >>> from mailman.rules.tests.test_dmarc import use_test_organizational_data
    >>> ignore = use_test_organizational_data(cleanups)

A message ``From:`` a domain without a DMARC policy does not set any flags.

    >>> from mailman.interfaces.mailinglist import DMARCMitigateAction
    >>> mlist.dmarc_mitigate_action = DMARCMitigateAction.munge_from
    >>> from mailman.testing.helpers import (specialized_message_from_string
    ...   as message_from_string)
    >>> msg = message_from_string("""\
    ... From: aperson@example.org
    ... To: ant@example.com
    ... Subject: A posted message
    ...
    ... """)
    >>> msgdata = {}
    >>> rule.check(mlist, msg, msgdata)
    False
    >>> msgdata
    {}

Even if the ``From:`` domain publishes ``p=reject``, no flags are set if the
list's action is ``no_mitigation``.

    >>> mlist.dmarc_mitigate_action = DMARCMitigateAction.no_mitigation
    >>> msg = message_from_string("""\
    ... From: aperson@example.biz
    ... To: ant@example.com
    ... Subject: A posted message
    ...
    ... """)
    >>> msgdata = {}
    >>> rule.check(mlist, msg, msgdata)
    False
    >>> msgdata
    {}

With a mitigation strategy chosen, the message is flagged.

    >>> mlist.dmarc_mitigate_action = DMARCMitigateAction.munge_from
    >>> msg = message_from_string("""\
    ... From: aperson@example.biz
    ... To: ant@example.com
    ... Subject: A posted message
    ...
    ... """)
    >>> msgdata = {}
    >>> rule.check(mlist, msg, msgdata)
    False
    >>> msgdata
    {'dmarc': True}

Subdomains which don't have a policy will check the organizational domain.

    >>> msg = message_from_string("""\
    ... From: aperson@sub.domain.example.biz
    ... To: ant@example.com
    ... Subject: A posted message
    ...
    ... """)
    >>> msgdata = {}
    >>> rule.check(mlist, msg, msgdata)
    False
    >>> msgdata
    {'dmarc': True}

We can set an address or regexp to apply DMARC mitigations regardless of the
domains policy.

    >>> mlist.dmarc_mitigate_action = DMARCMitigateAction.munge_from
    >>> msg = message_from_string("""\
    ... From: aperson@example.com
    ... To: ant@example.com
    ... Subject: A posted message
    ...
    ... """)
    >>> msgdata = {}
    >>> mlist.dmarc_addresses = ['^.*@example.com']
    >>> rule.check(mlist, msg, msgdata)
    False
    >>> msgdata
    {'dmarc': True}

The list's action can also be set to immediately discard or reject the
message.

    >>> mlist.dmarc_mitigate_action = DMARCMitigateAction.discard
    >>> msg = message_from_string("""\
    ... From: aperson@example.biz
    ... To: ant@example.com
    ... Subject: A posted message
    ... Message-ID: <xxx_message_id@example.biz>
    ...
    ... """)
    >>> msgdata = {}
    >>> rule.check(mlist, msg, msgdata)
    True
    >>> from mailman.testing.documentation import dump_msgdata    
    >>> dump_msgdata(msgdata)
    dmarc             : True
    dmarc_action      : discard
    moderation_reasons: ['DMARC moderation']
    moderation_sender : aperson@example.biz

We can reject the message with a default reason.

    >>> mlist.dmarc_mitigate_action = DMARCMitigateAction.reject
    >>> msg = message_from_string("""\
    ... From: aperson@example.biz
    ... To: ant@example.com
    ... Subject: A posted message
    ... Message-ID: <xxx_message_id@example.biz>
    ...
    ... """)
    >>> msgdata = {}
    >>> rule.check(mlist, msg, msgdata)
    True
    >>> dump_msgdata(msgdata)
    dmarc             : True
    dmarc_action      : reject
    moderation_reasons: ['You are not allowed to post to this mailing list...
    moderation_sender : aperson@example.biz

And, we can reject with a custom message.

    >>> mlist.dmarc_moderation_notice = 'A silly reason'
    >>> msg = message_from_string("""\
    ... From: aperson@example.biz
    ... To: ant@example.com
    ... Subject: A posted message
    ... Message-ID: <xxx_message_id@example.biz>
    ...
    ... """)
    >>> msgdata = {}
    >>> rule.check(mlist, msg, msgdata)
    True
    >>> dump_msgdata(msgdata)
    dmarc             : True
    dmarc_action      : reject
    moderation_reasons: ['A silly reason']
    moderation_sender : aperson@example.biz
