Files
LambdaMail/services/protocols/internal/application/usecase/trusted_delivery.go
T

66 lines
1.9 KiB
Go

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:]
}