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