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
JavaScript
'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