DEV Community

Nguyen Dong
Nguyen Dong

Posted on Originally published at atkvn.com

Wazuh rule with <hostname> matches in wazuh-logtest but never fires for agent events

For an event that comes from an agent, analysisd sets the host name to the agent name before rules run, so <hostname> in a rule is compared with that name. The alert still shows the syslog host under predecoder.hostname, which is why the rule looks like it should have matched. wazuh-logtest treats a pasted line as a local event, keeps the syslog host, and matches, so the test passes and production does not.

All of this was measured on Wazuh 4.14.7 unless stated otherwise.

Check it in one minute

Run your line through wazuh-logtest the way analysisd sees an agent event, by passing an agent-style location. Use your agent's ID, name and IP, and the location the event comes from:

/var/ossec/bin/wazuh-logtest -l '[002] (cae) 192.168.8.28->journald'
Enter fullscreen mode Exit fullscreen mode

Paste the log line. If your rule matched with the default location (stdin) but not with this one, the host name condition is the cause.

Why it happens

Every event reaches analysisd with a location. A local event has a location like stdin or a file path. An event from an agent has one that starts with the agent ID, like [002] (cae) 192.168.8.28->journald. In the 4.14.7 source (src/analysisd/cleanevent.c, around lines 543-584), a location that starts with [ makes analysisd take the text between the parentheses, the agent name, as the event's host name. For a local event it keeps the host name read from the syslog header.

<hostname> in a rule is compared with that value (rules.c, around lines 2875-2880), as an OS_Match expression by default. The predecoder.hostname in the alert JSON is parsed again from full_log when the alert is written (json_extended.c), so it shows the syslog host even when the rule compared against the agent name.

We checked it on a 4.14.7 manager with one su line whose syslog host is DN4, a parent rule reached on every run, and two children: one with <hostname>DN4</hostname>, one with <hostname>^cae$</hostname>.

wazuh-logtest -l <hostname>DN4</hostname> <hostname>^cae$</hostname>
stdin (default, local event) fires does not fire
[002] (cae) 192.168.8.28->journald does not fire fires
[002] (caesar) 192.168.8.29->journald does not fire does not fire

In all three runs logtest printed hostname: 'DN4' in its decoding phase. The value it prints is not the value the rule compares.

Fix

Match the agent name, anchored:

<hostname>^cae$</hostname>
Enter fullscreen mode Exit fullscreen mode

Without ^ and $, cae also matches an agent called caesar (see the third row above for the anchored rule not firing on caesar).

Know what it costs: if the agent is renamed, the rule stops matching and nothing tells you. Keep the agent name in the rule's description so a rename is easy to trace.

Test the way production runs: always pass the agent-style location to wazuh-logtest for rules meant for agent events.

<location> is not a shortcut: reading the same source, for agent events it is compared with the whole (cae) 192.168.8.28->journald string, not the journald the alert shows. We have not measured it.

Limits of what we measured

One release (4.14.7), one single-node throwaway manager, one su line, tested with wazuh-logtest and an agent-style location. We did not send a journald event through a real agent; the live behaviour matches a user report on wazuh/wazuh#39536, where a <hostname> step stopped every event from an agent named cae on two nightly runs. <location> was read from the source, not measured. Events that reach the manager over syslog (<remote>) rather than from an agent are not covered here.

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-hostname-rule-never-fires-for-agent-events.html

Top comments (1)

Collapse
 
onelifevibe profile image
OneLifeVibe •

The most useful detail here is that logtest prints hostname DN4 in the decoding phase even when the rule will not fire in production — that printed value is exactly what sends people down the wrong path. Testing with the agent-style location is the fix I would keep in a runbook, and the anchored ^cae$ point matters too: an unanchored cae would also match caesar, so a rule that finally fires is still wider than intended. One follow-up worth adding is a negative canary for a renamed agent, since a rename silently stops the rule with no error, which is the same silent-failure shape as the original bug.