|
|
@@ -656,71 +656,41 @@ class MvpService:
|
|
656
|
656
|
severity_str = (event.get("severity") or "medium").lower()
|
|
657
|
657
|
severity_id = _IRIS_SEVERITY_ID.get(severity_str, 3)
|
|
658
|
658
|
|
|
659
|
|
- note_lines: list[str] = []
|
|
660
|
|
-
|
|
661
|
|
- # Asset
|
|
662
|
|
- asset = event.get("asset") or {}
|
|
663
|
|
- if any(asset.get(k) for k in ("hostname", "user", "agent_id")):
|
|
664
|
|
- note_lines.append("## Asset")
|
|
665
|
|
- if asset.get("hostname"):
|
|
666
|
|
- note_lines.append(f" Hostname : {asset['hostname']}")
|
|
667
|
|
- if asset.get("user"):
|
|
668
|
|
- note_lines.append(f" User : {asset['user']}")
|
|
669
|
|
- if asset.get("agent_id"):
|
|
670
|
|
- note_lines.append(f" Agent ID : {asset['agent_id']}")
|
|
671
|
|
-
|
|
672
|
|
- # Network
|
|
673
|
|
- network = event.get("network") or {}
|
|
674
|
|
- net_fields = {k: network.get(k) for k in ("src_ip", "dst_ip", "dst_port", "dst_host", "proto") if network.get(k)}
|
|
675
|
|
- if net_fields:
|
|
676
|
|
- note_lines.append("\n## Network")
|
|
677
|
|
- for k, v in net_fields.items():
|
|
678
|
|
- note_lines.append(f" {k:<12}: {v}")
|
|
679
|
|
-
|
|
680
|
|
- # IOC verdicts
|
|
|
659
|
+ def _strip_nulls(obj: Any) -> Any:
|
|
|
660
|
+ """Recursively remove None values to keep the JSON compact."""
|
|
|
661
|
+ if isinstance(obj, dict):
|
|
|
662
|
+ return {k: _strip_nulls(v) for k, v in obj.items() if v is not None}
|
|
|
663
|
+ if isinstance(obj, list):
|
|
|
664
|
+ return [_strip_nulls(i) for i in obj]
|
|
|
665
|
+ return obj
|
|
|
666
|
+
|
|
681
|
667
|
raw = event.get("raw") or {}
|
|
682
|
|
- verdicts: list[dict[str, Any]] = raw.get("verdicts") or []
|
|
683
|
|
- if verdicts:
|
|
684
|
|
- note_lines.append("\n## IOC Verdicts")
|
|
|
668
|
+ note_data: dict[str, Any] = {
|
|
|
669
|
+ "asset": event.get("asset") or {},
|
|
|
670
|
+ "network": event.get("network") or {},
|
|
|
671
|
+ "tags": event.get("tags") or [],
|
|
|
672
|
+ }
|
|
|
673
|
+ if raw.get("verdicts"):
|
|
685
|
674
|
ioc_payload = raw.get("payload") or {}
|
|
686
|
|
- if ioc_payload.get("ioc_value"):
|
|
687
|
|
- note_lines.append(f" IOC : {ioc_payload.get('ioc_type','?')} / {ioc_payload['ioc_value']}")
|
|
688
|
|
- for v in verdicts:
|
|
689
|
|
- provider = v.get("provider", "?")
|
|
690
|
|
- matched = v.get("matched", False)
|
|
691
|
|
- conf = v.get("confidence", 0.0)
|
|
692
|
|
- sev = v.get("severity", "?")
|
|
693
|
|
- result = v.get("result") or {}
|
|
694
|
|
- note_lines.append(f"\n [{provider.upper()}]")
|
|
695
|
|
- note_lines.append(f" Matched : {'YES' if matched else 'no'}")
|
|
696
|
|
- note_lines.append(f" Confidence : {conf:.0%}")
|
|
697
|
|
- note_lines.append(f" Severity : {sev}")
|
|
698
|
|
- # Provider-specific detail
|
|
699
|
|
- providers_detail = (result.get("providers") or {})
|
|
700
|
|
- vt = providers_detail.get("virustotal", {})
|
|
701
|
|
- if vt.get("stats"):
|
|
702
|
|
- s = vt["stats"]
|
|
703
|
|
- note_lines.append(
|
|
704
|
|
- f" VT Stats : malicious={s.get('malicious',0)} "
|
|
705
|
|
- f"suspicious={s.get('suspicious',0)} "
|
|
706
|
|
- f"harmless={s.get('harmless',0)} "
|
|
707
|
|
- f"undetected={s.get('undetected',0)}"
|
|
708
|
|
- )
|
|
709
|
|
- ab = providers_detail.get("abuseipdb", {})
|
|
710
|
|
- if ab:
|
|
711
|
|
- note_lines.append(
|
|
712
|
|
- f" AbuseIPDB : score={ab.get('score',0)} "
|
|
713
|
|
- f"reports={ab.get('totalReports',0)}"
|
|
714
|
|
- )
|
|
715
|
|
- if result.get("reason"):
|
|
716
|
|
- note_lines.append(f" Reason : {result['reason']}")
|
|
717
|
|
-
|
|
718
|
|
- # Tags
|
|
719
|
|
- tags = event.get("tags") or []
|
|
720
|
|
- if tags:
|
|
721
|
|
- note_lines.append(f"\n## Tags\n {', '.join(str(t) for t in tags)}")
|
|
722
|
|
-
|
|
723
|
|
- alert_note = "\n".join(note_lines).strip()
|
|
|
675
|
+ note_data["ioc"] = {
|
|
|
676
|
+ "type": ioc_payload.get("ioc_type"),
|
|
|
677
|
+ "value": ioc_payload.get("ioc_value"),
|
|
|
678
|
+ "verdicts": [
|
|
|
679
|
+ {
|
|
|
680
|
+ "provider": v.get("provider"),
|
|
|
681
|
+ "matched": v.get("matched"),
|
|
|
682
|
+ "confidence": round(float(v.get("confidence") or 0), 4),
|
|
|
683
|
+ "severity": v.get("severity"),
|
|
|
684
|
+ "reason": (v.get("result") or {}).get("reason"),
|
|
|
685
|
+ "vt_stats": ((v.get("result") or {}).get("providers") or {}).get("virustotal", {}).get("stats"),
|
|
|
686
|
+ "abuseipdb_score": ((v.get("result") or {}).get("providers") or {}).get("abuseipdb", {}).get("score"),
|
|
|
687
|
+ "abuseipdb_reports": ((v.get("result") or {}).get("providers") or {}).get("abuseipdb", {}).get("totalReports"),
|
|
|
688
|
+ }
|
|
|
689
|
+ for v in raw["verdicts"]
|
|
|
690
|
+ ],
|
|
|
691
|
+ }
|
|
|
692
|
+
|
|
|
693
|
+ alert_note = json.dumps(_strip_nulls(note_data), indent=2, ensure_ascii=False)
|
|
724
|
694
|
|
|
725
|
695
|
payload: dict[str, Any] = {
|
|
726
|
696
|
"alert_title": event.get("title") or f"Wazuh alert {event_id}",
|