Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ var InsecureTLSConfig = &tls.Config{InsecureSkipVerify: true}
var SMTPServer = ""
var SMTPPort = 587
var SMTPSSLEnabled = false
var SMTPStartTLSEnabled = false
var SMTPInsecureSkipVerify = false
var SMTPForceAuthLogin = false
var SMTPAccount = ""
var SMTPFrom = ""
Expand Down
108 changes: 70 additions & 38 deletions common/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,52 @@ func shouldUseSMTPLoginAuth() bool {
}

func getSMTPAuth() smtp.Auth {
if shouldUseSMTPLoginAuth() {
return LoginAuth(SMTPAccount, SMTPToken)
return AutoSMTPAuth(SMTPAccount, SMTPToken)
}

func shouldAuthenticateSMTP() bool {
return SMTPAccount != "" && SMTPToken != ""
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

func smtpTLSConfig() *tls.Config {
return &tls.Config{
ServerName: SMTPServer,
InsecureSkipVerify: SMTPInsecureSkipVerify, // #nosec G402 -- admin-controlled SMTP compatibility option.
Comment on lines +37 to +40

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect declared Go/toolchain files and TLS configs to confirm explicit MinVersion coverage.
set -euo pipefail

printf 'Declared Go/toolchain files:\n'
fd -t f -HI 'go\.mod|go\.work|\.go-version|\.tool-versions|Dockerfile' . \
  -x sh -c 'printf "\n--- %s ---\n" "$1"; sed -n "1,80p" "$1"' sh {}

printf '\nTLS configs in Go sources:\n'
rg -nP 'tls\.Config\s*\{' -C2 --type go

Repository: QuantumNous/new-api

Length of output: 7063


🏁 Script executed:

# Find all tls.Config instantiations to assess scope
rg -nP 'tls\.Config\s*\{' --type go -A3

Repository: QuantumNous/new-api

Length of output: 811


🌐 Web query:

Go 1.25 default minimum TLS version crypto/tls

💡 Result:

In Go 1.25, the default minimum TLS version is TLS 1.2 [1][2]. This default applies to both TLS clients and servers when the MinVersion field in crypto/tls.Config is not explicitly set [2][3]. While TLS 1.0 remains the minimum version supported by the crypto/tls package, TLS 1.0 and TLS 1.1 are disabled by default [1][2][3]. For backward compatibility, the server-side default can be reverted to TLS 1.0 by setting the GODEBUG environment variable to tls10server=1 [1][2]. This specific backward compatibility setting was introduced in Go 1.22 and is scheduled to be removed in Go 1.27 [3][4].

Citations:


🌐 Web query:

SMTP TLS version requirements minimum TLS 1.2 compatibility

💡 Result:

As of June 2026, the industry standard and security best practice for SMTP is to support and prefer TLS 1.2 and TLS 1.3, while disabling all older, insecure versions [1][2][3]. Minimum TLS Version Requirements The IETF has deprecated TLS 1.0 and 1.1 due to known security vulnerabilities [1][3]. Consequently, the current minimum recommendation is TLS 1.2 [4][5]. Organizations are strongly advised to transition to TLS 1.2 or TLS 1.3 for all email submission, access, and relay traffic [1][6][3]. Major Provider Standards Major email service providers, including Microsoft (Office 365) and Google (Gmail), have moved to enforce these standards [7][8]. - Microsoft 365: Has deprecated TLS 1.0 and 1.1, requiring TLS 1.2 or higher for inbound SMTP connections [7][8]. - Google (Gmail): Requires TLS 1.2+ for bulk senders and strongly encourages it for all connections to ensure deliverability and security [8][9]. - General Industry: Using versions older than TLS 1.2 is widely considered obsolete and is a common cause of delivery failures and security warnings [1][2][8]. Implementation Recommendations To maintain secure and reliable mail flow, administrators should: 1. Configure MTAs to support only TLS 1.2 and 1.3 [1][2][3]. 2. Explicitly disable support for SSL 2.0, 3.0, TLS 1.0, and TLS 1.1 [2][3]. 3. Implement modern security protocols such as MTA-STS (Mail Transfer Agent Strict Transport Security) to enforce TLS requirements and protect against downgrade attacks [2][10]. 4. Where DNSSEC is available, deploy DANE (DNS-based Authentication of Named Entities) to provide further verification of MX certificates [2].

Citations:


Set an explicit minimum TLS version to prevent environment variable downgrades.

Although Go 1.25.1 defaults to TLS 1.2, explicitly setting MinVersion is a security best practice that prevents accidental downgrades via GODEBUG=tls10server=1 and makes the intent unmistakable in code. SMTP requires TLS 1.2+ per industry standards (Microsoft 365, Google, IETF).

Proposed fix
 func smtpTLSConfig() *tls.Config {
 	return &tls.Config{
 		ServerName:         SMTPServer,
+		MinVersion:         tls.VersionTLS12,
 		InsecureSkipVerify: SMTPInsecureSkipVerify, // `#nosec` G402 -- admin-controlled SMTP compatibility option.
 	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func smtpTLSConfig() *tls.Config {
return &tls.Config{
ServerName: SMTPServer,
InsecureSkipVerify: SMTPInsecureSkipVerify, // #nosec G402 -- admin-controlled SMTP compatibility option.
func smtpTLSConfig() *tls.Config {
return &tls.Config{
ServerName: SMTPServer,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: SMTPInsecureSkipVerify, // `#nosec` G402 -- admin-controlled SMTP compatibility option.
}
}
🧰 Tools
🪛 ast-grep (0.43.0)

[warning] 37-40: MinVersionis missing from this TLS configuration. By default, TLS 1.2 is currently used as the minimum when acting as a client, and TLS 1.0 when acting as a server. General purpose web applications should default to TLS 1.3 with all other protocols disabled. Only where it is known that a web server must support legacy clients with unsupported an insecure browsers (such as Internet Explorer 10), it may be necessary to enable TLS 1.0 to provide support. AddMinVersion: tls.VersionTLS13' to the TLS configuration to bump the minimum version to TLS 1.3.
Context: tls.Config{
ServerName: SMTPServer,
InsecureSkipVerify: SMTPInsecureSkipVerify, // #nosec G402 -- admin-controlled SMTP compatibility option.
}
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures

(missing-ssl-minversion-go)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/email.go` around lines 37 - 40, Add an explicit MinVersion field to
the tls.Config struct initialization in the smtpTLSConfig function to enforce
TLS 1.2 as the minimum version. This prevents accidental downgrades and makes
the security requirement explicit in the code, following best practices even
though Go defaults to TLS 1.2.

Source: Linters/SAST tools

}
}

func newSMTPClient(addr string) (*smtp.Client, error) {
if SMTPSSLEnabled || (SMTPPort == 465 && !SMTPStartTLSEnabled) {
conn, err := tls.Dial("tcp", addr, smtpTLSConfig())
if err != nil {
return nil, err
}
client, err := smtp.NewClient(conn, SMTPServer)
if err != nil {
_ = conn.Close()
return nil, err
}
return client, nil
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
return smtp.PlainAuth("", SMTPAccount, SMTPToken, SMTPServer)

client, err := smtp.Dial(addr)
if err != nil {
return nil, err
}

if SMTPStartTLSEnabled {
startTLSSupported, _ := client.Extension("STARTTLS")
if !startTLSSupported {
_ = client.Close()
return nil, fmt.Errorf("SMTP server does not support STARTTLS")
}
if err := client.StartTLS(smtpTLSConfig()); err != nil {
_ = client.Close()
return nil, err
}
}

return client, nil
}

func SendEmail(subject string, receiver string, content string) error {
Expand All @@ -56,47 +98,37 @@ func SendEmail(subject string, receiver string, content string) error {
addr := fmt.Sprintf("%s:%d", SMTPServer, SMTPPort)
to := strings.Split(receiver, ";")
var err error
if SMTPPort == 465 || SMTPSSLEnabled {
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
ServerName: SMTPServer,
}
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", SMTPServer, SMTPPort), tlsConfig)
if err != nil {
return err
}
client, err := smtp.NewClient(conn, SMTPServer)
if err != nil {
return err
}
defer client.Close()
client, err := newSMTPClient(addr)
if err != nil {
return err
}
defer client.Close()
if shouldAuthenticateSMTP() {
if err = client.Auth(auth); err != nil {
return err
}
if err = client.Mail(SMTPFrom); err != nil {
return err
}
receiverEmails := strings.Split(receiver, ";")
for _, receiver := range receiverEmails {
if err = client.Rcpt(receiver); err != nil {
return err
}
}
w, err := client.Data()
if err != nil {
return err
}
_, err = w.Write(mail)
if err != nil {
return err
}
err = w.Close()
if err != nil {
}
if err = client.Mail(SMTPFrom); err != nil {
return err
}
for _, receiver := range to {
if err = client.Rcpt(receiver); err != nil {
return err
}
} else {
err = smtp.SendMail(addr, auth, SMTPFrom, to, mail)
}
w, err := client.Data()
if err != nil {
return err
}
_, err = w.Write(mail)
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
err = client.Quit()
if err != nil {
SysError(fmt.Sprintf("failed to send email to %s: %v", receiver, err))
}
Expand Down
83 changes: 83 additions & 0 deletions common/email_ntlm_auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package common

import (
"errors"
"net/smtp"
"strings"

ntlmssp "github.com/Azure/go-ntlmssp"
)

type smtpAutoAuth struct {
username string
password string
mech string
}

func AutoSMTPAuth(username, password string) smtp.Auth {
return &smtpAutoAuth{username: username, password: password}
}

func (a *smtpAutoAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
useLoginAuth := SMTPForceAuthLogin
if !useLoginAuth && shouldUseSMTPLoginAuth() {
useLoginAuth = !(server != nil && len(server.Auth) == 1 && smtpServerSupportsAuth(server, "NTLM"))
}
if useLoginAuth {
a.mech = "LOGIN"
return "LOGIN", []byte{}, nil
}

switch {
case smtpServerSupportsAuth(server, "PLAIN"):
a.mech = "PLAIN"
return "PLAIN", []byte("\x00" + a.username + "\x00" + a.password), nil
case smtpServerSupportsAuth(server, "LOGIN"):
a.mech = "LOGIN"
return "LOGIN", []byte{}, nil
case smtpServerSupportsAuth(server, "NTLM"):
a.mech = "NTLM"
negotiateMessage, err := ntlmssp.NewNegotiateMessage("", "")
if err != nil {
return "", nil, err
}
return "NTLM", negotiateMessage, nil
default:
a.mech = "PLAIN"
return "PLAIN", []byte("\x00" + a.username + "\x00" + a.password), nil
}
}

func (a *smtpAutoAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if !more {
return nil, nil
}

switch a.mech {
case "LOGIN":
switch string(fromServer) {
case "Username:":
return []byte(a.username), nil
case "Password:":
return []byte(a.password), nil
default:
return nil, errors.New("unknown SMTP AUTH LOGIN challenge")
}
case "NTLM":
return ntlmssp.NewAuthenticateMessage(fromServer, a.username, a.password, nil)
default:
return nil, errors.New("unexpected SMTP auth challenge")
}
}

func smtpServerSupportsAuth(server *smtp.ServerInfo, mechanism string) bool {
if server == nil {
return false
}
for _, auth := range server.Auth {
if strings.EqualFold(auth, mechanism) {
return true
}
}
return false
}
Loading