Check email addresses seen in Wazuh alerts (authentication, user creation, IdP/Office 365/ GWorkspace logs) against the XposedOrNot data-breach database, and raise an enriched alert when an address is exposed.
No API key required. The free community API works out of the box; an optional key raises rate limits. A local cache and a per-email suppression window keep keyless usage within the free budget.
Wazuh version: targets Wazuh 4.x (tested against 4.14.x). Wazuh 5.x removed the
integratordPython-integration mechanism this integration uses; a 5.x version will follow once its replacement stabilizes.
alert ──▶ wazuh-integratord ──▶ custom-xposedornot.py ──▶ cache? ──▶ XposedOrNot API
│ │
▼ ▼
analysisd socket ◀── enriched event (masked email + breach facts)
│
▼
xon_rules.xml ──▶ alerts / dashboards
For each email found in a triggering alert: check the suppression window → check the local cache → (miss) call the API → cache the result → if breached and not recently alerted, write an enriched event to analysisd. The rules turn those events into leveled alerts.
- Wazuh manager 4.x
requestsin the manager's embedded Python (/var/ossec/framework/python/bin/python3). Wazuh ships it; if missing:/var/ossec/framework/python/bin/pip3 install requests.
# On the Wazuh manager:
cp integrations/custom-xposedornot.py /var/ossec/integrations/
chown root:wazuh /var/ossec/integrations/custom-xposedornot.py
chmod 750 /var/ossec/integrations/custom-xposedornot.py
cp rules/xon_rules.xml /var/ossec/etc/rules/
chown wazuh:wazuh /var/ossec/etc/rules/xon_rules.xmlAdd the <integration> block from config/ossec.conf.snippet.xml inside <ossec_config> in
/var/ossec/etc/ossec.conf, then restart:
systemctl restart wazuh-managerTarget only the alert groups/rules that actually carry email addresses — this preserves API
budget. In the <integration> block:
<group>authentication_success,adduser,office365,gcp,aws</group>— or<rule_id>...</rule_id>for precise targeting<api_key>...</api_key>— optional; omit for the free keyless API
Tuning via environment (all optional, defaults shown):
| Variable | Default | Purpose |
|---|---|---|
XON_CACHE_PATH |
/var/ossec/var/xon-cache.db |
sqlite cache location |
XON_CACHE_TTL_DAYS |
7 |
how long a lookup result is reused |
XON_SUPPRESS_HOURS |
24 |
max one enriched alert per email per window |
XON_MAX_EMAILS |
3 |
max email candidates checked per alert |
Keyless community API: 2 req/s, 25/hour, 100/day per IP. The cache is what makes this viable: each unique email costs one API call per TTL window. With the default 7-day TTL and 25/hour, steady-state capacity is roughly 600 unique new emails/day — ample for authentication-driven checks in most environments. High-volume sites should add a commercial key (console.xposedornot.com).
The script never blocks the Wazuh pipeline: 5s HTTP timeout, no long sleeps. On HTTP 429 it
records a cooldown and skips (logging once), so a rate-limit event can't stall integratord.
Dry-run mode prints the event it would send, touching no socket and no production cache:
./integrations/custom-xposedornot.py --test tests/fixtures/alert_auth_success.json
# optional API key as a third argumentA single-node Wazuh docker recipe for full end-to-end testing is in docker/.
| Rule | Level | Fires when |
|---|---|---|
| 100951 | 7 | Email found in ≥1 breach |
| 100952 | 10 | Any breach stored passwords in plaintext |
| 100953 | 10 | Email exposed in ≥5 breaches |
| 100954 | 12 | (example, disabled) breached email on a critical asset |
Rule IDs use the 100950–100999 range; if they collide with your local rules, renumber them
consistently (only the if_sid references inside the file need updating).
Only the email address is sent, over TLS, to xposedornot.com. Email addresses are never
written in clear text — logs and the enriched alert carry a masked form (t***@example.com)
plus a SHA-256 prefix for correlation. The optional API key is never logged. The alert payload
carries xon_schema: 1 so downstream dashboards/SOAR parsing survives future changes.
- No alerts appear — confirm the triggering alerts actually match your
<group>/<rule_id>and contain an email; run the alert through--test. Check/var/ossec/logs/integrations.log(enabledebugas the 5th<integration>arg is not needed; the script logs errors regardless). # XON rate limit reached ... cooling down— expected under keyless bursts; wait, narrow the targeting, or add a key.Unable to open socket— the script must run as part ofintegratordon the manager (permissionsroot:wazuh,750).