Codesecure SolutionsA Codesecure Solutions product
Pillar 01 of 12

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.

nullsoc.in / rules
The NullSOC rules list: one file per source, with rule counts, id ranges and findings in the last 24 hours.

168 rule files, one per source. Counts and 24 hour activity are live.

4,512
Rules shipped
Across 168 source files, every one enabled on first boot.
764
Decoders
Text and JSON, normalizing every source to ECS field names.
25
Operators
One registry, shared by rules, correlation, alerting and playbooks.
24h
Backtest window
Replay a draft rule over real history before you enable it.
The cascade

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_sid attaches a rule to a specific parent, if_group to 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 same fields, so one brute force is one finding.
nullsoc.in / rules / 0095-sshd_rules
The NullSOC rule file editor: an entire rule file as one highlighted YAML document, with validate and test actions.

One file, every rule in it. Validate and test run against live events before you save.

One grammar

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_ioc and 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.
0095-sshd_rules.ymlrule 5720, shipped
# fires once per source IP, not once per failure - id: 5720 level: 10 if_sid: 5716 frequency: 8 timeframe: 300s same: [source.ip] groups: [syslog, sshd, authentication_failures] mitre: [T1110] compliance: pci_dss: [10.2.4, 10.2.5, 11.4] hipaa: [164.312.b] nist_800_53: [AU.14, AC.7, SI.4] gdpr: [IV_35.7.d, IV_32.2] description: "sshd: Multiple authentication failures."

A real shipped rule, unedited. This is the whole file format.

Correlation

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 frequency threshold and a shared timeframe.
  • same groups a chain by source IP, user or host, so two unrelated attacks never merge into one.
  • different counts variation. Write user.name >= 6 and you have a password spray. Write source.geo.country and 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.
industry_correlations.ymlcorrelation 900021, shipped
# one source IP, failing logins against six or more different accounts - id: 900021 level: 12 timeframe: 600s steps: - match: rule.groups contains authentication_failed and source.ip exists frequency: 10 same: [source.ip] different: - user.name >= 6 mitre: [T1110] description: Password spray from one source IP

A shipped correlation, description shortened to fit. Match first, then group by source, then require variation.

Decoders

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.
nullsoc.in / decoders / 0095-sshd_decoders
The NullSOC decoder editor: a decoder file in YAML with prematch, parse template and field mapping, beside suggest parse and test actions.

Prematch, parse template and field map in one file, with a live parse pane beside it.

Test before you trust

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.
Engine facts
Rule modelparent trigger cascade, deepest match wins
Gatesdecoder, category, if_sid, if_group, event code
Rule formatYAML, one file per source
ImportWazuh XML ruleset, direct
Frequencycount and timeframe, grouped by same
Correlationordered steps, same, different, timeframe
Severitylevels 0 to 16, mapped to five labels
Compliance mapping6 frameworks, carried on the rule
ATT&CKtechnique ids on the rule, matrix on the page
Validationat load and at save, surfaced in Pipeline Health
Correlation use cases

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.

T1078 correlation 900011

Impossible travel

One account authenticates successfully from two different countries inside the same hour. Either the credentials are shared, or they are stolen.

same user.name
different source.geo.country_iso_code
window 1 hour
T1110 correlation 900021

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.

same source.ip
different user.name >= 6
threshold 10 failures in 10 min
T1110 correlation 900010

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.

same source.ip
sequence 6 failures then success
window 2 minutes
T1110 correlation 900020

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.

same user.name
sequence 5 failures then success
window 5 minutes
T1110 correlation 900013

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.

same source.ip
different agent.name
window 10 minutes
T1078 correlation 900012

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.

match authentication_success
guard country exists and is not home
window 1 hour
T1595 correlation 900022

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.

same source.ip
sequence recon or scan then attack
window 30 minutes
T1136 correlation 900023

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.

same agent.name
sequence account created then group changed
level 13, high
T1068 correlation 900501

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.

same agent.id
sequence 4 attacks then account added
level 15, critical

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.

Get started

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.