Detection Rules That Do Not Cry Wolf

Start With What You Will Actually Do About It

The limiting factor in detection is not which rules you can switch on. It is how many alerts a person can read carefully before they start closing them without reading. A rule that fires into a queue nobody works produces a log entry with extra steps, and alert fatigue is what that looks like from the inside.

So decide response capacity first, then build content to fit it. If nobody is on call overnight, a rule that pages at night is a rule that gets muted. If one analyst covers business hours, the honest design is a small set of high-confidence detections that page and a reviewed queue for everything else.

Every Rule Needs a Named Response

Before writing the logic, write the response. What does the person receiving this do first? What do they check, what decides whether it is real, and what happens next if it is?

If the answer is “look at it and close it”, the rule should not page anyone. Sort content into the tiers below and route each rule by the response you wrote for it, not by how interesting the technique is.

Writing the response first also exposes rules nobody can act on. A detection for activity you have no way to confirm and no ability to stop is a research question, not an alert.

  • Page: something is happening now and a person has to act
  • Ticket: worth investigating during working hours, with a due date
  • Dashboard or hunting only: useful context, no alert
  • Enrichment: never alerts on its own, but attaches to other alerts

How a Precise Rule Turns Noisy

A rule can be precise on the day it ships and noisy a quarter later, because the environment changes and the rule does not. The causes below are environment changes rather than tuning failures, and tightening the logic once will not settle them.

Treat detection content as code that decays. It needs an owner, a review rhythm, and a way to test changes before they reach the alert queue. When a rule starts firing on a new legitimate process, the first question is what changed in the estate, not what to suppress.

  • A deployment pipeline starts doing something that used to be unusual
  • A backup or reporting job runs at an hour the rule treats as suspicious
  • An administrator adopts a legitimate tool that looks like an attacker’s
  • A vulnerability scanner sweeps the estate and lights up every network rule
  • A cloud provider changes an API and a service account’s behavior shifts with it

Behavior First, Then Context

Artifact rules match a hash, a domain or a filename. They are cheap and precise, and they stop matching once the artifact changes. Behavior rules describe what the activity does: a process spawning a shell from an office document, an account signing in from a new location and immediately creating a mail forwarding rule, an unexpected host enumerating file shares.

Behavior alone is not enough, because behavior is common. Context separates the alert from the noise: which asset, whose identity, what privilege, what time, and what changed recently. The same command line from a build server and from a finance laptop are not the same event, and a rule that cannot tell them apart will fire on both.

That means much of the work sits in the data model rather than the rule text. Asset criticality, identity attributes and change records let one rule carry a condition that adapts, instead of a fixed threshold that fits nowhere.

Tuning Without Going Blind

Tuning is where noise reduction and coverage pull against each other. Broad suppression makes the queue calm without telling you what stopped being watched.

Exclude narrowly. Not “ignore this process”, but “ignore this process, from this path, run by this service account, on these hosts”. Every exclusion gets a reason, an owner and a review date, and it belongs in version control beside the rule rather than in a console where nobody will find it again.

Watch for the exclusion that is really a finding. If a rule keeps firing because an administrator uses a remote access tool your policy does not permit, the fix is the policy, not the filter.

How Do You Know a Rule Is Working?

Judge rules on dispositions, not volume. For each rule, track how often it fires, how often an alert led to an action, and how often it closed with nothing done. A rule that never fires and a rule that always closes as benign are both retirement candidates, for opposite reasons.

Two other measures are worth keeping. How long an alert waits before a human touches it tells you whether the tier is set correctly. How often the same underlying cause produces repeat alerts tells you when the answer is an environment fix rather than a rule fix.

Then test coverage deliberately. Agreed, scoped simulation of the techniques you care about shows whether the rule fires, whether the alert reaches a person, and whether that person knew what to do with it. Coverage read off a rule inventory is a guess.

Ship Rule Changes Like Code

Detection content deserves the same handling as any other change that can break production. Widening a filter changes what the organization can see, so it should be reviewable by someone other than its author before it takes effect.

A small test corpus helps more than a long review. Keep a set of stored events a rule must match and a set it must not, run each new version against both, and release a changed rule into a non-paging tier first so its volume is visible before anyone is woken by it. Retire rules along the same path rather than deleting them from a console, so the reason survives with the record.

Version control is what makes the rest auditable. Rule text, exclusions, tier, owner and response notes belong in one repository, so the question of who changed a detection and why has an answer that does not depend on memory.

Related service: Related service: The security monitoring service page covers the monitoring pipeline and the people who answer the alerts. This piece covers the layer above it: how the rules feeding that queue are written, tiered and maintained.

