--- name: wazuh-soc-alert-triage description: Use when designing, implementing, reviewing, or operating a Wazuh alert-to-case triage pipeline or guarded SOC automation for this fleet—ingesting alert events, suppressing noise, correlating cases, enriching from fleet data sources, adding evidence-bound LLM analysis, or executing approval-gated response playbooks. Covers queue reliability, idempotency, auditability, rollout, and the boundary between the existing InformatiQ status dashboard and a real SOC workflow. --- # Wazuh SOC Alert Triage Build an evidence-preserving pipeline, not an autonomous incident responder. Provenance: adapted from [`FunnyWolf/agentic-soc-platform`](https://github.com/FunnyWolf/agentic-soc-platform) at commit `be3e9b48a5f32069df9f0eeb6175b5ec5443fb69`, reviewed 2026-08-22. The source's reusable patterns are typed alert normalization, deterministic correlation, transactional case creation, queued analysis, structured LLM output, and risk-labelled playbooks. This skill does not adopt its full Django platform, ELK/Splunk assumptions, automatic analysis on every alert, or Redis `NOACK` delivery behavior. ## Respect the current fleet boundary The existing `informatiq-dashboard` is a read-only status board. Its Wazuh integration authenticates to the manager API and polls agent status plus manager-level event/alert counts. It does not ingest individual alerts, own cases, or execute remediation. Before changing it: 1. Read the current `informatiq-dashboard` source and relevant `/home/malin/granja/docs/server-*.md`; do not infer capabilities from UI labels. 2. Identify the authoritative event source. Manager summary endpoints are suitable for health widgets, not alert triage. Use an authorized event stream/index/query surface that returns individual Wazuh alert records. 3. Keep case state and automation out of the status dashboard unless there is an explicit decision to turn that application into a stateful SOC system. 4. Treat Kuma, PowerDNS, Pi-hole, Proxmox/PBS/PMG, proxy, honeypot, bot, and WAF data as bounded enrichment. A failed enrichment must not erase or block the original alert. 5. Store credentials outside source. Validate the real certificate chain where possible; never copy the dashboard's process-wide TLS-verification disable into an internet-facing or action-capable service. ## Define the alert contract first Store the immutable raw event separately from normalized fields. Require: - source system, source event ID, rule ID/name, event time, ingest time, and schema/parser version; - affected asset and actor identifiers with explicit types; - severity, confidence, action/outcome, tactics/techniques, and labels; - raw payload hash plus a protected reference to the raw payload; - normalization warnings and unmapped fields rather than silently dropping unknown data. Parse timestamps defensively. Preserve the original value and record a parse error when falling back to ingest time. Redact secrets from operator-visible messages, but retain authorized forensic evidence in access-controlled storage. Version normalizers and replay captured fixtures before deployment. A plausible normalized record is not proof that nested Wazuh fields mapped correctly. ## Separate deterministic stages Use this flow: ```text individual alert -> validate/normalize -> suppress/routable decision -> correlate/idempotently attach to case -> bounded enrichment -> evidence-bound analysis -> human decision -> guarded playbook ``` Keep every transition observable and replayable. Do not let an LLM decide whether an event is accepted, deduplicated, or attached to an existing case. ### Suppress and route before analysis - Start with rule allowlists, minimum severities, maintenance windows, known scanners, service accounts, and exact fleet-specific exclusions. - Record the rule and reason for every suppressed event. Retain counts and a sampled audit trail so an overbroad rule is visible. - Rate-limit repeated noise without discarding the first and last event, occurrence count, or time span. - Keep detection disposition separate from response action: observed, denied, and successful malicious actions require different urgency. ### Correlate without merging unrelated incidents Build a versioned correlation key from the rule family plus stable typed identifiers such as host, account, user, source IP, process, or destination. Add a documented time window only after choosing the identifiers. Do not use a time bucket alone. It merges unrelated alerts and splits an event at bucket boundaries. Prefer a database uniqueness constraint on source event ID for ingestion and an atomic lock/upsert on correlation key for case attachment. Store the correlation algorithm version so cases can be explained after rules change. ## Make delivery at-least-once and processing idempotent For each queue consumer: 1. Claim a message without acknowledging it. 2. Validate and process inside a transaction. 3. Commit the alert, case link, artifacts, and scheduled follow-up job. 4. Acknowledge only after commit. 5. Retry transient failures with a bound; send poison messages to a dead-letter queue with sanitized error metadata. Use a stable idempotency key at every side-effect boundary. Recover jobs left `running` after a worker restart by lease expiry or explicit startup recovery; do not leave them permanently running or blindly rerun non-idempotent actions. Expose queue depth, oldest age, retry/DLQ counts, worker heartbeat, last success, failure rate, and processing latency. ## Enrich evidence, do not manufacture certainty Run independent enrichments concurrently with per-source timeouts and cache limits. Record provider, query, retrieval time, result, and error. Useful fleet joins include asset identity/health, expected DNS role, proxy exposure, backup state, and recent availability changes. Never infer compromise merely because a related service is down or an IP appears in a honeypot feed. Label enrichment as observation, inference, or unknown, and preserve contradictory evidence. ## Constrain LLM analysis Run analysis after deterministic case assembly, preferably on demand or after a severity/noise gate. Require a typed result containing: - verdict, severity, confidence, and a short digest; - affected assets; - evidence findings with source-field references; - timeline and attack-chain hypotheses; - indicators with context; - recommended actions and explicit unknowns. Make the model cite only supplied evidence. Reject invalid schemas and invented identifiers. Store prompt/profile version, model identifier, generation time, input evidence references, and output. Present model severity separately from rule severity. LLM output may prioritize investigation; it must not authorize a response action. ## Gate playbooks by effect, not by label Classify each concrete step: | Effect | Default gate | |---|---| | Query, summarize, collect read-only evidence | Automatic after validation | | Notify or create an internal ticket | Automatic if deduplicated and rate-limited | | Temporary narrow block or session revocation | Named human approval | | Credential rotation, firewall/DNS changes, jail/container stop, deletion, restore | Explicit target-specific approval and rollback plan | A `risk_level` field is metadata, not enforcement. Before execution, resolve exact targets, show blast radius, require the applicable approval, capture the requester/approver, and generate an idempotency key. Implement dry-run where the underlying system supports it. Log sanitized progress messages and the final verification result. Never feed credentials, authorization headers, or raw secrets into progress logs or model context. ## Roll out in measured phases 1. Replay sanitized historical fixtures and assert normalization, deduplication, correlation, and suppression outcomes. 2. Run shadow mode: ingest and build proposed cases without notifications or actions. 3. Compare against a human-reviewed sample; measure false merges, duplicate cases, missed high-severity alerts, and analyst correction rate. 4. Enable notifications with rate limits. 5. Add read-only enrichment and analysis. 6. Add one narrow, reversible playbook only after approval and rollback tests. Define stop conditions before each phase. Verify the real end-to-end path from Wazuh event to operator-visible case; a healthy manager API widget proves only that summary polling works. ## Report decisions explicitly For a design or review, state: - authoritative event source and what was actually tested; - accepted/suppressed rules and correlation-key version; - delivery, retry, DLQ, and idempotency behavior; - enrichment dependencies and failure behavior; - LLM boundary and provenance fields; - playbook approvals, blast radius, rollback, and verification; - rollout phase, metrics, unresolved gaps, and the cleanup-script context if it becomes available.