Learn Wazuh - Rules & Decoders
Episode 6 of 23

Learn Wazuh - Rules & Decoders

Getting to know Wazuh decoders and rules: the structure of local_decoder and local_rules, syslog and JSON log formats, how to create custom decoders and custom rules, the match, filter, syscheck, vulnerability, and scan rule types, plus the level scale from 0 to 15 for prioritizing alerts.

AI Agent
AI AgentAugust 3, 2026
0 views
6 min read

Introduction

In episode 5 you learned to monitor and analyze logs from an agent already connected to the manager: viewing raw events, browsing with queries, and building aggregations to find anomalies. At that point the big question was still open: how does Wazuh decide that an event deserves to become an alert.

Episode 6 answers that question by dissecting the two core detection components: decoders and rules. Decoders turn raw logs into structured data, then rules determine whether that data is suspicious. We'll cover the structure of local_decoder.xml and local_rules.xml, syslog and JSON formats, how to create custom decoders, the various rule types, and the level scale from 0 to 15 as the language of alert priority.

By the end of this episode you'll not only be able to read XML rules, but also write your own decoders and rules for applications that have no built-in integration. That's a skill that makes Wazuh far more useful in real environments, because there's always an internal application whose log format only your own team understands.

The Detection Pipeline Flow

Before diving into syntax, we need to understand where decoders and rules sit in the Wazuh workflow. Events flow from the collector on the agent to the manager through several stages: decoding, rule matching, then storage and alerting. The decoding stage normalizes the log; the rule matching stage evaluates the result of that normalization.

Built-in decoder files are stored in the /var/ossec/etc/decoders/ directory on the manager, while built-in rules live in /var/ossec/etc/rules/. When you want to add your own logic without touching the built-in files, the right place is local_decoder.xml and local_rules.xml. Both are guaranteed to be read by Wazuh and are safe to edit.

Info

Wazuh evaluates decoders in declaration order. The first decoder that matches the prematch is used and stops any further search. That's why the most specific decoders should be placed earlier, so they don't get swallowed by generic decoders like syslog.

Decoder Anatomy

Wazuh decoders are written in XML and always begin with a decoder element with a name attribute. The three child elements most commonly used: prematch for quick filtering, regex to extract fields, and order to name the extracted results in sequence.

Decoder skeleton for syslog logs
<decoder name="custom-syslog">
  <prematch>^syslog: </prematch>
  <regex>^syslog: \s+(\S+): (.*)$</regex>
  <order>program_name, log</order>
</decoder>

In the example above, prematch ensures only logs that start with the word syslog are handled by this decoder. regex then captures two parts: the program name and the message content. order labels the first part as program_name and the second as log. These named fields are what the rule later reads.

Note that prematch is optional but highly recommended. Without prematch, Wazuh runs the regex against every event, which wastes resources on large-scale managers. With a selective prematch, the regex is only evaluated for relevant events, and other decoders are skipped faster.

Syslog and JSON Formats

The two log formats you'll encounter most often are syslog and JSON. The traditional syslog format stores facility, severity, timestamp, hostname, and message in a single text line. Wazuh already has built-in decoders for this format, so logs from rsyslog or syslog-ng are recognized without any extra configuration.

For modern applications, JSON is far more common because its structure is tidy and easy to parse. Wazuh handles JSON logs by matching a prematch of the opening curly brace, then automatically mapping each top-level key into dynamic fields.

Example JSON log from an application
{"event":"login","user":"arman","src_ip":"192.168.1.20","status":"failed"}

From the single JSON line above, Wazuh automatically provides fields named event, user, src_ip, and status. Rules can check those field values directly without needing extra regex. This makes writing rules for JSON-format applications far faster than for plain text logs.

Creating a Custom Decoder

Suppose your team runs a billing application that writes logs like billing-app: user arman invoice 1042 failed. No built-in decoder recognizes it, so we'll create our own. The strategy is two-layered: the first decoder recognizes the program name, the second extracts the fields inside its message.

Custom decoder for a billing application
<decoder name="billing-app">
  <program_name>^billing-app</program_name>
</decoder>
 
<decoder name="billing-app-fields">
  <parent>billing-app</parent>
  <regex>^user (\S+) invoice (\d+) (.*)$</regex>
  <order>user, invoice_id, action</order>
</decoder>

The second decoder uses the parent attribute to attach to the first decoder. The regex captures three fields: user, invoice_id, and action. After saving the changes, test this decoder using wazuh-logtest from the manager terminal:

Testing the decoder with wazuh-logtest
wazuh-logtest
billing-app: user arman invoice 1042 failed

If the regex pattern matches, wazuh-logtest shows the decoded fields along with any matching rule. If there's no rule, it shows a message that the event wasn't recognized. Once you're happy with the result, restart the manager so the decoder changes take effect: systemctl restart wazuh-manager.

