import crypto from 'crypto' export function sanitizeHtml(input: string): string { return input .replace(/[<>'"&]/g, (char) => { switch (char) { case '<': return '<' case '>': return '>' case '"': return '"' case "'": return ''' case '&': return '&' default: return char } }) } export function validateDomain(domain: string): boolean { const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\.[a-zA-Z]{2,}$/ return domainRegex.test(domain) && domain.length <= 253 } export function isValidIPAddress(ip: string): boolean { const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/ const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/ if (ipv4Regex.test(ip)) { return ip.split('.').every(part => { const num = parseInt(part, 10) return num >= 0 && num <= 255 }) } return ipv6Regex.test(ip) } export function hashPassword(password: string, salt?: string): { hash: string, salt: string } { const finalSalt = salt || crypto.randomBytes(32).toString('hex') const hash = crypto.pbkdf2Sync(password, finalSalt, 100000, 64, 'sha256').toString('hex') return { hash, salt: finalSalt } } export function verifyPassword(password: string, hash: string, salt: string): boolean { const { hash: computedHash } = hashPassword(password, salt) return crypto.timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(computedHash, 'hex')) } export function generateSecureToken(length = 32): string { return crypto.randomBytes(length).toString('hex') } export function rateLimitKey(req: any): string { const forwarded = req.headers['x-forwarded-for'] const ip = forwarded ? forwarded.split(',')[0] : req.connection?.remoteAddress return `rate_limit:${ip || 'unknown'}` } export class SecurityError extends Error { constructor(message: string, public code: string) { super(message) this.name = 'SecurityError' } }