From cc9773aa33fe5590483b97c27eda7e0f3ce4b0d4 Mon Sep 17 00:00:00 2001 From: jubnl Date: Mon, 11 May 2026 19:12:10 +0200 Subject: [PATCH] fix(notifications): prevent double-escaping HTML in password reset emails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit buildPasswordResetHtml passed a pre-built HTML block to buildEmailHtml, which then escaped it again — rendering raw tags as plain text in the email. --- server/src/services/notifications.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/services/notifications.ts b/server/src/services/notifications.ts index ecc9a3bc..94b6b34c 100644 --- a/server/src/services/notifications.ts +++ b/server/src/services/notifications.ts @@ -316,12 +316,12 @@ export function getEventText(lang: string, event: NotifEventType, params: Record // ── Email HTML builder ───────────────────────────────────────────────────── -export function buildEmailHtml(subject: string, body: string, lang: string, navigateTarget?: string): string { +export function buildEmailHtml(subject: string, body: string, lang: string, navigateTarget?: string, rawBody = false): string { const s = I18N[lang] || I18N.en; const appUrl = getAppUrl(); const ctaHref = escapeHtml(navigateTarget ? `${appUrl}${navigateTarget}` : (appUrl || '')); const safeSubject = escapeHtml(subject); - const safeBody = escapeHtml(body); + const safeBody = rawBody ? body : escapeHtml(body); return ` @@ -396,7 +396,7 @@ function buildPasswordResetHtml(subject: string, strings: PasswordResetStrings,

${safeExpiry}

${safeIgnore}

`; - return buildEmailHtml(subject, block, lang); + return buildEmailHtml(subject, block, lang, undefined, true); } /**