From 8e56f581027058cc87f12ae7bc837031fa180ae1 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sun, 29 Jun 2025 04:34:47 -0400 Subject: [PATCH] chore: Add additional validation to SMTP_SERVICE env (#9506) Related #9505 --- server/emails/mailer.tsx | 8 +++++--- server/env.ts | 32 ++++++++++++++++++++++++++++++++ server/utils/validators.ts | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/server/emails/mailer.tsx b/server/emails/mailer.tsx index 9c602595a9..ad324d30a2 100644 --- a/server/emails/mailer.tsx +++ b/server/emails/mailer.tsx @@ -3,6 +3,7 @@ import nodemailer, { Transporter } from "nodemailer"; import SMTPTransport from "nodemailer/lib/smtp-transport"; import Oy from "oy-vey"; import env from "@server/env"; +import { InternalError } from "@server/errors"; import Logger from "@server/logging/Logger"; import { trace } from "@server/logging/tracing"; import { baseStyles } from "./templates/components/EmailLayout"; @@ -65,9 +66,10 @@ export class Mailer { dir = "ltr" /* https://www.w3.org/TR/html4/struct/dirlang.html#blocklevel-bidi */, }: Oy.CustomTemplateRenderOptions) => { if (!title) { - throw new Error("`title` is a required option for `renderTemplate`"); - } else if (!bodyContent) { - throw new Error( + throw InternalError("`title` is a required option for `renderTemplate`"); + } + if (!bodyContent) { + throw InternalError( "`bodyContent` is a required option for `renderTemplate`" ); } diff --git a/server/env.ts b/server/env.ts index 7bf74c8ffe..2bca1e50dd 100644 --- a/server/env.ts +++ b/server/env.ts @@ -20,6 +20,7 @@ import { CannotUseWith, CannotUseWithout, CannotUseWithAny, + IsInCaseInsensitive, } from "@server/utils/validators"; import Deprecated from "./models/decorators/Deprecated"; import { getArg } from "./utils/args"; @@ -349,6 +350,37 @@ export class Environment { * See https://community.nodemailer.com/2-0-0-beta/setup-smtp/well-known-services/ */ @CannotUseWith("SMTP_HOST") + @IsInCaseInsensitive([ + "1und1", + "AOL", + "DebugMail.io", + "DynectEmail", + "FastMail", + "GandiMail", + "Gmail", + "Godaddy", + "GodaddyAsia", + "GodaddyEurope", + "hot.ee", + "Hotmail", + "iCloud", + "mail.ee", + "Mail.ru", + "Mailgun", + "Mailjet", + "Mandrill", + "Naver", + "Postmark", + "QQ", + "QQex", + "SendCloud", + "SendGrid", + "SES", + "Sparkpost", + "Yahoo", + "Yandex", + "Zoho", + ]) public SMTP_SERVICE = this.toOptionalString(environment.SMTP_SERVICE); @Public diff --git a/server/utils/validators.ts b/server/utils/validators.ts index 5266b98753..da0d7593a8 100644 --- a/server/utils/validators.ts +++ b/server/utils/validators.ts @@ -86,3 +86,38 @@ export function CannotUseWithAny( }); }; } + +export function IsInCaseInsensitive( + allowedValues: string[], + validationOptions?: ValidationOptions +) { + return function (object: object, propertyName: string) { + registerDecorator({ + name: "isInCaseInsensitive", + target: object.constructor, + propertyName, + constraints: [allowedValues], + options: validationOptions, + validator: { + validate(value: T, args: ValidationArguments) { + if (value === undefined || value === null) { + return true; + } + if (typeof value !== "string") { + return false; + } + const av = args.constraints[0] as string[]; + return av.some( + (allowedValue) => allowedValue.toLowerCase() === value.toLowerCase() + ); + }, + defaultMessage(args: ValidationArguments) { + const av = args.constraints[0] as string[]; + return `${propertyName} must be one of: ${av.join( + ", " + )} (case insensitive).`; + }, + }, + }); + }; +}