feat: surface most severe SPF result and hide "via" badge on spoofed mail
This commit is contained in:
@@ -5,6 +5,7 @@ import { Mail, Tag } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { Email, Identity } from '@/lib/jmap/types';
|
||||
import { parseSubAddress } from '@/lib/sub-addressing';
|
||||
import { isAuthenticationSpoofed } from '@/lib/email-headers';
|
||||
import { useSettingsStore } from '@/stores/settings-store';
|
||||
|
||||
interface EmailIdentityBadgeProps {
|
||||
@@ -29,10 +30,17 @@ export function EmailIdentityBadge({
|
||||
// Parse the from address to check for sub-addressing
|
||||
const parsedFrom = parseSubAddress(fromAddress, subAddressDelimiter);
|
||||
|
||||
// Find matching identity (email sent BY the user)
|
||||
const matchingIdentity = identities.find(
|
||||
(identity) => identity.email === fromAddress || identity.email === `${parsedFrom.baseUser}@${parsedFrom.domain}`
|
||||
);
|
||||
// Find matching identity (email sent BY the user). When the message is
|
||||
// likely spoofed, the From address can't be trusted, so we ignore any
|
||||
// identity match — otherwise a forged From matching one of the user's own
|
||||
// addresses would render a misleading "via <identity>" badge that implies
|
||||
// legitimacy.
|
||||
const spoofed = isAuthenticationSpoofed(email.authenticationResults);
|
||||
const matchingIdentity = spoofed
|
||||
? undefined
|
||||
: identities.find(
|
||||
(identity) => identity.email === fromAddress || identity.email === `${parsedFrom.baseUser}@${parsedFrom.domain}`
|
||||
);
|
||||
|
||||
// Check if email was sent TO a sub-address (received email)
|
||||
let receivedToTag: string | null = null;
|
||||
|
||||
@@ -4817,9 +4817,27 @@ export function EmailViewer({
|
||||
<section className="min-w-0">
|
||||
<SectionHeader>{t('details.authentication_security')}</SectionHeader>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{auth?.spf && (
|
||||
<AuthChip name="SPF" result={auth.spf.result} extra={auth.spf.domain} tooltip={t('authentication.tooltip_spf')} />
|
||||
)}
|
||||
{auth?.spf && (() => {
|
||||
// When multiple identities (HELO + MAIL FROM) were
|
||||
// evaluated, list each result in the tooltip for full
|
||||
// transparency; the chip itself shows the most severe.
|
||||
const breakdown = auth.spf.all && auth.spf.all.length > 1
|
||||
? auth.spf.all
|
||||
.map((r) => {
|
||||
const label = r.identity === 'mailfrom' ? 'MAIL FROM' : r.identity === 'helo' ? 'HELO' : 'SPF';
|
||||
return `${label}: ${translateAuthResult(r.result)}${r.domain ? ` (${r.domain})` : ''}`;
|
||||
})
|
||||
.join('\n')
|
||||
: null;
|
||||
return (
|
||||
<AuthChip
|
||||
name="SPF"
|
||||
result={auth.spf.result}
|
||||
extra={auth.spf.domain}
|
||||
tooltip={breakdown ? `${t('authentication.tooltip_spf')}\n\n${breakdown}` : t('authentication.tooltip_spf')}
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
{auth?.dkim && (
|
||||
<AuthChip name="DKIM" result={auth.dkim.result} extra={auth.dkim.domain} tooltip={t('authentication.tooltip_dkim')} />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user