You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Today's detection (evaluateSecurityRules in workers/ingestion-worker.ts) is single-event, single-provider, stateless. Every rule fires on one IngestedEvent row in isolation. That misses the most valuable detections in modern SaaS attacks:
"Okta admin role granted + GitHub org-admin add + Slack workspace-admin grant — same actor, < 1h" → privilege-escalation chain.
"5+ MFA factor resets across distinct users in 10m, same actor" → admin-driven account-takeover precursor.
"OAuth grant + immediate first use from a new IP geo for the same identity" → token-theft starter pattern.
"External user added to private repo + secret committed to that repo, < 5m" → insider/external collusion.
These are stateful, cross-provider, time-bucketed conditions. The current engine cannot express any of them.
Subscribes to the ingestion event bus (or polls IngestedEvent if event bus is off).
For each event, finds matching stateful rules + computes the partition key (actor, person, asset).
Loads or opens a CorrelationWindow for (rule_key, partition_key) valid at the event's occurredAt.
Updates aggregateState (HyperLogLog for distinct, fixed-window counts) and matchedEventIds.
Evaluates the threshold expression; on fire, opens a SecurityFinding linking all matchedEventIds.
A periodic sweeper closes expired open windows.
Backtest
Replay IngestedEvent rows in chronological order through the correlator worker against a candidate rule, accumulating fires. Reuses the #47 backtest harness.
Phasing
Phase
Scope
P1
CorrelationWindow schema; stateful_window rule type (single-input aggregation); correlator worker; backtest support
P2
stateful_sequence rule type (multi-input ordered/unordered); partition key on Person (#45)
P3
Distributed correlator (multi-instance) for high-throughput tenants; sharded windows by partition_key hash
Open questions
HyperLogLog vs. bounded distinct-set for distinct() — accuracy vs. memory.
How to express "anything OAuth + same actor within 5m" without exploding window count for noisy connectors.
Cross-tenant evaluation forbidden by construction; partition_key always scoped per org.
Problem
Today's detection (
evaluateSecurityRulesinworkers/ingestion-worker.ts) is single-event, single-provider, stateless. Every rule fires on oneIngestedEventrow in isolation. That misses the most valuable detections in modern SaaS attacks:These are stateful, cross-provider, time-bucketed conditions. The current engine cannot express any of them.
Goals
#45) / asset.IngestedEventdata.Non-goals
Proposed design
Rule grammar extension (YAML, on top of #47)
New domain model
Correlator worker
A new background worker (
internal/correlator/):IngestedEventif event bus is off).CorrelationWindowfor(rule_key, partition_key)valid at the event'soccurredAt.aggregateState(HyperLogLog fordistinct, fixed-window counts) andmatchedEventIds.SecurityFindinglinking allmatchedEventIds.Backtest
Replay
IngestedEventrows in chronological order through the correlator worker against a candidate rule, accumulating fires. Reuses the #47 backtest harness.Phasing
CorrelationWindowschema;stateful_windowrule type (single-input aggregation); correlator worker; backtest supportstateful_sequencerule type (multi-input ordered/unordered); partition key on Person (#45)Open questions
distinct()— accuracy vs. memory.References
IngestedEvent,SecurityFinding, durable-queue pattern fromIngestionJob.