Harden auth, payments, and frontend against review findings.

Close exploitable gaps in booking/payment flows, enforce token versioning and account checks, gate sensitive payment data, and add middleware plus input validation across admin routes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Michilis
2026-06-24 19:59:02 +00:00
co-authored by Cursor
parent fc4af38e8a
commit a6840ea953
37 changed files with 1432 additions and 528 deletions
+20 -3
View File
@@ -247,10 +247,20 @@ async function sendWithConsole(options: SendEmailOptions): Promise<SendEmailResu
/**
* Main send function that routes to the appropriate provider
*/
// Mask an email address for logs: keep first char + domain (e.g. j***@example.com).
function maskEmail(email: string): string {
const [local, domain] = String(email).split('@');
if (!domain) return '***';
const head = local.slice(0, 1);
return `${head}***@${domain}`;
}
async function sendEmail(options: SendEmailOptions): Promise<SendEmailResult> {
const provider = getEmailProvider();
console.log(`[Email] Sending email via ${provider} to ${Array.isArray(options.to) ? options.to.join(', ') : options.to}`);
const recipientCount = Array.isArray(options.to) ? options.to.length : 1;
const sample = Array.isArray(options.to) ? options.to[0] : options.to;
console.log(`[Email] Sending email via ${provider} to ${maskEmail(sample)}${recipientCount > 1 ? ` (+${recipientCount - 1} more)` : ''}`);
switch (provider) {
case 'resend':
@@ -478,7 +488,7 @@ export const emailService = {
// Replace variables
const finalSubject = replaceTemplateVariables(subject, allVariables);
const finalBodyContent = replaceTemplateVariables(bodyHtml, allVariables);
const finalBodyContent = replaceTemplateVariables(bodyHtml, allVariables, true);
const finalBodyHtml = wrapInBaseTemplate(finalBodyContent, { ...allVariables, subject: finalSubject });
const finalBodyText = bodyText ? replaceTemplateVariables(bodyText, allVariables) : undefined;
@@ -1292,7 +1302,14 @@ export const emailService = {
eventId?: string;
sentBy?: string | null;
}): Promise<{ success: boolean; logId?: string; error?: string }> {
const { to, toName, subject, bodyHtml, bodyText, replyTo, eventId, sentBy = null } = params;
const { to: rawTo, toName, subject: rawSubject, bodyHtml, bodyText, replyTo: rawReplyTo, eventId, sentBy = null } = params;
// Strip CR/LF from header-bound values to prevent email header injection
// (e.g. an attacker-supplied subject/replyTo smuggling extra headers/recipients).
const stripHeader = (v?: string) => (v ? v.replace(/[\r\n]+/g, ' ').trim() : v);
const to = stripHeader(rawTo) as string;
const subject = stripHeader(rawSubject) as string;
const replyTo = stripHeader(rawReplyTo);
const allVariables = {
...this.getCommonVariables(),