/** * Security Configuration API Routes * Manage thresholds, risk signatures, and response protocols * Admin-only endpoints for security configuration */ const express = require('express'); const router = express.Router(); const { authenticate } = require('../middleware/auth'); const { requirePermission } = require('../middleware/rbac'); const logger = require('../utils/logger'); const thresholdManager = require('../utils/thresholdManager'); const riskSignatureManager = require('../utils/riskSignatureManager'); const responseProtocolManager = require('../utils/responseProtocolManager'); // Validation middleware const validatePagination = (req, res, next) => { const limit = parseInt(req.query.limit) || 100; req.query.limit = Math.min(Math.max(limit, 1), 1000); next(); }; const validateIdParam = (req, res, next) => { if (!req.params.id || typeof req.params.id !== 'string') { return res.status(400).json({ success: false, message: 'Invalid ID parameter' }); } next(); }; // =========================== // THRESHOLD MANAGEMENT ROUTES // =========================== /** * GET /api/security-config/thresholds * Get all configured thresholds */ router.get('/thresholds', authenticate, requirePermission('security.manage'), validatePagination, async (req, res) => { try { const filters = { patternType: req.query.pattern_type, enabled: req.query.enabled !== undefined ? req.query.enabled === 'true' : undefined, limit: req.query.limit }; const thresholds = await thresholdManager.getThresholds(filters); const stats = await thresholdManager.getStatistics(); res.json({ success: true, data: thresholds, statistics: stats, count: thresholds.length }); } catch (error) { logger.error('[SecurityConfig API] Error getting thresholds:', error); res.status(500).json({ success: false, message: 'Failed to get thresholds', error: error.message }); } } ); /** * GET /api/security-config/thresholds/:id * Get threshold by ID */ router.get('/thresholds/:id', authenticate, requirePermission('security.manage'), validateIdParam, async (req, res) => { try { const threshold = await thresholdManager.getThresholdById(req.params.id); if (!threshold) { return res.status(404).json({ success: false, message: 'Threshold not found' }); } res.json({ success: true, data: threshold }); } catch (error) { logger.error('[SecurityConfig API] Error getting threshold:', error); res.status(500).json({ success: false, message: 'Failed to get threshold', error: error.message }); } } ); /** * POST /api/security-config/thresholds * Create new threshold */ router.post('/thresholds', authenticate, requirePermission('security.manage'), async (req, res) => { try { const { name, description, pattern_type, metric_name, operator, threshold_value, time_window_minutes, severity, enabled } = req.body; // Validation if (!name || !pattern_type || !metric_name || !operator || threshold_value === undefined || !severity) { return res.status(400).json({ success: false, message: 'Missing required fields: name, pattern_type, metric_name, operator, threshold_value, severity' }); } const validOperators = ['>=', '>', '<=', '<', '==', '!=']; if (!validOperators.includes(operator)) { return res.status(400).json({ success: false, message: 'Invalid operator. Must be one of: ' + validOperators.join(', ') }); } const validSeverities = ['low', 'medium', 'high', 'critical']; if (!validSeverities.includes(severity)) { return res.status(400).json({ success: false, message: 'Invalid severity. Must be one of: ' + validSeverities.join(', ') }); } const result = await thresholdManager.createThreshold({ name, description, pattern_type, metric_name, operator, threshold_value: parseInt(threshold_value), time_window_minutes: time_window_minutes ? parseInt(time_window_minutes) : 30, severity, enabled }, req.user.id); res.status(201).json({ success: true, message: 'Threshold created successfully', data: result }); } catch (error) { logger.error('[SecurityConfig API] Error creating threshold:', error); res.status(500).json({ success: false, message: 'Failed to create threshold', error: error.message }); } } ); /** * PUT /api/security-config/thresholds/:id * Update threshold */ router.put('/thresholds/:id', authenticate, requirePermission('security.manage'), validateIdParam, async (req, res) => { try { const updates = {}; const allowedFields = ['name', 'description', 'operator', 'threshold_value', 'time_window_minutes', 'severity', 'enabled']; for (const field of allowedFields) { if (req.body[field] !== undefined) { updates[field] = req.body[field]; } } if (Object.keys(updates).length === 0) { return res.status(400).json({ success: false, message: 'No valid fields to update' }); } await thresholdManager.updateThreshold(req.params.id, updates, req.user.id); res.json({ success: true, message: 'Threshold updated successfully' }); } catch (error) { logger.error('[SecurityConfig API] Error updating threshold:', error); res.status(500).json({ success: false, message: 'Failed to update threshold', error: error.message }); } } ); /** * DELETE /api/security-config/thresholds/:id * Delete threshold */ router.delete('/thresholds/:id', authenticate, requirePermission('security.manage'), validateIdParam, async (req, res) => { try { await thresholdManager.deleteThreshold(req.params.id, req.user.id); res.json({ success: true, message: 'Threshold deleted successfully' }); } catch (error) { logger.error('[SecurityConfig API] Error deleting threshold:', error); res.status(500).json({ success: false, message: 'Failed to delete threshold', error: error.message }); } } ); // =========================== // RISK SIGNATURE ROUTES // =========================== /** * GET /api/security-config/signatures * Get all risk signatures */ router.get('/signatures', authenticate, requirePermission('security.manage'), validatePagination, async (req, res) => { try { const filters = { signatureType: req.query.signature_type, threatLevel: req.query.threat_level, enabled: req.query.enabled !== undefined ? req.query.enabled === 'true' : undefined, limit: req.query.limit }; const signatures = await riskSignatureManager.getSignatures(filters); const stats = await riskSignatureManager.getStatistics(); res.json({ success: true, data: signatures, statistics: stats, count: signatures.length }); } catch (error) { logger.error('[SecurityConfig API] Error getting signatures:', error); res.status(500).json({ success: false, message: 'Failed to get signatures', error: error.message }); } } ); /** * GET /api/security-config/signatures/:id * Get signature by ID */ router.get('/signatures/:id', authenticate, requirePermission('security.manage'), validateIdParam, async (req, res) => { try { const signature = await riskSignatureManager.getSignatureById(req.params.id); if (!signature) { return res.status(404).json({ success: false, message: 'Signature not found' }); } res.json({ success: true, data: signature }); } catch (error) { logger.error('[SecurityConfig API] Error getting signature:', error); res.status(500).json({ success: false, message: 'Failed to get signature', error: error.message }); } } ); /** * POST /api/security-config/signatures * Create new risk signature */ router.post('/signatures', authenticate, requirePermission('security.manage'), async (req, res) => { try { const { name, description, signature_type, pattern, match_type, threat_level, confidence, enabled, auto_block } = req.body; // Validation if (!name || !signature_type || !pattern || !match_type || !threat_level) { return res.status(400).json({ success: false, message: 'Missing required fields: name, signature_type, pattern, match_type, threat_level' }); } const validMatchTypes = ['regex', 'regex_case_insensitive', 'exact', 'contains', 'custom']; if (!validMatchTypes.includes(match_type)) { return res.status(400).json({ success: false, message: 'Invalid match_type. Must be one of: ' + validMatchTypes.join(', ') }); } const validThreatLevels = ['low', 'medium', 'high', 'critical']; if (!validThreatLevels.includes(threat_level)) { return res.status(400).json({ success: false, message: 'Invalid threat_level. Must be one of: ' + validThreatLevels.join(', ') }); } const result = await riskSignatureManager.createSignature({ name, description, signature_type, pattern, match_type, threat_level, confidence: confidence !== undefined ? parseFloat(confidence) : 0.8, enabled, auto_block }, req.user.id); res.status(201).json({ success: true, message: 'Signature created successfully', data: result }); } catch (error) { logger.error('[SecurityConfig API] Error creating signature:', error); res.status(500).json({ success: false, message: 'Failed to create signature', error: error.message }); } } ); /** * PUT /api/security-config/signatures/:id * Update risk signature */ router.put('/signatures/:id', authenticate, requirePermission('security.manage'), validateIdParam, async (req, res) => { try { const updates = {}; const allowedFields = ['name', 'description', 'pattern', 'match_type', 'threat_level', 'confidence', 'enabled', 'auto_block']; for (const field of allowedFields) { if (req.body[field] !== undefined) { updates[field] = req.body[field]; } } if (Object.keys(updates).length === 0) { return res.status(400).json({ success: false, message: 'No valid fields to update' }); } await riskSignatureManager.updateSignature(req.params.id, updates, req.user.id); res.json({ success: true, message: 'Signature updated successfully' }); } catch (error) { logger.error('[SecurityConfig API] Error updating signature:', error); res.status(500).json({ success: false, message: 'Failed to update signature', error: error.message }); } } ); /** * DELETE /api/security-config/signatures/:id * Delete risk signature */ router.delete('/signatures/:id', authenticate, requirePermission('security.manage'), validateIdParam, async (req, res) => { try { await riskSignatureManager.deleteSignature(req.params.id, req.user.id); res.json({ success: true, message: 'Signature deleted successfully' }); } catch (error) { logger.error('[SecurityConfig API] Error deleting signature:', error); res.status(500).json({ success: false, message: 'Failed to delete signature', error: error.message }); } } ); // =========================== // RESPONSE PROTOCOL ROUTES // =========================== /** * GET /api/security-config/protocols * Get all response protocols */ router.get('/protocols', authenticate, requirePermission('security.manage'), validatePagination, async (req, res) => { try { const filters = { triggerType: req.query.trigger_type, severity: req.query.severity, enabled: req.query.enabled !== undefined ? req.query.enabled === 'true' : undefined, limit: req.query.limit }; const protocols = await responseProtocolManager.getProtocols(filters); const stats = await responseProtocolManager.getStatistics(); res.json({ success: true, data: protocols, statistics: stats, count: protocols.length }); } catch (error) { logger.error('[SecurityConfig API] Error getting protocols:', error); res.status(500).json({ success: false, message: 'Failed to get protocols', error: error.message }); } } ); /** * GET /api/security-config/protocols/:id * Get protocol by ID */ router.get('/protocols/:id', authenticate, requirePermission('security.manage'), validateIdParam, async (req, res) => { try { const protocol = await responseProtocolManager.getProtocolById(req.params.id); if (!protocol) { return res.status(404).json({ success: false, message: 'Protocol not found' }); } res.json({ success: true, data: protocol }); } catch (error) { logger.error('[SecurityConfig API] Error getting protocol:', error); res.status(500).json({ success: false, message: 'Failed to get protocol', error: error.message }); } } ); /** * POST /api/security-config/protocols * Create new response protocol */ router.post('/protocols', authenticate, requirePermission('security.manage'), async (req, res) => { try { const { name, description, trigger_type, trigger_condition, actions, severity, enabled, auto_execute, cooldown_minutes } = req.body; // Validation if (!name || !trigger_type || !trigger_condition || !actions || !severity) { return res.status(400).json({ success: false, message: 'Missing required fields: name, trigger_type, trigger_condition, actions, severity' }); } const validTriggerTypes = ['anomaly', 'threshold', 'signature']; if (!validTriggerTypes.includes(trigger_type)) { return res.status(400).json({ success: false, message: 'Invalid trigger_type. Must be one of: ' + validTriggerTypes.join(', ') }); } const validSeverities = ['low', 'medium', 'high', 'critical']; if (!validSeverities.includes(severity)) { return res.status(400).json({ success: false, message: 'Invalid severity. Must be one of: ' + validSeverities.join(', ') }); } if (!Array.isArray(actions) || actions.length === 0) { return res.status(400).json({ success: false, message: 'actions must be a non-empty array' }); } const result = await responseProtocolManager.createProtocol({ name, description, trigger_type, trigger_condition, actions, severity, enabled, auto_execute, cooldown_minutes: cooldown_minutes ? parseInt(cooldown_minutes) : 60 }, req.user.id); res.status(201).json({ success: true, message: 'Protocol created successfully', data: result }); } catch (error) { logger.error('[SecurityConfig API] Error creating protocol:', error); res.status(500).json({ success: false, message: 'Failed to create protocol', error: error.message }); } } ); /** * PUT /api/security-config/protocols/:id * Update response protocol */ router.put('/protocols/:id', authenticate, requirePermission('security.manage'), validateIdParam, async (req, res) => { try { const updates = {}; const allowedFields = ['name', 'description', 'trigger_condition', 'actions', 'severity', 'enabled', 'auto_execute', 'cooldown_minutes']; for (const field of allowedFields) { if (req.body[field] !== undefined) { updates[field] = req.body[field]; } } if (Object.keys(updates).length === 0) { return res.status(400).json({ success: false, message: 'No valid fields to update' }); } await responseProtocolManager.updateProtocol(req.params.id, updates, req.user.id); res.json({ success: true, message: 'Protocol updated successfully' }); } catch (error) { logger.error('[SecurityConfig API] Error updating protocol:', error); res.status(500).json({ success: false, message: 'Failed to update protocol', error: error.message }); } } ); /** * DELETE /api/security-config/protocols/:id * Delete response protocol */ router.delete('/protocols/:id', authenticate, requirePermission('security.manage'), validateIdParam, async (req, res) => { try { await responseProtocolManager.deleteProtocol(req.params.id, req.user.id); res.json({ success: true, message: 'Protocol deleted successfully' }); } catch (error) { logger.error('[SecurityConfig API] Error deleting protocol:', error); res.status(500).json({ success: false, message: 'Failed to delete protocol', error: error.message }); } } ); /** * GET /api/security-config/protocols/:id/history * Get execution history for protocol */ router.get('/protocols/:id/history', authenticate, requirePermission('security.manage'), validateIdParam, validatePagination, async (req, res) => { try { const history = await responseProtocolManager.getExecutionHistory({ protocolId: req.params.id, limit: req.query.limit }); res.json({ success: true, data: history, count: history.length }); } catch (error) { logger.error('[SecurityConfig API] Error getting protocol history:', error); res.status(500).json({ success: false, message: 'Failed to get protocol history', error: error.message }); } } ); // =========================== // DASHBOARD/OVERVIEW ROUTES // =========================== /** * GET /api/security-config/dashboard * Get security configuration dashboard overview */ router.get('/dashboard', authenticate, requirePermission('security.manage'), async (req, res) => { try { const [thresholdStats, signatureStats, protocolStats] = await Promise.all([ thresholdManager.getStatistics(), riskSignatureManager.getStatistics(), responseProtocolManager.getStatistics() ]); res.json({ success: true, data: { thresholds: thresholdStats, signatures: signatureStats, protocols: protocolStats } }); } catch (error) { logger.error('[SecurityConfig API] Error getting dashboard:', error); res.status(500).json({ success: false, message: 'Failed to get dashboard data', error: error.message }); } } ); module.exports = router;