payload
Version:
Node, React, Headless CMS and Application Framework built on Next.js
57 lines (56 loc) • 2.67 kB
JavaScript
/**
* Sanitizes user data for emails to prevent injection of HTML, executable code, or other malicious content.
* This function ensures the content is safe by:
* - Removing HTML tags
* - Removing control characters
* - Normalizing whitespace
* - Escaping special HTML characters
* - Allowing only letters, numbers, spaces, and basic punctuation
* - Limiting length (default 100 characters)
*
* @param data - data to sanitize
* @param maxLength - maximum allowed length (default is 100)
* @returns a sanitized string safe to include in email content
*/ export function sanitizeUserDataForEmail(data, maxLength = 100) {
if (typeof data !== 'string') {
return '';
}
// Decode HTML numeric entities like < or <
const decodedEntities = data.replace(/&#x([0-9a-fA-F]+);/g, (_, hex)=>String.fromCharCode(parseInt(hex, 16))).replace(/&#(\d+);/g, (_, dec)=>String.fromCharCode(parseInt(dec, 10)));
// Remove HTML tags
const noTags = decodedEntities.replace(/<[^>]+>/g, '');
const noInvisible = noTags.replace(/[\u200B-\u200F\u2028-\u202F\u2060-\u206F\uFEFF]/g, '');
// Remove control characters except common whitespace
const noControls = [
...noInvisible
].filter((char)=>{
const code = char.charCodeAt(0);
return code >= 32 || // printable and above
code === 9 || // tab
code === 10 || // new line
code === 13 // return
;
}).join('');
// Remove '(?' and backticks `
let noInjectionSyntax = noControls.replace(/\(\?/g, '').replace(/`/g, '');
// {{...}} remove braces but keep inner content
noInjectionSyntax = noInjectionSyntax.replace(/\{\{(.*?)\}\}/g, '$1');
// Escape special HTML characters
const escaped = noInjectionSyntax.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
// Normalize whitespace to single space
const normalizedWhitespace = escaped.replace(/\s+/g, ' ');
// Allow:
// - Unicode letters (\p{L})
// - Unicode numbers (\p{N})
// - Unicode marks (\p{M}, e.g. accents)
// - Unicode spaces (\p{Zs})
// - Punctuation: common ascii + inverted ! and ?
const allowedPunctuation = " .,!?'" + '"¡¿、!()〆-';
// Escape regex special characters
const escapedPunct = allowedPunctuation.replace(/[[\]\\^$*+?.()|{}]/g, '\\$&');
const pattern = `[^\\p{L}\\p{N}\\p{M}\\p{Zs}${escapedPunct}]`;
const cleaned = normalizedWhitespace.replace(new RegExp(pattern, 'gu'), '');
// Trim and limit length, trim again to remove trailing spaces
return cleaned.slice(0, maxLength).trim();
}
//# sourceMappingURL=sanitizeUserDataForEmail.js.map