733 lines
19 KiB
JavaScript
733 lines
19 KiB
JavaScript
/**
|
|
* 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;
|