tractatus/src/models/MissedBreach.model.js
TheFlow 97ed03d180 feat(research): add missed breach tracking system for framework effectiveness measurement
Implements comprehensive system for tracking governance framework false negatives:

Backend:
- src/models/MissedBreach.model.js - Schema with severity, cost tracking, miss reasons
- src/controllers/missedBreach.controller.js - CRUD operations and statistics
- src/routes/missedBreach.routes.js - Admin-only API endpoints
- src/routes/index.js - Route integration at /api/admin/missed-breaches

Functionality:
- Report missed breaches with classification (NO_RULE_EXISTS, RULE_TOO_NARROW, etc.)
- Track actual/estimated costs of missed violations
- Calculate effectiveness rate: detected / (detected + missed)
- Breakdown by miss reason with examples
- Link to original audit logs where available

Statistics:
- Total missed breaches by severity
- Average time to detection
- Cost impact analysis
- Effectiveness comparison vs audit logs

Purpose:
- Measure true framework detection rate (not just blocked actions)
- Identify blind spots in governance rules
- Calculate realistic cost avoidance (avoiding "framework theater")
- Support research integrity claims with empirical data

Related: Cross-environment audit sync (production metrics)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 12:26:53 +13:00

346 lines
8.2 KiB
JavaScript

/*
* Copyright 2025 John G Stroh
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* MissedBreach Model
*
* Tracks incidents where the governance framework FAILED to catch a violation
* Critical for research integrity and framework improvement
*
* Purpose:
* - Measure framework effectiveness (true positive rate)
* - Identify blind spots in governance rules
* - Calculate realistic cost avoidance (detected vs missed)
* - Improve framework through pattern analysis
*/
const mongoose = require('mongoose');
const missedBreachSchema = new mongoose.Schema({
// Incident identification
title: {
type: String,
required: true,
description: 'Brief description of the missed breach'
},
incidentDate: {
type: Date,
required: true,
index: true,
description: 'When the breach occurred (not when it was discovered)'
},
discoveredDate: {
type: Date,
default: Date.now,
index: true,
description: 'When the breach was discovered'
},
// Severity and impact
severity: {
type: String,
enum: ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'],
required: true,
index: true,
description: 'Actual severity of the breach'
},
actualCost: {
type: Number,
default: null,
description: 'Actual cost incurred (if known), in dollars'
},
estimatedCost: {
type: Number,
default: null,
description: 'Estimated cost if actual unknown, in dollars'
},
// What happened
description: {
type: String,
required: true,
description: 'Detailed description of what went wrong'
},
activityType: {
type: String,
enum: [
'CLIENT_COMMUNICATION',
'CODE_GENERATION',
'DOCUMENTATION',
'DEPLOYMENT',
'COMPLIANCE_REVIEW',
'DATA_MANAGEMENT',
'INFRASTRUCTURE',
'OTHER'
],
required: true,
description: 'Type of activity that caused the breach'
},
// Why it was missed
missReason: {
type: String,
enum: [
'NO_RULE_EXISTS', // No governance rule covered this case
'RULE_TOO_NARROW', // Rule exists but didn't match this case
'CLASSIFICATION_ERROR', // Activity classifier misclassified
'ENFORCEMENT_GAP', // Rule exists but wasn't enforced
'USER_OVERRIDE', // User bypassed framework (--no-verify)
'UNKNOWN'
],
required: true,
index: true,
description: 'Why the framework missed this breach'
},
missReasonDetails: {
type: String,
default: null,
description: 'Additional context on why it was missed'
},
// Link to original decision (if available)
linkedAuditLog: {
type: mongoose.Schema.Types.ObjectId,
ref: 'AuditLog',
default: null,
description: 'Audit log entry where framework allowed this action'
},
sessionId: {
type: String,
default: null,
index: true,
description: 'Session where the breach occurred (if known)'
},
// Remediation
remediation: {
type: String,
default: null,
description: 'What was done to fix the breach'
},
preventionAdded: {
type: Boolean,
default: false,
description: 'Whether a new rule/fix was added to prevent recurrence'
},
newRuleId: {
type: String,
default: null,
description: 'ID of new governance rule added (e.g., inst_085)'
},
// Reporting
reportedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
default: null,
description: 'User who reported the missed breach'
},
verifiedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
default: null,
description: 'Admin who verified this was truly a missed breach'
},
status: {
type: String,
enum: ['REPORTED', 'VERIFIED', 'REMEDIATED', 'FALSE_POSITIVE'],
default: 'REPORTED',
index: true,
description: 'Current status of the breach report'
},
// Metadata
environment: {
type: String,
enum: ['development', 'production', 'staging'],
default: 'development',
index: true
},
tags: {
type: [String],
default: [],
description: 'Tags for categorization and searching'
}
}, {
timestamps: true,
collection: 'missedBreaches'
});
// Indexes for common queries
missedBreachSchema.index({ incidentDate: -1 });
missedBreachSchema.index({ severity: 1, incidentDate: -1 });
missedBreachSchema.index({ missReason: 1, incidentDate: -1 });
missedBreachSchema.index({ status: 1, incidentDate: -1 });
missedBreachSchema.index({ environment: 1, incidentDate: -1 });
// Virtual for time to detection
missedBreachSchema.virtual('timeToDetection').get(function() {
if (!this.discoveredDate || !this.incidentDate) return null;
return Math.floor((this.discoveredDate - this.incidentDate) / (1000 * 60 * 60 * 24)); // Days
});
// Static methods
/**
* Get missed breach statistics
*/
missedBreachSchema.statics.getStatistics = async function(startDate, endDate) {
const matchStage = {};
if (startDate && endDate) {
matchStage.incidentDate = { $gte: startDate, $lte: endDate };
}
const stats = await this.aggregate([
{ $match: matchStage },
{
$group: {
_id: null,
totalMissed: { $sum: 1 },
bySeverity: {
$push: '$severity'
},
byReason: {
$push: '$missReason'
},
totalActualCost: {
$sum: { $ifNull: ['$actualCost', 0] }
},
totalEstimatedCost: {
$sum: { $ifNull: ['$estimatedCost', 0] }
},
avgTimeToDetection: {
$avg: {
$divide: [
{ $subtract: ['$discoveredDate', '$incidentDate'] },
1000 * 60 * 60 * 24 // Convert to days
]
}
}
}
}
]);
return stats[0] || null;
};
/**
* Calculate framework effectiveness rate
* Requires audit log stats for comparison
*/
missedBreachSchema.statics.calculateEffectivenessRate = async function(startDate, endDate, auditStats) {
const missedStats = await this.getStatistics(startDate, endDate);
if (!missedStats || !auditStats) {
return null;
}
const detected = auditStats.blocked || 0;
const missed = missedStats.totalMissed || 0;
const total = detected + missed;
if (total === 0) return null;
return {
detected,
missed,
total,
detectionRate: (detected / total) * 100,
missRate: (missed / total) * 100,
effectiveness: detected / total
};
};
/**
* Find breaches by reason
*/
missedBreachSchema.statics.findByReason = function(reason, options = {}) {
const query = { missReason: reason };
if (options.status) {
query.status = options.status;
}
return this.find(query)
.sort({ incidentDate: -1 })
.limit(options.limit || 0);
};
/**
* Get breach breakdown by reason
*/
missedBreachSchema.statics.getBreakdownByReason = async function(startDate, endDate) {
const matchStage = {};
if (startDate && endDate) {
matchStage.incidentDate = { $gte: startDate, $lte: endDate };
}
const breakdown = await this.aggregate([
{ $match: matchStage },
{
$group: {
_id: '$missReason',
count: { $sum: 1 },
totalCost: {
$sum: {
$ifNull: [
'$actualCost',
{ $ifNull: ['$estimatedCost', 0] }
]
}
},
examples: {
$push: {
title: '$title',
severity: '$severity',
incidentDate: '$incidentDate'
}
}
}
},
{
$project: {
_id: 0,
reason: '$_id',
count: 1,
totalCost: 1,
recentExamples: { $slice: ['$examples', 3] }
}
},
{ $sort: { count: -1 } }
]);
return breakdown;
};
const MissedBreach = mongoose.model('MissedBreach', missedBreachSchema);
module.exports = MissedBreach;