Detection you can read,
test, and take with you.
A parent trigger cascade over 4,512 rules and 764 decoders, compatible with the Wazuh ruleset so community content imports and runs unchanged. Every rule is YAML. Every rule can be replayed against real history before it is ever enabled.
168 rule files, one per source. Counts and 24 hour activity are live.
A rule only runs when its parent already matched.
Detection is not a flat list evaluated against every event. Rules form a tree gated on the decoder that produced the event, and the deepest rule that matches wins. That single design decision is why the engine holds thousands of rules without melting: a Windows event never touches an SSH rule.
- ▸Decoder gating. Each event only evaluates the rules whose gate matches its decoder, plus the ungated ones.
- ▸Parent chains. An
if_sidattaches a rule to a specific parent,if_groupto any rule carrying a group. - ▸Category and event code sub gates. A rule pinned to event 4625 is skipped outright for a 3009 event.
- ▸Frequency rules. Fire on a count inside a window, grouped by
samefields, so one brute force is one finding.
One file, every rule in it. Validate and test run against live events before you save.
Learn the condition language once. Use it everywhere.
Rules, correlations, alert triggers and playbook conditions all evaluate through the same operator registry. Adding an operator is one file, and every surface picks it up at once. There are no dialects to keep straight and no place where the same expression means something different.
- ▸25 operators including
contains,matches_regex,in_cidr,in_kvdb,in_iocand the geo family. - ▸Editors that help. Line numbers, per token highlighting, per key autocomplete from the live field catalog, find and replace.
- ▸Compliance travels with the rule. One mapping block feeds all six frameworks and the reports built on them.
- ▸Import what you already have. The Wazuh XML ruleset imports directly, with variables, categories and frequency intact.
A real shipped rule, unedited. This is the whole file format.
The attack is the sequence, not the event.
A failed login is noise. Forty failures then a success from the same address is an incident. Correlation runs over the finding stream in the same grammar as the rules, expressing ordered multi step patterns with grouping, time windows and variation counts.
- ▸Ordered steps each with its own
frequencythreshold and a sharedtimeframe. - ▸
samegroups a chain by source IP, user or host, so two unrelated attacks never merge into one. - ▸
differentcounts variation. Writeuser.name >= 6and you have a password spray. Writesource.geo.countryand you have impossible travel. - ▸Backtest included. Replay the last 24 hours of findings through a draft correlation and see every time it would have fired.
A shipped correlation, description shortened to fit. Match first, then group by source, then require variation.
Every source, one field vocabulary.
A decoder turns a raw line or a vendor JSON blob into ECS field names, so a rule written against source.ip works whether that address arrived as srcip, client_ip or remote_addr. Add the mapping once in a field group and every source that uses that name is normalized from then on.
- ▸Text and JSON in one file. A source gets a syslog parser and a JSON mapper, and the engine picks whichever matches.
- ▸Live parse pane. Paste a real line, or pull one from live traffic, and watch the fields come out as you type.
- ▸Enrichment as configuration. GeoIP, ASN and field renaming are editable field groups, not compiled behaviour.
- ▸Honest labels. Every event records whether your decoder parsed it or it passed through untouched, so gaps are visible instead of silent.
Prematch, parse template and field map in one file, with a live parse pane beside it.
A rule that cannot fire should tell you so.
Detection fails quietly. A rule with a dead parent, an unparseable condition or a window that can never be reached simply never fires, and nothing turns red. NullSOC treats that as a bug class and surfaces it at load, at save and on the page.
- ▸Backtest on real history. Replay 24 hours through the full parent chain and see the exact events that would have fired it.
- ▸Validation at save. A file that would store an unrunnable rule is rejected with the reason, before it reaches the engine.
- ▸Dormant rule detection. Dead parents and unreachable groups are computed at load and raised in Pipeline Health.
- ▸Full trace. Every finding keeps the identifier of the raw line that produced it, all the way back through decode.
The attacks that no single
event ever reveals.
A failed login is noise. A failed login from the address that succeeded ten seconds later is an incident. These ship enabled, covering reconnaissance, initial access, credential access, privilege escalation and persistence, and every one is a YAML file you can tune or extend.
Impossible travel
One account authenticates successfully from two different countries inside the same hour. Either the credentials are shared, or they are stolen.
Password spray
A single address fails logins against six or more different accounts. Low and slow enough per account to stay under a lockout threshold, obvious the moment you group by source.
Brute force, then a success
Six SSH authentication failures from one address, followed by a success from that same address. The success is the part that matters, and alone it looks routine.
Credential compromise
Five failures against one account, then that account logs in. Grouped by user rather than source, so it still fires when the attacker rotates addresses.
Spraying across hosts
One source address failing logins against several different machines. A per host threshold never sees it, because no single host is attacked hard enough to trip.
Login from an unexpected country
A successful authentication whose source geography is outside the countries you operate in. The country list is a condition in the file, so it is yours to set.
Reconnaissance, then exploitation
A scan or web probe from an address, followed by an actual attack from the same address. Separates the internet background noise from someone who came back.
Backdoor account
A new account created on a host, then that account added to a privileged group. Two ordinary administrative events that together are persistence.
Escalation, then a new account
Four attack events against a host, then a user added on that same host. The classic shape of an intrusion establishing a way back in.
Every card above is a file in industry_correlations.yml or local_correlations.yml. Backtest any of them against your last 24 hours before you enable it, and write your own in the same grammar.
Bring a rule you never trusted.
We will backtest it live.
Pick the detection you muted because it was too noisy. We will replay it against real history on the call and show you exactly what it would have caught.
Powered by Codesecure Solutions. Self hosted, cloud or fully managed.
