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.

782 lines (776 loc) 28 kB
'use strict'; var events = require('events'); var crypto = require('crypto'); var msgpack = require('msgpack-lite'); var stringify = require('fast-json-stable-stringify'); var errorHandler = require('../../../../utils/errorHandler.js'); var index = require('../../../../components/fortified-function/index.js'); var Logger = require('../../server/utils/Logger.js'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto); var msgpack__namespace = /*#__PURE__*/_interopNamespaceDefault(msgpack); /** * FortifyJS IPC Manager * Secure inter-process communication with encryption and message queuing */ /** * Secure IPC manager with encryption, message queuing, and broadcast capabilities */ class IPCManager extends events.EventEmitter { constructor(config, errorLogger) { super(); this.messageQueues = new Map(); this.messageHandlers = new Map(); this.pendingMessages = new Map(); this.config = config; this.errorLogger = errorLogger; this.isEnabled = config.ipc?.enabled !== false; // Generate encryption key for secure IPC this.encryptionKey = this.generateEncryptionKey(); if (this.isEnabled) { this.setupIPC(); } } /** * Setup IPC with security and message handling */ setupIPC() { // Setup message handlers from config if (this.config.ipc?.events) { Object.entries(this.config.ipc.events).forEach(([eventName, handler]) => { this.messageHandlers.set(eventName, handler); }); } // Setup cluster message handling this.setupClusterMessageHandling(); // Initialize message queues for workers this.initializeMessageQueues(); Logger.logger.info("cluster", "IPC Manager initialized with secure communication"); } /** * Setup cluster message handling with fortified security */ setupClusterMessageHandling() { const fortifiedHandler = index.func(async (worker, message) => { await this.handleIncomingMessage(worker, message); }, { ultraFast: "maximum", auditLog: true, errorHandling: "graceful", }); // Handle messages from workers (master process) if (process.env.NODE_ENV !== "worker") { // Setup proper cluster event handling for master process const clusterModule = require("cluster"); // Listen for messages from all workers clusterModule.on("message", (worker, message) => { fortifiedHandler(worker, message); }); // Setup worker lifecycle event handlers clusterModule.on("fork", (worker) => { worker.on("message", (message) => { fortifiedHandler(worker, message); }); }); } // Handle messages from master (worker process) if (process.env.NODE_ENV === "worker") { process.on("message", (message) => { // Create worker object with proper identification const workerObj = { id: process.env.WORKER_ID || process.pid?.toString() || "unknown", process: { pid: process.pid }, isDead: () => false, }; fortifiedHandler(workerObj, message); }); } } /** * Initialize message queues for workers */ initializeMessageQueues() { const queueConfig = this.config.ipc?.messageQueue || {}; const maxSize = queueConfig.maxSize || 1000; const timeout = queueConfig.timeout || 30000; // Create default queue this.messageQueues.set("default", { messages: [], maxSize, timeout, }); } /** * Handle incoming IPC messages with security validation */ async handleIncomingMessage(worker, message) { try { // Skip Node.js internal cluster messages if (this.isNodeInternalMessage(message)) { return; // Silently ignore internal messages } // Deserialize message first const deserializedMessage = this.deserializeMessage(message); // Validate message structure if (!this.isValidIPCMessage(deserializedMessage)) { // Only log if it looks like it might be our message format if (this.looksLikeFortifyMessage(deserializedMessage)) { // Debug logging to understand what messages are failing if (process.env.DEBUG_IPC) { Logger.logger.info("cluster", "Invalid FortifyJS message:", { original: message, deserialized: deserializedMessage, hasId: !!deserializedMessage?.id, hasType: !!deserializedMessage?.type, hasFrom: !!deserializedMessage?.from, hasTo: !!deserializedMessage?.to, hasTimestamp: !!deserializedMessage?.timestamp, }); } throw new Error("Invalid IPC message structure"); } return; // Silently ignore non-FortifyJS messages } const ipcMessage = deserializedMessage; // Decrypt message if encrypted if (ipcMessage.encrypted && this.config.security?.encryptIPC) { ipcMessage.data = this.decryptMessage(ipcMessage.data); } // Verify message signature if security is enabled if (this.config.security?.encryptIPC && ipcMessage.signature) { if (!this.verifyMessageSignature(ipcMessage)) { throw new Error("Message signature verification failed"); } } // Handle different message types await this.processMessage(ipcMessage, worker); } catch (error) { // Only log errors for messages that look like they should be ours const deserializedMessage = this.deserializeMessage(message); if (this.looksLikeFortifyMessage(deserializedMessage)) { const securityError = errorHandler.createSecurityError(`IPC message handling failed: ${error.message}`, errorHandler.ErrorType.INTERNAL, errorHandler.ErrorSeverity.MEDIUM, "IPC_MESSAGE_ERROR", { operation: "ipc_message_handling" }); this.errorLogger.logError(securityError); } } } /** * Process IPC message based on type */ async processMessage(message, worker) { switch (message.type) { case "request": await this.handleRequest(message, worker); break; case "response": await this.handleResponse(message); break; case "broadcast": await this.handleBroadcast(message); break; case "event": await this.handleEvent(message); break; default: console.warn(`Unknown IPC message type: ${message.type}`); } // Emit IPC event this.emit("ipc:message", message.from, message.to, message.data); } /** * Handle request messages */ async handleRequest(message, _worker) { const handler = this.messageHandlers.get(message.data.event); if (handler) { try { const result = handler(message.data.payload, message.from); // Send response back await this.sendResponse(message.from, message.id, result); } catch (error) { await this.sendResponse(message.from, message.id, { error: error.message, }); } } else { await this.sendResponse(message.from, message.id, { error: "No handler found", }); } } /** * Handle response messages */ async handleResponse(message) { const pending = this.pendingMessages.get(message.id); if (pending) { clearTimeout(pending.timeout); this.pendingMessages.delete(message.id); if (message.data.error) { pending.reject(new Error(message.data.error)); } else { pending.resolve(message.data.result); } } } /** * Handle broadcast messages */ async handleBroadcast(message) { const handler = this.messageHandlers.get(message.data.event); if (handler) { try { handler(message.data.payload, message.from); } catch (error) { console.error(`Broadcast handler error: ${error.message}`); } } this.emit("ipc:broadcast", message.from, message.data); } /** * Handle event messages */ async handleEvent(message) { this.emit(message.data.event, message.data.payload, message.from); } /** * Send message to specific worker */ async sendToWorker(workerId, data) { if (!this.isEnabled) return; const message = this.createMessage("event", workerId, data); await this.deliverMessage(message); } /** * Send message to all workers */ async sendToAllWorkers(data) { if (!this.isEnabled) return; const message = this.createMessage("broadcast", "broadcast", data); await this.deliverMessage(message); } /** * Broadcast message to all workers */ async broadcast(data) { await this.sendToAllWorkers(data); } /** * Send message to random worker */ async sendToRandomWorker(data) { if (!this.isEnabled) return; const workers = this.getAvailableWorkers(); if (workers.length === 0) { throw new Error("No available workers for message delivery"); } const randomWorker = workers[Math.floor(Math.random() * workers.length)]; await this.sendToWorker(randomWorker, data); } /** * Send message to least loaded worker */ async sendToLeastLoadedWorker(data) { if (!this.isEnabled) return; const workers = this.getAvailableWorkers(); if (workers.length === 0) { throw new Error("No available workers for message delivery"); } // Find least loaded worker based on current metrics let leastLoadedWorker = workers[0]; let lowestLoad = this.getWorkerLoad(leastLoadedWorker); for (const workerId of workers) { const load = this.getWorkerLoad(workerId); if (load < lowestLoad) { lowestLoad = load; leastLoadedWorker = workerId; } } await this.sendToWorker(leastLoadedWorker, data); } /** * Send request and wait for response */ async sendRequest(workerId, event, payload, timeout = 5000) { if (!this.isEnabled) { throw new Error("IPC is disabled"); } const message = this.createMessage("request", workerId, { event, payload, }); return new Promise((resolve, reject) => { const timeoutHandle = setTimeout(() => { this.pendingMessages.delete(message.id); reject(new Error("Request timeout")); }, timeout); this.pendingMessages.set(message.id, { resolve, reject, timeout: timeoutHandle, }); this.deliverMessage(message).catch(reject); }); } /** * Send response to request */ async sendResponse(workerId, requestId, result) { const message = this.createMessage("response", workerId, { result }); message.id = requestId; // Use same ID as request await this.deliverMessage(message); } /** * Create IPC message with security features */ createMessage(type, to, data) { const message = { id: this.generateMessageId(), type, from: process.env.NODE_ENV !== "worker" ? "master" : `worker_${process.env.WORKER_ID || "unknown"}`, to, data, timestamp: Date.now(), }; // Encrypt message if security is enabled if (this.config.security?.encryptIPC) { message.data = this.encryptMessage(message.data); message.encrypted = true; message.signature = this.signMessage(message); } return message; } /** * Deliver message to target using real cluster IPC */ async deliverMessage(message) { try { // Serialize message using msgpack for better performance const serializedMessage = this.serializeMessage(message); if (message.to === "broadcast") { // Broadcast to all workers using real cluster API await this.broadcastToAllWorkers(serializedMessage); } else if (process.env.NODE_ENV !== "worker") { // Send from master to specific worker await this.sendToSpecificWorker(message.to, serializedMessage); } else { // Send from worker to master if (process.send) { process.send(serializedMessage); } else { throw new Error("Process.send not available in worker"); } } } catch (error) { const securityError = errorHandler.createSecurityError(`Message delivery failed: ${error.message}`, errorHandler.ErrorType.INTERNAL, errorHandler.ErrorSeverity.MEDIUM, "IPC_DELIVERY_ERROR", { operation: "ipc_message_delivery" }); this.errorLogger.logError(securityError); throw error; } } /** * Broadcast message to all workers using real cluster API */ async broadcastToAllWorkers(serializedMessage) { try { const clusterModule = require("cluster"); if (clusterModule.workers) { const broadcastPromises = Object.values(clusterModule.workers).map((worker) => { return new Promise((resolve) => { if (worker && !worker.isDead() && worker.send) { worker.send(serializedMessage, (error) => { if (error) { console.warn(`Failed to send message to worker ${worker.id}:`, error.message); } resolve(); // Continue even if one worker fails }); } else { resolve(); } }); }); await Promise.all(broadcastPromises); } } catch (error) { throw new Error(`Broadcast failed: ${error.message}`); } } /** * Send message to specific worker using real cluster API */ async sendToSpecificWorker(workerId, serializedMessage) { try { const clusterModule = require("cluster"); const id = workerId.replace("worker_", "").split("_")[0]; const worker = clusterModule.workers?.[id]; if (worker && !worker.isDead() && worker.send) { return new Promise((resolve, reject) => { worker.send(serializedMessage, (error) => { if (error) { reject(new Error(`Failed to send message to worker ${workerId}: ${error.message}`)); } else { resolve(); } }); }); } else { throw new Error(`Worker ${workerId} not found or not available`); } } catch (error) { throw new Error(`Send to worker failed: ${error.message}`); } } /** * Serialize message using msgpack for better performance */ serializeMessage(message) { try { // Use msgpack for binary serialization (more efficient than JSON) if (this.config.ipc?.serialization === "msgpack") { return { ...message, data: msgpack__namespace.encode(message.data), _serialized: "msgpack", }; } else { // Use stable JSON stringify for consistent serialization return { ...message, data: stringify(message.data), _serialized: "json", }; } } catch (error) { // Fallback to regular JSON return { ...message, data: JSON.stringify(message.data), _serialized: "json", }; } } /** * Deserialize message data */ deserializeMessage(message) { try { if (message._serialized === "msgpack") { return { ...message, data: msgpack__namespace.decode(message.data), }; } else { return { ...message, data: JSON.parse(message.data), }; } } catch (error) { // Return as-is if deserialization fails return message; } } /** * Check if message is a Node.js internal cluster message */ isNodeInternalMessage(message) { // Node.js cluster internal messages typically have these patterns if (!message || typeof message !== "object") { return true; // Ignore non-object messages } // Check for Node.js specific properties first if (message.cmd || message.act || message.NODE_UNIQUE_ID || message.NODE_CHANNEL_FD) { return true; } // Check for common Node.js cluster message types if (message.cmd === "NODE_CLUSTER" || message.cmd === "NODE_HANDLE" || message.cmd === "NODE_HANDLE_ACK" || message.cmd === "NODE_HANDLE_NACK") { return true; } // Check for server/worker lifecycle messages if (typeof message === "object" && (message.listening !== undefined || message.disconnect !== undefined || message.suicide !== undefined || message.exitedAfterDisconnect !== undefined)) { return true; } // Common Node.js cluster internal message patterns const internalPatterns = [ '"cmd":', // Node.js cluster command messages '"act":', // Node.js cluster action messages "NODE_", // Node.js internal prefixes '"listening":', // Server listening events '"disconnect":', // Disconnect events '"suicide":', // Worker suicide flag '"exitedAfterDisconnect":', // Worker exit flag ]; // Check if message has internal patterns const messageStr = JSON.stringify(message); for (const pattern of internalPatterns) { if (messageStr.includes(pattern)) { return true; } } // Check for messages that don't have our required structure // but might be valid Node.js messages if (!message.id && !message.type && !message.from && !message.to && !message._serialized) { return true; // Likely a Node.js internal message } return false; } /** * Check if message looks like a FortifyJS message */ looksLikeFortifyMessage(message) { if (!message || typeof message !== "object") { return false; } // Check for FortifyJS cluster lifecycle messages (these are valid but incomplete) const clusterLifecycleTypes = [ "worker_ready", "worker_started", "worker_stopped", "worker_error", "cluster_ready", "health_check", "metrics_update", ]; if (message.type && clusterLifecycleTypes.includes(message.type)) { return false; // These are valid FortifyJS messages but don't need full IPC structure } // Check if it has some FortifyJS IPC message characteristics return (message.id || message.from || message.to || message.timestamp || message._serialized || message.encrypted); } /** * Validate IPC message structure */ isValidIPCMessage(message) { return (message && typeof message.id === "string" && typeof message.type === "string" && typeof message.from === "string" && typeof message.to === "string" && typeof message.timestamp === "number"); } /** * Generate unique message ID */ generateMessageId() { return `msg_${Date.now()}_${Math.random() .toString(36) .substring(2, 11)}`; } /** * Generate encryption key */ generateEncryptionKey() { return crypto__namespace.randomBytes(32).toString("hex"); } /** * Encrypt message data using modern crypto API */ encryptMessage(data) { try { const algorithm = "aes-256-gcm"; const key = crypto__namespace.scryptSync(this.encryptionKey, "salt", 32); const iv = crypto__namespace.randomBytes(16); const cipher = crypto__namespace.createCipheriv(algorithm, key, iv); cipher.setAAD(Buffer.from("ipc-message", "utf8")); let encrypted = cipher.update(JSON.stringify(data), "utf8", "hex"); encrypted += cipher.final("hex"); const authTag = cipher.getAuthTag(); return JSON.stringify({ encrypted, iv: iv.toString("hex"), authTag: authTag.toString("hex"), }); } catch (error) { // Fallback to simple base64 encoding if encryption fails return Buffer.from(JSON.stringify(data)).toString("base64"); } } /** * Decrypt message data using modern crypto API */ decryptMessage(encryptedData) { try { const encryptedObj = JSON.parse(encryptedData); if (encryptedObj.encrypted && encryptedObj.iv && encryptedObj.authTag) { const algorithm = "aes-256-gcm"; const key = crypto__namespace.scryptSync(this.encryptionKey, "salt", 32); const iv = Buffer.from(encryptedObj.iv, "hex"); const decipher = crypto__namespace.createDecipheriv(algorithm, key, iv); decipher.setAuthTag(Buffer.from(encryptedObj.authTag, "hex")); decipher.setAAD(Buffer.from("ipc-message", "utf8")); let decrypted = decipher.update(encryptedObj.encrypted, "hex", "utf8"); decrypted += decipher.final("utf8"); return JSON.parse(decrypted); } else { // Fallback: assume it's base64 encoded const decoded = Buffer.from(encryptedData, "base64").toString("utf8"); return JSON.parse(decoded); } } catch (error) { // If all else fails, try to parse as plain JSON try { return JSON.parse(encryptedData); } catch { return encryptedData; } } } /** * Sign message for integrity verification */ signMessage(message) { const messageString = JSON.stringify({ id: message.id, type: message.type, from: message.from, to: message.to, timestamp: message.timestamp, }); return crypto__namespace .createHmac("sha256", this.encryptionKey) .update(messageString) .digest("hex"); } /** * Verify message signature */ verifyMessageSignature(message) { if (!message.signature) return false; const expectedSignature = this.signMessage(message); return crypto__namespace.timingSafeEqual(Buffer.from(message.signature, "hex"), Buffer.from(expectedSignature, "hex")); } /** * Register event handler */ registerHandler(event, handler) { this.messageHandlers.set(event, handler); } /** * Unregister event handler */ unregisterHandler(event) { this.messageHandlers.delete(event); } /** * Set worker manager reference for integration */ setWorkerManager(workerManager) { this.workerManager = workerManager; } /** * Get available workers from cluster */ getAvailableWorkers() { try { const clusterModule = require("cluster"); if (clusterModule.workers) { return Object.values(clusterModule.workers) .filter((worker) => worker && !worker.isDead()) .map((worker) => `worker_${worker.id}`); } return []; } catch (error) { return []; } } /** * Get worker load score for load balancing */ getWorkerLoad(workerId) { try { // If worker manager is available, get real metrics if (this.workerManager) { const worker = this.workerManager.getWorker(workerId); if (worker) { // Calculate load based on CPU and memory usage return (worker.cpu.usage + worker.memory.percentage) / 2; } } // Fallback: use random load to distribute evenly return Math.random() * 100; } catch (error) { return 50; // Default moderate load } } /** * Get IPC statistics */ getStats() { return { enabled: this.isEnabled, messageQueues: this.messageQueues.size, pendingMessages: this.pendingMessages.size, handlers: this.messageHandlers.size, availableWorkers: this.getAvailableWorkers().length, }; } } exports.IPCManager = IPCManager; //# sourceMappingURL=IPCManager.js.map