Related service: security monitoring and detection.

What a Behavior Rule Needs From the Log Source

A behavior rule can only test fields that exist in the events you actually store, and the gap between what a platform is capable of emitting and what it emits with default settings is where new rules quietly fail. Windows process creation is the standard example. Event 4688 records the new process and the creating process, but it records the command line only when audit process creation is enabled and the "Include command line in process creation events" policy is turned on. Sysmon event ID 1 carries the parent command line and file hashes that the native event does not. A rule written against a public example that assumes hashes and full ancestry will match nothing on a host that only produces 4688.

The same gap exists in cloud sources. CloudTrail records management events by default, while data events such as S3 object access and Lambda invocations are opt in and billed separately, so a rule about object reads finds nothing until someone enables them. Identity sign in and audit logs stay inside the identity service, with short portal retention, until a diagnostic setting exports them to somewhere a rule engine can query. VPC flow logs aggregate over a one or ten minute interval and carry no payload, so they support volume and peer analysis but cannot support content matching, and fields such as TCP flags appear only in a custom format.

Two more things decide whether the rule works as written. Field names differ per source, so a rule that matches process.command_line against one mapping silently matches nothing against another, and a rule engine that treats a missing field as false will not tell you the difference. Time is the other one: events carry both an event timestamp and an index timestamp, host clocks drift when NTP is broken, and a timestamp written without an offset makes ordering across sites guesswork. Before writing the logic, confirm the following for each source the rule touches.

  • The field is emitted with the settings currently deployed, not with the settings the documentation assumes
  • It survives parsing intact, since long command lines and URLs are commonly truncated at a fixed length
  • It is searchable in the tier the rule engine queries, not merely retained somewhere
  • It is populated on every host in scope, rather than only those covered by one agent policy
  • Its timestamps carry a UTC offset, and the hosts producing them are actually synchronized

Rules That Fail Silently

A rule that produces nothing looks identical whether the technique never occurred, the agent stopped reporting, a parser broke after an upgrade, or an index was renamed. Before retiring a rule that never fires, confirm the events it reads are still arriving, because a dead feed retires rules on its own and never says so.

The direct test is a canary. Generate a harmless event on a schedule that must traverse the same path as real detections, on a sample of hosts, and write a dedicated non paging rule that alerts on its absence rather than its presence. This exercises collection, transport, parsing, the rule engine and the routing step together, which is what you want, since any one of them failing produces the same silence.

Alongside the canary, a handful of pipeline health signals catch the same failures earlier and locate them more precisely.

  • Events per source per hour, compared against the same hour a week earlier so weekly seasonality does not mask a drop
  • Reporting agent count against the asset inventory, which finds hosts that were rebuilt without the agent
  • Parse failure and unknown source type rates, which spike after a vendor changes a log format
  • Ingest lag, measured as index time minus event time, which shows a backlog before a queue drains into loss
  • Schema differences after an agent or API version upgrade, where a renamed field breaks matching without breaking ingestion
  • Quota or license enforcement that begins sampling or dropping data instead of refusing it

Rules That Carry State

A stateless rule evaluates one event and either matches or does not. A stateful rule counts, counts distinct values, tracks first occurrences, or requires a sequence within a span, and it can be correct about every individual event while being wrong about the window. That is a different class of defect, and reading the rule text will not reveal it.

Scheduled rules have to reconcile two intervals: how often the rule runs and how far back it looks. If the lookback equals the schedule and any event arrives after its window has passed, that event is never evaluated by anything. The usual answer is a lookback longer than the interval plus a stable alert key to deduplicate the resulting overlap, which trades duplicate suppression work for not losing late events.

First seen logic depends on history the engine can query, not history you have merely kept. A rule that alerts on a service account authenticating from a country it has not used in thirty days needs thirty days of searchable data, and data moved to a cold or frozen tier can be retained and still be invisible to the rule. The same constraint governs how long a rule stays unreliable after a platform migration, since the baseline restarts when the searchable history does. Each stateful rule should have an explicit answer to the following.

  • What the entity key is: user, host, source address, or a pair, since the key decides what a count means
  • What the window is, and whether it slides or resets on a boundary
  • What happens to an event that arrives after its window closed
  • What happens to accumulated state on rule edit, engine restart, or platform upgrade
  • How the count behaves during a backfill, when a day of events arrives in a minute

Suppression, Deduplication and Throttling Are Not the Same Control

These three get collapsed into the single word tuning, and they have different consequences. Suppression prevents the match from being evaluated or recorded, so nothing survives to look at later. Deduplication still records every match and groups them into one alert with a count and a member list. Throttling caps how many alerts or notifications an interval may produce and discards or delays the remainder.

