Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions integrations/socradar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

After a one-time installation, the integration runs fully automatically:

1. **Inbound (SOCRadar → Wazuh):** A wodle command runs every 1 minute, fetches new incidents from SOCRadar API v4 using epoch timestamps, and outputs JSON to stdout. Wazuh decodes the JSON and generates alerts based on severity-mapped rules.
1. **Inbound (SOCRadar → Wazuh):** A wodle command runs every 1 minute, fetches incidents from SOCRadar API v4 using epoch timestamps (with a trailing overlap window so late-visible alarms are not skipped), and outputs JSON to stdout. Deduplication uses `seen_alarm_ids` in the state file. Wazuh decodes the JSON and generates alerts based on severity-mapped rules.

2. **Outbound (Wazuh → SOCRadar):** When a SOCRadar alert triggers in Wazuh, the custom integration sends feedback to SOCRadar — auto-tagging incidents as `wazuh-ingested`, posting Wazuh context as comments, and optionally updating incident status/severity.

Expand Down Expand Up @@ -128,7 +128,16 @@ Create `/var/ossec/etc/socradar.conf`:
"min_severity": null,
"alarm_main_types": [],
"initial_lookback_hours": 24,
"max_pages": 10,
"fetch_overlap_seconds": 900,
"max_pages": null,
"http_timeout_seconds": 120,
"http_retries": 0,
"page_sleep_seconds": 2,
"lookback_page_sleep_seconds": 2,
"max_retry_pages": 25,
"max_retry_pages_per_run": 1,
"retry_backoff_seconds": 60,
"retry_backoff_max_seconds": 3600,

