streamflow/backend/routes/log-management.js
2025-12-17 00:42:43 +00:00

298 lines
7.8 KiB
JavaScript

/**
* Log Management API Routes (CWE-53 Compliance)
* Admin-only endpoints for log retention, archival, and integrity
*/
const express = require('express');
const router = express.Router();
const { authenticate } = require('../middleware/auth');
const { requirePermission } = require('../middleware/rbac');
const { readLimiter, modifyLimiter } = require('../middleware/rateLimiter');
const logManagement = require('../jobs/logManagement');
const SecurityAuditLogger = require('../utils/securityAudit');
const logger = require('../utils/logger');
const path = require('path');
const fs = require('fs').promises;
/**
* GET /api/log-management/statistics
* Get log management statistics
*/
router.get('/statistics',
authenticate,
requirePermission('security.view_audit'),
readLimiter,
async (req, res) => {
try {
const stats = await logManagement.getStatistics();
await SecurityAuditLogger.logSensitiveDataAccess(req.user.userId, 'log_statistics', {
ip: req.ip,
userAgent: req.headers['user-agent']
});
res.json({
success: true,
data: stats
});
} catch (error) {
logger.error('[LogManagement API] Error getting statistics:', error);
res.status(500).json({
success: false,
message: 'Failed to get log statistics'
});
}
}
);
/**
* GET /api/log-management/archives
* List available log archives
*/
router.get('/archives',
authenticate,
requirePermission('security.view_audit'),
readLimiter,
async (req, res) => {
try {
const archives = await logManagement.listArchives();
await SecurityAuditLogger.logSensitiveDataAccess(req.user.userId, 'log_archives_list', {
ip: req.ip,
userAgent: req.headers['user-agent'],
recordCount: archives.length
});
res.json({
success: true,
data: archives
});
} catch (error) {
logger.error('[LogManagement API] Error listing archives:', error);
res.status(500).json({
success: false,
message: 'Failed to list archives'
});
}
}
);
/**
* POST /api/log-management/cleanup
* Manual trigger for log cleanup
* Admin only
*/
router.post('/cleanup',
authenticate,
requirePermission('security.manage'),
modifyLimiter,
async (req, res) => {
try {
const { retentionDays } = req.body;
const days = parseInt(retentionDays) || 90;
if (days < 7) {
return res.status(400).json({
success: false,
message: 'Retention days must be at least 7'
});
}
const result = await logManagement.manualCleanup(days);
await SecurityAuditLogger.logAdminActivity(req.user.userId, 'log_cleanup_manual', {
ip: req.ip,
userAgent: req.headers['user-agent'],
retentionDays: days,
...result
});
res.json({
success: true,
message: `Deleted ${result.auditDeleted + result.aggregatedDeleted} old log entries`,
data: result
});
} catch (error) {
logger.error('[LogManagement API] Error during manual cleanup:', error);
res.status(500).json({
success: false,
message: 'Failed to perform log cleanup'
});
}
}
);
/**
* POST /api/log-management/verify-integrity
* Manual trigger for integrity verification
* Admin only
*/
router.post('/verify-integrity',
authenticate,
requirePermission('security.view_audit'),
modifyLimiter,
async (req, res) => {
try {
const result = await logManagement.manualIntegrityCheck();
if (!result) {
return res.status(500).json({
success: false,
message: 'Integrity verification failed'
});
}
await SecurityAuditLogger.logAdminActivity(req.user.userId, 'log_integrity_check', {
ip: req.ip,
userAgent: req.headers['user-agent'],
verified: result.verified,
tampered: result.tampered
});
res.json({
success: true,
message: result.tampered > 0
? `⚠️ WARNING: ${result.tampered} tampered logs detected!`
: `All ${result.verified} logs verified successfully`,
data: result,
alert: result.tampered > 0
});
} catch (error) {
logger.error('[LogManagement API] Error during integrity check:', error);
res.status(500).json({
success: false,
message: 'Failed to verify log integrity'
});
}
}
);
/**
* GET /api/log-management/archives/download/:filename
* Download a log archive
* Admin only
*/
router.get('/archives/download/:filename',
authenticate,
requirePermission('security.view_audit'),
readLimiter,
async (req, res) => {
try {
const { filename } = req.params;
// Security: prevent path traversal
if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
return res.status(400).json({
success: false,
message: 'Invalid filename'
});
}
// Security: only allow .json.gz files
if (!filename.endsWith('.json.gz')) {
return res.status(400).json({
success: false,
message: 'Invalid file type'
});
}
const archiveDir = path.join(__dirname, '../../data/log-archives');
const filePath = path.join(archiveDir, filename);
// Check if file exists
try {
await fs.access(filePath);
} catch (error) {
return res.status(404).json({
success: false,
message: 'Archive not found'
});
}
// Log the access
await SecurityAuditLogger.logSensitiveDataAccess(req.user.userId, 'log_archive_download', {
ip: req.ip,
userAgent: req.headers['user-agent'],
filename,
accessMethod: 'download'
});
// Send file
res.download(filePath, filename);
} catch (error) {
logger.error('[LogManagement API] Error downloading archive:', error);
res.status(500).json({
success: false,
message: 'Failed to download archive'
});
}
}
);
/**
* DELETE /api/log-management/archives/:filename
* Delete a log archive
* Admin only
*/
router.delete('/archives/:filename',
authenticate,
requirePermission('security.manage'),
modifyLimiter,
async (req, res) => {
try {
const { filename } = req.params;
// Security: prevent path traversal
if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
return res.status(400).json({
success: false,
message: 'Invalid filename'
});
}
// Security: only allow .json.gz files
if (!filename.endsWith('.json.gz')) {
return res.status(400).json({
success: false,
message: 'Invalid file type'
});
}
const archiveDir = path.join(__dirname, '../../data/log-archives');
const filePath = path.join(archiveDir, filename);
// Check if file exists
try {
await fs.access(filePath);
} catch (error) {
return res.status(404).json({
success: false,
message: 'Archive not found'
});
}
// Delete file
await fs.unlink(filePath);
// Log the deletion
await SecurityAuditLogger.logAdminActivity(req.user.userId, 'log_archive_deleted', {
ip: req.ip,
userAgent: req.headers['user-agent'],
filename
});
res.json({
success: true,
message: 'Archive deleted successfully'
});
} catch (error) {
logger.error('[LogManagement API] Error deleting archive:', error);
res.status(500).json({
success: false,
message: 'Failed to delete archive'
});
}
}
);
module.exports = router;