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:
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user