215 أسطر
8.8 KiB
HTML
215 أسطر
8.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ar" dir="rtl">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>SIEM Dashboard | لوحة المراقبة الأمنية</title>
|
|
<style>
|
|
:root{
|
|
--bg:#0b0f0d;
|
|
--panel:#101613;
|
|
--line:#1d2622;
|
|
--text:#d7e4dc;
|
|
--dim:#7b9384;
|
|
--amber:#f0b429;
|
|
--green:#3ddc84;
|
|
--red:#ff5c5c;
|
|
--crit:#ff2d55;
|
|
}
|
|
*{box-sizing:border-box;}
|
|
body{
|
|
margin:0; background:var(--bg); color:var(--text);
|
|
font-family:'IBM Plex Mono','Courier New',monospace;
|
|
font-size:14px;
|
|
}
|
|
header{
|
|
display:flex; justify-content:space-between; align-items:center;
|
|
padding:18px 28px; border-bottom:1px solid var(--line);
|
|
background:linear-gradient(180deg,#0d1310,#0b0f0d);
|
|
}
|
|
header h1{
|
|
margin:0; font-size:18px; letter-spacing:2px; font-weight:600;
|
|
color:var(--amber); text-transform:uppercase;
|
|
}
|
|
header h1 span{color:var(--dim); font-weight:400; font-size:12px; letter-spacing:1px;}
|
|
.status{display:flex; align-items:center; gap:8px; font-size:12px; color:var(--dim);}
|
|
.dot{width:8px; height:8px; border-radius:50%; background:var(--green); animation:pulse 1.6s infinite;}
|
|
@keyframes pulse{0%,100%{opacity:1;} 50%{opacity:.25;}}
|
|
|
|
.toolbar{
|
|
display:flex; gap:12px; align-items:center; padding:12px 28px;
|
|
border-bottom:1px solid var(--line); flex-wrap:wrap;
|
|
}
|
|
button, label.filebtn{
|
|
background:var(--panel); color:var(--text); border:1px solid var(--line);
|
|
padding:7px 14px; border-radius:3px; cursor:pointer; font-family:inherit; font-size:12px;
|
|
}
|
|
button:hover, label.filebtn:hover{border-color:var(--amber); color:var(--amber);}
|
|
#source-note{color:var(--dim); font-size:12px;}
|
|
|
|
main{padding:20px 28px;}
|
|
.cards{display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:14px; margin-bottom:22px;}
|
|
.card{
|
|
background:var(--panel); border:1px solid var(--line); border-radius:4px;
|
|
padding:14px 16px;
|
|
}
|
|
.card .label{color:var(--dim); font-size:11px; text-transform:uppercase; letter-spacing:1px;}
|
|
.card .value{font-size:28px; font-weight:700; margin-top:6px;}
|
|
.card.high .value{color:var(--red);}
|
|
.card.crit .value{color:var(--crit);}
|
|
.card.ips .value{color:var(--amber);}
|
|
.card.ok .value{color:var(--green);}
|
|
|
|
.grid2{display:grid; grid-template-columns:1.4fr 1fr; gap:18px;}
|
|
@media (max-width:900px){.grid2{grid-template-columns:1fr;}}
|
|
|
|
.panel{background:var(--panel); border:1px solid var(--line); border-radius:4px; overflow:hidden;}
|
|
.panel h2{
|
|
margin:0; padding:12px 16px; font-size:12px; letter-spacing:1px;
|
|
text-transform:uppercase; color:var(--dim); border-bottom:1px solid var(--line);
|
|
}
|
|
table{width:100%; border-collapse:collapse; font-size:12.5px;}
|
|
th,td{padding:9px 14px; text-align:right; border-bottom:1px solid var(--line);}
|
|
th{color:var(--dim); font-weight:500; font-size:11px; text-transform:uppercase;}
|
|
tr:hover td{background:#131a16;}
|
|
.sev{padding:2px 8px; border-radius:10px; font-size:10.5px; font-weight:700; letter-spacing:.5px;}
|
|
.sev.critical{background:rgba(255,45,85,.15); color:var(--crit);}
|
|
.sev.high{background:rgba(255,92,92,.15); color:var(--red);}
|
|
.sev.medium{background:rgba(240,180,41,.15); color:var(--amber);}
|
|
.sev.low{background:rgba(61,220,132,.15); color:var(--green);}
|
|
.src-tag{color:var(--dim); font-size:11px;}
|
|
.ipcell{font-weight:700; color:var(--text);}
|
|
.bar-wrap{padding:14px 16px;}
|
|
.bar-row{display:flex; align-items:center; gap:10px; margin-bottom:9px; font-size:12px;}
|
|
.bar-row .lbl{width:90px; color:var(--dim); flex-shrink:0;}
|
|
.bar-track{flex:1; background:#1a221d; border-radius:3px; height:10px; overflow:hidden;}
|
|
.bar-fill{height:100%; background:var(--amber); border-radius:3px;}
|
|
.empty{padding:30px; text-align:center; color:var(--dim);}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<header>
|
|
<h1>SIEM <span>/ نظام مراقبة الأحداث الأمنية</span></h1>
|
|
<div class="status"><span class="dot" id="live-dot"></span><span id="last-update">لم يتم التحميل بعد</span></div>
|
|
</header>
|
|
|
|
<div class="toolbar">
|
|
<button id="refresh-btn">تحديث الآن ⟳</button>
|
|
<label class="filebtn">
|
|
تحميل alerts.json يدويًا
|
|
<input type="file" id="file-input" accept=".json" style="display:none;">
|
|
</label>
|
|
<span id="source-note">يحاول تحميل alerts.json تلقائيًا كل 10 ثوانٍ (يتطلب تشغيل عبر خادم محلي)</span>
|
|
</div>
|
|
|
|
<main>
|
|
<div class="cards" id="cards"></div>
|
|
<div class="grid2">
|
|
<div class="panel">
|
|
<h2>التنبيهات (Alerts)</h2>
|
|
<div id="alerts-table"></div>
|
|
</div>
|
|
<div class="panel">
|
|
<h2>عناوين IP المشبوهة</h2>
|
|
<div id="ips-table"></div>
|
|
<div class="panel" style="border:none; margin-top:4px;">
|
|
<h2>التنبيهات حسب المصدر</h2>
|
|
<div class="bar-wrap" id="source-bars"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<script>
|
|
const SEV_LABEL = {critical:"حرج", high:"عالي", medium:"متوسط", low:"منخفض"};
|
|
const SRC_LABEL = {web:"Web Server", auth:"Auth / OS", firewall:"Firewall"};
|
|
let autoTimer = null;
|
|
|
|
function render(data){
|
|
document.getElementById("last-update").textContent =
|
|
"آخر تحديث: " + new Date(data.generated_at).toLocaleString("ar-EG");
|
|
|
|
const s = data.stats || {};
|
|
const bySeverity = s.by_severity || {};
|
|
document.getElementById("cards").innerHTML = `
|
|
<div class="card"><div class="label">إجمالي التنبيهات</div><div class="value">${s.total_alerts ?? 0}</div></div>
|
|
<div class="card crit"><div class="label">حرجة</div><div class="value">${bySeverity.critical ?? 0}</div></div>
|
|
<div class="card high"><div class="label">عالية الخطورة</div><div class="value">${bySeverity.high ?? 0}</div></div>
|
|
<div class="card ok"><div class="label">متوسطة/منخفضة</div><div class="value">${(bySeverity.medium ?? 0) + (bySeverity.low ?? 0)}</div></div>
|
|
<div class="card ips"><div class="label">IP مشبوهة</div><div class="value">${s.unique_suspicious_ips ?? 0}</div></div>
|
|
`;
|
|
|
|
const alerts = data.alerts || [];
|
|
document.getElementById("alerts-table").innerHTML = alerts.length ? `
|
|
<table>
|
|
<thead><tr><th>الخطورة</th><th>النوع</th><th>IP</th><th>المصدر</th><th>التفاصيل</th></tr></thead>
|
|
<tbody>
|
|
${alerts.map(a => `
|
|
<tr>
|
|
<td><span class="sev ${a.severity}">${SEV_LABEL[a.severity] || a.severity}</span></td>
|
|
<td>${a.type}</td>
|
|
<td class="ipcell">${a.ip}</td>
|
|
<td class="src-tag">${SRC_LABEL[a.source] || a.source}</td>
|
|
<td>${a.detail}</td>
|
|
</tr>`).join("")}
|
|
</tbody>
|
|
</table>` : `<div class="empty">لا توجد تنبيهات حاليًا</div>`;
|
|
|
|
const ips = data.suspicious_ips || [];
|
|
document.getElementById("ips-table").innerHTML = ips.length ? `
|
|
<table>
|
|
<thead><tr><th>IP</th><th>عدد التنبيهات</th><th>درجة الخطورة</th></tr></thead>
|
|
<tbody>
|
|
${ips.map(i => `<tr><td class="ipcell">${i.ip}</td><td>${i.alert_count}</td><td>${i.score}</td></tr>`).join("")}
|
|
</tbody>
|
|
</table>` : `<div class="empty">لا توجد عناوين مشبوهة</div>`;
|
|
|
|
const bySource = s.by_source || {};
|
|
const maxVal = Math.max(1, ...Object.values(bySource));
|
|
document.getElementById("source-bars").innerHTML = Object.keys(SRC_LABEL).map(key => {
|
|
const val = bySource[key] || 0;
|
|
const pct = Math.round((val / maxVal) * 100);
|
|
return `<div class="bar-row">
|
|
<div class="lbl">${SRC_LABEL[key]}</div>
|
|
<div class="bar-track"><div class="bar-fill" style="width:${pct}%"></div></div>
|
|
<div>${val}</div>
|
|
</div>`;
|
|
}).join("");
|
|
}
|
|
|
|
async function fetchData(){
|
|
try{
|
|
const res = await fetch("output/alerts.json", {cache:"no-store"});
|
|
if(!res.ok) throw new Error("HTTP " + res.status);
|
|
const data = await res.json();
|
|
render(data);
|
|
document.getElementById("live-dot").style.background = "var(--green)";
|
|
}catch(e){
|
|
document.getElementById("source-note").textContent =
|
|
"تعذّر تحميل alerts.json تلقائيًا (" + e.message + "). استخدم زر التحميل اليدوي، أو شغّل خادم محلي: python3 -m http.server";
|
|
document.getElementById("live-dot").style.background = "var(--red)";
|
|
}
|
|
}
|
|
|
|
document.getElementById("refresh-btn").addEventListener("click", fetchData);
|
|
document.getElementById("file-input").addEventListener("change", (ev) => {
|
|
const file = ev.target.files[0];
|
|
if(!file) return;
|
|
const reader = new FileReader();
|
|
reader.onload = () => {
|
|
try{
|
|
render(JSON.parse(reader.result));
|
|
document.getElementById("source-note").textContent = "تم التحميل يدويًا من ملف: " + file.name;
|
|
}catch(e){
|
|
alert("ملف JSON غير صالح: " + e.message);
|
|
}
|
|
};
|
|
reader.readAsText(file);
|
|
});
|
|
|
|
fetchData();
|
|
autoTimer = setInterval(fetchData, 10000);
|
|
</script>
|
|
</body>
|
|
</html>
|