diff --git a/infra/debug-dashboard/app.py b/infra/debug-dashboard/app.py index 0e7d51b..339b7de 100644 --- a/infra/debug-dashboard/app.py +++ b/infra/debug-dashboard/app.py @@ -69,6 +69,15 @@ def event_metadata(payload): } +def payload_value(payload, *names, default=None): + if not isinstance(payload, dict): + return default + for name in names: + if name in payload: + return payload[name] + return default + + @app.get("/") def overview(): with db_connection() as connection, connection.cursor() as cursor: @@ -387,13 +396,55 @@ def device(machine_name): key=lambda entry: (entry["latest"], entry["count"]), reverse=True, )[:25] + ransomware_raw = payload.get("RansomwareBeta") or payload.get("ransomwareBeta") or {} + sensor_labels = { + "security-process-4688": "Prozessstarts (Security 4688)", + "powershell-script-block-4104": "PowerShell-Skriptblöcke (4104)", + "sysmon-process-1": "Sysmon-Prozesse (1)", + "security-file-audit-4663": "Datei-Auditing (4663)", + } + ransomware_sensors = [ + { + "label": sensor_labels.get(payload_value(sensor, "Name", "name"), payload_value(sensor, "Name", "name", default="Unbekannter Sensor")), + "enabled": payload_value(sensor, "Enabled", "enabled", default=False), + "available": payload_value(sensor, "Available", "available", default=False), + "state": payload_value(sensor, "State", "state", default="unknown"), + "event_count": payload_value(sensor, "EventCount", "eventCount", default=0), + } + for sensor in payload_value(ransomware_raw, "Sensors", "sensors", default=[]) + ] + ransomware_beta = { + "enabled": payload_value(ransomware_raw, "Enabled", "enabled", default=False), + "state": payload_value(ransomware_raw, "State", "state", default="disabled"), + "reason": payload_value(ransomware_raw, "Reason", "reason", default="Keine Ransomware-Beta-Daten verfuegbar."), + "signals": [ + { + "timestamp": payload_value(signal, "Timestamp", "timestamp", default="-"), + "category": payload_value(signal, "Category", "category", default="Signal"), + "process": payload_value(signal, "Process", "process", default="-"), + "source": payload_value(signal, "Source", "source", default="-"), + "confidence": payload_value(signal, "Confidence", "confidence", default="low"), + } + for signal in payload_value(ransomware_raw, "Signals", "signals", default=[]) + ], + "smbSessions": [ + { + "clientComputerName": payload_value(session, "ClientComputerName", "clientComputerName", default="-"), + "clientUserName": payload_value(session, "ClientUserName", "clientUserName", default="-"), + "openFileCount": payload_value(session, "OpenFileCount", "openFileCount", default=0), + "sessionId": payload_value(session, "SessionId", "sessionId", default="-"), + } + for session in payload_value(ransomware_raw, "SmbSessions", "smbSessions", default=[]) + ], + } return render_template( "device.html", report=report, event=event_metadata(payload), payload=payload, security_events=security_events, - ransomware_beta=payload.get("RansomwareBeta") or payload.get("ransomwareBeta") or {}, + ransomware_beta=ransomware_beta, + ransomware_sensors=ransomware_sensors, 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 ac3100a..d1933a9 100644 --- a/infra/debug-dashboard/static/app.css +++ b/infra/debug-dashboard/static/app.css @@ -31,3 +31,16 @@ table { width:100%; border-collapse:collapse; font-family:'Roboto',sans-serif; f .network-panel { overflow:hidden; }.network-flows { display:grid; gap:8px; }.network-flow { display:grid; grid-template-columns:minmax(150px,.9fr) minmax(130px,1.25fr) minmax(210px,1.2fr); align-items:center; gap:16px; padding:13px 14px; border:1px solid #dce6ee; border-radius:7px; background:#fbfdff; color:var(--ink); text-decoration:none; transition:transform .18s ease,box-shadow .18s ease; }.network-flow:hover { transform:translateX(3px); box-shadow:0 8px 18px rgba(31,68,99,.1); }.flow-endpoint { display:grid; gap:3px; }.flow-endpoint span { color:var(--muted); font:700 9px 'Roboto',sans-serif; letter-spacing:.1em; text-transform:uppercase; }.flow-endpoint strong { font-size:14px; }.flow-endpoint small { color:var(--muted); font-size:11px; }.flow-line { position:relative; display:flex; align-items:center; gap:7px; min-height:22px; }.flow-line:before { position:absolute; right:0; left:0; height:3px; background:#d7e4ed; content:''; }.flow-line i { z-index:1; width:max(5%,var(--flow)); height:7px; border-radius:6px; background:linear-gradient(90deg,#245a85,#b8e36a); }.flow-line small { z-index:1; margin-left:auto; padding:2px 5px; border-radius:8px; background:#fff; color:#456174; font:700 10px 'Roboto',sans-serif; } @media (max-width:850px) { .network-flow { grid-template-columns:1fr; gap:9px; }.flow-line { order:3; }.flow-endpoint.target { order:2; } } .network-map-panel { overflow:hidden; }.map-toolbar { display:flex; flex-wrap:wrap; gap:8px; margin:0 0 16px; }.map-toolbar label { flex:1 1 240px; display:grid; gap:5px; color:var(--muted); font:700 9px 'Roboto',sans-serif; letter-spacing:.1em; text-transform:uppercase; }.map-toolbar input,.map-toolbar button { min-height:38px; padding:8px 10px; border:1px solid var(--line); border-radius:5px; background:#fff; color:var(--ink); font:700 12px 'Roboto',sans-serif; }.map-toolbar button { cursor:pointer; background:#f5f9fc; }.network-map-layout { display:grid; grid-template-columns:minmax(0,1fr) 260px; min-height:560px; overflow:hidden; border:1px solid #d7e4ed; border-radius:9px; background:radial-gradient(circle at 18% 12%,#f5fbff,transparent 28rem),#edf4f8; }.network-map-layout #network-map { min-height:560px; background-image:linear-gradient(rgba(36,90,133,.05) 1px,transparent 1px),linear-gradient(90deg,rgba(36,90,133,.05) 1px,transparent 1px); background-size:32px 32px; }.network-map-layout aside { padding:22px; border-left:1px solid #d7e4ed; background:#fff; }.network-map-layout aside strong { display:block; margin:8px 0 12px; font-size:18px; line-height:1.15; letter-spacing:-.035em; }.network-map-layout aside p { color:var(--muted); font-size:13px; line-height:1.5; }.network-map-layout dl { display:grid; grid-template-columns:1fr; gap:4px; margin:18px 0 0; }.network-map-layout dt { color:var(--muted); font-size:10px; font-weight:700; text-transform:uppercase; }.network-map-layout dd { margin:0 0 10px; font-size:13px; overflow-wrap:anywhere; }.inspector-arrow { color:var(--green); font-size:13px; }.legend.source { background:#245a85; }.legend.target { background:#14735b; } @media (max-width:850px) { .network-map-layout { grid-template-columns:1fr; }.network-map-layout aside { border-top:1px solid #d7e4ed; border-left:0; }.network-map-layout #network-map { min-height:460px; } } .map-hero { display:flex; align-items:end; justify-content:space-between; gap:28px; margin:0 -2vw 26px; padding:38px 3vw 30px; border-radius:14px; color:#eaf2f8; background:radial-gradient(circle at 82% 10%,rgba(79,163,223,.25),transparent 20rem),linear-gradient(132deg,#102a43,#0c1c2a 70%); box-shadow:0 18px 50px rgba(13,30,44,.22); }.map-hero h1 { max-width:650px; margin:8px 0 14px; font-size:clamp(42px,6vw,76px); line-height:.87; letter-spacing:-.07em; }.map-hero h1 em { color:#b8e36a; font-style:normal; }.map-hero p { max-width:620px; margin:0; color:#b7cad9; font-size:15px; line-height:1.55; }.map-hero .eyebrow { color:#9cc8e8; }.map-hero-status { display:grid; min-width:145px; gap:4px; padding:16px 18px; border:1px solid rgba(184,227,106,.35); border-radius:10px; background:rgba(11,31,45,.55); }.map-hero-status span { color:#b8e36a; font:700 9px 'Roboto',sans-serif; letter-spacing:.13em; }.map-hero-status strong { font-size:37px; line-height:1; letter-spacing:-.06em; }.map-hero-status small { color:#b7cad9; }.map-stat-strip { display:grid; grid-template-columns:repeat(4,1fr); gap:1px; margin:-10px 2vw 28px; border:1px solid #d8e5ee; border-radius:9px; overflow:hidden; background:#d8e5ee; box-shadow:0 10px 22px rgba(31,68,99,.08); }.map-stat-strip article { padding:15px 18px; background:#fff; }.map-stat-strip span,.map-stat-strip small { display:block; color:var(--muted); font:700 9px 'Roboto',sans-serif; letter-spacing:.1em; text-transform:uppercase; }.map-stat-strip strong { display:block; margin:8px 0 4px; font-size:29px; letter-spacing:-.06em; }.network-map-panel { margin-top:0; padding:0; border:0; border-radius:12px; background:#102a43; box-shadow:0 20px 44px rgba(16,42,67,.2); }.map-header { display:flex; justify-content:space-between; align-items:end; gap:16px; padding:24px 26px 18px; color:#eef6fa; }.map-header .eyebrow { color:#9cc8e8; }.map-header h2 { margin:6px 0 0; font-size:30px; letter-spacing:-.05em; }.map-legend { display:flex; gap:12px; color:#b7cad9; font-size:11px; }.map-legend span { display:flex; align-items:center; gap:5px; }.map-legend i { width:8px; height:8px; border-radius:50%; background:#4fa3df; }.map-legend i.target { background:#3bca99; border-radius:2px; }.map-legend i.hot { background:#ffb454; }.map-toolbar { align-items:end; margin:0; padding:0 26px 18px; border-bottom:1px solid rgba(156,200,232,.16); }.map-toolbar label { flex:0 1 180px; color:#9cc8e8; }.map-toolbar .search-field { flex:1 1 240px; }.map-toolbar input,.map-toolbar select,.map-toolbar button { min-height:40px; border:1px solid rgba(156,200,232,.24); border-radius:6px; background:#17374e; color:#eef6fa; font:600 12px 'Roboto',sans-serif; }.map-toolbar button { cursor:pointer; background:#245a85; }.map-toolbar button:hover { background:#326f9f; }.map-actions { display:flex; gap:7px; }.network-map-layout { grid-template-columns:minmax(0,1fr) 280px; min-height:610px; border:0; border-radius:0; background:#0d1e2c; }.network-map-layout #network-map { min-height:610px; background-image:radial-gradient(circle at 50% 0,rgba(79,163,223,.1),transparent 28rem),linear-gradient(rgba(156,200,232,.045) 1px,transparent 1px),linear-gradient(90deg,rgba(156,200,232,.045) 1px,transparent 1px); background-size:auto,36px 36px,36px 36px; }.network-map-layout aside { padding:24px; border-left:1px solid rgba(156,200,232,.16); background:#112b3d; color:#eef6fa; }.network-map-layout aside .eyebrow { color:#9cc8e8; }.network-map-layout aside p { color:#b7cad9; }.network-map-layout dt { color:#82b7dc; }.network-map-layout dd { color:#eef6fa; }.inspector-arrow { color:#b8e36a; }.legend.source { background:#4fa3df; }.legend.target { background:#3bca99; } @media (max-width:850px) { .map-hero { flex-direction:column; align-items:start; margin:0 0 20px; }.map-stat-strip { grid-template-columns:repeat(2,1fr); margin:0 0 20px; }.map-header { align-items:start; flex-direction:column; }.map-toolbar { padding:0 18px 18px; }.network-map-layout { grid-template-columns:1fr; }.network-map-layout aside { border-top:1px solid rgba(156,200,232,.16); border-left:0; } } + +:root { --font-sans:'Manrope','Segoe UI',sans-serif; --font-mono:'IBM Plex Mono','Cascadia Code',monospace; --surface:#f7fafc; --surface-strong:#edf4f8; --navy:#0c2438; --blue:#2e6b9a; } +body,body * { font-family:var(--font-sans); } +pre,code,.raw-json pre { font-family:var(--font-mono); } +body { background:radial-gradient(circle at 8% -10%,rgba(102,176,225,.24),transparent 31rem),radial-gradient(circle at 94% 6%,rgba(85,201,155,.16),transparent 24rem),linear-gradient(180deg,#edf4f8 0,#f8fafc 42%,#eef4f7 100%); } +.masthead { position:sticky; z-index:5; top:0; backdrop-filter:blur(16px); background:rgba(12,36,56,.94); } +.dashboard-hero { display:grid; grid-template-columns:minmax(0,1fr) 230px; gap:28px; min-height:276px; margin:0 0 18px; padding:38px; border:1px solid rgba(134,192,225,.24); border-radius:18px; color:#ecf5fb; background:radial-gradient(circle at 88% 8%,rgba(96,183,227,.28),transparent 19rem),linear-gradient(135deg,#102f49,#0a1d2c 72%); box-shadow:0 24px 50px rgba(18,51,75,.18); overflow:hidden; } +.dashboard-hero .eyebrow { color:#a6d2ec; }.dashboard-hero h1 { max-width:730px; margin:11px 0 15px; font-size:clamp(38px,5.2vw,66px); line-height:.94; letter-spacing:-.067em; }.dashboard-hero p { max-width:620px; margin:0; color:#bdd3e1; font-size:15px; line-height:1.65; } +.dashboard-status { align-self:end; display:grid; gap:5px; padding:19px; border:1px solid rgba(168,216,241,.26); border-radius:14px; background:rgba(4,22,35,.34); box-shadow:inset 0 1px rgba(255,255,255,.06); }.dashboard-status > span:not(.status-orb) { color:#a9c7d8; font-size:10px; font-weight:800; letter-spacing:.11em; text-transform:uppercase; }.dashboard-status strong { font-size:30px; letter-spacing:-.055em; }.dashboard-status small { color:#c5d9e5; font-size:11px; }.status-orb { width:10px; height:10px; margin-bottom:4px; border-radius:50%; background:#72d39c; box-shadow:0 0 0 6px rgba(114,211,156,.13); }.dashboard-hero.warning .status-orb { background:#ffbe62; box-shadow:0 0 0 6px rgba(255,190,98,.13); }.dashboard-hero.critical .status-orb { background:#f4796d; box-shadow:0 0 0 6px rgba(244,121,109,.13); } +.quick-metrics { display:grid; grid-template-columns:repeat(4,1fr); gap:10px; margin:0 0 35px; }.quick-metrics article { min-height:112px; padding:18px 20px; border:1px solid #d6e3ec; border-radius:12px; background:rgba(255,255,255,.86); box-shadow:0 10px 22px rgba(30,68,94,.055); }.quick-metrics span,.quick-metrics small { display:block; color:#607b8e; font-size:10px; font-weight:800; letter-spacing:.08em; text-transform:uppercase; }.quick-metrics strong { display:block; margin:10px 0 6px; font-size:30px; letter-spacing:-.06em; }.quick-metrics small { color:#7d94a4; font-size:9px; letter-spacing:.045em; text-transform:none; } +.page-intro { max-width:770px; margin:10px 0 30px; }.page-intro h1 { margin:10px 0 13px; color:var(--navy); font-size:clamp(38px,5vw,62px); line-height:.94; letter-spacing:-.07em; }.page-intro h1 em { color:var(--green); font-style:normal; }.page-intro p { max-width:600px; margin:0; color:var(--muted); font-size:15px; line-height:1.65; }.reports-panel { margin-top:0; } +.sensor-coverage-panel { margin-top:8px; }.sensor-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(215px,1fr)); gap:11px; }.sensor-card { min-height:160px; display:grid; align-content:start; gap:10px; padding:17px; border:1px solid #dbe7ee; border-radius:11px; background:linear-gradient(145deg,#fff,#f6fafc); }.sensor-card > div { display:flex; align-items:center; gap:7px; }.sensor-card strong { color:#17354a; font-size:14px; line-height:1.3; }.sensor-card p { margin:0; color:#607b8e; font-size:12px; line-height:1.55; }.sensor-dot { width:8px; height:8px; border-radius:50%; background:#6abf90; box-shadow:0 0 0 4px rgba(106,191,144,.13); }.sensor-state { color:#5d788a; font-size:9px; font-weight:800; letter-spacing:.1em; text-transform:uppercase; }.sensor-card.disabled { background:#f4f6f7; }.sensor-card.disabled .sensor-dot,.sensor-card.not-installed .sensor-dot { background:#9caeba; box-shadow:0 0 0 4px rgba(156,174,186,.13); }.sensor-card.query-failed { border-color:#f2c59c; background:#fff8ef; }.sensor-card.query-failed .sensor-dot,.sensor-card.truncated .sensor-dot { background:#e9a64d; box-shadow:0 0 0 4px rgba(233,166,77,.13); }.sensor-empty { width:100%; padding:24px; border:1px dashed #b8cbd7; border-radius:10px; color:#607b8e; text-align:center; font-size:13px; }.ransomware-panel + .sensor-coverage-panel { margin-top:8px; } +@media (max-width:850px) { .dashboard-hero { grid-template-columns:1fr; min-height:0; padding:28px 24px; }.dashboard-status { align-self:auto; }.quick-metrics { grid-template-columns:repeat(2,1fr); }.quick-metrics article { min-height:102px; }.page-intro { margin-top:4px; }.sensor-grid { grid-template-columns:1fr; } } diff --git a/infra/debug-dashboard/templates/base.html b/infra/debug-dashboard/templates/base.html index 55b34d8..842b27f 100644 --- a/infra/debug-dashboard/templates/base.html +++ b/infra/debug-dashboard/templates/base.html @@ -6,7 +6,7 @@ {% block title %}OfficeCom Sentinel{% endblock %} - + diff --git a/infra/debug-dashboard/templates/device.html b/infra/debug-dashboard/templates/device.html index 1384bc5..3e35fd9 100644 --- a/infra/debug-dashboard/templates/device.html +++ b/infra/debug-dashboard/templates/device.html @@ -4,6 +4,7 @@

{{ 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] }}
{% if ransomware_beta.enabled %}
Passive Beta

Ransomware-Frueherkennung {{ ransomware_beta.state }}

{{ ransomware_beta.reason }}

{% for signal in ransomware_beta.signals %}{% else %}{% endfor %}
ZeitpunktSignalProzessQuelleBewertung
{{ signal.timestamp }}{{ signal.category }}{{ signal.process }}{{ signal.source }}{{ signal.confidence }}
Keine Signale im aktuellen Beta-Zeitfenster.
{% endif %} +{% if ransomware_beta.enabled %}
Beta-Abdeckung

Erkennungsquellen Verfuegbarkeit im letzten Scan

Ein unauffaelliger Zeitraum ist nur dann belastbar, wenn die benoetigten Datenquellen erreichbar waren.

{% for sensor in ransomware_sensors %}
{{ sensor.state }}
{{ sensor.label }}

{% if not sensor.enabled %}Nicht fuer diesen Client aktiviert.{% elif sensor.available %}{{ sensor.event_count }} Ereignisse im Zeitfenster verarbeitet.{% elif sensor.state == 'not-installed' %}Protokollquelle ist auf diesem System nicht installiert.{% else %}Quelle konnte im letzten Scan nicht verwendet werden.{% endif %}

{% else %}
Dieser Client sendet noch keine Abdeckungsdaten. Nach dem Update auf Beta 1.5.0-beta.5 erscheint die Sensoransicht automatisch.
{% endfor %}
{% endif %} {% if ransomware_beta.smbSessions %}
Incident-Kontext

Aktive SMB-Sitzungen Nur bei Ransomware-Warnung oder kritisch erfasst

{% for session in ransomware_beta.smbSessions %}{% endfor %}
ClientBenutzerOffene DateienSitzung
{{ session.clientComputerName }}{{ session.clientUserName }}{{ session.openFileCount }}{{ session.sessionId }}
{% endif %}
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 }}
diff --git a/infra/debug-dashboard/templates/overview.html b/infra/debug-dashboard/templates/overview.html index e3eb04a..56e3ba6 100644 --- a/infra/debug-dashboard/templates/overview.html +++ b/infra/debug-dashboard/templates/overview.html @@ -1,8 +1,19 @@ {% extends "base.html" %} {% block content %} -
-
OfficeCom Sentinel Uebersicht{% if summary[2] %}Kritische Ereignisse erfordern Aufmerksamkeit{% elif summary[1] %}Hinweise im Bestand pruefen{% else %}Sicherheitslage stabil{% endif %}
- {% if summary[2] %}KRITISCH{% elif summary[1] %}PRUEFEN{% else %}STABIL{% endif %} +
+
+ OfficeCom Sentinel Uebersicht +

