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
+28 -6
View File
@@ -1216,8 +1216,26 @@ Spanglish`,
},
];
// Helper function to replace template variables
export function replaceTemplateVariables(template: string, variables: Record<string, any>): string {
// Escape HTML-significant characters so substituted variable values can't inject markup.
function escapeHtmlValue(value: string): string {
return value
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;');
}
// Helper function to replace template variables.
// When `escapeHtml` is true (HTML rendering contexts), substituted *values* are
// HTML-escaped to prevent stored-XSS via attacker-influenced variables (e.g. event
// location, attendee name). The template markup itself is never escaped. Subjects and
// plain-text bodies pass `escapeHtml=false` so they don't show literal entities.
export function replaceTemplateVariables(
template: string,
variables: Record<string, any>,
escapeHtml: boolean = false
): string {
let result = template;
// Handle conditional blocks {{#if variable}}...{{/if}}
@@ -1229,16 +1247,20 @@ export function replaceTemplateVariables(template: string, variables: Record<str
// Replace simple variables {{variable}}
const variableRegex = /\{\{(\w+)\}\}/g;
result = result.replace(variableRegex, (match, varName) => {
return variables[varName] !== undefined ? String(variables[varName]) : match;
if (variables[varName] === undefined) return match;
const raw = String(variables[varName]);
return escapeHtml ? escapeHtmlValue(raw) : raw;
});
return result;
}
// Helper to wrap content in the base template
// Helper to wrap content in the base template.
// `content` is treated as trusted HTML (it is the already-rendered body). The wrapper's
// own variables (subject, year, etc.) are HTML-escaped on substitution.
export function wrapInBaseTemplate(content: string, variables: Record<string, any>): string {
const wrappedContent = baseEmailWrapper.replace('{{content}}', content);
return replaceTemplateVariables(wrappedContent, variables);
const wrappedContent = baseEmailWrapper.replace('{{content}}', () => content);
return replaceTemplateVariables(wrappedContent, variables, true);
}
// Get all available variables for a template by slug