Monitoring: Threshold-Debounce auf sliding-window (statt floor-to-hour)
Bug: zweimal CRITICAL-Alert für dieselbe Brute-Force-Erkennung kam an. Ursache: detectThresholds() hat als Cutoff für den "existing"-Check floor(now, hour) genutzt. Bei Stundenwechsel resettete der Bucket und der nächste Cron-Lauf fand nichts mehr "in der aktuellen Stunde" → erzeugte zweites SUSPICIOUS-Event → zweite Mail. Fix: gleitendes 60min-Fenster (now - 60min). Pro IP gibt es jetzt zuverlässig max. 1 CRITICAL-Alert pro Stunde, unabhängig von der absoluten Uhrzeit. Live-verifiziert in DB: zwei Alerts kamen um 07:41 und 08:00 – genau das Pattern, das der Stunden-Reset erzeugt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d57a9d01c1
commit
aedd350332
|
|
@ -155,14 +155,16 @@ export async function detectThresholds(): Promise<void> {
|
|||
});
|
||||
for (const g of grouped) {
|
||||
if ((g._count as number) < b.threshold) continue;
|
||||
// Prüfen ob wir für diese (IP+Type+Stunde) schon einen CRITICAL emittiert haben
|
||||
const hourBucket = new Date(now.getTime() - (now.getTime() % (60 * 60 * 1000)));
|
||||
// Debounce: pro IP max. 1 SUSPICIOUS-Alert pro 60min (sliding window).
|
||||
// Vorher: floor(now, hour) → resettete bei Stundenwechsel und produzierte
|
||||
// doppelte Alerts (Bug aus Runde 10).
|
||||
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
|
||||
const existing = await prisma.securityEvent.findFirst({
|
||||
where: {
|
||||
type: 'SUSPICIOUS',
|
||||
severity: 'CRITICAL',
|
||||
ipAddress: g.ipAddress,
|
||||
createdAt: { gte: hourBucket },
|
||||
createdAt: { gte: oneHourAgo },
|
||||
},
|
||||
});
|
||||
if (existing) continue;
|
||||
|
|
|
|||
Loading…
Reference in New Issue