UNPKG

fortify2-js

Version:

MOST POWERFUL JavaScript Security Library! Military-grade cryptography + 19 enhanced object methods + quantum-resistant algorithms + perfect TypeScript support. More powerful than Lodash with built-in security.

326 lines (323 loc) 11.4 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") { try { // **ULTRA-FAST PATH: Try simple approach first** 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, }; /** * **CONVENIENCE FUNCTIONS: Quick access to common serialization patterns** */ // Ultra-fast serialization for performance-critical paths const fastStringify = (obj) => SafeSerializer.stringify(obj, { fastMode: true, maxLength: 1000 }); // Safe serialization for complex objects const safeStringify = (obj) => SafeSerializer.stringify(obj, { fastMode: false }); // Express-safe serialization for req/res objects const expressStringify = (obj) => SafeSerializer.expressStringify(obj, { fastMode: false }); // Cache key generation const generateSafeCacheKey = (args, prefix) => SafeSerializer.generateCacheKey(args, prefix); exports.SafeSerializer = SafeSerializer; exports.expressStringify = expressStringify; exports.fastStringify = fastStringify; exports.generateSafeCacheKey = generateSafeCacheKey; exports.safeStringify = safeStringify; //# sourceMappingURL=safe-serializer.js.map