From 26c5abc509c1ce498d7a683b8dd0f8ee66495608 Mon Sep 17 00:00:00 2001 From: radargoger Date: Thu, 20 Aug 2026 22:22:50 +0300 Subject: [PATCH 1/2] fix(socradar): overlap fetch windows to avoid missing late-visible OPEN alarms - Add configurable `fetch_overlap_seconds` (default 900 / 15 minutes) so each steady-state wodle run re-queries `min(last_run, now - overlap) -> now`, covering SOCRadar API lag after the cursor advances. - Keep inbound dedup on `seen_alarm_ids` (Wazuh does not dedupe by `alarm_id`). - Skip outbound auto-tag / comment when `wazuh-ingested` is already present on the alert payload tags. - Wire the new default into `socradar.conf.template`, `install.sh`, and README (wodle v1.0.3, integration v1.0.2). --- integrations/socradar/README.md | 32 ++++++++++++------- integrations/socradar/install.sh | 1 + .../socradar/integration/custom-socradar.py | 28 ++++++++++++---- integrations/socradar/socradar.conf.template | 10 +++--- integrations/socradar/wodles/socradar.py | 20 +++++++++--- 5 files changed, 64 insertions(+), 27 deletions(-) diff --git a/integrations/socradar/README.md b/integrations/socradar/README.md index 3f429bb6..1524779b 100644 --- a/integrations/socradar/README.md +++ b/integrations/socradar/README.md @@ -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. @@ -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, @@ -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 | @@ -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 | diff --git a/integrations/socradar/install.sh b/integrations/socradar/install.sh index d7530427..caf4384a 100755 --- a/integrations/socradar/install.sh +++ b/integrations/socradar/install.sh @@ -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, diff --git a/integrations/socradar/integration/custom-socradar.py b/integrations/socradar/integration/custom-socradar.py index 1c40fc47..33c4b30d 100644 --- a/integrations/socradar/integration/custom-socradar.py +++ b/integrations/socradar/integration/custom-socradar.py @@ -18,7 +18,7 @@ /var/ossec/integrations/custom-socradar.py Author: SOCRadar Integration Team -Version: 1.0.0 +Version: 1.0.2 """ import json @@ -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 @@ -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", []) diff --git a/integrations/socradar/socradar.conf.template b/integrations/socradar/socradar.conf.template index 85ca2d9a..99cae22f 100644 --- a/integrations/socradar/socradar.conf.template +++ b/integrations/socradar/socradar.conf.template @@ -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": { diff --git a/integrations/socradar/wodles/socradar.py b/integrations/socradar/wodles/socradar.py index f903cbd9..4473409c 100644 --- a/integrations/socradar/wodles/socradar.py +++ b/integrations/socradar/wodles/socradar.py @@ -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 @@ -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}" @@ -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) From 864374ecbae874a9357aab26e7fb7e2b9d6dea11 Mon Sep 17 00:00:00 2001 From: radargoger Date: Fri, 21 Aug 2026 08:00:45 +0300 Subject: [PATCH 2/2] docs(socradar): add provenance and maintenance block to README CONTRIBUTING.md requires community-contributed integrations to document original source, adapting party, tested versions, maintainer and support boundary. Replace the single-line Author section with a table covering all five fields. --- integrations/socradar/README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/integrations/socradar/README.md b/integrations/socradar/README.md index 1524779b..fc87afc7 100644 --- a/integrations/socradar/README.md +++ b/integrations/socradar/README.md @@ -304,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 - -SOCRadar Integration Team — integrations@socradar.io +## Provenance and Maintenance + +| 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. |