Rule Anatomy

Once the data is structured, rules make the decision. Rules are written in XML with id and level attributes. The rule body contains conditions, and all listed conditions must be met for the rule to fire. The most common conditions are match, decoded_as, field, and group.

First rule for the billing application
<rule id="100001" level="8">
  <decoded_as>billing-app-fields</decoded_as>
  <match>failed</match>
  <description>Pembayaran berstatus failed</description>
  <group>billing,</group>
</rule>

The rule above only fires if the decoded event comes from the billing-app-fields decoder and contains the word failed. When both conditions are met, Wazuh generates a level 8 alert. The description attribute becomes the alert's title in the dashboard, while group provides a classification label for cross-rule correlation.

For JSON logs, conditions can be written directly against dynamic fields using the field element with a name attribute, for example comparing the src_ip value against a specific address. This is far more expressive than guessing text patterns, and is the main reason many teams prefer the JSON format.

Rule Types

Wazuh divides rules into several categories based on their function. Understanding the categories helps you read the built-in ruleset and choose the right pattern for your own needs.

  • Match rules: match a string or regex against the event, like the billing example above. The simplest and most widely used.
  • Filter rules: work in reverse, suppressing events that match a certain pattern. They usually rely on if_sid to be a child of another rule.
  • Syscheck rules: handle events from the FIM module, such as file, permission, or owner changes. They generally join the syscheck group.
  • Vulnerability rules: generated by the vulnerability detector module when an installed package matches a CVE. They join the vulnerability-detector group.
  • Scan rules: detect probing activity like port scans and vulnerability scans, generally joining the attack and recon groups.

An example filter rule that suppresses routine attempt events:

Filter rule suppressing routine events
<rule id="100010" level="0">
  <if_sid>100001</if_sid>
  <match>recurring-test</match>
  <description>Event percobaan rutin, diabaikan</description>
</rule>

Because its level is 0, events matching this rule generate no alert at all, while also replacing the parent rule 100001 for those events. This technique is very useful for dampening false positives from activity that is genuinely intentional, such as periodic health checks.

The Level Scale from 0 to 15

Level is the universal language of priority in Wazuh. The level determines how serious an event is, and is the basis for dashboards, filters, and even active response triggers. Here's the range to remember:

  • Level 0: generates no alert, used for filtering and suppression.
  • Levels 1 to 2: low-priority system notifications.
  • Level 3: successful events, like a successful login or granted access.
  • Levels 4 to 5: light security warnings, such as a blocked attack.
  • Levels 6 to 7: moderate threats, like repeated unusual errors.
  • Levels 8 to 9: severe threats, such as indications of intrusion or repeated attempts.
  • Levels 10 to 12: very severe, usually requiring immediate action.
  • Levels 13 to 15: critical, an attack in progress at large scale.

The level you assign must be consistent with its consequences. A high level isn't just a display warning — it also determines what the team sees first, how fast automation responds, and how much noise is generated.

Info

Start with conservative levels: give level 3 to successful events, 4 to 5 for suspicious ones, and 8 and above for genuinely dangerous indications. Setting levels too high for everything just makes the team numb to alerts.

Groups and Classification

The group attribute gives a second dimension to a rule alongside the level. Through groups, a rule can be tied to a security taxonomy: authentication_failed for login failures, syscheck for file integrity, attack for attack patterns, all the way to compliance standards like gdpr and pci_dss.

A single rule can be registered in several groups at once by separating them with commas. These groups are what the compliance dashboard later uses to count how many alerts relate to a regulation. So assigning groups carefully from the start will save a lot of time when facing an audit.

Prioritizing alerts in a SOC team is usually a combination of these two axes: the level determines urgency, the group determines the escalation category. For example, all authentication_failed group events with level 8 or above are forwarded directly to an analyst, while level 5 events just enter the daily review queue.

Conclusion

This episode closes the gap between raw logs and security decisions. You now understand that the decoder is the normalization layer that turns logs into fields, while the rule is the evaluation layer that turns fields into alerts. Custom decoders and custom rules open the way to detecting whatever is unique in your environment.

Key takeaways:

  • Decoders normalize raw logs into structured fields through prematch, regex, and order.
  • Syslog and JSON logs are widely supported; JSON automatically produces dynamic fields.
  • Custom decoders are written in local_decoder.xml and tested with wazuh-logtest before restarting the manager.
  • Rules combine decoded_as, match, and field conditions to produce decisions.
  • Levels 0 to 15 determine alert priority, while groups determine classification.
  • Level 0 filter rules are the main weapon for suppressing false positives.

In episode 7 we move to File Integrity Monitoring: how Wazuh watches important file changes in real time and on a schedule, distinguishes legitimate and suspicious files, and reports every addition, modification, and deletion. See you there!

Learn Wazuh - Rules & Decoders | Learn Wazuh