fix(compose): replies ignore Reply-To and prefill the wrong address #493

Open
opened 2026-07-10 19:15:00 +00:00 by JMR-dev · 0 comments
JMR-dev commented 2026-07-10 19:15:00 +00:00 (Migrated from github.com)

Verified finding(s) from the 2026-07-09 whole-repo multi-agent review (independent finder, then adversarial verifier; verdict CONFIRMED).

Triage: above the cut — fix dispatched immediately; this issue tracks the fix to Done.

app/src/main/kotlin/org/libremail/data/ReplyBuilder.kt:49 — high

Replies always target the original From address; the Reply-To header is never used (ReplyContext built in ImapClient doesn't even carry it), so replies to any sender that sets Reply-To are prefilled to the wrong address.

Failure scenario: User replies to a message from a support desk, newsletter, or mailing list that sets 'From: noreply@corp.example' + 'Reply-To: support@corp.example'. reply() sets to = context.fromEmail = noreply@corp.example; ImapClient.ReplyContext (ImapClient.kt:519) reads only message.from, ignoring message.replyTo. The reply is sent to the unmonitored From address and never reaches the intended recipient — the RFC 5322 reply-addressing contract is broken for a very common class of mail. Reply-All is similarly wrong (Reply-To never enters the recipient set).

Verifier justification (CONFIRMED): ReplyContext (app/src/main/kotlin/org/libremail/mail/ImapClient.kt:92-100) carries no Reply-To field; fetchForReply builds it from val from = message.from?.firstOrNull() as? InternetAddress and fromEmail = from?.address.orEmpty() (lines 517-520), never calling message.getReplyTo(). ReplyBuilder.reply then sets to = context.fromEmail (ReplyBuilder.kt:49), and replyAllCc only merges toRecipients+ccRecipients, so Reply-To never enters any recipient set for REPLY or REPLY_ALL. Grep over app/src/main confirms 'replyTo' appears nowhere in the mail/compose path (only in the unrelated DebugReport feature). Concrete trigger: reply to any message with 'From: noreply@corp.example' + 'Reply-To: support@corp.example' — the compose To is prefilled with noreply@corp.example and, if sent unedited, the reply goes to an unmonitored address, violating RFC 5322 reply addressing. No known-intentional design covers this. Rated high (not critical): mail goes to the message's visible From address, which the user sees prefilled and can edit — a user-visible malfunction / likely-missed mail, not silent delivery to an unrelated third party.

Defective line: to = context.fromEmail,

Fix hint: Add a replyToEmails: List (or replyToEmail) field to ReplyContext and populate it in ImapClient.fetchForReply from message.getReplyTo() (Jakarta Mail already falls back to From when the header is absent); in ReplyBuilder use it for the reply To and exclude those addresses (alongside fromEmail) in replyAllCc. Mirror the same in any Outlook/Graph reply-context builder if one exists.

Verified finding(s) from the 2026-07-09 whole-repo multi-agent review (independent finder, then adversarial verifier; verdict **CONFIRMED**). **Triage: above the cut — fix dispatched immediately; this issue tracks the fix to Done.** ## `app/src/main/kotlin/org/libremail/data/ReplyBuilder.kt:49` — high Replies always target the original From address; the Reply-To header is never used (ReplyContext built in ImapClient doesn't even carry it), so replies to any sender that sets Reply-To are prefilled to the wrong address. **Failure scenario:** User replies to a message from a support desk, newsletter, or mailing list that sets 'From: noreply@corp.example' + 'Reply-To: support@corp.example'. reply() sets to = context.fromEmail = noreply@corp.example; ImapClient.ReplyContext (ImapClient.kt:519) reads only message.from, ignoring message.replyTo. The reply is sent to the unmonitored From address and never reaches the intended recipient — the RFC 5322 reply-addressing contract is broken for a very common class of mail. Reply-All is similarly wrong (Reply-To never enters the recipient set). **Verifier justification (CONFIRMED):** ReplyContext (app/src/main/kotlin/org/libremail/mail/ImapClient.kt:92-100) carries no Reply-To field; fetchForReply builds it from `val from = message.from?.firstOrNull() as? InternetAddress` and `fromEmail = from?.address.orEmpty()` (lines 517-520), never calling message.getReplyTo(). ReplyBuilder.reply then sets `to = context.fromEmail` (ReplyBuilder.kt:49), and replyAllCc only merges toRecipients+ccRecipients, so Reply-To never enters any recipient set for REPLY or REPLY_ALL. Grep over app/src/main confirms 'replyTo' appears nowhere in the mail/compose path (only in the unrelated DebugReport feature). Concrete trigger: reply to any message with 'From: noreply@corp.example' + 'Reply-To: support@corp.example' — the compose To is prefilled with noreply@corp.example and, if sent unedited, the reply goes to an unmonitored address, violating RFC 5322 reply addressing. No known-intentional design covers this. Rated high (not critical): mail goes to the message's visible From address, which the user sees prefilled and can edit — a user-visible malfunction / likely-missed mail, not silent delivery to an unrelated third party. **Defective line:** `to = context.fromEmail,` **Fix hint:** Add a replyToEmails: List<String> (or replyToEmail) field to ReplyContext and populate it in ImapClient.fetchForReply from message.getReplyTo() (Jakarta Mail already falls back to From when the header is absent); in ReplyBuilder use it for the reply To and exclude those addresses (alongside fromEmail) in replyAllCc. Mirror the same in any Outlook/Graph reply-context builder if one exists.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMail#493