From fac54b415f7096870173c2715c5cebb9a61b60c0 Mon Sep 17 00:00:00 2001
From: Linus Rath <139418639+rathlinus@users.noreply.github.com>
Date: Sat, 31 Oct 2026 04:51:26 +0100
Subject: [PATCH] fix: show the via badge only when the identity's name adds
something to the sender
---
src/components/email/MessageHeader.tsx | 6 +++---
src/lib/__tests__/via-badge.test.ts | 25 +++++++++++++++++++++++++
src/lib/email-headers.ts | 15 +++++++++++++++
3 files changed, 43 insertions(+), 3 deletions(-)
create mode 100644 src/lib/__tests__/via-badge.test.ts
diff --git a/src/components/email/MessageHeader.tsx b/src/components/email/MessageHeader.tsx
index ad236b1b..123e0e67 100644
--- a/src/components/email/MessageHeader.tsx
+++ b/src/components/email/MessageHeader.tsx
@@ -12,7 +12,7 @@ import { useLocaleStore } from '../../stores/locale-store';
import { useAccountKeywordDefs } from '../../stores/keywords-store';
import { emailDisplayDate, formatHeaderDate, formatHeaderTime, formatFullDateTime } from '../../lib/email-date';
import {
- deriveHeaderInfo, deliveryDeltaMs, formatDelta, isAuthenticationSpoofed, findReceivingIdentity,
+ deriveHeaderInfo, deliveryDeltaMs, formatDelta, isAuthenticationSpoofed, findReceivingIdentity, viaIdentityNameShown,
getSenderVerification, describeSenderVerification,
type AuthenticationResults, type EmailHeaderInfo,
} from '../../lib/email-headers';
@@ -138,7 +138,7 @@ export function MessageHeader({ email, identities, headerInfo, onToggleStar, onA
if (spoofed || identities.length === 0) return null;
const fromEmail = from?.email?.toLowerCase();
const sentAs = fromEmail ? identities.find((i) => i.email?.toLowerCase() === fromEmail) : undefined;
- if (sentAs) return { identity: sentAs, direction: 'from' as const };
+ if (sentAs) return viaIdentityNameShown(sentAs, from) ? { identity: sentAs, direction: 'from' as const } : null;
const received = findReceivingIdentity(identities, email);
if (received && identities.length > 1 && received.id !== identities[0].id) {
return { identity: received, direction: 'to' as const };
@@ -258,7 +258,7 @@ export function MessageHeader({ email, identities, headerInfo, onToggleStar, onA
{viaIdentity.direction === 'from'
- ? t('identities.badge.identity_short', 'via {name}', { name: viaIdentity.identity.email })
+ ? t('identities.badge.identity_short', 'via {name}', { name: viaIdentity.identity.name })
: t('email_viewer.received_via', 'to {name}', { name: viaIdentity.identity.email })}
diff --git a/src/lib/__tests__/via-badge.test.ts b/src/lib/__tests__/via-badge.test.ts
new file mode 100644
index 00000000..e9d77b24
--- /dev/null
+++ b/src/lib/__tests__/via-badge.test.ts
@@ -0,0 +1,25 @@
+import { describe, it, expect } from 'vitest';
+import fs from 'node:fs';
+import path from 'node:path';
+import { viaIdentityNameShown } from '../email-headers';
+
+// Mail sent as one of the user's identities read "Ann " with a
+// "via ann@x" badge: the sender's address a second time (webmail
+// email-identity-badge.tsx shows the identity's name, and only when it adds
+// something).
+
+describe('"via" badge', () => {
+ it("shows the identity's name only when it differs from what the sender line says", () => {
+ const from = { name: 'Ann Smith', email: 'ann@example.org' };
+ expect(viaIdentityNameShown({ name: 'Support', email: 'ann@example.org' }, from)).toBe(true);
+ expect(viaIdentityNameShown({ name: 'Ann Smith', email: 'ann@example.org' }, from)).toBe(false);
+ expect(viaIdentityNameShown({ name: 'ann@example.org', email: 'ann@example.org' }, from)).toBe(false);
+ expect(viaIdentityNameShown({ name: '', email: 'ann@example.org' }, from)).toBe(false);
+ });
+
+ it("names the identity, not its address, in the reader's badge", () => {
+ const header = fs.readFileSync(path.resolve(__dirname, '../../components/email/MessageHeader.tsx'), 'utf8');
+ expect(header).toMatch(/'via \{name\}', \{ name: viaIdentity\.identity\.name \}/);
+ expect(header).toMatch(/viaIdentityNameShown\(sentAs, from\)/);
+ });
+});
diff --git a/src/lib/email-headers.ts b/src/lib/email-headers.ts
index 767e5007..a7bf2688 100644
--- a/src/lib/email-headers.ts
+++ b/src/lib/email-headers.ts
@@ -97,6 +97,21 @@ const SPF_SEVERITY: Record = {
pass: 0,
};
+/**
+ * Whether "via " on a message sent as one of the user's identities
+ * tells the reader anything: only when the identity's name differs from its
+ * address, the From address and the name already shown as the sender, so it
+ * no longer reads "ann@x via ann@x" (webmail email-identity-badge.tsx).
+ */
+export function viaIdentityNameShown(
+ identity: Pick,
+ from: { name?: string | null; email?: string | null } | undefined,
+): boolean {
+ const name = identity.name?.trim();
+ if (!name) return false;
+ return name !== identity.email && name !== from?.email && name !== from?.name;
+}
+
/**
* Whether the authentication results indicate the visible From identity can't
* be trusted (i.e. the message is likely spoofed). Used to suppress UI that