228 lines
5.8 KiB
JavaScript
228 lines
5.8 KiB
JavaScript
|
|
/**
|
||
|
|
* 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
|
||
|
|
};
|