/** * Route Protection Utility * Ensures all routes have proper error handling (CWE-391 compliance) * Wraps database callbacks and async operations with error handlers */ const logger = require('./logger'); const { sanitizeError } = require('./errorHandler'); /** * Wrap database callback to ensure errors are caught * * @param {Function} callback - Database callback function * @param {Object} res - Express response object * @param {string} context - Context for error logging * @returns {Function} Wrapped callback */ function wrapDbCallback(callback, res, context = 'Database operation') { return function(err, ...args) { if (err) { logger.error(`${context} error:`, { error: err.message, stack: err.stack, context }); const sanitized = sanitizeError(err); return res.status(500).json({ error: sanitized.message, code: sanitized.code, timestamp: new Date().toISOString() }); } try { return callback(err, ...args); } catch (callbackError) { logger.error(`${context} callback error:`, { error: callbackError.message, stack: callbackError.stack, context }); const sanitized = sanitizeError(callbackError); return res.status(500).json({ error: sanitized.message, code: sanitized.code, timestamp: new Date().toISOString() }); } }; } /** * Wrap sync route handler to catch any errors * * @param {Function} handler - Route handler function * @returns {Function} Wrapped handler */ function wrapSyncHandler(handler) { return function(req, res, next) { try { return handler(req, res, next); } catch (error) { logger.error('Sync route handler error:', { error: error.message, stack: error.stack, method: req.method, path: req.path }); next(error); } }; } /** * Wrap async route handler to catch promise rejections * * @param {Function} handler - Async route handler function * @returns {Function} Wrapped handler */ function wrapAsyncHandler(handler) { return function(req, res, next) { Promise.resolve(handler(req, res, next)) .catch((error) => { logger.error('Async route handler error:', { error: error.message, stack: error.stack, method: req.method, path: req.path }); next(error); }); }; } /** * Smart route wrapper - automatically detects and wraps handlers * * @param {Function} handler - Route handler (sync or async) * @returns {Function} Wrapped handler */ function protectRoute(handler) { // Check if handler is async if (handler.constructor.name === 'AsyncFunction') { return wrapAsyncHandler(handler); } return wrapSyncHandler(handler); } /** * Protect all routes in a router * * @param {Object} router - Express router * @param {Array} routes - Array of route definitions */ function protectAllRoutes(router, routes) { routes.forEach(({ method, path, middleware = [], handler }) => { const wrappedHandler = protectRoute(handler); router[method](path, ...middleware, wrappedHandler); }); } /** * Create a safe database wrapper with automatic error handling * * @param {Object} db - Database connection * @returns {Object} Wrapped database object */ function createSafeDb(db) { return { // Wrap db.run run: (sql, params, callback) => { try { return db.run(sql, params, (err, ...args) => { if (err) { logger.error('Database run error:', { error: err.message, sql: sql.substring(0, 100) // Log first 100 chars only }); } if (callback) { try { return callback(err, ...args); } catch (callbackError) { logger.error('Database run callback error:', callbackError); } } }); } catch (error) { logger.error('Database run exception:', error); if (callback) { callback(error); } } }, // Wrap db.get get: (sql, params, callback) => { try { return db.get(sql, params, (err, ...args) => { if (err) { logger.error('Database get error:', { error: err.message, sql: sql.substring(0, 100) }); } if (callback) { try { return callback(err, ...args); } catch (callbackError) { logger.error('Database get callback error:', callbackError); } } }); } catch (error) { logger.error('Database get exception:', error); if (callback) { callback(error); } } }, // Wrap db.all all: (sql, params, callback) => { try { return db.all(sql, params, (err, ...args) => { if (err) { logger.error('Database all error:', { error: err.message, sql: sql.substring(0, 100) }); } if (callback) { try { return callback(err, ...args); } catch (callbackError) { logger.error('Database all callback error:', callbackError); } } }); } catch (error) { logger.error('Database all exception:', error); if (callback) { callback(error); } } }, // Pass through other methods close: (...args) => db.close(...args), serialize: (...args) => db.serialize(...args), parallelize: (...args) => db.parallelize(...args) }; } module.exports = { wrapDbCallback, wrapSyncHandler, wrapAsyncHandler, protectRoute, protectAllRoutes, createSafeDb };