worked poc
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TuningDecision:
|
||||
keep: bool
|
||||
reason: str
|
||||
|
||||
|
||||
class AlertTuner:
|
||||
"""Small second-stage noise filter for the dashboard/incident database.
|
||||
|
||||
It intentionally does not replace Suricata threshold.config. The latter is
|
||||
the right place for sensor-level suppressions and thresholds. This filter
|
||||
is a final guardrail so low-priority or explicitly ignored alerts do not
|
||||
flood SQLite and the UI.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
max_severity: int,
|
||||
ignore_sids: str = "",
|
||||
ignore_categories: str = "",
|
||||
) -> None:
|
||||
self.max_severity = max(0, int(max_severity))
|
||||
self.ignore_sids = _parse_int_set(ignore_sids)
|
||||
self.ignore_categories = {
|
||||
value.strip().casefold()
|
||||
for value in (ignore_categories or "").split(",")
|
||||
if value.strip()
|
||||
}
|
||||
|
||||
def evaluate(self, event: dict[str, Any]) -> TuningDecision:
|
||||
alert = event.get("alert") or {}
|
||||
sid = _as_int(alert.get("signature_id"))
|
||||
severity = _as_int(alert.get("severity"))
|
||||
category = str(alert.get("category") or "").strip()
|
||||
|
||||
if sid is not None and sid in self.ignore_sids:
|
||||
return TuningDecision(False, "ignored_sid")
|
||||
|
||||
if category and category.casefold() in self.ignore_categories:
|
||||
return TuningDecision(False, "ignored_category")
|
||||
|
||||
if self.max_severity > 0:
|
||||
if severity is None:
|
||||
return TuningDecision(False, "invalid_severity")
|
||||
if severity > self.max_severity:
|
||||
return TuningDecision(False, "low_priority")
|
||||
|
||||
return TuningDecision(True, "accepted")
|
||||
|
||||
|
||||
def _parse_int_set(value: str) -> set[int]:
|
||||
result: set[int] = set()
|
||||
for item in (value or "").split(","):
|
||||
item = item.strip()
|
||||
if not item:
|
||||
continue
|
||||
try:
|
||||
result.add(int(item))
|
||||
except ValueError:
|
||||
continue
|
||||
return result
|
||||
|
||||
|
||||
def _as_int(value: Any) -> int | None:
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
Reference in New Issue
Block a user