{% if summary[2] %}Sicherheitslage
braucht Aufmerksamkeit.{% elif summary[1] %}Signale im Bestand
gezielt pruefen.{% else %}Sicherheitslage
unter Kontrolle.{% endif %}

+

Verdichtete Endpoint-Signale, Upload-Gesundheit und organisationsweite Einordnung an einem Ort.

+
+
Aktueller Zustand{% if summary[2] %}Kritisch{% elif summary[1] %}Pruefen{% else %}Stabil{% endif %}{{ current_alert_count }} aktuelle Auffaelligkeit{{ '' if current_alert_count == 1 else 'en' }}
+
+ +
+
Geraete{{ summary[0] }}{{ coverage[1] }} melden aktuell
+
Warnungen{{ summary[1] }}im letzten Status
+
Kritisch{{ summary[2] }}sofort sichtbar
+
Abdeckung{{ coverage[1] }}/{{ coverage[0] }}{{ coverage[2] }} stumm > 36 Std.
@@ -16,14 +27,6 @@
-
-
Geraete{{ summary[0] }}
-
Warnungen{{ summary[1] }}
-
Kritisch{{ summary[2] }}
-
Ereignisse{{ summary[3] }}
-
Letzte Meldung{{ summary[7] or '-' }}
-
-

Geraeteabdeckung

Bekannt{{ coverage[0] }}
Meldend < 36 Std.{{ coverage[1] }}
Stumm > 36 Std.{{ coverage[2] }}
diff --git a/infra/debug-dashboard/templates/reports.html b/infra/debug-dashboard/templates/reports.html index d1b5894..439d838 100644 --- a/infra/debug-dashboard/templates/reports.html +++ b/infra/debug-dashboard/templates/reports.html @@ -1,7 +1,8 @@ {% extends "base.html" %} {% block title %}Berichte - OC Sentinel{% endblock %} {% block content %} -
+
Wochenberichte

Sicherheitsberichte
auf einen Blick.

Alle automatisch erzeugten Organisationsberichte mit direktem Zugriff auf die finale HTML-Vorschau.

+
Archiv

Gesendete Berichte {{ reports|length }} Eintraege

OrganisationZeitraumGeraeteWarnungKritischEventsErstellt
{% for row in reports %}{% else %}{% endfor %}
OrganisationZeitraumGeraeteWarnungKritischEventsErstellt
{{ row[1] }}{{ row[2] }} bis {{ row[3] }}{{ row[5] }}{{ row[6] }}{{ row[7] }}{{ row[8] }}{{ row[4] }}
Keine Wochenberichte.
{% endblock %}