Files
agent-skills/skills/wazuh-soc-alert-triage/SKILL.md
T
Malin 8dd10ed81c skill: add wazuh-soc-alert-triage
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).
2026-08-22 14:14:17 +02:00

189 lines
8.8 KiB
Markdown

---
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.