Wazuh custom rule never fires: shadowed by a sibling rule (and analysisd -t exits 0)

Wazuh custom rule never fires: shadowed by a sibling rule (and analysisd -t exits 0)

You wrote a custom rule. wazuh-analysisd -t exits 0, a one-line logtest even shows it matching, and in production it never fires. One cause: another child of the same parent matched the event first. When several children of one parent can match an event, one of them takes it. In our measurements, on Wazuh 4.14.7, the higher level went first, and at equal level the rule loaded first went first. If a sibling with a higher level matches your event, your rule never fires, and neither the config check nor a one-line logtest shows it. Why it happens We placed one rule in local_rules.xml and changed only its parent and level between runs: $PARENT Accepted password probe Enter fullscreen mode Exit fullscreen mode Two inputs through wazuh-logtest: S1, one Accepted password line; S2, 8 Failed password lines from the same IP within 8 seconds, then the same Accepted password line. Case Parent Level -T S1 lands on S2 lands on A 5715 9 exit 0, no warning 100080 (ours) 40112 level 12, ours loses B 5715 13 exit 0, no warning 100080 100080 level 13, ours wins C 5700 9 exit 0, no warning 100080 100080 D 40112 12 exit 0, no warning 5715 level 3, ours does not fire 100080 control no custom rule 5715 level 3 40112 level 12 We ran the matrix twice; all 8 verdicts matched. Four things follow. A against B: same parent, only the level changed from 9 to 13, and the outcome of S2 flipped. Level decides the order among siblings, not if_sid and not the file. At equal level, load order decides. On a test ruleset on a throwaway 4.14.7 manager, a sibling at the same level that was loaded earlier took the event. This matches a reading of the loader source: a new child is inserted in front of the first sibling with a lower level, and behind siblings with the same level. The config check says nothing about this. wazuh-analysisd -t exited 0 with no warning in all four cases, including the one where our rule lost. One line in logtest is not enough. In case A, a one-line test passes. Only the sequence shows the loss. Check it in one minute Feed wazuh-logtest the real sequence of events, not one line, and read which rule took the last one: /var/ossec/bin/wazuh-logtest &1 | grep -E "^[[:space:]]+(id|level|description): " | tail -3 Enter fullscreen mode Exit fullscreen mode If the ID is a sibling's rather than yours, your rule is shadowed on that sequence. Keep the 2>&1: logtest on 4.14 prints its results to stderr. To find the sequence, look at alerts from siblings of your rule, meaning other children of the same parent. Their full_log is the event that sibling took. Confirm before you fix A sibling that fired proves only that the sibling matched something. It does not prove your rule would have matched the same event. On a test ruleset we built three candidates this way; one of them, a rule on a user name that none of the events had, looked shadowed and was not. The check that separated them: on a throwaway manager with a copy of the rules, raise your rule's level to 15, replay the full_log of the sibling's alerts through wazuh-logtest, and see whether any of them flips to your rule. If some flip, the rule is shadowed on those events. If none flip, your rule does not match them, and the problem is the rule's own conditions. In our test, the two real cases flipped (2 of 2 and 1 of 2 samples) and the false one flipped 0 times. Fix Each of these was measured on the same pair of rules (the table above). Which one is right depends on what you want the rule to mean. Rank above the sibling. Case B: level 13 over the sibling's 12 won both inputs. This also raises the severity of your alert, which may not be what you want. Hang the rule higher in the tree. Case C: with parent 5700 at level 9, our rule took both inputs. Hang the rule on the sibling. Case D: with parent 40112, the rule fires only after the burst of failures, and a lone login falls back to 5715. Use this when the correlated case is the one you care about. At equal level, the rule loaded first wins. Moving your rule earlier can fix it, but a file that sorts before the parent's file gets your rule dropped at load. Check for 7617/7619 after any rename. In B and C, 40112 no longer fires on S2: your rule takes the event instead. If you rely on 40112 as a brute-force alert, use D. After the change, feed the same sequence through logtest again and confirm the last line lands on your rule ID. Limits of what we measured One release (4.14.7), one single-node manager, one parent (5715) and one higher-level sibling (40112) for the level test, replayed through wazuh-logtest. The equal-level case and the replay check ran on test rules we wrote, on a throwaway 4.14.7 manager. We have not measured if_matched_sid or if_group chains, clusters, or live traffic. The replay check needs the full_log of the sibling's alerts; we have not measured it on Windows eventchannel or JSON events. Wazuh 4.14.8 (released 25 September 2026) ships byte-identical source files for rule loading and matching (rules-config.c, rules.c, cleanevent.c, logmsg.h, list_op.c); we compared the files, we did not rerun the tests on 4.14.8. Full write-up with the lab output: https://atkvn.com/fix-rule-shadowed-by-sibling.html

Original Source

Read the full article at Dev →

KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.