skip to content

How can an asset-owner lookup inside a scheduled SIEM detection create a blind spot?

level: seniorimportance: nice to knowfreq 34%

answer

  1. enrichment must not decide eligibility
  2. unmatched key, empty output field
  3. a downstream filter drops those rows
  4. unregistered assets are not a random sample
  5. track the per-run match rate

basics

~20 s

Events whose key is missing from the lookup come back with the enriched fields empty, and any later filter on those fields drops them. Unregistered assets never alert, while the rule keeps firing elsewhere and looks healthy.

solid answer

~50 s

In SPL, `lookup` is left-outer: an event whose key is absent from the table survives with the output fields unset. The blind spot appears the moment you act on the enriched field — `| where owner_team="payments"`, or using `join type=inner` for the enrichment, or in KQL reaching for `join` (default `innerunique`) instead of the `lookup` operator. Every event from an asset the table does not know about then disappears, and the database that was spun up last week and never registered is exactly the one an intruder finds attractive. The fixes are cheap: keep the enrichment non-filtering, give unmatched keys an explicit value with `fillnull` or a lookup default, route that unowned bucket somewhere a human reviews, and track the per-run match rate as a health signal for the rule. A rule that still produces alerts is not evidence its enrichment covers the estate.

go deeper

for a junior

Know that a lookup adds fields from a table to matching events, and that events whose key is not in the table keep going with those fields empty rather than gaining a value.

for a middle

Explain how an empty enriched field turns into a dropped event: a downstream comparison on that field, an inner join used for enrichment, or a KQL join left at its default flavour.

for a senior

Show that you design enrichment to be additive, give unmatched keys an explicit value, and monitor the per-run match rate, and that you can articulate why unregistered assets are the risky population rather than a random one.

for a principal

Own the split between detection coverage and inventory coverage: decide who is accountable for the unregistered bucket and how a lookup's freshness is guaranteed, rather than leaving it as an invisible dependency of every rule that uses it.

## Enrichment is not correlation, and it is not a filter A lookup attaches attributes from a static or scheduled table — an asset inventory, a service-account-to-owning-team map, an HR feed — to events that already matched the detection. Its job is to make an alert triageable: which team owns this database, is this service account still in use, who is the human behind it. In a managed cloud database estate where the audit stream is the SOC's only view, that mapping is the difference between *some service account read the customer table* and *the shared application service account owned by the payments team, used from a contractor's workstation, read the customer table*. The mistake is letting the enrichment decide **which events are eligible to alert**. ## The mechanics that produce the blind spot In SPL, `lookup my_assets.csv database OUTPUT owner_team` keeps every event. Where the key does not match, `owner_team` is simply unset. That is safe on its own. It stops being safe when: - a later command filters on the enriched field, for example `| where owner_team!="sandbox"` or `| search owner_team=*`, which discards every unmatched event as a side effect; - the enrichment is written as `join type=inner` against a search over an inventory index, which drops non-matching rows by definition; - in Kusto, the author uses `join` — whose default flavour is `innerunique` — rather than the `lookup` operator or an explicit `kind=leftouter`. In each case the rule's population silently becomes *assets present in the table* rather than *assets in the estate*. And the failure is invisible from the outside: the rule keeps alerting on the known estate, its runtime and alert volume look normal, and no error is ever raised. ## Why this failure mode is adversary-relevant rather than merely untidy The assets missing from the inventory are not a random sample. They are the recently created, the temporarily provisioned, the shadow project, the restored snapshot someone stood up to debug a support case and left running with a copy of production data. That population is systematically more attractive to an intruder and systematically less covered by everything else — patching, ownership, access review — for the same reason it is missing from the lookup. So the enrichment does not just lose coverage; it loses it precisely where the risk concentrates. The direction of the claim also matters. An empty `owner_team` is a statement about your inventory, not about the asset. It does not mean the database is unimportant, untouched, or out of scope. It means nobody wrote it down. ## Building it so the blind spot cannot open **Keep enrichment additive.** Detection logic filters on event fields; the lookup only adds context after the decision to alert has been made. **Give unmatched keys a value.** `| fillnull value="UNREGISTERED" owner_team` — or a default match configured on the lookup definition — so the field is never empty and no downstream comparison can quietly drop the row. **Make the unregistered bucket somebody's work.** Those events are not a detection backlog; they are a coverage backlog. Send them to a periodic report for the team that owns the asset inventory, and keep the detection alerting on the behaviour regardless of whether ownership is known. An alert that says *unregistered database, unknown owner, bulk read of a customer table* is more urgent than the same alert with an owner, not less. **Measure the match rate.** For each run, record the share of events that matched a lookup row. A slow decline means the inventory is falling behind the estate; a sudden drop usually means the feed that builds the table broke, or its key format changed — a hostname that gained a domain suffix, a resource identifier that gained a region prefix — which silently zeroes the match rate while the file still looks populated. **Watch freshness in both directions.** A stale table also holds rows that are wrong rather than missing: a decommissioned owning team, a rotated service account name. That routes real alerts to nobody, which is a different failure with the same root cause. ## Testing it The test that catches all of this is one fixture: an event whose key is deliberately absent from the lookup. If the rule still fires on it, the enrichment is additive. If it does not, the enrichment is a filter, and you have just found the population your detection cannot see.

  • What single metric would you track on an enrichment lookup?
    The share of events per run that matched a row, recorded alongside the table's row count and last-modified time. A gentle decline means the inventory is lagging the estate; a cliff usually means the feed broke or the key format changed. Alert volume is useless here, because the rule keeps firing on the assets that do match.
  • The unregistered bucket has four hundred events a day. How do you keep it useful?
    Treat it as a coverage backlog, not an alert queue: a periodic report owned by whoever maintains the asset inventory, listing distinct unmatched keys rather than every event. The detection itself stays unchanged and keeps alerting on the behaviour, with the owner field simply reading unregistered.
  • How would you prove the enrichment is additive rather than filtering?
    Run the rule against a fixture event whose key is deliberately absent from the lookup table. If it still alerts, the enrichment is additive. If it does not, you have found the population the rule cannot see, and the fix is a default value plus removing any downstream comparison on the enriched field.

It is a guest list used as a door policy: the people who never got onto the list are not turned away, they are recorded as never having arrived.

saying these in an interview costs you the question

  • Filters on the enriched field and assumes unmatched events still alert
  • Uses an inner join to attach a static inventory table
  • Reads an empty owner field as meaning the asset is unimportant
  • Never measures how many events matched the lookup
  • Assumes the table is current because the rule still fires

context