diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index cdf91d2d0..d32220999 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -22,7 +22,7 @@ import { TagPicker } from "./tag-picker"; import { useMeasuredTagDisplay } from "@/hooks/use-tag-display"; import { useKeywordFormat } from "@/hooks/use-keyword-format"; import { getEmailTagIds } from "@/lib/thread-utils"; -import { getSecurityStatus, extractListHeaders } from "@/lib/email-headers"; +import { getSecurityStatus, extractListHeaders, getSenderVerification } from "@/lib/email-headers"; import { emailToReadView } from "@/lib/plugin-projection"; import { generateEmailSource } from "@/lib/email-source"; import { @@ -3020,6 +3020,19 @@ export function EmailViewer({ } const sender = email.from?.[0]; + const senderVerification = getSenderVerification(email.authenticationResults, sender?.email); + const senderVerificationLabel = senderVerification?.status === 'failed' + ? t('sender_check.failed_label') + : t('sender_check.unverified_label'); + const senderVerificationMessage = !senderVerification + ? '' + : senderVerification.status === 'failed' + ? senderVerification.sentFrom + ? t('sender_check.failed_sent_from', { domain: senderVerification.domain, host: senderVerification.sentFrom }) + : t('sender_check.failed', { domain: senderVerification.domain }) + : senderVerification.sentFrom + ? t('sender_check.unverified_sent_from', { domain: senderVerification.domain, host: senderVerification.sentFrom }) + : t('sender_check.unverified', { domain: senderVerification.domain }); const isStarred = email.keywords?.$flagged; const isUnread = !email.keywords?.$seen; const isImportant = email.keywords?.["$important"]; @@ -4511,6 +4524,20 @@ export function EmailViewer({ onViewContact={handleViewContactSidebar} className="text-sm text-start" /> + {senderVerification && ( + + + {senderVerificationLabel} + + )} {replyToDifferent && ( @@ -4816,12 +4843,36 @@ export function EmailViewer({ )} {/* Unified Notification Banner - External Content + Calendar Invitation + Read Receipt */} - {((hasBlockedContent && !allowExternalContent && externalContentPolicy !== 'allow') || + {(senderVerification || + (hasBlockedContent && !allowExternalContent && externalContentPolicy !== 'allow') || hasCalendarInvitation || (readReceiptResponse === 'ask' && shouldOfferReadReceipt)) && (
+ {/* Sender the server's checks don't back */} + {senderVerification && ( +
+
+ +
+
+
+ {senderVerificationLabel} +
+
+ {senderVerificationMessage} +
+
+ {t('sender_check.caution')} +
+
+
+ )} + {/* External Content Controls */} {hasBlockedContent && !allowExternalContent && externalContentPolicy !== 'allow' && (
@@ -4847,7 +4898,9 @@ export function EmailViewer({ {t('load_external_content')} )} - {email.from?.[0]?.email && ( + {/* Trusting a forged address would load remote content + for the next forgery too. */} + {email.from?.[0]?.email && !senderVerification && (