Only suppression destroys evidence, which is why it belongs to the narrow, documented exclusions and nothing else. Throttling has the worst failure timing of the three, because it engages when volume is highest, and volume is highest during an actual incident. Applying a rate limit to the notification channel rather than to alert creation keeps the record complete while still protecting the person receiving pages.

Deduplication is usually the control people actually wanted. Grouping by entity turns three hundred alerts about one host into one alert with three hundred members, which changes the reading experience without changing what was captured, and it keeps the repeat count visible as a number instead of hiding it as a filtered volume.

Rules That Have to Move Between Platforms

Sigma is a YAML format for expressing detection logic independently of any one query language, with backends that convert a rule into SPL, KQL, EQL, Lucene and others, and processing pipelines that map generic field names onto a specific schema. Simple field equality, wildcards and value lists translate cleanly. Aggregations, joins against reference tables, sequence and correlation constructs, and anything using a platform specific function do not translate reliably, and support for them varies by backend rather than by the specification.

Regular expressions are the most common surprise in a conversion. RE2, used by Go and by query languages built on it, supports no backreferences and no lookaround, so a PCRE pattern that relies on either will be rejected outright rather than degraded. Case sensitivity defaults differ between engines, and wildcard handling of backslashes in Windows paths differs again, so a pattern that is correct in one dialect can match nothing or match everything in another.

The honest tradeoff is that a fully portable rule set is a lowest common denominator rule set. Some detections genuinely need the engine feature, such as a native process ancestry function or a sequence with a maximum span, and rewriting those as portable logic makes them both slower and less accurate. Writing the ordinary majority in a portable format and the few that need engine features natively, with the reason recorded on the native ones, costs less than converting everything twice.

Frequently Asked Questions

Is detection content the same thing as a SIEM?

No. Detection content is the rules, exclusions, tier assignments and response notes; the SIEM, EDR or cloud detection service is the engine that runs them and the pipeline that feeds it. The distinction matters most during a platform change, because parsers, field mappings and collection settings are the expensive part to move and rule text is usually the cheapest. A rule is also not a control. It observes and reports, and it prevents nothing on its own unless the platform executing it can also take an action.

Where do these rules run, and does that change what they can see?

Endpoint agent rules evaluate on the host, see process, file and registry activity with parent lineage available locally, and on many agents can act on the result. Backend rules evaluate after ingest, can join across identity, network and cloud sources, but see only fields that actually shipped and are subject to ingest lag. Cloud provider detections run on the provider's own telemetry and observe control plane activity that never reaches your collector. Most estates need all three, and the same technique often requires a differently written rule in each.

What has to exist in the logs before writing rules is worth the effort?

The fields the rules read, present in the stored event rather than in the vendor's documentation. Verify per source that the field is emitted with the settings currently deployed, that it survives parsing without truncation, that it is searchable in the tier the rule engine queries, and that timestamps carry an offset so events from different hosts can be ordered. Where a field is missing, changing the collection setting is almost always cheaper than writing a rule that works around its absence.

How does bringing up a new rule set actually proceed?

Confirm sources and field availability first, then backtest each candidate rule against stored history to see what it would have produced. Estimate by entity rather than by total count, because four hundred hits from one build server and four hundred hits from three hundred users are different problems, and only the second is a tiering question. Fix the parsing and collection gaps the backtest exposes before adjusting rule logic, since a rule tuned against incomplete data has to be tuned again once the data is complete.

What do we have to decide or supply?

A named person, not a team alias, who can approve rule changes and sign off on the scope of any simulation. A description of what your automation and service accounts are expected to do, since those accounts produce most of the activity that resembles an attacker and nobody outside your organization can infer their intended behavior. And a decision about who may change collection settings, because agent configuration, Windows audit policy and cloud diagnostic settings frequently sit with teams outside security, and rules stall waiting on them.

Can we start from the vendor's default rule set?

Yes, and it is a reasonable starting point, but treat it as third party content rather than as yours. Defaults are written for a generic estate, they carry the vendor's severity values rather than your routing decisions, and many assume field mappings your pipeline may not produce. They also update on the vendor's schedule, so what fires can change without any corresponding change on your side, which is worth pinning or reviewing the way you would review a dependency update.

When is a detection rule the wrong answer?

When the behavior can be removed rather than watched. If macros from internet sources can be blocked by policy, legacy authentication protocols can be disabled, or local administrator rights can be withdrawn, the configuration change ends the entire class of alert and costs nobody a night. Detection earns its place where the same activity is legitimate in some contexts and not in others, which is exactly the case prevention cannot decide on its own.