1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2026-10-29 13:42:30 +00:00

fix(pop3): reject deleted messages and failed TLS handshakes (#620)

- RETR and TOP now respond "-ERR You deleted message N" for messages
  marked deleted, as RFC 1939 sections 5 (RETR) and 7 (TOP) require;
  previously the message content was still sent.
- STLS: a failed TLS handshake previously continued the session with
  the dead TLS connection installed as s.conn and a bogus tlsState,
  producing confusing follow-on errors. RFC 2595 section 4 requires
  the negotiation to complete before further commands, so a failure
  is now fatal: log it and terminate the session.

Updates the scripted tests that previously pinned the old behavior,
and adds a STLS handshake failure regression test.

Signed-off-by: James Hillyerd <james@hillyerd.com>
This commit is contained in:
James Hillyerd
2026-09-16 18:10:40 -07:00
committed by GitHub
parent 8bad098be9
commit a85a4ab996
3 changed files with 73 additions and 16 deletions
+13 -14
View File
@@ -452,19 +452,20 @@ func TestTransactionRetrArgumentErrors(t *testing.T) {
}
}
// Documents current behavior: RFC 1939 requires a negative status indicator
// when RETR refers to a message marked as deleted, but the handler does not
// check the retain flag. Flagged for a follow-up fix.
func TestTransactionRetrDeletedMessage(t *testing.T) {
// RFC 1939 §5: a RETR message-number may NOT refer to a message marked as
// deleted; the server responds with a negative status indicator.
func TestTransactionRetrDeletedMessageRejected(t *testing.T) {
ds := newMemStore(t)
test.DeliverToStore(t, ds, "mailbox", "One", time.Now())
_, size2 := test.DeliverToStore(t, ds, "mailbox", "Two", time.Now())
_, size1 := test.DeliverToStore(t, ds, "mailbox", "One", time.Now())
test.DeliverToStore(t, ds, "mailbox", "Two", time.Now())
server := setupPOPServer(t, ds, false, false)
script := slices.Concat(loginSteps(2), []scriptStep{
{"DELE 2", []string{"+OK Deleted message 2"}},
{"RETR 2", []string{fmt.Sprintf("+OK %v bytes follows", size2),
"To: somebody@host", "From: somebodyelse@host", "Subject: Two", "", "Test Body", "."}},
{"RETR 2", []string{"-ERR You deleted message 2"}},
// A retained message still downloads fine.
{"RETR 1", []string{fmt.Sprintf("+OK %v bytes follows", size1),
"To: somebody@host", "From: somebodyelse@host", "Subject: One", "", "Test Body", "."}},
{"QUIT", []string{"+OK We will process your deletes"}},
})
playPOP3Session(t, server, script)
@@ -514,18 +515,16 @@ func TestTransactionTopArgumentErrors(t *testing.T) {
}
}
// Documents current behavior: RFC 1939 (TOP, section 7) requires a negative
// status indicator when TOP refers to a message marked as deleted, but the
// handler does not check the retain flag. Flagged for a follow-up fix.
func TestTransactionTopDeletedMessage(t *testing.T) {
// RFC 1939 §7 (TOP): a message-number may NOT refer to a message marked as
// deleted.
func TestTransactionTopDeletedMessageRejected(t *testing.T) {
ds := newMemStore(t)
test.DeliverToStore(t, ds, "mailbox", "One", time.Now())
server := setupPOPServer(t, ds, false, false)
script := slices.Concat(loginSteps(1), []scriptStep{
{"DELE 1", []string{"+OK Deleted message 1"}},
{"TOP 1 0", []string{"+OK Top of message follows",
"To: somebody@host", "From: somebodyelse@host", "Subject: One", "", "."}},
{"TOP 1 0", []string{"-ERR You deleted message 1"}},
{"QUIT", []string{"+OK We will process your deletes"}},
})
playPOP3Session(t, server, script)