UNPKG

xypriss-security

Version:

XyPriss Security is an advanced JavaScript security library designed for enterprise applications. It provides military-grade encryption, secure data structures, quantum-resistant cryptography, and comprehensive security utilities for modern web applicatio

339 lines (336 loc) 11.9 kB
'use strict'; /** * Safe Serialization Utility for FortifiedFunction * Handles cyclic structures, Express objects, and performance optimization */ class SafeSerializer { /** * **ULTRA-FAST: Primary serialization method with performance optimization** */ static stringify(obj, options = {}) { const opts = { ...this.DEFAULT_OPTIONS, ...options }; // **ULTRA-FAST PATH: Try simple JSON.stringify first** if (opts.fastMode) { try { const result = JSON.stringify(obj); if (result.length <= opts.maxLength) { return result; } } catch { // Fall through to safe serialization } } // **SAFE PATH: Handle complex objects with cyclic references** return this.safeStringify(obj, opts); } /** * **EXPRESS-SAFE: Enhanced JSON.stringify for Express objects** */ static expressStringify(obj, options = {}) { const opts = { ...this.DEFAULT_OPTIONS, ...options }; try { return JSON.stringify(obj, this.createExpressReplacer(opts)); } catch (error) { // Fallback to safe serialization return this.safeStringify(obj, opts); } } /** * **EXPRESS REPLACER: Handles Express req/res objects and circular references** */ static createExpressReplacer(options) { const seen = new WeakSet(); let depth = 0; return function (key, value) { // Track depth if (key === "") depth = 0; else depth++; if (depth > options.maxDepth) { return "[Max Depth Exceeded]"; } // Handle null/undefined if (value === null || value === undefined) { return value; } // Handle circular references if (typeof value === "object" && value !== null) { if (seen.has(value)) { return "[Circular Reference]"; } seen.add(value); } // Handle Express Request objects if (value && typeof value === "object" && value.constructor && value.constructor.name === "IncomingMessage") { return { method: value.method, url: value.url, headers: value.headers, query: value.query, params: value.params, body: value.body, ip: value.ip, _type: "[Express Request]", }; } // Handle Express Response objects if (value && typeof value === "object" && value.constructor && value.constructor.name === "ServerResponse") { return { statusCode: value.statusCode, statusMessage: value.statusMessage, headersSent: value.headersSent, _type: "[Express Response]", }; } // Handle functions if (typeof value === "function") { return `[Function: ${value.name || "anonymous"}]`; } // Handle large strings if (typeof value === "string" && value.length > options.truncateStrings) { return (value.substring(0, options.truncateStrings) + "...[truncated]"); } // Handle Buffers if (value instanceof Buffer) { return `[Buffer: ${value.length} bytes]`; } // Handle other special objects if (value instanceof Date) { return value.toISOString(); } if (value instanceof RegExp) { return value.toString(); } if (value instanceof Error) { return { name: value.name, message: value.message, stack: value.stack, _type: "[Error]", }; } return value; }; } /** * **SAFE SERIALIZATION: Handles all edge cases** */ static safeStringify(obj, options) { const seen = new WeakSet(); let depth = 0; const replacer = (_key, value) => { // Handle primitive values if (value === null || typeof value !== "object") { if (typeof value === "string" && value.length > options.truncateStrings) { return (value.substring(0, options.truncateStrings) + "...[truncated]"); } return value; } // Check depth limit depth++; if (depth > options.maxDepth) { depth--; return "[Max Depth Exceeded]"; } // Handle cyclic references if (seen.has(value)) { return `[Circular:${value.constructor?.name || "Object"}]`; } seen.add(value); // Handle special Express objects if (value.constructor) { const constructorName = value.constructor.name; // Express Request object if (constructorName === "IncomingMessage" || constructorName === "Request") { const result = { method: value.method, url: value.url, headers: this.sanitizeHeaders(value.headers), params: value.params, query: value.query, body: value.body ? "[Request Body]" : undefined, }; depth--; return result; } // Express Response object if (constructorName === "ServerResponse" || constructorName === "Response") { const result = { statusCode: value.statusCode, statusMessage: value.statusMessage, headersSent: value.headersSent, }; depth--; return result; } // Other problematic objects if (["Socket", "Server", "Agent", "TLSSocket"].includes(constructorName)) { depth--; return `[${constructorName}:${value.constructor.name}]`; } } // Handle functions if (typeof value === "function") { depth--; return `[Function:${value.name || "anonymous"}]`; } // Handle Buffers if (Buffer.isBuffer(value)) { depth--; return `[Buffer:${value.length}bytes]`; } // Handle large arrays if (Array.isArray(value) && value.length > 100) { depth--; return `[Array:${value.length}items]`; } // Handle Error objects if (value instanceof Error) { depth--; return { name: value.name, message: value.message, stack: value.stack ? "[Stack Trace]" : undefined, }; } depth--; return value; }; try { const result = JSON.stringify(obj, replacer); // Check length limit if (result.length > options.maxLength) { return (result.substring(0, options.maxLength) + "...[truncated]"); } return result; } catch (error) { // Ultimate fallback return `[Serialization Error: ${error instanceof Error ? error.message : "Unknown"}]`; } } /** * **UTILITY: Sanitize HTTP headers for safe logging** */ static sanitizeHeaders(headers) { if (!headers || typeof headers !== "object") { return headers; } const sanitized = {}; const sensitiveHeaders = [ "authorization", "cookie", "x-api-key", "x-auth-token", ]; for (const [key, value] of Object.entries(headers)) { const lowerKey = key.toLowerCase(); if (sensitiveHeaders.includes(lowerKey)) { sanitized[key] = "[REDACTED]"; } else { sanitized[key] = value; } } return sanitized; } /** * **ULTRA-FAST: Generate cache key with safe serialization** */ static generateCacheKey(args, prefix = "cache") { if (!args || args.length === 0) { return `${prefix}:empty`; } // **EXPRESS DETECTION: Check if arguments contain Express req/res objects** const hasExpressObjects = args.some((arg) => arg && typeof arg === "object" && arg.constructor && (arg.constructor.name === "IncomingMessage" || arg.constructor.name === "ServerResponse" || arg.constructor.name === "Request" || arg.constructor.name === "Response" || (arg.method && arg.url && arg.headers) || // Express Request-like (arg.statusCode !== undefined && arg.headersSent !== undefined)) // Express Response-like ); if (hasExpressObjects) { // **EXPRESS-SAFE PATH: Use Express-safe serialization** const safe = this.expressStringify(args, { fastMode: false, maxDepth: 3, maxLength: 300, truncateStrings: 50, }); return `${prefix}:express:${safe}`; } try { // **ULTRA-FAST PATH: Try simple approach first for non-Express objects** const simple = JSON.stringify(args); if (simple.length <= 500) { return `${prefix}:${simple}`; } } catch { // Fall through to safe approach } // **SAFE PATH: Use safe serialization** const safe = this.stringify(args, { fastMode: false, maxDepth: 5, maxLength: 500, truncateStrings: 100, }); return `${prefix}:${safe}`; } /** * **DEBUG: Safe debug logging** */ static debugLog(label, obj, maxLength = 200) { const serialized = this.stringify(obj, { fastMode: true, maxLength, maxDepth: 3, truncateStrings: 50, }); console.log(`[DEBUG] ${label}: ${serialized}`); } /** * **AUDIT: Safe audit logging with full details** */ static auditLog(obj) { return this.stringify(obj, { fastMode: false, maxDepth: 8, maxLength: 5000, truncateStrings: 500, includeNonEnumerable: false, }); } } SafeSerializer.DEFAULT_OPTIONS = { maxDepth: 10, maxLength: 10000, includeNonEnumerable: false, truncateStrings: 1000, fastMode: false, }; // Cache key generation const generateSafeCacheKey = (args, prefix) => SafeSerializer.generateCacheKey(args, prefix); exports.SafeSerializer = SafeSerializer; exports.generateSafeCacheKey = generateSafeCacheKey; //# sourceMappingURL=safe-serializer.js.map