diff --git a/infra/debug-dashboard/app.py b/infra/debug-dashboard/app.py index 7da9da6..7d9545a 100644 --- a/infra/debug-dashboard/app.py +++ b/infra/debug-dashboard/app.py @@ -171,22 +171,32 @@ def device(machine_name): abort(404) payload = report[12] - ninja = payload.get("NinjaOne") or payload.get("ninjaOne") or {} - runtime = payload.get("Runtime") or payload.get("runtime") or {} - raw_export = { - "organization_name": ninja.get("OrganizationName") or ninja.get("organizationName") or "Nicht zugeordnet", - "organization_id": ninja.get("OrganizationId") or ninja.get("organizationId") or "-", - "generated_at": payload.get("GeneratedAtUtc") or payload.get("generatedAtUtc") or report[4], - "client_version": payload.get("ClientVersion") or payload.get("clientVersion") or report[3] or "-", - "upload_status": runtime.get("UploadStatus") or runtime.get("uploadStatus") or "unbekannt", - "error_count": len(payload.get("Errors") or payload.get("errors") or []), - } + event_groups = {} + for entry in payload.get("Events") or payload.get("events") or []: + event_type = entry.get("Target") or entry.get("target") or "Sicherheitsereignis" + account = entry.get("Username") or entry.get("username") or "-" + source_ip = entry.get("SourceIp") or entry.get("sourceIp") or "-" + key = (event_type, account, source_ip) + group = event_groups.setdefault( + key, + {"type": event_type, "account": account, "source_ip": source_ip, "count": 0, "latest": "-"}, + ) + group["count"] += 1 + timestamp = entry.get("Timestamp") or entry.get("timestamp") or "-" + if timestamp > group["latest"]: + group["latest"] = timestamp + + security_events = sorted( + event_groups.values(), + key=lambda entry: (entry["latest"], entry["count"]), + reverse=True, + )[:25] return render_template( "device.html", report=report, event=event_metadata(payload), payload=payload, - raw_export=raw_export, + security_events=security_events, payload_pretty=json.dumps(payload, indent=2, ensure_ascii=False), ) diff --git a/infra/debug-dashboard/static/app.css b/infra/debug-dashboard/static/app.css index 459cda6..7937595 100644 --- a/infra/debug-dashboard/static/app.css +++ b/infra/debug-dashboard/static/app.css @@ -27,4 +27,4 @@ table { width:100%; border-collapse:collapse; font-family:'Roboto',sans-serif; f .recipient-form input:focus,.recipient-form select:focus { outline:2px solid rgba(36,90,133,.25); border-color:#245a85; } .rule-actions .button-secondary { border-color:var(--line); background:#f8fbfd; } .recipient-intro { max-width:720px; margin:6px 0 28px; }.recipient-intro h1 { margin:9px 0 10px; font-size:46px; line-height:1; letter-spacing:-.055em; }.recipient-intro p { margin:0; color:var(--muted); font-size:16px; line-height:1.55; }.recipient-intro strong { color:var(--ink); }.recipient-create-panel { margin-top:0; border-color:#c8dbe8; }.recipient-create-panel .panel-heading h2,.recipient-rules-panel .panel-heading h2 { margin:7px 0 8px; }.recipient-create-panel .panel-heading p { margin:0 0 20px; }.recipient-form button { white-space:nowrap; }.recipient-rules-panel { padding-bottom:12px; }.recipient-rules-panel .panel-heading { display:flex; align-items:end; justify-content:space-between; gap:16px; }.recipient-rules-panel .panel-heading h2 { margin-bottom:20px; }.recipient-rules-panel .panel-heading small { display:inline-block; margin-left:7px; padding:4px 7px; border-radius:12px; background:#edf4f8; color:#4d687b; font-size:10px; font-weight:700; letter-spacing:.04em; vertical-align:middle; }.recipient-table td { height:64px; }.recipient-table tr:last-child td { border-bottom:0; }.recipient-email { color:#245a85; font-weight:500; }.actions-heading { text-align:right; }.recipient-table .rule-actions { justify-content:flex-end; }.empty-state { padding:30px 10px !important; color:var(--muted); text-align:center; } -.compact-metrics { grid-template-columns:repeat(5,1fr); }.compact-metrics article { min-height:108px; }.compact-metrics article small { display:block; margin-top:6px; color:var(--muted); font:500 11px 'Roboto',sans-serif; }.raw-export-card { border-color:#c8dbe8 !important; background:#f2f8fb !important; }.raw-export-panel { margin-top:8px; }.raw-export-panel .panel-heading h2 { margin:7px 0 8px; }.raw-export-panel .panel-heading p { margin:0 0 20px; }.raw-export-summary { display:grid; grid-template-columns:repeat(4,1fr); gap:10px; }.raw-export-summary article { padding:15px; border:1px solid #dce6ee; border-radius:6px; background:#f8fbfd; }.raw-export-summary span,.raw-export-summary small { display:block; color:var(--muted); font:700 10px 'Roboto',sans-serif; letter-spacing:.07em; text-transform:uppercase; }.raw-export-summary strong { display:block; margin:8px 0 4px; color:var(--ink); font-size:16px; overflow-wrap:anywhere; }.raw-export-summary small { font-weight:500; letter-spacing:0; text-transform:none; }.raw-json { margin-top:18px; border-top:1px solid var(--line); }.raw-json summary { padding:14px 0; color:#245a85; cursor:pointer; font:700 12px 'Roboto',sans-serif; }.raw-json pre { margin-bottom:0; } @media (max-width:850px) { .compact-metrics,.raw-export-summary { grid-template-columns:repeat(2,1fr); }.compact-metrics article:last-child { grid-column:span 2; } } +.compact-metrics { grid-template-columns:repeat(4,1fr); }.event-summary-panel { margin-top:8px; }.event-summary-panel .panel-heading h2,.raw-export-panel .panel-heading h2 { margin:7px 0 8px; }.event-summary-panel .panel-heading p,.raw-export-panel .panel-heading p { margin:0 0 20px; }.event-count { display:inline-grid; min-width:28px; min-height:28px; place-items:center; border-radius:14px; background:#fff0d7; color:var(--amber); font:700 12px 'Roboto',sans-serif; }.raw-export-panel { margin-top:8px; }.raw-json { margin-top:18px; border-top:1px solid var(--line); }.raw-json summary { padding:14px 0; color:#245a85; cursor:pointer; font:700 12px 'Roboto',sans-serif; }.raw-json pre { margin-bottom:0; } @media (max-width:850px) { .compact-metrics { grid-template-columns:repeat(2,1fr); }.compact-metrics article:last-child { grid-column:span 2; } } diff --git a/infra/debug-dashboard/templates/device.html b/infra/debug-dashboard/templates/device.html index 7ffca90..7bd8961 100644 --- a/infra/debug-dashboard/templates/device.html +++ b/infra/debug-dashboard/templates/device.html @@ -2,6 +2,7 @@ {% block title %}{{ report[0] }} - OC Sentinel{% endblock %} {% block content %}

{{ report[0] }}

{{ report[6] }}{% if report[8] %}{{ 'aktuell' if event.is_current else 'historisch' }}: {{ event.label }}{% endif %}
-
Ereignisse{{ report[8] }}
Quell-IPs{{ report[9] }}
CVEs{{ report[10] }}
Kritische CVEs{{ report[11] }}
Roh-ExportJSON{{ raw_export.upload_status }}
-
Technische Daten

Roh-Export

Zusammenfassung der zuletzt eingegangenen Clientdaten.

Organisation{{ raw_export.organization_name }}ID {{ raw_export.organization_id }}
Erstellt{{ raw_export.generated_at }}UTC-Zeitstempel des Clients
Client{{ raw_export.client_version }}Upload: {{ raw_export.upload_status }}
Scanner-Hinweise{{ raw_export.error_count }}gemeldete Fehler
Vollstaendigen JSON-Export anzeigen
{{ payload_pretty }}
+
Ereignisse{{ report[8] }}
Quell-IPs{{ report[9] }}
CVEs{{ report[10] }}
Kritische CVEs{{ report[11] }}
+
Schnelluebersicht

Erkannte Sicherheitsereignisse

Fehlgeschlagene Anmeldungen und weitere Vorfaelle aus dem letzten Scan, nach Konto und Quell-IP zusammengefasst.

{% for entry in security_events %}{% else %}{% endfor %}
VorfallKontoQuell-IPLetzter ZeitpunktAnzahl
{{ entry.type }}{{ entry.account }}{{ entry.source_ip }}{{ entry.latest }}{{ entry.count }}
Keine sicherheitsrelevanten Ereignisse im letzten Scan.
+
Technische Daten

Roh-Export

Vollstaendige, unveraenderte Nutzlast des zuletzt eingegangenen Scans.

JSON-Rohdaten
{{ payload_pretty }}
{% endblock %}