Files
siem-backend/web/index.html
groot c49bfa1c34
Some checks failed
release-tag / release-image (push) Failing after 5m54s
Vollständiges Redesign
2026-07-23 14:45:35 +02:00

23 lines
7.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Greenfield SIEM</title>
<style>
:root{font-family:Inter,Segoe UI,Arial,sans-serif;color:#172033;background:#f4f7fb}*{box-sizing:border-box}body{margin:0}header{background:#101828;color:white;padding:16px 24px;display:flex;justify-content:space-between;align-items:center}header b{font-size:18px}main{padding:22px;max-width:1800px;margin:auto}.cards{display:grid;grid-template-columns:repeat(4,minmax(170px,1fr));gap:12px}.card,.panel{background:white;border:1px solid #e4e7ec;border-radius:10px;padding:16px;box-shadow:0 1px 2px #1018280f}.value{font-size:28px;font-weight:700;margin-top:8px}.muted{color:#667085;font-size:13px}.tabs{display:flex;gap:8px;margin:18px 0}.tabs button,button{border:1px solid #d0d5dd;background:white;border-radius:7px;padding:8px 12px;cursor:pointer}.tabs button.active,button.primary{background:#175cd3;color:white;border-color:#175cd3}.filters{display:grid;grid-template-columns:repeat(6,minmax(120px,1fr));gap:8px;margin-bottom:10px}input,select{width:100%;padding:8px;border:1px solid #d0d5dd;border-radius:7px}table{width:100%;border-collapse:collapse;font-size:13px}th,td{text-align:left;border-bottom:1px solid #eaecf0;padding:8px;vertical-align:top}th{color:#475467;background:#f9fafb;position:sticky;top:0}.tablewrap{max-height:65vh;overflow:auto}.sev-critical{color:#b42318;font-weight:700}.sev-high{color:#c4320a;font-weight:700}.sev-medium{color:#b54708;font-weight:700}.pill{display:inline-block;padding:2px 7px;border-radius:999px;background:#eef2f6;font-size:12px}.hidden{display:none}.error{background:#fef3f2;color:#b42318;padding:10px;border-radius:6px;margin:10px 0}@media(max-width:900px){.cards{grid-template-columns:1fr 1fr}.filters{grid-template-columns:1fr 1fr}}
</style></head><body>
<header><b>Greenfield SIEM</b><span class="muted" style="color:#d0d5dd">Tenant: {{.Tenant}}</span></header>
<main><div id="err"></div><div class="cards"><div class="card"><div class="muted">Events / 24h</div><div id="events24" class="value"></div></div><div class="card"><div class="muted">Aktive Hosts / 24h</div><div id="hosts24" class="value"></div></div><div class="card"><div class="muted">Offene High/Critical</div><div id="highdet" class="value"></div></div><div class="card"><div class="muted">Pipeline</div><div id="pipeline" class="value">OK</div></div></div>
<div class="tabs"><button class="active" data-tab="events">Events</button><button data-tab="detections">Detections</button><button data-tab="agents">Agents</button></div>
<section id="events" class="panel"><div class="filters"><input id="fhost" placeholder="Host"><input id="fuser" placeholder="User"><input id="fip" placeholder="Source-IP"><input id="fcode" placeholder="Event-ID"><input id="fchannel" placeholder="Channel"><select id="fhours"><option value="1">1 Stunde</option><option value="24" selected>24 Stunden</option><option value="168">7 Tage</option><option value="720">30 Tage</option><option value="2160">90 Tage</option></select></div><button class="primary" onclick="loadEvents()">Suchen</button><div class="tablewrap"><table><thead><tr><th>Zeit</th><th>Host</th><th>Event</th><th>Aktion</th><th>User</th><th>Source-IP</th><th>Workstation</th><th>Prozess</th></tr></thead><tbody id="eventrows"></tbody></table></div></section>
<section id="detections" class="panel hidden"><div class="tablewrap"><table><thead><tr><th>Zuletzt</th><th>Schwere</th><th>Regel</th><th>Host/User</th><th>Zusammenfassung</th><th>Status</th></tr></thead><tbody id="detrows"></tbody></table></div></section>
<section id="agents" class="panel hidden"><div class="tablewrap"><table><thead><tr><th>Host</th><th>Enabled</th><th>IP</th><th>First seen</th><th>Last seen</th></tr></thead><tbody id="agentrows"></tbody></table></div></section></main>
<script>
const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
async function get(url,opt){const r=await fetch(url,opt);const d=await r.json();if(!r.ok)throw new Error(d.error||r.statusText);return d}
function fail(e){document.getElementById('err').innerHTML='<div class="error">'+esc(e.message)+'</div>';document.getElementById('pipeline').textContent='Fehler'}
async function summary(){try{const d=await get('/api/summary');events24.textContent=Number(d.events_24h||0).toLocaleString('de-DE');hosts24.textContent=Number(d.active_hosts||0).toLocaleString('de-DE');const x=d.detections||{};highdet.textContent=Number((x.high||0)+(x.critical||0)).toLocaleString('de-DE')}catch(e){fail(e)}}
async function loadEvents(){try{const q=new URLSearchParams({hours:fhours.value,limit:'500'});[['host',fhost],['user',fuser],['ip',fip],['event_code',fcode],['channel',fchannel]].forEach(([k,x])=>{if(x.value.trim())q.set(k,x.value.trim())});const d=await get('/api/events?'+q);eventrows.innerHTML=d.map(x=>`<tr><td>${esc(x.event_time)}</td><td>${esc(x.host_name)}</td><td><b>${esc(x.event_code)}</b><br><span class="pill">${esc(x.channel)}</span></td><td>${esc(x.category)} / ${esc(x.action)}<br><span class="muted">${esc(x.outcome)}</span></td><td>${esc(x.user_name||x.target_user||x.subject_user)}</td><td>${esc(x.source_ip)}</td><td>${esc(x.workstation)}</td><td>${esc(x.process_path||x.message)}</td></tr>`).join('')}catch(e){fail(e)}}
async function loadDetections(){try{const d=await get('/api/detections');detrows.innerHTML=d.map(x=>`<tr><td>${esc(x.LastSeen||x.last_seen)}</td><td class="sev-${esc(x.Severity||x.severity)}">${esc(x.Severity||x.severity)}</td><td>${esc(x.RuleName||x.rule_name)}</td><td>${esc(x.Hostname||x.hostname)}<br>${esc(x.UserName||x.user_name)}<br><span class="muted">${esc(x.Workstation||x.workstation||x.SourceIP||x.source_ip)}</span></td><td>${esc(x.Summary||x.summary)}</td><td><select onchange="setStatus(${Number(x.ID||x.id)},this.value)">${['open','investigating','closed','false_positive'].map(s=>`<option ${s==(x.Status||x.status)?'selected':''}>${s}</option>`).join('')}</select></td></tr>`).join('')}catch(e){fail(e)}}
async function setStatus(id,status){try{await get('/api/detections/status',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({id,status})});summary()}catch(e){fail(e)}}
async function loadAgents(){try{const d=await get('/api/agents');agentrows.innerHTML=d.map(x=>`<tr><td>${esc(x.hostname||x.Hostname)}</td><td>${(x.enabled??x.Enabled)?'ja':'nein'}</td><td>${esc(x.last_ip||x.LastIP)}</td><td>${esc(x.first_seen||x.FirstSeen)}</td><td>${esc(x.last_seen||x.LastSeen)}</td></tr>`).join('')}catch(e){fail(e)}}
document.querySelectorAll('.tabs button').forEach(b=>b.onclick=()=>{document.querySelectorAll('.tabs button').forEach(x=>x.classList.remove('active'));b.classList.add('active');document.querySelectorAll('main section').forEach(x=>x.classList.add('hidden'));document.getElementById(b.dataset.tab).classList.remove('hidden');if(b.dataset.tab==='detections')loadDetections();if(b.dataset.tab==='agents')loadAgents()});summary();loadEvents();setInterval(summary,15000);
</script></body></html>