mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-28 21:17:36 +00:00
fix(waf-api): crowdsec overlay must not crash the warm refresh (ref #744)
Some checks are pending
License Headers / check (push) Waiting to run
Some checks are pending
License Headers / check (push) Waiting to run
_overlay_crowdsec_stats received the already-decorated stats (top_countries as a
list of {country,count} dicts, not a dict) and did sorted(key=lambda x:-x[1]) →
TypeError on a str → aborted _refresh_warm_caches, freezing the warm cache and
zeroing the WAF dashboard's per-IP/vhost/tracked-attacker panels. Accept the
list shape, coerce counts to int, and wrap the overlay call defensively so a
CrowdSec hiccup can never wipe the WAF stats.
This commit is contained in:
parent
c6d6eb5c75
commit
e5f0d22dc6
|
|
@ -646,7 +646,13 @@ def _overlay_crowdsec_stats(stats: dict, alerts: List[dict]) -> None:
|
|||
today = 0
|
||||
by_sev = defaultdict(int, stats.get("by_severity") or {})
|
||||
by_cat = defaultdict(int, stats.get("by_category") or {})
|
||||
by_country = defaultdict(int, stats.get("top_countries") or {})
|
||||
# top_countries may already have been decorated into a list of
|
||||
# {"country","count"} dicts (the overlay can run after _decorate_dashboard_stats);
|
||||
# accept either shape so we never crash the warm refresh and lose the WAF stats.
|
||||
_tc = stats.get("top_countries") or {}
|
||||
if isinstance(_tc, list):
|
||||
_tc = {d.get("country", "??"): int(d.get("count", 0) or 0) for d in _tc}
|
||||
by_country = defaultdict(int, _tc)
|
||||
for a in alerts:
|
||||
by_sev[a["severity"]] += 1
|
||||
by_cat[a["category"]] += 1
|
||||
|
|
@ -666,7 +672,7 @@ def _overlay_crowdsec_stats(stats: dict, alerts: List[dict]) -> None:
|
|||
stats["by_severity"] = dict(by_sev)
|
||||
stats["by_category"] = dict(by_cat)
|
||||
stats["top_countries"] = [{"country": c, "count": n}
|
||||
for c, n in sorted(by_country.items(), key=lambda x: -x[1])[:5]]
|
||||
for c, n in sorted(by_country.items(), key=lambda x: -int(x[1] or 0))[:5]]
|
||||
if alerts:
|
||||
a0 = alerts[0]
|
||||
ago = "recently"
|
||||
|
|
@ -697,7 +703,14 @@ def _refresh_warm_caches() -> dict:
|
|||
except Exception:
|
||||
cs_alerts = []
|
||||
if cs_alerts:
|
||||
_overlay_crowdsec_stats(new_stats, cs_alerts)
|
||||
# Defensive: a CrowdSec-overlay failure must NEVER abort the refresh and
|
||||
# leave the warm cache frozen — the WAF top_ips/top_vhosts/tracked-attacker
|
||||
# data (the dashboard's per-IP/vhost panels) lives in new_stats and must
|
||||
# survive even if the optional CrowdSec donut overlay throws.
|
||||
try:
|
||||
_overlay_crowdsec_stats(new_stats, cs_alerts)
|
||||
except Exception:
|
||||
pass
|
||||
if not new_alerts:
|
||||
new_alerts = cs_alerts # feed the live feed / threat list too
|
||||
new_stats["vhosts_count"] = _protected_vhost_count()
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user