hub v0.18.0: app-email passthrough POST /api/v1/mail → Resend SMTP
Raw-MIME passthrough (STARTTLS, AUTH LOGIN) — separate from the notify HTTP-API alert path (which drops inline CID images). Per-customer token-bucket rate limit, From-header allowlist backstop. Resend key stays hub-side. No new external dep. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
package mailrelay
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFromDomain(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
raw string
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{"bare", "From: vaultwarden@felhom.eu\r\n\r\nbody", "felhom.eu", false},
|
||||
{"display name", "From: Vaultwarden <vaultwarden@felhom.eu>\r\n\r\nbody", "felhom.eu", false},
|
||||
{"uppercase domain", "From: x@FELHOM.EU\r\n\r\nbody", "felhom.eu", false},
|
||||
{"missing from", "To: x@y.eu\r\n\r\nbody", "", true},
|
||||
{"garbage", "From: not-an-address\r\n\r\nbody", "", true},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
got, err := FromDomain([]byte(c.raw))
|
||||
if c.wantErr {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, got %q", got)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != c.want {
|
||||
t.Fatalf("got %q, want %q", got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// loginAuth must answer the LOGIN challenges with username then password and ignore the
|
||||
// final non-"more" continuation.
|
||||
func TestLoginAuth(t *testing.T) {
|
||||
a := &loginAuth{username: "resend", password: "secret-key"}
|
||||
|
||||
mech, ir, err := a.Start(nil)
|
||||
if err != nil || mech != "LOGIN" || ir != nil {
|
||||
t.Fatalf("Start = (%q,%v,%v), want (LOGIN,nil,nil)", mech, ir, err)
|
||||
}
|
||||
|
||||
resp, err := a.Next([]byte("Username:"), true)
|
||||
if err != nil || string(resp) != "resend" {
|
||||
t.Fatalf("username challenge → %q, %v", resp, err)
|
||||
}
|
||||
resp, err = a.Next([]byte("Password:"), true)
|
||||
if err != nil || string(resp) != "secret-key" {
|
||||
t.Fatalf("password challenge → %q, %v", resp, err)
|
||||
}
|
||||
// Unexpected challenge while more is expected → error (never blindly leak creds).
|
||||
if _, err := a.Next([]byte("Something:"), true); err == nil {
|
||||
t.Fatal("unexpected challenge should error")
|
||||
}
|
||||
// Non-more continuation → no response, no error.
|
||||
if resp, err := a.Next(nil, false); err != nil || resp != nil {
|
||||
t.Fatalf("non-more Next → %q, %v", resp, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user