The owner's decision (2026-10-16) on the security review's F1: a Sender: line - the sending agent's, shown beside From: as "on behalf of", and signed since 2.8.0 - was neither held to the login nor, before, signed: an application could write its own From: and Sender: ceo@bank.com under this server's signature. Now, when a message has one, it must be one address the application may send as, and at most one such line (RFC 5322 § 3.6); From: is judged first. The refusal is "550 5.7.1 Sender: <address> - <why>", a send-log row of reason "sender" (store.ReasonSender), tagged "wrong Sender"; the message page names the line. The log tailer recovers such a row from cleanup's line as it does a From: refusal (fromRefusalRe reads either). Help text as in the design commit d9d9af3; guide, architecture, security and CHANGELOG say it, security.md keeps Resent-From: (signed, not judged) as an accepted risk. Tests: TestSenderLineHeldToTheSameRule (own address, a name, another application's, a stranger's, two addresses, two lines, From: first), the recovery of a Sender: refusal line, the tag, the message page; e2e sender_check_holds_the_sender_line (own Sender: passes and is signed, another domain's refused at DATA and logged). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ const (
|
||||
ReasonLimit = "limit" // a level-2 rate limit
|
||||
ReasonClient = "client" // the application's list of client addresses
|
||||
ReasonFrom = "from" // the From: line is not the application's
|
||||
ReasonSender = "sender" // the Sender: line is not the application's (2.8.0)
|
||||
)
|
||||
|
||||
// Who decided a "bounced" row the log did not (send_log.reason).
|
||||
|
||||
@@ -20,8 +20,8 @@ const (
|
||||
// refusal. The journal-milter refuses at MAIL FROM with a 4xx when a level-2
|
||||
// rate limit or the application's client-address list says no (Reason is
|
||||
// ReasonLimit or ReasonClient; such a row has no queue-id). The sender check
|
||||
// refuses a From: line that is not the application's with a 550 (ReasonFrom;
|
||||
// its row has the queue-id). Every refused row is left out of the level-2
|
||||
// refuses a From: or Sender: line that is not the application's with a
|
||||
// 550 (ReasonFrom, ReasonSender; its row has the queue-id). Every refused row is left out of the level-2
|
||||
// message count (it was never sent).
|
||||
StatusRejected = "rejected"
|
||||
)
|
||||
@@ -38,7 +38,8 @@ type SendLogEntry struct {
|
||||
To string
|
||||
Subject string
|
||||
// Reason and HeaderFrom are set on refused rows only: why the message
|
||||
// was refused (Reason*), and for ReasonFrom what its From: line said.
|
||||
// was refused (Reason*), and for ReasonFrom and ReasonSender the address
|
||||
// of the line refused (the column's name is from before Sender: was).
|
||||
Reason string
|
||||
HeaderFrom string
|
||||
// ClientIP is the client a refusal at MAIL FROM came from (InsertRejected
|
||||
|
||||
Reference in New Issue
Block a user