Your Detection Fired Twenty-Nine Times. It Found One Thing.
During a tuning cycle we reviewed the cases a rule set had produced over several days. One rule accounted for nearly half of them.
It had not malfunctioned. It had correctly identified an ongoing credential attack, and then correctly identified the same ongoing credential attack every hour for the rest of the week.
Every case was accurate. Every case was actionable in the sense that the underlying problem was real and unresolved. And every case after the first was worthless, because the analyst already knew.
The Failure Is In The Design, Not The Logic
Scheduled detections are usually built to answer a question about a window: did this behaviour occur in the last hour? For behaviour that starts and stops, that is the right question.
For behaviour that persists, it is the wrong one. A campaign running for a week is not a hundred and sixty eight separate events. It is one event with duration, and reporting it as though it were new each hour buries the genuinely new events underneath it.
The damage is not the volume itself. It is what volume does to attention. An analyst who opens nine identical cases learns that this rule does not require reading, and applies that lesson to the tenth, which is the one that differs.
Three Questions, Not One
A rule watching persistent behaviour should be answering three separate questions, each with its own reporting cadence.
Has this appeared somewhere new? A new account, a new source range, a new system. That is a genuine event and should report immediately, once.
Has it escalated? A password accepted where previously all attempts failed. A successful sign-in. A control removed. That is a change in state and should report immediately, every time it happens.
Is it still happening? Yes, it is, and it was yesterday too. That is a status update and belongs in a daily summary, not an alert.
Most detection platforms give you one schedule and one condition, so all three questions get the same answer. The fix is to put the distinction inside the query rather than in the schedule.
Implementation
Run hourly, and gate the output.
WHERE escalation_in_last_hour > 0
OR first_observed > now() - INTERVAL 61 MINUTE
OR toHour(toTimeZone(now(), 'Australia/Sydney')) = 7
The first condition catches state change. The second catches novelty, and it fires exactly once because it compares against a longer history. The third is the daily digest, and it should be expressed in the timezone the analysts work in rather than the timezone the database runs in.
Analysis window and alerting window are different things. Analyse over twenty four hours so a paused campaign still appears in the digest. Scope the immediate triggers to the last hour so a state change alerts once rather than for a full day afterwards.
Say Which One It Is
The alert text should tell the reader what kind of alert it is. A daily summary that reads like breaking news wastes the reader's time twice: once when they respond urgently, and again when they learn not to.
Language that works:
- This account has been seen using this protocol since [date], totalling [n]
- attempts from [n] addresses. This alert is a daily summary of activity that is
- already known and unresolved, not a new event.
And where the activity has paused:
- The most recent attempt was [n] hours ago, so the activity has paused rather
- than stopped. Campaigns of this kind commonly resume.
Neither sentence adds detection capability. Both change what the analyst does in the first ten seconds.
The Measure That Matters
Cases produced is not a measure of coverage. It is a measure of how much reading you have asked somebody to do.
The rule described above went from reporting hourly to reporting roughly once a day per affected account, with immediate alerting preserved for escalation. It detects exactly the same thing. It is now something a person will read.