Codex-evaluated agentic-soc-platform (github.com/FunnyWolf/agentic-soc-platform) against this fleet's Wazuh deployment and informatiq-dashboard monitoring integration. Adopted the reusable architecture only (evidence-preserving normalization, deterministic correlation, approval-gated playbooks, shadow-mode rollout) - not the platform itself. Rejected BagelHole/DevOps- Security-Agent-Skills (163 skills, all duplicate/out-of-scope/unsafe) and guillaumemeyer/watermarks-remover (narrow, no reusable technique beyond existing c2patool/ExifTool).
8.8 KiB
name, description
| name | description |
|---|---|
| wazuh-soc-alert-triage | 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
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:
- Read the current
informatiq-dashboardsource and relevant/home/malin/granja/docs/server-*.md; do not infer capabilities from UI labels. - 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.
- 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.
- 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.
- 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:
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:
- Claim a message without acknowledging it.
- Validate and process inside a transaction.
- Commit the alert, case link, artifacts, and scheduled follow-up job.
- Acknowledge only after commit.
- 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
- Replay sanitized historical fixtures and assert normalization, deduplication, correlation, and suppression outcomes.
- Run shadow mode: ingest and build proposed cases without notifications or actions.
- Compare against a human-reviewed sample; measure false merges, duplicate cases, missed high-severity alerts, and analyst correction rate.
- Enable notifications with rate limits.
- Add read-only enrichment and analysis.
- 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.