From 3f4533b9dedb526e5891354164b07b59a8557cc5 Mon Sep 17 00:00:00 2001
From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com>
Date: Sun, 27 Sep 2026 17:23:05 +0530
Subject: [PATCH] fix(email): read spf/dkim verdicts from their own
Authentication-Results clause
The r3 fold scoped only the dmarc verdict to its clause. SPF and DKIM
still came from a whole-string, last-match-wins regex scan, so the same
quoted/comment smuggle closed for dmarc still authenticated a spoofed
From (GHSA-rxqh-5572-8m77), e.g.
spf=fail smtp.mailfrom="x spf=pass smtp.mailfrom=example.com "@evil.test
spf=fail (spf=pass) smtp.mailfrom=a@example.com
spf=fail smtp.mailfrom=a.spf=pass@example.com
dkim=pass header.d=evil.test header.i="x header.d=example.com y"@evil.test
Every verdict now comes from the leading method=result token of its
own clause (from _ar_clauses, comments dropped), and its domains only
from that clause. Properties are read by a token scanner that consumes
quoted-strings and other key=value tokens whole, so quoted contents are
never read as properties while a quoted value still is
(header.from="example.com"). SPF fails closed on more than one spf
clause; DKIM accepts any single dkim=pass clause whose own header.d
aligns (multi-signature mail is normal), never mixing clauses. The
whole-string methods/props (methods["dmarc"] was dead) are gone.
Also: a stray ')' at depth 0 is now unbalanced (it split header.from
out of the dmarc clause); a From with more than 64 '(' takes the silent
empty-sender drop instead of a parseaddr RecursionError logged as an
error; the cap test asserts the cap directly instead of wall-clock
timing; the empty-From drop assertion moves next to the other
_extract_email_address rejects; and the untested >1-dmarc, unbalanced
and backslash-escape rules get reject strings.
---
plugins/platforms/email/adapter.py | 66 ++++++++++++++++++------------
tests/gateway/test_email.py | 58 +++++++++++++++++++-------
2 files changed, 82 insertions(+), 42 deletions(-)
diff --git a/plugins/platforms/email/adapter.py b/plugins/platforms/email/adapter.py
index 0a8510f4ef..29c22674fe 100644
--- a/plugins/platforms/email/adapter.py
+++ b/plugins/platforms/email/adapter.py
@@ -55,14 +55,17 @@ _HTML_SUBS = ((re.compile(r"
", re.IGNORECASE), "\n"), (re.compile(r"
"), (re.compile(r"\n{3,}"), "\n\n"))
# ``display