feat(protocols): trusted senders are not filed as spam, when the message proves its sender
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b536ed13e7
commit
d7f9659b5e
7 files changed
+226
No files matched your search
@@ -0,0 +1,65 @@
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"lambdamail/protocols/internal/application/port"
|
||||
)
|
||||
|
||||
// SetTrustedSenders lets a mailbox's trusted senders skip the spam folder.
|
||||
// Optional: without it a spam verdict files the message in Junk for everyone.
|
||||
func (uc *ProcessInboundEmailUseCase) SetTrustedSenders(trusted port.TrustedSenders) {
|
||||
uc.trusted = trusted
|
||||
}
|
||||
|
||||
// rescuedFromJunk reports whether a message the spam filter condemned should
|
||||
// reach the inbox because this mailbox trusts who it is from.
|
||||
//
|
||||
// The From header is what a person trusts, and it is also the easiest field to
|
||||
// forge, so it only counts when the message proves it came from that address:
|
||||
// DMARC passed, or SPF passed for the very domain written in From. Without the
|
||||
// proof, trusting a contact would hand a spammer a way in by writing that
|
||||
// contact's address on their mail.
|
||||
//
|
||||
// Fails closed: a lookup that errors leaves the message in Junk, which is where
|
||||
// it was going anyway.
|
||||
func (uc *ProcessInboundEmailUseCase) rescuedFromJunk(
|
||||
ctx context.Context,
|
||||
mailboxID uuid.UUID,
|
||||
fromAddress, envelopeSender string,
|
||||
auth port.InboundAuthResult,
|
||||
) bool {
|
||||
if uc.trusted == nil || fromAddress == "" {
|
||||
return false
|
||||
}
|
||||
if !provesSender(fromAddress, envelopeSender, auth) {
|
||||
return false
|
||||
}
|
||||
trusted, err := uc.trusted.IsTrusted(ctx, mailboxID, fromAddress)
|
||||
if err != nil {
|
||||
log.Printf("trusted senders: %v", err)
|
||||
return false
|
||||
}
|
||||
return trusted
|
||||
}
|
||||
|
||||
func provesSender(fromAddress, envelopeSender string, auth port.InboundAuthResult) bool {
|
||||
if auth.DMARC == port.AuthResultPass {
|
||||
return true
|
||||
}
|
||||
return auth.SPF == port.AuthResultPass &&
|
||||
domainOf(fromAddress) != "" &&
|
||||
domainOf(fromAddress) == domainOf(normaliseAddress(envelopeSender))
|
||||
}
|
||||
|
||||
func domainOf(address string) string {
|
||||
at := strings.LastIndex(address, "@")
|
||||
if at < 0 || at == len(address)-1 {
|
||||
return ""
|
||||
}
|
||||
return address[at+1:]
|
||||
}
|
||||
Reference in new issue
Block a user