@ordojs/security
Version:
Security package for OrdoJS with XSS, CSRF, and injection protection
113 lines • 3.4 kB
JavaScript
/**
* Escape special HTML characters to prevent XSS attacks in template interpolations
*/
/**
* Map of characters that need to be escaped in HTML
*/
const HTML_ESCAPE_MAP = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/',
'`': '`',
'=': '=',
};
/**
* Regular expression to match characters that need to be escaped
*/
const HTML_ESCAPE_REGEX = /[&<>"'`=\/]/g;
/**
* Escape HTML special characters to prevent XSS attacks
* @param value String value to escape
* @returns Escaped HTML string
*/
export function escapeHtml(value) {
return String(value).replace(HTML_ESCAPE_REGEX, (char) => HTML_ESCAPE_MAP[char] || char);
}
/**
* Template escaper for automatic HTML escaping in template interpolations
*/
export class TemplateEscaper {
options;
/**
* Create a new TemplateEscaper instance
* @param options Template escaper options
*/
constructor(options = {}) {
this.options = {
escapeByDefault: true,
allowRawHtml: false,
...options,
};
}
/**
* Escape a value for safe HTML interpolation
* @param value Value to escape
* @returns Escaped value
*/
escape(value) {
if (value === null || value === undefined) {
return '';
}
if (typeof value === 'object') {
return escapeHtml(JSON.stringify(value));
}
return escapeHtml(String(value));
}
/**
* Create a tagged template function that automatically escapes interpolated values
* @returns Tagged template function
*/
createEscapedTemplate() {
return (strings, ...values) => {
let result = '';
for (let i = 0; i < strings.length; i++) {
result += strings[i];
if (i < values.length) {
result += this.escape(values[i]);
}
}
return result;
};
}
/**
* Create a tagged template function that allows raw HTML (use with caution)
* @returns Tagged template function for raw HTML
* @throws Error if raw HTML is not allowed in options
*/
createRawTemplate() {
if (!this.options.allowRawHtml) {
throw new Error('Raw HTML templates are not allowed. Set allowRawHtml: true in options to enable.');
}
return (strings, ...values) => {
let result = '';
for (let i = 0; i < strings.length; i++) {
result += strings[i];
if (i < values.length) {
result += String(values[i]);
}
}
return result;
};
}
}
/**
* Default template escaper instance
*/
export const defaultTemplateEscaper = new TemplateEscaper();
/**
* Tagged template function that automatically escapes interpolated values
* @example html\`<div>\${userInput}</div>\` // userInput is automatically escaped
*/
export const html = defaultTemplateEscaper.createEscapedTemplate();
/**
* Create a raw HTML tagged template (use with caution)
* Must be explicitly enabled by setting allowRawHtml: true
*/
export function createRawHtmlTemplate() {
const escaper = new TemplateEscaper({ allowRawHtml: true });
return escaper.createRawTemplate();
}
//# sourceMappingURL=template-escaper.js.map