diff --git a/server/api/config/global/comment-email-test.post.ts b/server/api/config/global/comment-email-test.post.ts index 90046ac..1ae2340 100644 --- a/server/api/config/global/comment-email-test.post.ts +++ b/server/api/config/global/comment-email-test.post.ts @@ -2,12 +2,16 @@ import { dbGlobal } from "drizzle-pkg/lib/db"; import { users } from "drizzle-pkg/lib/schema/auth"; import { eq } from "drizzle-orm"; import { requireAdmin } from "#server/utils/admin-guard"; +import { assertUnderRateLimit } from "#server/utils/simple-rate-limit"; +import { getRequestIP } from "h3"; import { CommentEmailTestValidationError, sendCommentEmailTestMail, } from "#server/service/comment-email/test-mail"; export default defineWrappedResponseHandler(async (event) => { + const ip = getRequestIP(event, { xForwardedFor: true }) ?? "unknown"; + assertUnderRateLimit(`admin-config-comment-email-test:${ip}`, 5, 60_000); const admin = await requireAdmin(event); const [adminRow] = await dbGlobal .select({ diff --git a/server/service/comment-email/test-mail.test.ts b/server/service/comment-email/test-mail.test.ts index d1fce96..6cccd86 100644 --- a/server/service/comment-email/test-mail.test.ts +++ b/server/service/comment-email/test-mail.test.ts @@ -23,4 +23,8 @@ describe("comment email test mail validation", () => { test("returns 400-equivalent validation error when admin email is empty", () => { expect(() => assertAdminEmailReady("")).toThrow(CommentEmailTestValidationError); }); + + test("returns 400-equivalent validation error when admin email format is invalid", () => { + expect(() => assertAdminEmailReady("not-an-email")).toThrow(CommentEmailTestValidationError); + }); }); diff --git a/server/service/comment-email/test-mail.ts b/server/service/comment-email/test-mail.ts index b2639bb..5813129 100644 --- a/server/service/comment-email/test-mail.ts +++ b/server/service/comment-email/test-mail.ts @@ -12,6 +12,7 @@ type CommentEmailConfig = { }; const logger = log4js.getLogger("COMMENT_EMAIL_TEST"); +const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; export class CommentEmailTestValidationError extends Error { constructor(message: string) { @@ -40,6 +41,9 @@ export function assertAdminEmailReady(adminEmail: string | null | undefined): as if (!adminEmail || adminEmail.trim().length === 0) { throw new CommentEmailTestValidationError("当前管理员账号未配置邮箱,无法接收测试邮件"); } + if (!EMAIL_REGEX.test(adminEmail.trim())) { + throw new CommentEmailTestValidationError("当前管理员账号邮箱格式不合法"); + } } export async function sendCommentEmailTestMail(input: { @@ -54,6 +58,9 @@ export async function sendCommentEmailTestMail(input: { host: input.config.smtpHost, port: input.config.smtpPort, secure: input.config.smtpSecure, + connectionTimeout: 10_000, + greetingTimeout: 10_000, + socketTimeout: 15_000, auth: { user: input.config.smtpUser, pass: input.config.smtpPass,