112 lines
2.3 KiB
JavaScript
112 lines
2.3 KiB
JavaScript
/**
|
|
* Sanitization utilities for user input
|
|
* Prevents XSS attacks by escaping HTML special characters
|
|
*/
|
|
|
|
/**
|
|
* Escape HTML special characters to prevent XSS
|
|
* @param {string} str - String to escape
|
|
* @returns {string} Escaped string
|
|
*/
|
|
const escapeHtml = (str) => {
|
|
if (typeof str !== "string") {
|
|
return str;
|
|
}
|
|
|
|
const htmlEscapeMap = {
|
|
"&": "&",
|
|
"<": "<",
|
|
">": ">",
|
|
'"': """,
|
|
"'": "'",
|
|
"/": "/",
|
|
};
|
|
|
|
return str.replace(/[&<>"'/]/g, (char) => htmlEscapeMap[char]);
|
|
};
|
|
|
|
/**
|
|
* Sanitize object by escaping all string values
|
|
* @param {Object} obj - Object to sanitize
|
|
* @returns {Object} Sanitized object
|
|
*/
|
|
const sanitizeObject = (obj) => {
|
|
if (typeof obj !== "object" || obj === null) {
|
|
return obj;
|
|
}
|
|
|
|
const sanitized = {};
|
|
for (const [key, value] of Object.entries(obj)) {
|
|
if (typeof value === "string") {
|
|
sanitized[key] = escapeHtml(value);
|
|
} else if (typeof value === "object" && value !== null) {
|
|
sanitized[key] = sanitizeObject(value);
|
|
} else {
|
|
sanitized[key] = value;
|
|
}
|
|
}
|
|
|
|
return sanitized;
|
|
};
|
|
|
|
/**
|
|
* Strip all HTML tags from a string
|
|
* @param {string} str - String to strip
|
|
* @returns {string} String without HTML tags
|
|
*/
|
|
const stripHtml = (str) => {
|
|
if (typeof str !== "string") {
|
|
return str;
|
|
}
|
|
|
|
return str.replace(/<[^>]*>/g, "");
|
|
};
|
|
|
|
/**
|
|
* Validate and sanitize URL
|
|
* @param {string} url - URL to validate
|
|
* @returns {string|null} Sanitized URL or null if invalid
|
|
*/
|
|
const sanitizeUrl = (url) => {
|
|
if (typeof url !== "string") {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const parsed = new URL(url);
|
|
// Only allow http and https protocols
|
|
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
return null;
|
|
}
|
|
return parsed.toString();
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sanitize filename for safe storage
|
|
* @param {string} filename - Filename to sanitize
|
|
* @returns {string} Sanitized filename
|
|
*/
|
|
const sanitizeFilename = (filename) => {
|
|
if (typeof filename !== "string") {
|
|
return "file";
|
|
}
|
|
|
|
// Remove path separators and null bytes
|
|
return filename
|
|
.replace(/[\/\\]/g, "")
|
|
.replace(/\0/g, "")
|
|
.replace(/[^a-zA-Z0-9._-]/g, "-")
|
|
.substring(0, 255);
|
|
};
|
|
|
|
module.exports = {
|
|
escapeHtml,
|
|
sanitizeObject,
|
|
stripHtml,
|
|
sanitizeUrl,
|
|
sanitizeFilename,
|
|
};
|