From 6133bcd95168446fc3c0a3f1fc09835d4f30c284 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 29 Oct 2026 04:50:15 +0100 Subject: [PATCH] fix: trust an invitation that passed dmarc even when a forwarder broke its first signature --- src/lib/__tests__/calendar-invitation.test.ts | 17 +++++++++++++++++ src/lib/calendar-invitation.ts | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/src/lib/__tests__/calendar-invitation.test.ts b/src/lib/__tests__/calendar-invitation.test.ts index c3cffb2e..a1f6520f 100644 --- a/src/lib/__tests__/calendar-invitation.test.ts +++ b/src/lib/__tests__/calendar-invitation.test.ts @@ -197,6 +197,23 @@ describe('getInvitationTrustAssessment', () => { .toBe('trusted'); }); + // A mailing list or a forwarder that rewrote the message broke the first + // signature; the sender's own one passed, and so did DMARC. + it('trusts a DMARC pass of the receiving server over a broken signature listed first', () => { + expect(getInvitationTrustAssessment(request, forged( + 'dkim=fail header.d=lists.example.net header.s=l1; dkim=pass header.d=example.com header.s=k1; spf=softfail smtp.mailfrom=lists.example.net; dmarc=pass header.from=example.com', + ), 'request')).toMatchObject({ level: 'trusted', reason: null }); + // A pass only a lower header claims is the sender's word, not the server's. + const below = email({ + from: [{ email: 'alice@example.com' }], + headers: [ + { name: 'Authentication-Results', value: 'mx.example.org; dkim=fail header.d=example.com; dmarc=fail header.from=example.com' }, + { name: 'Authentication-Results', value: 'relay.example; dmarc=pass header.from=example.com' }, + ], + }); + expect(getInvitationTrustAssessment(request, below, 'request').reason).toBe('authentication_failed'); + }); + it('warns about a forged organizer whose mail passes only for the forger\'s domain', () => { const e = email({ from: [{ email: 'mallory@forger.example' }], diff --git a/src/lib/calendar-invitation.ts b/src/lib/calendar-invitation.ts index 41241a7c..c800ed2d 100644 --- a/src/lib/calendar-invitation.ts +++ b/src/lib/calendar-invitation.ts @@ -483,6 +483,10 @@ export function hasAlignedAuthenticationPass( } function hasAuthenticationFailure(auth?: AuthenticationResults | null): boolean { + // DMARC passed on the receiving server's own header: one aligned method + // passed, and that is the verdict. A mailing list's or a forwarder's + // signature that broke on the way, listed first, flagged such a message. + if (auth?.dmarc?.result === 'pass') return false; return Boolean( auth?.dmarc?.result === 'fail' || auth?.dkim?.result === 'fail'