summaryrefslogtreecommitdiffstats
path: root/internal/smtpclient/client_test.go
diff options
context:
space:
mode:
authorGab Virebent <gabriel1@virebent.art>2026-08-24 17:34:45 +0200
committerGab Virebent <gabriel1@virebent.art>2026-08-24 17:34:45 +0200
commite9fbbe3373eb66a345f5e3829e2563b94dc92051 (patch)
tree950d75fda88574afb46b4aeab36e96f3c089a3bb /internal/smtpclient/client_test.go
parentfb83c4d70616ec23d8a5397409a5d31c70b70d66 (diff)
downloadn2usenet-main.tar.gz
n2usenet-main.tar.xz
n2usenet-main.zip
Harden transport and preserve profile identitiesHEADmain
Diffstat (limited to 'internal/smtpclient/client_test.go')
-rw-r--r--internal/smtpclient/client_test.go99
1 files changed, 99 insertions, 0 deletions
diff --git a/internal/smtpclient/client_test.go b/internal/smtpclient/client_test.go
index d9eec6d..e91d128 100644
--- a/internal/smtpclient/client_test.go
+++ b/internal/smtpclient/client_test.go
@@ -3,6 +3,7 @@ package smtpclient
import (
"bufio"
"context"
+ "encoding/base64"
"net"
"strings"
"testing"
@@ -91,3 +92,101 @@ func TestSendUsesConfiguredEnvelopeFrom(t *testing.T) {
t.Fatal("configured envelope sender was not used")
}
}
+
+func TestSessionAuthPlain(t *testing.T) {
+ server, client := net.Pipe()
+ defer client.Close()
+
+ command := make(chan string, 1)
+ go func() {
+ defer server.Close()
+ r := bufio.NewReader(server)
+ line, err := r.ReadString('\n')
+ if err != nil {
+ command <- ""
+ return
+ }
+ command <- line
+ _, _ = server.Write([]byte("235 2.7.0 authentication successful\r\n"))
+ }()
+
+ session := newSession(client)
+ if err := session.authPlain("n2usenet@virebent.art", "test-password"); err != nil {
+ t.Fatalf("authPlain returned error: %v", err)
+ }
+
+ line := <-command
+ const prefix = "AUTH PLAIN "
+ if !strings.HasPrefix(line, prefix) {
+ t.Fatalf("unexpected AUTH command: %q", line)
+ }
+ token := strings.TrimSpace(strings.TrimPrefix(line, prefix))
+ decoded, err := base64.StdEncoding.DecodeString(token)
+ if err != nil {
+ t.Fatalf("decode AUTH payload: %v", err)
+ }
+ if got, want := string(decoded), "\x00n2usenet@virebent.art\x00test-password"; got != want {
+ t.Fatalf("unexpected AUTH payload: got %q want %q", got, want)
+ }
+}
+
+func TestSessionAuthPlainRejectsNUL(t *testing.T) {
+ session := &session{}
+ if err := session.authPlain("n2usenet\x00admin", "test-password"); err == nil {
+ t.Fatal("authPlain accepted a username containing NUL")
+ }
+ if err := session.authPlain("n2usenet", "test\x00password"); err == nil {
+ t.Fatal("authPlain accepted a password containing NUL")
+ }
+}
+
+func TestCheckDoesNotStartMailTransaction(t *testing.T) {
+ server, client := net.Pipe()
+ defer client.Close()
+
+ commands := make(chan string, 8)
+ go func() {
+ defer close(commands)
+ defer server.Close()
+ r := bufio.NewReader(server)
+ w := bufio.NewWriter(server)
+ _, _ = w.WriteString("220 test\r\n")
+ _ = w.Flush()
+ for {
+ line, err := r.ReadString('\n')
+ if err != nil {
+ return
+ }
+ commands <- line
+ switch {
+ case strings.HasPrefix(line, "EHLO "):
+ _, _ = w.WriteString("250 test\r\n")
+ case strings.HasPrefix(line, "QUIT"):
+ _, _ = w.WriteString("221 bye\r\n")
+ _ = w.Flush()
+ return
+ default:
+ _, _ = w.WriteString("500 unexpected\r\n")
+ }
+ _ = w.Flush()
+ }
+ }()
+
+ mailer := New(Config{
+ Host: "mail.virebent.art",
+ Port: 25,
+ HELO: "n2usenet.virebent.art",
+ RequireTLS: false,
+ Timeout: 5 * time.Second,
+ }, func(context.Context, string, string) (net.Conn, error) {
+ return client, nil
+ })
+ if err := mailer.Check(context.Background()); err != nil {
+ t.Fatalf("Check returned error: %v", err)
+ }
+ for command := range commands {
+ if strings.HasPrefix(command, "MAIL ") || strings.HasPrefix(command, "RCPT ") || strings.HasPrefix(command, "DATA") {
+ t.Fatalf("health check started a mail transaction: %q", command)
+ }
+ }
+}