From 0fc20c8ffda1840063a85023a4c89d73ff5850ec Mon Sep 17 00:00:00 2001 From: goetchstone Date: Tue, 21 Jul 2026 06:23:41 -0400 Subject: [PATCH 1/2] security: escape email HTML + 3 hardening fixes Unescaped user input in every email template (HTML injection into recipient + staff inboxes), login open-redirect, false AES-256-GCM privacy claim, and admin password written to deploy logs. --- .gitignore | 3 + __tests__/email-escape.test.ts | 69 +++++++++++++++++++ app/api/tickets/reply/route.ts | 3 +- app/privacy/page.tsx | 5 +- components/auth/login-form.tsx | 10 ++- deploy.sh | 22 +++--- prisma/seed.ts | 18 ++--- server/email/escape.ts | 17 +++++ .../email/templates/appointment-reminder.ts | 6 +- server/email/templates/appointment.ts | 13 +++- server/email/templates/invoice.ts | 6 +- server/email/templates/ticket.ts | 17 +++-- server/trpc/routers/leads.ts | 5 +- 13 files changed, 161 insertions(+), 33 deletions(-) create mode 100644 __tests__/email-escape.test.ts create mode 100644 server/email/escape.ts diff --git a/.gitignore b/.gitignore index a39720b..2c166b8 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,9 @@ yarn-error.log* .env* !.env.example +# First-run admin credentials written by the seed (0600). Never commit. +.first-admin-credentials + # vercel .vercel diff --git a/__tests__/email-escape.test.ts b/__tests__/email-escape.test.ts new file mode 100644 index 0000000..90347ef --- /dev/null +++ b/__tests__/email-escape.test.ts @@ -0,0 +1,69 @@ +// __tests__/email-escape.test.ts +import { describe, it, expect } from "vitest"; +import { escapeHtml } from "@/server/email/escape"; +import { ticketReplyHtml, ticketConfirmationHtml } from "@/server/email/templates/ticket"; +import { appointmentConfirmationHtml } from "@/server/email/templates/appointment"; + +describe("escapeHtml", () => { + it("neutralizes every HTML-significant character", () => { + expect(escapeHtml(`