feat: show notification source (agent vs SAC) in alerts and reports

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-31 11:51:53 +10:00
parent a45b641b8c
commit 3d4d1f3c76
6 changed files with 246 additions and 37 deletions
+84 -30
View File
@@ -8,7 +8,11 @@ from datetime import datetime
from typing import Any
from app.models import Event, Host, Problem
from app.services.daily_report_format import normalize_report_body
from app.services.daily_report_format import (
append_notification_source_html,
normalize_report_body,
resolve_product_label,
)
LOGON_TYPE_NAMES: dict[int, str] = {
2: "Интерактивный (консоль)",
@@ -91,6 +95,57 @@ def _line(emoji: str, label: str, value: str) -> str:
return f"{emoji} {label}: {value}\n"
def _event_generated_by(event: Event) -> str:
details = _details_dict(event)
gb = details.get("generated_by")
if isinstance(gb, str) and gb.strip().lower() in ("agent", "sac"):
return gb.strip().lower()
stats = details.get("stats")
if isinstance(stats, dict) and stats.get("sac_generated"):
return "sac"
if event.type in ("report.daily.rdp", "report.daily.ssh") and details.get("report_body"):
return "agent"
return "agent"
def _event_product_version(event: Event) -> tuple[str | None, str | None]:
details = _details_dict(event)
platform = None
if event.type == "report.daily.ssh":
platform = "ssh"
elif event.type == "report.daily.rdp":
platform = "windows"
elif event.host is not None:
if event.host.os_family == "linux":
platform = "ssh"
elif event.host.os_family == "windows":
platform = "windows"
product, version = resolve_product_label(event.host, platform)
stats = details.get("stats")
if isinstance(stats, dict):
av = stats.get("agent_version") or stats.get("product_version")
if av:
version = str(av).strip()
payload = event.payload if isinstance(event.payload, dict) else {}
source = payload.get("source")
if isinstance(source, dict):
if source.get("product"):
product = str(source["product"]).strip()
if source.get("product_version"):
version = str(source["product_version"]).strip()
return product, version
def _append_event_source(html_msg: str, event: Event) -> str:
product, version = _event_product_version(event)
return append_notification_source_html(
html_msg,
generated_by=_event_generated_by(event),
product=product,
product_version=version,
)
def format_rdp_login_html(event: Event) -> str:
details = _details_dict(event)
is_success = event.type == "rdp.login.success"
@@ -222,34 +277,29 @@ def format_generic_event_html(event: Event) -> str:
return msg.rstrip()
def format_daily_report_html(event: Event) -> str:
details = _details_dict(event)
platform = "windows" if event.type == "report.daily.rdp" else "ssh"
body = _detail(details, "report_body", default="")
if body and body != "-":
body = normalize_report_body(body, event.host, platform)
parts: list[str] = []
for i, line in enumerate(body.split("\n")):
esc = html.escape(line)
if i == 0 and "ЕЖЕДНЕВНЫЙ ОТЧЕТ" in line:
parts.append(f"<b>{esc.strip()}</b>")
else:
parts.append(esc)
return "\n".join(parts)
report_html = details.get("report_html")
if isinstance(report_html, str) and report_html.strip():
text = sanitize_telegram_html(report_html.strip())
return re.sub(r"\n{3,}", "\n\n", text)
body = _detail(details, "report_body", default=event.summary)
if body != "-":
return html.escape(body)
return format_generic_event_html(event)
def format_event_telegram_html(event: Event) -> str:
def _format_event_body_html(event: Event) -> str:
if event.type in ("report.daily.ssh", "report.daily.rdp"):
return format_daily_report_html(event)
details = _details_dict(event)
platform = "windows" if event.type == "report.daily.rdp" else "ssh"
body = _detail(details, "report_body", default="")
if body and body != "-":
body = normalize_report_body(body, event.host, platform)
parts: list[str] = []
for i, line in enumerate(body.split("\n")):
esc = html.escape(line)
if i == 0 and "ЕЖЕДНЕВНЫЙ ОТЧЕТ" in line:
parts.append(f"<b>{esc.strip()}</b>")
else:
parts.append(esc)
return "\n".join(parts)
report_html = details.get("report_html")
if isinstance(report_html, str) and report_html.strip():
text = sanitize_telegram_html(report_html.strip())
return re.sub(r"\n{3,}", "\n\n", text)
body = _detail(details, "report_body", default=event.summary)
if body != "-":
return html.escape(body)
return format_generic_event_html(event)
if event.type in ("rdp.login.success", "rdp.login.failed"):
return format_rdp_login_html(event)
if event.type in ("ssh.login.success", "ssh.login.failed"):
@@ -265,6 +315,10 @@ def format_event_telegram_html(event: Event) -> str:
return format_generic_event_html(event)
def format_event_telegram_html(event: Event) -> str:
return _append_event_source(_format_event_body_html(event), event)
def _format_rdg_html(event: Event) -> str:
details = _details_dict(event)
ok = event.type.endswith(".success")
@@ -305,5 +359,5 @@ def format_problem_telegram_html(problem: Problem, event: Event | None = None) -
"rdp.shadow.control.permission",
"winrm.session.started",
):
msg += "\n" + format_event_telegram_html(event)
return msg.rstrip()
msg += "\n" + _format_event_body_html(event)
return append_notification_source_html(msg, generated_by="sac")