/** * Missed Breach Controller * * Tracks governance framework false negatives for research integrity */ const MissedBreach = require('../models/MissedBreach.model'); const AuditLog = require('../models/AuditLog.model'); /** * Report a new missed breach * POST /api/admin/missed-breaches */ async function reportMissedBreach(req, res) { try { const { title, incidentDate, severity, description, activityType, missReason, missReasonDetails, linkedAuditLog, sessionId, actualCost, estimatedCost, environment, tags } = req.body; // Validate required fields if (!title || !incidentDate || !severity || !description || !activityType || !missReason) { return res.status(400).json({ success: false, error: 'Missing required fields: title, incidentDate, severity, description, activityType, missReason' }); } const missedBreach = new MissedBreach({ title, incidentDate: new Date(incidentDate), severity, description, activityType, missReason, missReasonDetails, linkedAuditLog, sessionId, actualCost, estimatedCost, environment: environment || process.env.NODE_ENV || 'development', tags: tags || [], reportedBy: req.user?._id || null, status: 'REPORTED' }); await missedBreach.save(); res.status(201).json({ success: true, missedBreach: missedBreach.toObject() }); } catch (error) { console.error('Error reporting missed breach:', error); res.status(500).json({ success: false, error: 'Failed to report missed breach' }); } } /** * Get all missed breaches * GET /api/admin/missed-breaches?status=VERIFIED&severity=HIGH */ async function getMissedBreaches(req, res) { try { const { status, severity, missReason, environment, startDate, endDate, limit = 100 } = req.query; const query = {}; if (status) query.status = status; if (severity) query.severity = severity; if (missReason) query.missReason = missReason; if (environment && environment !== 'all') query.environment = environment; if (startDate && endDate) { query.incidentDate = { $gte: new Date(startDate), $lte: new Date(endDate) }; } const breaches = await MissedBreach.find(query) .sort({ incidentDate: -1 }) .limit(parseInt(limit)) .populate('reportedBy', 'name email') .populate('verifiedBy', 'name email') .lean(); res.json({ success: true, count: breaches.length, breaches }); } catch (error) { console.error('Error fetching missed breaches:', error); res.status(500).json({ success: false, error: 'Failed to fetch missed breaches' }); } } /** * Get missed breach statistics * GET /api/admin/missed-breaches/statistics */ async function getMissedBreachStatistics(req, res) { try { const { startDate, endDate } = req.query; const start = startDate ? new Date(startDate) : new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); const end = endDate ? new Date(endDate) : new Date(); // Get missed breach stats const missedStats = await MissedBreach.getStatistics(start, end); // Get audit log stats for comparison const auditStats = await AuditLog.getStatistics(start, end); // Calculate effectiveness rate const effectiveness = await MissedBreach.calculateEffectivenessRate( start, end, auditStats ); // Get breakdown by reason const byReason = await MissedBreach.getBreakdownByReason(start, end); res.json({ success: true, dateRange: { start, end }, missedBreaches: missedStats, auditLogs: auditStats, effectiveness, breakdownByReason: byReason }); } catch (error) { console.error('Error fetching missed breach statistics:', error); res.status(500).json({ success: false, error: 'Failed to fetch statistics' }); } } /** * Update a missed breach (verify, remediate, etc.) * PATCH /api/admin/missed-breaches/:id */ async function updateMissedBreach(req, res) { try { const { id } = req.params; const updates = req.body; // If verifying, record who verified it if (updates.status === 'VERIFIED' && req.user) { updates.verifiedBy = req.user._id; } const missedBreach = await MissedBreach.findByIdAndUpdate( id, updates, { new: true, runValidators: true } ); if (!missedBreach) { return res.status(404).json({ success: false, error: 'Missed breach not found' }); } res.json({ success: true, missedBreach: missedBreach.toObject() }); } catch (error) { console.error('Error updating missed breach:', error); res.status(500).json({ success: false, error: 'Failed to update missed breach' }); } } /** * Delete a missed breach (if it was a false positive) * DELETE /api/admin/missed-breaches/:id */ async function deleteMissedBreach(req, res) { try { const { id } = req.params; const missedBreach = await MissedBreach.findByIdAndDelete(id); if (!missedBreach) { return res.status(404).json({ success: false, error: 'Missed breach not found' }); } res.json({ success: true, message: 'Missed breach deleted' }); } catch (error) { console.error('Error deleting missed breach:', error); res.status(500).json({ success: false, error: 'Failed to delete missed breach' }); } } module.exports = { reportMissedBreach, getMissedBreaches, getMissedBreachStatistics, updateMissedBreach, deleteMissedBreach };