streamflow/backend/routes/security-config.js

734 lines
19 KiB
JavaScript
Raw Normal View History

/**
* 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;