const path = require('path'); const DANGEROUS_EXTENSIONS = [ '.php', '.php3', '.php4', '.php5', '.phtml', '.phar', '.jsp', '.jspx', '.jsw', '.jsv', '.jspf', '.asp', '.aspx', '.asa', '.asax', '.ascx', '.ashx', '.asmx', '.htaccess', '.htpasswd' ]; function sanitizeInput(str) { if (typeof str !== 'string') return str; let sanitized = str.replace(/[&<>"']/g, (char) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char])); sanitized = sanitized.replace(/(?:javascript|data|vbscript|expression|on\w+)\s*:/gi, ''); return sanitized.replace(/\x00/g, ''); } function decodeHtmlEntities(str) { if (typeof str !== 'string') return str; const entityMap = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'", '#x27': "'", '#x2F': '/', '#x60': '`' }; const decodeOnce = (input) => input.replace(/&(#x[0-9a-fA-F]+|#\d+|[a-zA-Z]+);/g, (match, code) => { if (code[0] === '#') { const isHex = code[1]?.toLowerCase() === 'x'; const num = isHex ? parseInt(code.slice(2), 16) : parseInt(code.slice(1), 10); return Number.isNaN(num) ? match : String.fromCharCode(num); } const mapped = entityMap[code]; return mapped !== undefined ? mapped : match; }); let output = str; let decoded = decodeOnce(output); while (decoded !== output) { output = decoded; decoded = decodeOnce(output); } return output; } function escapeHtml(str) { if (typeof str !== 'string') return str; return str.replace(/[&<>"']/g, (char) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char])); } function isSafePathSegment(name) { return ( typeof name === 'string' && name.length > 0 && name.length <= 255 && !name.includes('..') && !/[/\\]/.test(name) && !/[\x00-\x1F]/.test(name) ); } function isFileExtensionSafe(filename) { if (!filename || typeof filename !== 'string') return false; const ext = path.extname(filename).toLowerCase(); const nameLower = filename.toLowerCase(); if (DANGEROUS_EXTENSIONS.includes(ext)) return false; if (['.htaccess', '.htpasswd'].includes(nameLower)) return false; return !DANGEROUS_EXTENSIONS.some((dangerExt) => nameLower.includes(`${dangerExt}.`)); } module.exports = { sanitizeInput, decodeHtmlEntities, escapeHtml, isSafePathSegment, isFileExtensionSafe };