From 9491d5d25d83d4ff86b9c450f237783684bdd6e7 Mon Sep 17 00:00:00 2001 From: David Baer Date: Thu, 23 Jan 2020 12:50:28 -0500 Subject: [PATCH] Record DMARC report errors, allow additional options for DKIM policy override. --- dmarcreceiver/commands.py | 9 ++++----- dmarcreceiver/model.py | 19 ++++++++++++++++--- setup.py | 2 +- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/dmarcreceiver/commands.py b/dmarcreceiver/commands.py index c6083ae..86ec7b4 100644 --- a/dmarcreceiver/commands.py +++ b/dmarcreceiver/commands.py @@ -24,13 +24,15 @@ def parse_metadata(tree): date_begin = datetime.fromtimestamp(int(date_begin)) date_end, = tree.xpath('date_range/end/text()') date_end = datetime.fromtimestamp(int(date_end)) + errors = [ ReportError(txt) for txt in tree.xpath('errors/text()') ] return Report( org_name=org_name, email=email, extra_contact_info=extra_contact_info, report_id=report_id, date_begin=date_begin, - date_end=date_end + date_end=date_end, + errors=errors ) def scoop_elements(obj, tree, *elements): @@ -64,10 +66,7 @@ def parse_report(f): scoop_elements(record, record_node.xpath('./identifiers')[0], 'envelope_to', 'header_from') for dkim_node in record_node.xpath('./auth_results/dkim'): dkim = DKIMResult() - scoop_elements(dkim, dkim_node, 'domain', 'result', 'human_result') - t = dkim_node.xpath('./selector/text()') - if len(t) > 0: - dkim.human_result = t[0] + scoop_elements(dkim, dkim_node, 'domain', 'selector', 'result', 'human_result') record.dkim_results.append(dkim) for spf_node in record_node.xpath('./auth_results/spf'): spf = SPFResult() diff --git a/dmarcreceiver/model.py b/dmarcreceiver/model.py index 72be5f8..132ea44 100644 --- a/dmarcreceiver/model.py +++ b/dmarcreceiver/model.py @@ -24,9 +24,19 @@ def init_model(): Alignment = Enum('r', 's', name='alignment') Disposition = Enum('none', 'quarantine', 'reject', name='disposition') DMARCResult = Enum('pass', 'fail', name='dmarc_result') -PolicyOverride = Enum('forwarded', 'sampled_out', 'trusted_forwarder', 'other', name='policy_override') -SPFResultType = Enum('none', 'neutral', 'pass', 'fail', 'softfail', 'temperror', 'permerror', name='spf_result') -DKIMResultType = Enum('none', 'pass', 'fail', 'policy', 'neutral', 'temperror', 'permerror', name='dkim_result') +PolicyOverride = Enum( + 'forwarded', 'sampled_out', 'trusted_forwarder', + 'mailing_list', 'local_policy', 'other', + name='policy_override' +) +SPFResultType = Enum( + 'none', 'neutral', 'pass', 'fail', 'softfail', 'temperror', 'permerror', + name='spf_result' +) +DKIMResultType = Enum( + 'none', 'pass', 'fail', 'policy', 'neutral', 'temperror', 'permerror', + name='dkim_result' +) class INET(satypes.TypeDecorator): impl = satypes.CHAR @@ -59,6 +69,8 @@ class ReportError(DeclarativeBase): report_id = Column(Integer, ForeignKey(Report.id, onupdate='CASCADE', ondelete='CASCADE'), nullable=False) error = Column(String, nullable=False) + report = relationship(Report, backref='errors') + class ReportRecord(DeclarativeBase): __tablename__ = 'report_records' id = Column(Integer, primary_key=True) @@ -87,6 +99,7 @@ class DKIMResult(DeclarativeBase): id = Column(Integer, primary_key=True) record_id = Column(Integer, ForeignKey(ReportRecord.id, onupdate='CASCADE', ondelete='CASCADE'), nullable=False) domain = Column(String, nullable=False) + selector = Column(String, nullable=True) result = Column(DKIMResultType, nullable=False) human_result = Column(String, nullable=True) diff --git a/setup.py b/setup.py index ddbdb4e..eac0c5d 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ install_requires=[ setup( name='DMARCReceiver', - version='1.0', + version='1.1', description='Receive DMARC reports', author='David Baer', author_email='david@amyanddavid.net',