Files
routeros-suricata-tzsp/app/adaptive.py
T
2026-08-15 18:29:36 +02:00

59 lines
2.3 KiB
Python

from __future__ import annotations
import math
from typing import Any
def score_rule(row: dict[str, Any]) -> dict[str, Any]:
"""Turn alert history into a conservative tuning recommendation.
This module never disables signatures automatically. It only proposes
thresholding when observed noise is high and incident usefulness is low.
"""
item = dict(row)
hits = max(0, int(item.get("hits") or 0))
rows = max(0, int(item.get("rows") or 0))
unique_src = max(0, int(item.get("unique_src") or 0))
unique_dst = max(0, int(item.get("unique_dst") or 0))
incidents = max(0, int(item.get("incidents") or 0))
blocked = max(0, int(item.get("blocked") or 0))
severity = max(1, min(4, int(item.get("severity") or 4)))
incident_ratio = incidents / rows if rows else 0.0
duplicate_ratio = max(0.0, min(1.0, (hits - rows) / hits)) if hits else 0.0
concentration = hits / max(1, unique_src + unique_dst)
raw = min(48.0, math.log10(hits + 1) * 16.0)
raw += duplicate_ratio * 28.0
raw += min(18.0, math.log2(concentration + 1) * 4.0)
raw -= min(32.0, incident_ratio * 55.0)
raw -= 10.0 if severity == 1 else 4.0 if severity == 2 else 0.0
raw -= 8.0 if blocked else 0.0
noise_score = max(0, min(100, round(raw)))
recommendation = "keep"
reason = "Useful/low-volume signature"
proposed = None
if hits >= 100 and noise_score >= 70 and incidents == 0:
recommendation = "limit"
count = 1 if hits >= 1000 else 3 if hits >= 300 else 5
seconds = 60 if hits >= 300 else 120
proposed = {"type": "limit", "track": "by_src", "count": count, "seconds": seconds}
reason = "High alert volume with no incident correlation"
elif hits >= 40 and noise_score >= 55 and incident_ratio < 0.05:
recommendation = "review"
reason = "Repeated signature with low incident correlation"
elif incident_ratio >= 0.25 or severity == 1:
recommendation = "keep"
reason = "High-value or frequently incident-correlated signature"
item.update({
"noise_score": noise_score,
"incident_ratio": round(incident_ratio, 4),
"duplicate_ratio": round(duplicate_ratio, 4),
"recommendation": recommendation,
"recommendation_reason": reason,
"proposed_threshold": proposed,
})
return item