fix: show the via badge only when the identity's name adds something to the sender

This commit is contained in:
Linus Rath
2026-10-31 04:51:26 +01:00
parent ed490b7188
commit fac54b415f
3 changed files with 43 additions and 3 deletions
+3 -3
View File
@@ -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
<View style={[styles.badge, { backgroundColor: c.primaryBg }]}>
<Text style={[styles.badgeText, { color: c.primary }]}>
{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 })}
</Text>
</View>
+25
View File
@@ -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 <ann@x>" 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\)/);
});
});
+15
View File
@@ -97,6 +97,21 @@ const SPF_SEVERITY: Record<SpfResult, number> = {
pass: 0,
};
/**
* Whether "via <identity>" 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<Identity, 'name' | 'email'>,
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