From 0e90c1347585f71e3b17241c8fd931499e6872e5 Mon Sep 17 00:00:00 2001 From: Sean Whalen <44679+seanthegeek@users.noreply.github.com> Date: Fri, 24 Jul 2026 23:13:26 -0400 Subject: [PATCH] Persist additional_info_uri from parsed SMTP TLS failure details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot caught that the savers read additional_information_uri from the parsed failure-detail dict, but the parser's key is additional_info_uri (SMTPTLSFailureDetailsOptional in types.py, set in parse_smtp_tls_report_json), so the URI was never persisted — the read-side half of the dead-field bug whose write-side half (wrong constructor kwarg) was fixed earlier. Read the parser's key first, keeping the long-form key as a fallback for dicts built by other callers. Regression test proven to fail on the unfixed savers. Also restructured the expected combined-string test values into named locals so no implicit string concatenation sits inside a list literal. Co-Authored-By: Claude Fable 5 --- parsedmarc/elastic.py | 7 ++++++- parsedmarc/opensearch.py | 7 ++++++- tests/test_elastic.py | 25 +++++++++++++++++++------ tests/test_opensearch.py | 25 +++++++++++++++++++------ 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/parsedmarc/elastic.py b/parsedmarc/elastic.py index ca15f321..758c11fd 100644 --- a/parsedmarc/elastic.py +++ b/parsedmarc/elastic.py @@ -1296,7 +1296,12 @@ def save_smtp_tls_report_to_elasticsearch( if "receiving_mx_hostname" in failure_detail: receiving_mx_hostname = failure_detail["receiving_mx_hostname"] - if "additional_information_uri" in failure_detail: + # The parser's key is additional_info_uri (see + # SMTPTLSFailureDetailsOptional in types.py); accept the + # long-form key too for dicts built by other callers. + if "additional_info_uri" in failure_detail: + additional_information_uri = failure_detail["additional_info_uri"] + elif "additional_information_uri" in failure_detail: additional_information_uri = failure_detail[ "additional_information_uri" ] diff --git a/parsedmarc/opensearch.py b/parsedmarc/opensearch.py index 432d0bc0..adc3e182 100644 --- a/parsedmarc/opensearch.py +++ b/parsedmarc/opensearch.py @@ -1268,7 +1268,12 @@ def save_smtp_tls_report_to_opensearch( if "receiving_mx_hostname" in failure_detail: receiving_mx_hostname = failure_detail["receiving_mx_hostname"] - if "additional_information_uri" in failure_detail: + # The parser's key is additional_info_uri (see + # SMTPTLSFailureDetailsOptional in types.py); accept the + # long-form key too for dicts built by other callers. + if "additional_info_uri" in failure_detail: + additional_information_uri = failure_detail["additional_info_uri"] + elif "additional_information_uri" in failure_detail: additional_information_uri = failure_detail[ "additional_information_uri" ] diff --git a/tests/test_elastic.py b/tests/test_elastic.py index b2dab2a2..3aad7de6 100644 --- a/tests/test_elastic.py +++ b/tests/test_elastic.py @@ -1190,6 +1190,9 @@ class TestSaveSmtpTlsReport(unittest.TestCase): "sending_mta_ip": "192.0.2.1", "receiving_ip": "203.0.113.1", "receiving_mx_hostname": "mx1.example.com", + "additional_info_uri": ( + "https://reports.example.com/tls-help" + ), }, { "result_type": "starttls-not-supported", @@ -1220,14 +1223,24 @@ class TestSaveSmtpTlsReport(unittest.TestCase): self.assertEqual( list(doc.policies_combined), ["example.com / sts", "example.net / tlsa"] ) + expected_detail_expired = ( + "example.com / sts / certificate-expired / 192.0.2.1 / " + "203.0.113.1 / mx1.example.com" + ) + expected_detail_starttls = ( + "example.com / sts / starttls-not-supported / 192.0.2.2 / " + "203.0.113.2 / mx2.example.com" + ) self.assertEqual( list(doc.failure_details_combined), - [ - "example.com / sts / certificate-expired / 192.0.2.1 / " - "203.0.113.1 / mx1.example.com", - "example.com / sts / starttls-not-supported / 192.0.2.2 / " - "203.0.113.2 / mx2.example.com", - ], + [expected_detail_expired, expected_detail_starttls], + ) + # The parser emits additional_info_uri (SMTPTLSFailureDetailsOptional + # in types.py); the saver must persist it on the declared + # additional_information_uri field rather than dropping it. + self.assertEqual( + doc.policies[0].failure_details[0].additional_information_uri, + "https://reports.example.com/tls-help", ) diff --git a/tests/test_opensearch.py b/tests/test_opensearch.py index df57f9d4..835586aa 100644 --- a/tests/test_opensearch.py +++ b/tests/test_opensearch.py @@ -1314,6 +1314,9 @@ class TestSaveSmtpTlsReport(unittest.TestCase): "sending_mta_ip": "192.0.2.1", "receiving_ip": "203.0.113.1", "receiving_mx_hostname": "mx1.example.com", + "additional_info_uri": ( + "https://reports.example.com/tls-help" + ), }, { "result_type": "starttls-not-supported", @@ -1344,14 +1347,24 @@ class TestSaveSmtpTlsReport(unittest.TestCase): self.assertEqual( list(doc.policies_combined), ["example.com / sts", "example.net / tlsa"] ) + expected_detail_expired = ( + "example.com / sts / certificate-expired / 192.0.2.1 / " + "203.0.113.1 / mx1.example.com" + ) + expected_detail_starttls = ( + "example.com / sts / starttls-not-supported / 192.0.2.2 / " + "203.0.113.2 / mx2.example.com" + ) self.assertEqual( list(doc.failure_details_combined), - [ - "example.com / sts / certificate-expired / 192.0.2.1 / " - "203.0.113.1 / mx1.example.com", - "example.com / sts / starttls-not-supported / 192.0.2.2 / " - "203.0.113.2 / mx2.example.com", - ], + [expected_detail_expired, expected_detail_starttls], + ) + # The parser emits additional_info_uri (SMTPTLSFailureDetailsOptional + # in types.py); the saver must persist it on the declared + # additional_information_uri field rather than dropping it. + self.assertEqual( + doc.policies[0].failure_details[0].additional_information_uri, + "https://reports.example.com/tls-help", )