"integration": {
"auto_tag": true,
Expand Down Expand Up @@ -191,17 +200,18 @@ sudo /var/ossec/bin/wazuh-control restart
| `ca_bundle_path` | string | `null` | Optional CA bundle path (PEM) for proxy/self-signed environments |
| `verbose` | boolean | `false` | Enable verbose DEBUG logging (to `/var/ossec/logs/socradar-wodle.log`) |
| `log_level` | string | `INFO` | Log level: `ERROR`, `WARN`, `INFO`, `DEBUG` (overrides `verbose`) |
| `fetch_status` | string | `OPEN` | Filter: OPEN, RESOLVED, etc. |
| `fetch_limit` | integer | `100` | Page size per API call (capped at 100 by the script) |
| `min_severity` | string | `null` | Minimum severity filter |
| `fetch_status` | string | *(omit)* | If set (e.g. `OPEN`), sent as API `status` filter. If omitted, no status filter is applied. Installer sets `OPEN`. |
| `fetch_limit` | integer | `100` | Page size per API call (`DEFAULT_PAGE_SIZE`; hard-capped at 100) |
| `min_severity` | string | `null` | If set, sent as API `severities` query param (not a numeric minimum in code) |
| `alarm_main_types` | array | `[]` | Filter by main type (empty = all) |
| `initial_lookback_hours` | integer | `24` | Hours to look back on first run |
| `max_pages` | integer | `null` | Optional safety limit for pagination (useful during first runs) |
| `fetch_overlap_seconds` | integer | `900` | On steady-state runs, re-query this many seconds before `now` when that is earlier than `last_run`. Covers SOCRadar API lag. Duplicates suppressed via `seen_alarm_ids`. Set `0` to disable. |
| `max_pages` | integer | `null` | Optional page cap. `null`/omitted = unlimited (`get_max_pages` returns no limit) |
| `http_timeout_seconds` | integer | `120` | HTTP request timeout per API call |
| `http_retries` | integer | `0` | Extra in-run HTTP retries for transient errors (recommended: keep low; main retry mechanism is the state retry queue) |
| `http_retries` | integer | `0` | Extra in-run HTTP retries for transient errors (main retry mechanism is the state retry queue) |
| `page_sleep_seconds` | number | `2` | Sleep between page requests during pagination (steady-state runs) |
| `lookback_page_sleep_seconds` | number | `2` | Sleep between page requests during pagination on the first run (large lookback) |
| `max_retry_pages` | integer | `200` | Max number of failed pages kept in the persistent retry queue |
| `lookback_page_sleep_seconds` | number | *(same as `page_sleep_seconds`)* | Sleep between pages on the first run; defaults to `page_sleep_seconds` |
| `max_retry_pages` | integer | `25` | Max failed pages kept in the persistent retry queue (code default; legacy key `max_retry_windows` also accepted) |
| `max_retry_pages_per_run` | integer | `1` | How many queued failed pages to attempt per run |
| `retry_backoff_seconds` | integer | `60` | Base backoff for queued page retries (exponential) |
| `retry_backoff_max_seconds` | integer | `3600` | Maximum backoff for queued page retries |
Expand All @@ -210,8 +220,8 @@ Outbound (Wazuh → SOCRadar) settings are under `integration` in the same confi

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `integration.auto_tag` | boolean | `true` | Add the `wazuh-ingested` tag to the alarm |
| `integration.post_wazuh_context` | boolean | `true` | Post rule/level/context as a SOCRadar comment |
| `integration.auto_tag` | boolean | `true` | Add the `wazuh-ingested` tag to the alarm (skipped if the tag is already present on the alert payload) |
| `integration.post_wazuh_context` | boolean | `true` | Post rule/level/context as a SOCRadar comment (skipped if `wazuh-ingested` is already present) |
| `integration.auto_close_rule_ids` | array[int] | `[]` | If Wazuh rule ID matches, close as FALSE_POSITIVE |
| `integration.auto_resolve_rule_ids` | array[int] | `[]` | If Wazuh rule ID matches, resolve alarm |
| `integration.escalate_threshold` | integer | `12` | If Wazuh alert level >= threshold, severity may be escalated |
Expand Down Expand Up @@ -294,6 +304,12 @@ Expected: Rule 100803, Level 10.
- [SOCRadar Platform](https://platform.socradar.com)
- [Wazuh Custom Integration Guide](https://documentation.wazuh.com/current/user-manual/manager/integration-with-external-apis.html)

## Author
## Provenance and Maintenance

SOCRadar Integration Team — integrations@socradar.io
| Field | Detail |
|-------|--------|
| **Original source** | Developed by SOCRadar as the vendor's native Wazuh integration. It is not derived from or adapted from a third-party article, repository or implementation. |
| **Adapted by** | SOCRadar Integration Team — no third-party adaptation layer. |
| **Tested versions** | Wazuh Manager 4.14.3 (targets Wazuh 4.x); Python 3.6 or later on the manager host, no external packages; SOCRadar Incident API v4. Component versions in this directory: wodle `wodles/socradar.py` 1.0.3, integration `integration/custom-socradar.py` 1.0.2. |
| **Maintainer** | SOCRadar Integration Team — integrations@socradar.io |
| **Support boundary** | Vendor-maintained. Questions and issues concerning the scripts, decoder and rules in this directory are handled by SOCRadar through integrations@socradar.io or the customer's existing SOCRadar support channel. An active SOCRadar account with API access is required. Wazuh platform issues unrelated to this integration remain with the Wazuh support channels. |
1 change: 1 addition & 0 deletions integrations/socradar/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ cat > "$WAZUH_HOME/etc/socradar.conf" << CONFEOF
"min_severity": null,
"alarm_main_types": [],
"initial_lookback_hours": $LOOKBACK,
"fetch_overlap_seconds": 900,
"integration": {
"auto_tag": true,
"post_wazuh_context": true,
Expand Down
28 changes: 21 additions & 7 deletions integrations/socradar/integration/custom-socradar.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
/var/ossec/integrations/custom-socradar.py

Author: SOCRadar Integration Team
Version: 1.0.0
Version: 1.0.2
"""

import json
Expand All @@ -39,8 +39,9 @@

SOCRADAR_BASE_URL = "https://platform.socradar.com/api"

VERSION = "1.0.1"
VERSION = "1.0.2"
USER_AGENT = f"wazuh-socradar-integration/{VERSION}"
WAZUH_INGESTED_TAG = "wazuh-ingested"

# SSL context (initialized in main() after config is loaded)
SSL_CTX = None
Expand Down Expand Up @@ -405,18 +406,31 @@ def process_alert(config, alert):

integration_config = config.get("integration", {})

tags = socradar_data.get("tags") or []
if not isinstance(tags, list):
tags = []
already_ingested = any(
str(t).strip().lower() == WAZUH_INGESTED_TAG for t in tags
)

log("INFO", f"Processing alarm {alarm_id} | Rule: {rule_id}, Level: {rule_level}")

# --- Action 1: Auto-tag ---
if integration_config.get("auto_tag", True):
add_tag(config, alarm_id, "wazuh-ingested")
_throttle_outbound()
if already_ingested:
log("INFO", f"Tag '{WAZUH_INGESTED_TAG}' already present on alarm {alarm_id}, skipping tag")
else:
add_tag(config, alarm_id, WAZUH_INGESTED_TAG)
_throttle_outbound()

# --- Action 2: Post Wazuh context as comment ---
if integration_config.get("post_wazuh_context", True):
comment = build_wazuh_comment(alert)
add_comment(config, alarm_id, comment)
_throttle_outbound()
if already_ingested:
log("INFO", f"Alarm {alarm_id} already wazuh-ingested, skipping comment")
else:
comment = build_wazuh_comment(alert)
add_comment(config, alarm_id, comment)
_throttle_outbound()

# --- Action 3: Auto-close by rule ID ---
auto_close_rules = integration_config.get("auto_close_rule_ids", [])
Expand Down
10 changes: 5 additions & 5 deletions integrations/socradar/socradar.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@
"verbose": false,
"log_level": "INFO",

"_comment_fetch": "=== Wodle Fetch Settings ===",
"_comment_fetch": "=== Wodle Fetch Settings (defaults match wodles/socradar.py) ===",
"fetch_status": "OPEN",
"fetch_limit": 50,
"fetch_limit": 100,
"min_severity": null,
"alarm_main_types": [],
"initial_lookback_hours": 24,
"max_pages": 10,
"fetch_overlap_seconds": 900,
"max_pages": null,
"http_timeout_seconds": 120,
"http_retries": 0,
"page_sleep_seconds": 2,
"lookback_page_sleep_seconds": 2,
"max_retry_pages": 200,
"max_retry_pages": 25,
"max_retry_pages_per_run": 1,
"retry_backoff_seconds": 60,
"retry_backoff_max_seconds": 3600,
"auto_comment_on_fetch": false,

"_comment_integration": "=== Integration (Wazuh → SOCRadar) Settings ===",
"integration": {
Expand Down
20 changes: 16 additions & 4 deletions integrations/socradar/wodles/socradar.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
4. Emit in that order → oldest first, newest last

Author: SOCRadar Integration Team
Version: 1.0.2
Version: 1.0.3
"""

import json
Expand All @@ -35,7 +35,7 @@
import urllib.parse
from datetime import datetime, timezone

VERSION = "1.0.2"
VERSION = "1.0.3"
USER_AGENT = f"wazuh-socradar-integration/{VERSION}"


Expand Down Expand Up @@ -959,13 +959,25 @@ def main():
SSL_CTX = build_ssl_context(config)
state = load_state()

# Time window: last_run → now
# Time window: last_run → now, with configurable overlap to cover API lag
end_epoch = now_epoch()

last_run_epoch = state.get("last_run_epoch")
is_first_run = not bool(last_run_epoch)
if last_run_epoch:
start_epoch = last_run_epoch
start_epoch = int(last_run_epoch)
# Re-query a trailing window so alarms that appear late on the API
# (after last_run advanced) are still visible. Dedup via seen_alarm_ids.
overlap = _get_int(config, "fetch_overlap_seconds", default=900, min_value=0, max_value=86400)
if overlap:
overlapped_start = end_epoch - overlap
if overlapped_start < start_epoch:
log(
"INFO",
f"Applying fetch overlap | raw_start={start_epoch} "
f"overlap_seconds={overlap} -> start={overlapped_start}",
)
start_epoch = overlapped_start
else:
# First run: look back N hours (default 24)
lookback_hours = config.get("initial_lookback_hours", 24)
Expand Down