Paste mixed logs from one noisy source. RuleForge clusters them into classes, you label each alarm / watch / noise, and it emits a tuned Wazuh decoder + rule set — noise silenced at level 0, a brute-force rule for repeated auth failures.
How it works: cluster the lines → label each class
alarm / watch / noise → emit a decoder + rules, where noise sits at
level 0 (logged, never alerted) and an
auth-failure class gets a brute-force frequency rule. The
whole point is false-positive control.
mixed logs
paste many event types from one noisy sourcelog classes
one card per cluster — set a verdict to tune the rules- Audit2 linesAlarmAcmeSpot Audit: Oct 20 12:04:33 acmegw AcmeSpot: Event=Login Result=Failure IP=10.0.0.5 Username=j.doefailurelogin
- AcmeSpotUrl1 lineNoiseACMESPOT-URL: Oct 22 12:44:09 acmegw AcmeSpotUrl: 22/Oct/2025:12:44:09 +0300|10.0.0.1|GET|captive.apple.com|/hotspot-detectno signal keywords
- AcmeSpotUrl1 lineWatchACMESPOT-URL: Oct 22 12:44:11 acmegw AcmeSpotUrl: 22/Oct/2025:12:44:11 +0300|10.0.0.9|POST|example.com|/loginlogin
- Authentication1 lineWatchAcmeSpot Authentication: Oct 20 00:08:36 acmegw portald[2279]: Successful UAM login from username=guest IP=172.16.0.7successfullogin
wazuh output
<!-- RuleForge decoders — put in /var/ossec/etc/decoders/local_decoder.xml --> <!-- Generated by LogForge - Wazuh decoder (OS_Regex dialect, not PCRE) sample: AcmeSpot Audit: Oct 20 12:04:33 acmegw AcmeSpot: Event=Login Result=Failure IP=10.0.0.5 Username=j.doe test with: /var/ossec/bin/wazuh-logtest --> <decoder name="logforge-audit-freeform"> <prematch>^\w+ \w+: \w+ \d+ </prematch> </decoder> <decoder name="logforge-audit-freeform"> <parent>logforge-audit-freeform</parent> <regex>^AcmeSpot Audit: Oct 20 (\d+:\d+:\d+) acmegw AcmeSpot: Event=Login Result=Failure IP=10.0.0.5 (\S+)</regex> <order>timestamp, literal</order> </decoder> <!-- Generated by LogForge - Wazuh decoder (OS_Regex dialect, not PCRE) sample: ACMESPOT-URL: Oct 22 12:44:09 acmegw AcmeSpotUrl: 22/Oct/2025:12:44:09 +0300|10.0.0.1|GET|captive.apple.com|/hotspot-detect test with: /var/ossec/bin/wazuh-logtest --> <decoder name="logforge-acmespoturl-delimited"> <prematch>^\w+: \w+ \w+ \w+:\w+:\w+ \w+ \w+: \w+/\w+/\w+:\w+:\w+:\w+ \p\w+\|</prematch> </decoder> <!-- Generated by LogForge - Wazuh decoder (OS_Regex dialect, not PCRE) sample: ACMESPOT-URL: Oct 22 12:44:11 acmegw AcmeSpotUrl: 22/Oct/2025:12:44:11 +0300|10.0.0.9|POST|example.com|/login test with: /var/ossec/bin/wazuh-logtest --> <decoder name="logforge-acmespoturl-delimited"> <prematch>^\w+: \w+ \w+ \w+:\w+:\w+ \w+ \w+: \w+/\w+/\w+:\w+:\w+:\w+ \p\w+\|</prematch> </decoder> <!-- Generated by LogForge - Wazuh decoder (OS_Regex dialect, not PCRE) sample: AcmeSpot Authentication: Oct 20 00:08:36 acmegw portald[2279]: Successful UAM login from username=guest IP=172.16.0.7 test with: /var/ossec/bin/wazuh-logtest --> <decoder name="logforge-authentication-freeform"> <prematch>^\w+ \w+: \w+ \d+ \d+:\d+:\d+ \w+ </prematch> </decoder>
- 4 classes: 1 alarm, 2 watch, 1 noise
- class c0f0636a: brute-force rule skipped: decoder did not capture a srcip
- class ac893bf0: decoder captured no fields — description falls back to "AcmeSpotUrl event"
- class 1c5e27e6: decoder captured no fields — description falls back to "AcmeSpotUrl event"
- class debaa53e: decoder captured no fields — description falls back to "Authentication event"
Two files: paste the decoders tab into
/var/ossec/etc/decoders/local_decoder.xml
and the rules tab into
/var/ossec/etc/rules/local_rules.xml.
Rule ids start at 100000 (the Wazuh
user range). This demo: 4 classes: 1 alarm, 2 watch, 1 noise.
Cluster → label → decoder + rules
A noisy source emits dozens of event shapes. One catch-all rule over all of them is alert fatigue. RuleForge splits the source into structural classes so you can silence the benign ones and alert on the rest.
Cluster
Mixed lines are normalized (timestamps, IPs, ports and counters masked) and grouped by structural signature — all "URL access" lines together, all "login failure" lines together — into log classes.
Label & verdict
Each class gets a human label and a heuristic verdict from its signal keywords: alarm (fail / denied / invalid…), watch (success / login / accepted…), or noise. You override any verdict with one click.
Decoder + rules
Every class becomes a Wazuh decoder (via the LogForge engine) plus a rule at the level its verdict dictates — noise at 0, watch at 3, alarm at 5 — with a brute-force frequency rule for repeated auth failures from one IP.
Three verdicts, three rule levels
Every class gets one of three analyst verdicts, and the verdict alone decides the emitted rule level. Noise at level 0 is the false-positive killer: the event is still logged and searchable, it just never pages anyone.
| verdict | what it emits |
|---|---|
| Alarm level 5 (+10 brute-force child) | Security-relevant classes — failed logins, denied, blocked, invalid, attack. Emits an alerting rule; auth-failure classes whose decoder captured a source IP also get a frequency rule (6 hits from one IP in 120s = brute force). |
| Watch level 3 | Notable but benign — a successful login, an accepted session, an established connection. Emits a low-level rule that is logged and searchable but does not page anyone. |
| Noise level 0 | Benign / infrastructure chatter — captive-portal probes, health checks, routine URL access. Emits a level-0 rule so the event is still logged but never raises an alert. This is the false-positive killer. |
The brute-force rule
When a class looks like an auth failure — failed, invalid, or denied logins — and its decoder captured a source IP, RuleForge adds a Wazuh frequency rule that correlates repeated failures from one IP into a single high-severity alert.
<rule id="100001" level="10" frequency="6" timeframe="120">
<if_matched_sid>100000</if_matched_sid>
<same_source_ip />
<description>possible brute force —
repeated failures from one IP</description>
</rule> Deterministic by design. Class ids are a stable hash of
the class signature, and generation uses no Date.now() or
Math.random() — so a verdict you set survives a re-cluster and the same paste
always yields byte-identical XML you can commit to version control.
FAQ
What does RuleForge actually generate?
Two ready-to-paste Wazuh files: a decoders file for /var/ossec/etc/decoders/local_decoder.xml and a rules file for /var/ossec/etc/rules/local_rules.xml. The decoders extract fields from each log class; the rules alert (or stay silent) according to the verdict you assigned each class. Rule ids start at 100000 — the Wazuh user range.
How does it cut false positives?
One broad rule over a mixed source is alert fatigue: it fires on captive-portal probes and health checks the same as on a real brute-force. RuleForge splits the source into structural classes and lets you mark the benign ones as noise (level 0 — logged but never alerted) while the security-relevant classes alert at level 5. Signal without the noise.
What is the brute-force rule?
When a class looks like an auth failure (failed / invalid / denied logins) AND its decoder captured a source IP, RuleForge adds a Wazuh frequency child rule: <if_matched_sid> + <same_source_ip /> with frequency="6" timeframe="120". That is the canonical correlation for 6 failures from one IP within 120 seconds. If no source IP was captured, it notes that instead of emitting a rule that cannot fire.
Is any log data uploaded?
No. Clustering, verdict suggestion and decoder + rule generation all run 100% in your browser. Nothing you paste is ever sent anywhere — there is no network call in this tool at all.
Is the output deterministic?
Yes. The same paste and the same verdict overrides always produce byte-identical decoder and rule XML — no Date.now(), no Math.random(). Class ids are a stable hash of the class signature, so a verdict you set survives a re-cluster and you can commit the generated files to version control.
How is this different from LogForge?
LogForge takes ONE log format and writes a parser (regex, Grok, Wazuh decoder + a starter rule) for it. RuleForge takes MIXED logs from one noisy source, clusters them into classes, and writes a tuned decoder + rule SET across all of them — with per-class verdicts so noise stays silent. RuleForge reuses the LogForge engine for the per-class decoders.
Got a single log format and just need a decoder + rule for it? → Build one parser with LogForge — paste any log line and get a working regex, Grok, or Wazuh decoder + starter rule.
Sizing the ingest these rules will run over? → SIEM sizing calculator · or read the docs .