Skip to content

Build1 publisher3 min readPublished

Wazuh 4.14.7 passes its config check while discarding rules written above their parent

Wazuh 4.14.7 ignores a custom rule whose if_sid parent appears later in the same file, yet wazuh-analysisd -t still exits 0, a dev.to lab test found. The only sign is two warnings in the output, so a deploy gate that reads only the exit code ships a dead rule.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

Illustration accompanying Wazuh 4.14.7 passes its config check while discarding rules written above their parent
Generated illustration

What happened

  • A rule whose if_sid points to its own id is also discarded at load, according to the same measurements on Wazuh 4.14.7.
  • Seven test rules raised the manager's enabled count from 8463 to 8468, an increase of five, and the two missing ids were the forward-reference child and the self-referencing rule.
  • Renaming the file so it loads last fixes neither case, because Wazuh reads each file top to bottom and needs a parent defined before its children.
  • A manager restart resets frequency counters, so two failures, a restart and two more failures never tripped a rule set to fire on four.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision A deploy gate built on the exit code of wazuh-analysisd -t will pass dead rules, so the gate has to parse the warnings or compare enabled-rule counts before and after.
  • constraint Order inside a rule file becomes a correctness property, so any tool that generates Wazuh rules has to sort parents ahead of their children.
  • constraint A single-line wazuh-logtest pass cannot validate composite or sibling-sensitive rules; only replayed event sequences show counter resets and level precedence.
  • exposure Detections lost at the agent or dropped at load leave no trace on the alert side, so coverage gaps surface only when someone reads the manager and agent logs.

According to the dev.to write-up, Wazuh resolves `if_sid` at the moment it reads a rule, against the rules it has already loaded [2]. In the write-up's case A, child 999910 names parent 999911, defined a few lines further down the same file [3]. The manager prints two warnings and keeps going [3]:

``` WARNING: (7617): Signature ID '999911' was not found and will be ignored in the 'if_sid' option of rule '999910'. WARNING: (7619): Empty 'if_sid' value. Rule '999910' will be ignored. ```

The warning text describes two steps. The unknown id is dropped from `if_sid`, the field is then empty, and a rule with an empty `if_sid` is ignored [3]. Case B, rule 999920 naming itself as parent, is not loaded either [4]. Through all of it, `wazuh-analysisd -t` exits 0 and the restart is clean [1].

Counting enabled rules before and after a change catches a drop without knowing its cause. The lab's files held seven rules, and the manager reported 8468 enabled with them and 8463 without [5]. The difference is five, so two were dropped [1]. The missing ids are 999910 and 999920 [5].

The same lab had already shown that order between files matters: a child of sshd rule 5715 was dropped from 0094-test.xml and fired from 0096-test.xml [7]. Renaming cannot fix the in-file cases, because each file is read top to bottom [6]. Parents go above children, and no rule can be its own parent [6]. The authors grep for both codes, in the `-t` output before a restart and in `/var/ossec/logs/ossec.log` after one [14]:

``` /var/ossec/bin/wazuh-analysisd -t 2>&1 | grep -E '\((7617|7619)\)' ```

Every rule id in a 7619 line is a rule that is not running [14].

Loading is only the first gate. Children of one parent are tried highest level first, and a child can attach through `if_group` as well as `if_sid` [8]. Stock rule 40112, level 12, covers failures followed by a success and hangs off 5715 through the `authentication_success` group [8]. The authors added a child of 5715 matching `Accepted password` to local_rules.xml [9]. One sequence sent a lone accepted login. The other sent eight failed passwords from one IP within eight seconds, then the same accepted line [9]. According to the write-up, only the child's level changed between runs, and the level decided which rule got the event after the burst [9]. The rule was valid, so `-t` reported nothing [9].

Composite rules hold state that a one-line logtest cannot exercise [10]. A rule with `frequency="5" timeframe="60"` fed twelve matches fired on the 5th and 10th events, since the count restarts at zero after an alert [10]. With `<same_source_ip/>` and two IPs alternating, it fired on the 9th and 10th, the fifth from each address [11]. Rule changes usually go out with a restart, and a restart clears the count, so the deploy itself resets the rules it ships [12]. All four events in the restart test reached the manager [12].

The last loss happens before analysis, and logtest only sees what is pasted into it [15]. With the client buffer off, one agent was flooded with 900,000 lines and 57,047 arrived [13]. About 6.3% got through [2]. The only trace was one warning in the agent's own ossec.log [13].

This is careful work, and each finding names the rule ids involved and can be replayed. In my context, a Wazuh rule counts as deployed when three things hold [14][5][9]:

1. The 7617/7619 grep on the `-t` output comes back empty. 2. After the restart, the enabled count rises by exactly the number of rules added. 3. A replayed multi-line sequence through wazuh-logtest yields the alert at the expected level.

The first two catch both in-file cases [5][14]. Only the third sees sibling precedence and counters [9][10].

What to watch

  • Whether a Wazuh release after 4.14.7 makes 7617/7619 drops return a nonzero exit from wazuh-analysisd -t.
  • Whether later versions resolve if_sid across a whole file before discarding rules, making parent order within a file irrelevant.
  • Repeat flood results with the agent client buffer turned on, to show how much of the roughly 94% loss it prevents.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories