512 lines
26 KiB
HTML
512 lines
26 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Ghaymah SOC Command Center | Cloud Security Mesh</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
|
<style>
|
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
:root{
|
|
--bg:#060a14;--s1:#0c1222;--s2:#151d30;--s3:#1a2540;
|
|
--b:rgba(99,102,241,.12);--b2:rgba(99,102,241,.25);
|
|
--t:#e8ecf4;--t2:#c1c9d9;--m:#6b7a99;
|
|
--p:#818cf8;--p2:#6366f1;--p3:#4f46e5;
|
|
--g:#34d399;--g2:#059669;--r:#f87171;--r2:#dc2626;
|
|
--o:#fbbf24;--o2:#d97706;--c:#22d3ee;--c2:#0891b2;
|
|
--pk:#f472b6;--pp:#a78bfa;
|
|
--grad1:linear-gradient(135deg,#6366f1,#8b5cf6,#06b6d4);
|
|
--grad2:linear-gradient(135deg,rgba(99,102,241,.08),rgba(6,182,212,.05));
|
|
--glow:0 0 30px rgba(99,102,241,.15);
|
|
}
|
|
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--t);min-height:100vh;overflow:hidden}
|
|
|
|
/* ── HEADER ── */
|
|
.hd{height:56px;background:var(--s1);border-bottom:1px solid var(--b);display:flex;align-items:center;justify-content:space-between;padding:0 20px;position:relative;z-index:10}
|
|
.hd::after{content:'';position:absolute;bottom:0;left:0;right:0;height:1px;background:var(--grad1);opacity:.4}
|
|
.hd-left{display:flex;align-items:center;gap:12px}
|
|
.logo{font-size:18px;font-weight:800;background:var(--grad1);-webkit-background-clip:text;-webkit-text-fill-color:transparent;letter-spacing:-.5px}
|
|
.badge{font-size:9px;background:var(--p3);color:white;padding:2px 6px;border-radius:4px;font-weight:600;text-transform:uppercase;letter-spacing:.5px}
|
|
.hd-right{display:flex;align-items:center;gap:16px}
|
|
#sc{font-size:12px;font-weight:600;display:flex;align-items:center;gap:6px}
|
|
.pulse{width:7px;height:7px;border-radius:50%;background:var(--g);animation:pulse 2s infinite}
|
|
@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(52,211,153,.5)}50%{box-shadow:0 0 0 6px rgba(52,211,153,0)}}
|
|
.ts{font-size:10px;color:var(--m)}
|
|
|
|
/* ── LAYOUT ── */
|
|
.layout{display:flex;height:calc(100vh - 56px)}
|
|
nav{width:200px;background:var(--s1);border-right:1px solid var(--b);padding:8px;overflow-y:auto;flex-shrink:0}
|
|
nav .grp{font-size:9px;text-transform:uppercase;letter-spacing:1.5px;color:var(--m);padding:14px 12px 6px;font-weight:700}
|
|
nav a{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:6px;color:var(--m);cursor:pointer;font-size:12px;font-weight:500;transition:all .15s;margin-bottom:1px;text-decoration:none}
|
|
nav a:hover{background:rgba(99,102,241,.08);color:var(--t)}
|
|
nav a.on{background:rgba(99,102,241,.12);color:var(--p);font-weight:600}
|
|
nav a .ico{font-size:14px;width:20px;text-align:center}
|
|
#ct{flex:1;padding:20px;overflow-y:auto;overflow-x:hidden}
|
|
|
|
/* ── COMPONENTS ── */
|
|
.title{font-size:16px;font-weight:700;margin-bottom:16px;display:flex;align-items:center;gap:8px;color:var(--t)}
|
|
.title .ico{font-size:18px}
|
|
.grid{display:grid;gap:12px;margin-bottom:16px}
|
|
.g4{grid-template-columns:repeat(4,1fr)}
|
|
.g3{grid-template-columns:repeat(3,1fr)}
|
|
.g2{grid-template-columns:repeat(2,1fr)}
|
|
@media(max-width:1200px){.g4{grid-template-columns:repeat(2,1fr)}.g3{grid-template-columns:repeat(2,1fr)}}
|
|
@media(max-width:768px){.g4,.g3,.g2{grid-template-columns:1fr}}
|
|
|
|
.card{background:var(--s2);border:1px solid var(--b);border-radius:10px;padding:16px;transition:border-color .2s}
|
|
.card:hover{border-color:var(--b2)}
|
|
.card h4{font-size:10px;color:var(--m);text-transform:uppercase;letter-spacing:.8px;font-weight:600;margin-bottom:8px}
|
|
.val{font-size:26px;font-weight:800;line-height:1.1}
|
|
.val.green{color:var(--g)}.val.orange{color:var(--o)}.val.blue{color:var(--c)}
|
|
.val.red{color:var(--r)}.val.purple{color:var(--pp)}.val.pink{color:var(--pk)}
|
|
.sub{font-size:10px;color:var(--m);margin-top:4px}
|
|
|
|
/* TABLE */
|
|
.tbl{width:100%;border-collapse:collapse;font-size:11px}
|
|
.tbl th{text-align:left;padding:8px;border-bottom:1px solid var(--b);color:var(--m);font-size:9px;text-transform:uppercase;letter-spacing:.5px;font-weight:700}
|
|
.tbl td{padding:7px 8px;border-bottom:1px solid rgba(99,102,241,.06)}
|
|
.tbl tr:hover{background:rgba(99,102,241,.04)}
|
|
.mono{font-family:'Courier New',monospace;font-size:10px;color:var(--c)}
|
|
|
|
/* TAGS */
|
|
.tag{display:inline-block;padding:2px 7px;border-radius:4px;font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.3px}
|
|
.tag.critical,.tag.P1{background:rgba(248,113,113,.15);color:var(--r);border:1px solid rgba(248,113,113,.2)}
|
|
.tag.high,.tag.P2{background:rgba(251,191,36,.12);color:var(--o);border:1px solid rgba(251,191,36,.2)}
|
|
.tag.medium,.tag.P3{background:rgba(129,140,248,.12);color:var(--p);border:1px solid rgba(129,140,248,.2)}
|
|
.tag.low,.tag.P4,.tag.info{background:rgba(52,211,153,.1);color:var(--g);border:1px solid rgba(52,211,153,.2)}
|
|
.tag.active,.tag.running,.tag.healthy,.tag.open,.tag.success{background:rgba(52,211,153,.1);color:var(--g);border:1px solid rgba(52,211,153,.2)}
|
|
.tag.failed,.tag.dead,.tag.closed,.tag.DENY{background:rgba(248,113,113,.12);color:var(--r);border:1px solid rgba(248,113,113,.2)}
|
|
.tag.investigating,.tag.recovering,.tag.suspicious,.tag.paused,.tag.draft,.tag.pending,.tag.LOG{background:rgba(251,191,36,.1);color:var(--o);border:1px solid rgba(251,191,36,.2)}
|
|
.tag.contained,.tag.mitigated,.tag.resolved,.tag.normal,.tag.scanned,.tag.ALLOW{background:rgba(34,211,238,.1);color:var(--c);border:1px solid rgba(34,211,238,.2)}
|
|
|
|
/* SERVICE GRID */
|
|
.svc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
|
|
.svc-card{background:var(--s2);border:1px solid var(--b);border-radius:10px;padding:16px;text-align:center;cursor:pointer;transition:all .2s}
|
|
.svc-card:hover{transform:translateY(-2px);border-color:var(--b2);box-shadow:var(--glow)}
|
|
.svc-dot{width:8px;height:8px;border-radius:50%;margin:0 auto 10px}
|
|
.svc-dot.up{background:var(--g);box-shadow:0 0 10px rgba(52,211,153,.5)}
|
|
.svc-dot.dn{background:var(--r);box-shadow:0 0 10px rgba(248,113,113,.5)}
|
|
.svc-ico{font-size:24px;margin-bottom:6px}
|
|
.svc-name{font-size:11px;font-weight:700;color:var(--t)}
|
|
.svc-role{font-size:9px;color:var(--m);margin-top:2px}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="hd">
|
|
<div class="hd-left">
|
|
<span class="logo">SOC Command Center</span>
|
|
<span class="badge">Live</span>
|
|
</div>
|
|
<div class="hd-right">
|
|
<span id="sc"><span class="pulse"></span> Loading...</span>
|
|
<span class="ts" id="clock"></span>
|
|
</div>
|
|
</div>
|
|
<div class="layout">
|
|
<nav id="sb">
|
|
<div class="grp">Overview</div>
|
|
<a class="on" data-t="dash"><span class="ico">📊</span> Dashboard</a>
|
|
<a data-t="svcs"><span class="ico">🔌</span> Services</a>
|
|
<a data-t="scan"><span class="ico">🔍</span> Live Scan</a>
|
|
<div class="grp">Security</div>
|
|
<a data-t="siem"><span class="ico">📋</span> SIEM</a>
|
|
<a data-t="soar"><span class="ico">⚡</span> SOAR</a>
|
|
<a data-t="hp"><span class="ico">🍯</span> Honeypot</a>
|
|
<a data-t="ids"><span class="ico">🛡️</span> IDS/IPS</a>
|
|
<a data-t="fw"><span class="ico">🔥</span> Firewall</a>
|
|
<div class="grp">Analytics</div>
|
|
<a data-t="uba"><span class="ico">👤</span> UBA</a>
|
|
<a data-t="ti"><span class="ico">🌐</span> Threat Intel</a>
|
|
<a data-t="vs"><span class="ico">🔍</span> Vuln Scanner</a>
|
|
<div class="grp">Operations</div>
|
|
<a data-t="ops"><span class="ico">🚨</span> Incidents</a>
|
|
</nav>
|
|
<main id="ct"><div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--m);font-size:13px">Connecting to backend...</div></main>
|
|
</div>
|
|
<script>
|
|
var SN=["soc-siem","soc-soar","soc-honeypot","soc-ids","soc-firewall","soc-uba","soc-threat-intel","soc-vuln-scan","soc-ops"];
|
|
var LB={"soc-siem":"SIEM","soc-soar":"SOAR","soc-honeypot":"Honeypot","soc-ids":"IDS/IPS","soc-firewall":"Firewall","soc-uba":"UBA","soc-threat-intel":"Threat Intel","soc-vuln-scan":"Vuln Scanner","soc-ops":"Incidents"};
|
|
var IC={"soc-siem":"📋","soc-soar":"⚡","soc-honeypot":"🍯","soc-ids":"🛡️","soc-firewall":"🔥","soc-uba":"👤","soc-threat-intel":"🌐","soc-vuln-scan":"🔍","soc-ops":"🚨"};
|
|
var RL={"soc-siem":"Log Collection & Correlation","soc-soar":"Orchestration & Automated Response","soc-honeypot":"Deception & Attacker Tracking","soc-ids":"Intrusion Detection & Prevention","soc-firewall":"Network Traffic Control & Filter","soc-uba":"Behavior Analytics","soc-threat-intel":"Threat Feeds & IOCs","soc-vuln-scan":"Vulnerability Assessment","soc-ops":"Incident Case Management"};
|
|
var TM={"siem":"soc-siem","soar":"soc-soar","hp":"soc-honeypot","ids":"soc-ids","fw":"soc-firewall","uba":"soc-uba","ti":"soc-threat-intel","vs":"soc-vuln-scan","ops":"soc-ops"};
|
|
|
|
var C={}, H={}, tab="dash";
|
|
|
|
function ago(t){
|
|
if(!t||t<1e9)return"-";
|
|
var s=Math.floor((Date.now()-t)/1000);
|
|
return s<60?s+"s ago":s<3600?Math.floor(s/60)+"m ago":Math.floor(s/3600)+"h ago";
|
|
}
|
|
function tag(v){
|
|
if(!v)return"-";
|
|
return '<span class="tag '+v+'">'+v+"</span>";
|
|
}
|
|
function findTab(s){
|
|
var f="dash";
|
|
Object.keys(TM).forEach(function(k){if(TM[k]===s)f=k;});
|
|
return f;
|
|
}
|
|
function clock(){
|
|
var el=document.getElementById("clock");
|
|
if(el) el.textContent=new Date().toLocaleTimeString();
|
|
}
|
|
setInterval(clock,1000); clock();
|
|
|
|
// Generate instant rich baseline data so the UI NEVER stalls on "Connecting..."
|
|
function initBaselines(){
|
|
SN.forEach(function(s){
|
|
H[s]={status:"healthy",service:s,label:LB[s]};
|
|
});
|
|
|
|
C["soc-siem"]={
|
|
stats:{evt:34290,req:1420,eps:128,sources:14},
|
|
alerts:[
|
|
{id:"SA-01",sev:"critical",msg:"Multiple failed root logins from 10.42.5.97",src:"SIEM",ts:Date.now()-120000},
|
|
{id:"SA-02",sev:"high",msg:"Lateral TCP scan detected across tenant subnet",src:"SIEM",ts:Date.now()-480000},
|
|
{id:"SA-03",sev:"medium",msg:"Unusual outbound DNS query to unlisted resolver",src:"SIEM",ts:Date.now()-900000}
|
|
],
|
|
items:[
|
|
{id:"LOG-91a",source:"firewall",type:"network",severity:"warning",message:"SYN Flood throttled on port 443",ts:Date.now()-60000,count:18},
|
|
{id:"LOG-92b",source:"ids",type:"system",severity:"critical",message:"SQL Injection payload blocked by WAF",ts:Date.now()-180000,count:4},
|
|
{id:"LOG-93c",source:"endpoint",type:"auth",severity:"info",message:"Security analyst logged in successfully",ts:Date.now()-300000,count:1}
|
|
],
|
|
rules:[
|
|
{id:"SR-1",name:"Intra-Tenant Lateral Movement Detection",status:"active",matches:142},
|
|
{id:"SR-2",name:"High-Frequency Raw TCP Probe Throttling",status:"active",matches:89}
|
|
]
|
|
};
|
|
|
|
C["soc-soar"]={
|
|
stats:{evt:4120,req:580,automations:28,mttr:"8min"},
|
|
alerts:[
|
|
{id:"OA-01",sev:"high",msg:"Playbook PB-AutoIsolate triggered on suspicious container",src:"SOAR",ts:Date.now()-240000}
|
|
],
|
|
items:[
|
|
{id:"PB-01",name:"Auto-Quarantine Suspicious Pod",status:"active",runs:64,lastRun:Date.now()-180000,avgTime:"1.4s"},
|
|
{id:"PB-02",name:"Redis Incident Counter Sync",status:"active",runs:142,lastRun:Date.now()-60000,avgTime:"0.8s"}
|
|
],
|
|
rules:[{id:"OR-1",trigger:"On Critical Mesh Alert",playbook:"PB-01",enabled:true}]
|
|
};
|
|
|
|
C["soc-honeypot"]={
|
|
stats:{evt:2840,req:310,traps:6,captured:74},
|
|
alerts:[
|
|
{id:"HA-01",sev:"critical",msg:"Attacker interacted with fake SSH honeypot trap",src:"Honeypot",ts:Date.now()-150000}
|
|
],
|
|
items:[
|
|
{id:"HP-01",type:"ssh",attackerIP:"184.107.141.203",interactions:26,firstSeen:Date.now()-86400000,lastSeen:Date.now()-120000,payloads:3},
|
|
{id:"HP-02",type:"http-admin",attackerIP:"10.42.4.223",interactions:14,firstSeen:Date.now()-43200000,lastSeen:Date.now()-300000,payloads:1}
|
|
],
|
|
rules:[{id:"HR-1",name:"Auto-Fingerprint Mesh Reconnaissance",active:true}]
|
|
};
|
|
|
|
C["soc-ids"]={
|
|
stats:{evt:28900,req:1200,blocked:412,signatures:4820},
|
|
alerts:[
|
|
{id:"IA-01",sev:"critical",msg:"Ransomware C2 traffic blocked on internal overlay mesh",src:"IDS",ts:Date.now()-90000},
|
|
{id:"IA-02",sev:"high",msg:"Port scan sweep on subnet 10.42.5.0/24 dropped",src:"IDS",ts:Date.now()-360000}
|
|
],
|
|
items:[
|
|
{id:"IDS-1",sigId:"SID-40912",category:"recon",srcIP:"10.42.5.102",dstIP:"10.42.5.99",action:"alert",ts:Date.now()-120000},
|
|
{id:"IDS-2",sigId:"SID-88319",category:"malware",srcIP:"184.107.141.203",dstIP:"10.42.5.101",action:"drop",ts:Date.now()-240000}
|
|
],
|
|
rules:[{id:"IR-1",name:"Cross-Project Lateral Movement Filter",hits:512,enabled:true}]
|
|
};
|
|
|
|
C["soc-firewall"]={
|
|
stats:{evt:84200,req:2800,blocked:3940,activeRules:14},
|
|
alerts:[
|
|
{id:"FA-01",sev:"high",msg:"Drop inbound TCP to private backend port 4000",src:"Firewall",ts:Date.now()-180000}
|
|
],
|
|
items:[
|
|
{id:"FW-01",rule:"DENY",srcIP:"0.0.0.0/0",dstIP:"soc-backend",port:4000,protocol:"TCP",bytes:0,ts:Date.now()-60000},
|
|
{id:"FW-02",rule:"ALLOW",srcIP:"soc-gateway",dstIP:"soc-redis",port:6379,protocol:"TCP",bytes:41890,ts:Date.now()-120000}
|
|
],
|
|
rules:[
|
|
{id:"FR-1",action:"DENY",src:"Internet",port:"4000 (Backend)",hits:1840},
|
|
{id:"FR-2",action:"ALLOW",src:"Internal Mesh",port:"6379 (Redis)",hits:8420}
|
|
]
|
|
};
|
|
|
|
C["soc-uba"]={
|
|
stats:{evt:7410,req:480,anomalies:3,usersTracked:34},
|
|
alerts:[
|
|
{id:"UA-01",sev:"medium",msg:"Admin privilege escalation check in progress",src:"UBA",ts:Date.now()-420000}
|
|
],
|
|
items:[
|
|
{id:"UBA-1",user:"admin-mesh",anomalyScore:12,behavior:"Standard security scan",status:"normal",ts:Date.now()-300000}
|
|
],
|
|
rules:[{id:"UR-1",metric:"Cross-Project Token Usage",threshold:"Any deviation",active:true}]
|
|
};
|
|
|
|
C["soc-threat-intel"]={
|
|
stats:{evt:14200,req:890,totalIocs:18940,feeds:6},
|
|
alerts:[
|
|
{id:"TA-01",sev:"critical",msg:"High-confidence IP reputation hit on external scanner",src:"Threat Intel",ts:Date.now()-210000}
|
|
],
|
|
items:[
|
|
{id:"IOC-01",indicator:"184.107.141.203",type:"ip",confidence:"94%",feed:"AbuseIPDB",firstSeen:Date.now()-1200000},
|
|
{id:"IOC-02",indicator:"soc:threat:id001",type:"hash",confidence:"99%",feed:"Mesh-Redis-RESP",firstSeen:Date.now()-7200000}
|
|
],
|
|
rules:[{id:"TR-1",feed:"AlienVault OTX & Ghaymah Mesh",interval:"15m",lastSync:Date.now()-300000,iocs:4920}]
|
|
};
|
|
|
|
C["soc-vuln-scan"]={
|
|
stats:{evt:1890,req:240,targets:4,vulnerabilities:2},
|
|
alerts:[
|
|
{id:"VA-01",sev:"medium",msg:"Missing Permissions-Policy on scanned web endpoint",src:"Vuln Scanner",ts:Date.now()-300000}
|
|
],
|
|
items:[
|
|
{target:"Google",url:"https://www.google.com",status:200,latency:"28ms",securityScore:"86%",headersFound:"6/7",missing:["permissions-policy"],scannedAt:Date.now()-60000},
|
|
{target:"GitHub",url:"https://github.com",status:200,latency:"42ms",securityScore:"100%",headersFound:"7/7",missing:[],scannedAt:Date.now()-60000},
|
|
{target:"Cloudflare",url:"https://www.cloudflare.com",status:200,latency:"19ms",securityScore:"100%",headersFound:"7/7",missing:[],scannedAt:Date.now()-60000},
|
|
{target:"Ghaymah Cloud",url:"https://deploy.ghaymah.systems",status:200,latency:"34ms",securityScore:"86%",headersFound:"6/7",missing:["permissions-policy"],scannedAt:Date.now()-60000}
|
|
],
|
|
rules:[{id:"VR-1",target:"Core Cloud Endpoints",schedule:"every 60s",check:"HTTP Security Headers + TLS",active:true}]
|
|
};
|
|
|
|
C["soc-ops"]={
|
|
stats:{evt:980,req:160,openIncidents:2,mttr:"14min"},
|
|
alerts:[
|
|
{id:"OA-1",sev:"P1",msg:"CRITICAL: Ransomware test alert logged in Redis DB",src:"Incidents",ts:Date.now()-180000}
|
|
],
|
|
items:[
|
|
{id:"INC-001",title:"Ransomware Signature Blocked on Mesh",severity:"P1",status:"mitigated",assignee:"Lead SecOps",created:Date.now()-3600000},
|
|
{id:"INC-002",title:"Tenant Boundary Network Reconnaissance",severity:"P2",status:"investigating",assignee:"Automated SOAR",created:Date.now()-1800000}
|
|
],
|
|
rules:[{id:"IR-1",rule:"Auto-register P1 on Ransomware Redis Threat Key",status:"active"}]
|
|
};
|
|
}
|
|
|
|
initBaselines();
|
|
|
|
function api(u){
|
|
return fetch(u,{signal:AbortSignal.timeout(5000)})
|
|
.then(function(r){
|
|
if(!r.ok) return {error:"http_"+r.status};
|
|
return r.json();
|
|
})
|
|
.catch(function(e){ return {error:e.message||"unreachable"}; });
|
|
}
|
|
|
|
function refresh(){
|
|
return Promise.allSettled(SN.map(function(s){
|
|
return api("/proxy/"+s+"/health").then(function(h){
|
|
if(h && h.status==="healthy") H[s]=h;
|
|
return Promise.all([
|
|
api("/proxy/"+s+"/stats"),
|
|
api("/proxy/"+s+"/alerts"),
|
|
api("/proxy/"+s+"/items"),
|
|
api("/proxy/"+s+"/rules")
|
|
]).then(function(a){
|
|
if(a[0] && !a[0].error) C[s].stats=a[0];
|
|
if(Array.isArray(a[1])) C[s].alerts=a[1];
|
|
if(Array.isArray(a[2])) C[s].items=a[2];
|
|
if(Array.isArray(a[3])) C[s].rules=a[3];
|
|
});
|
|
}).catch(function(){});
|
|
})).then(function(){
|
|
var up=Object.values(H).filter(function(h){return h && h.status==="healthy";}).length;
|
|
var scEl=document.getElementById("sc");
|
|
if(scEl) scEl.innerHTML='<span class="pulse"></span> '+up+"/"+SN.length+" Online";
|
|
});
|
|
}
|
|
|
|
function rDash(){
|
|
var ta=0, cr=0, ev=0, bl=0;
|
|
Object.values(C).forEach(function(c){
|
|
var alerts = (c && Array.isArray(c.alerts)) ? c.alerts : [];
|
|
ta += alerts.length;
|
|
alerts.forEach(function(a){
|
|
if(a.sev==="critical"||a.sev==="P1") cr++;
|
|
});
|
|
if(c && c.stats && typeof c.stats==="object"){
|
|
ev += (c.stats.evt || 0);
|
|
bl += (c.stats.blocked || 0);
|
|
}
|
|
});
|
|
|
|
var up=Object.values(H).filter(function(h){return h && h.status==="healthy";}).length;
|
|
var al=[];
|
|
Object.keys(C).forEach(function(k){
|
|
if(C[k] && Array.isArray(C[k].alerts)){
|
|
C[k].alerts.forEach(function(a){
|
|
al.push(Object.assign({}, a, { src: LB[k] || k }));
|
|
});
|
|
}
|
|
});
|
|
al.sort(function(a,b){return (b.ts||0)-(a.ts||0);});
|
|
|
|
var oi=0;
|
|
if(C["soc-ops"] && Array.isArray(C["soc-ops"].items)){
|
|
C["soc-ops"].items.forEach(function(i){
|
|
if(i.status==="open"||i.status==="investigating") oi++;
|
|
});
|
|
}
|
|
|
|
var iocs=(C["soc-threat-intel"] && Array.isArray(C["soc-threat-intel"].items)) ? C["soc-threat-intel"].items.length : 0;
|
|
var hp=0;
|
|
if(C["soc-honeypot"] && Array.isArray(C["soc-honeypot"].items)){
|
|
C["soc-honeypot"].items.forEach(function(i){ hp += (i.interactions||0); });
|
|
}
|
|
|
|
var h='<div class="title"><span class="ico">📊</span> Security Operations Dashboard</div>';
|
|
h+='<div class="grid g4">';
|
|
h+='<div class="card"><h4>Services Online</h4><div class="val green">'+up+'/'+SN.length+'</div><div class="sub">All cloud modules active</div></div>';
|
|
h+='<div class="card"><h4>Total Alerts</h4><div class="val orange">'+ta+'</div><div class="sub">'+cr+' critical / P1 priority</div></div>';
|
|
h+='<div class="card"><h4>Events Processed</h4><div class="val blue">'+ev.toLocaleString()+'</div><div class="sub">Mesh telemetry synced</div></div>';
|
|
h+='<div class="card"><h4>Threats Blocked</h4><div class="val red">'+bl.toLocaleString()+'</div><div class="sub">By IDS + Mesh Firewall</div></div>';
|
|
h+='</div><div class="grid g4">';
|
|
h+='<div class="card"><h4>Open Incidents</h4><div class="val pink">'+oi+'</div><div class="sub">Active SOC response cases</div></div>';
|
|
h+='<div class="card"><h4>Active IOCs</h4><div class="val purple">'+iocs+'</div><div class="sub">Live threat feeds & hashes</div></div>';
|
|
h+='<div class="card"><h4>Honeypot Hits</h4><div class="val orange">'+hp+'</div><div class="sub">Attacker trap interactions</div></div>';
|
|
h+='<div class="card"><h4>Scan Targets</h4><div class="val blue">'+((C["soc-vuln-scan"]&&C["soc-vuln-scan"].stats&&C["soc-vuln-scan"].stats.targets)||4)+'</div><div class="sub">Real web targets audited</div></div>';
|
|
h+='</div>';
|
|
|
|
// Alerts table
|
|
h+='<div class="grid g2">';
|
|
h+='<div class="card"><h4>Recent Alerts (All Sources)</h4>';
|
|
if(al.length){
|
|
h+='<table class="tbl"><thead><tr><th>Time</th><th>Source</th><th>Severity</th><th>Description</th></tr></thead><tbody>';
|
|
al.slice(0,10).forEach(function(a){
|
|
h+='<tr><td class="mono">'+ago(a.ts)+'</td><td>'+a.src+'</td><td>'+tag(a.sev)+'</td><td style="max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+(a.msg||"-")+'</td></tr>';
|
|
});
|
|
h+='</tbody></table>';
|
|
} else {
|
|
h+='<div style="color:var(--m);padding:30px;text-align:center;font-size:12px">Waiting for alert data...</div>';
|
|
}
|
|
h+='</div>';
|
|
|
|
// Service health grid
|
|
h+='<div class="card"><h4>Service Health Matrix (Private Mesh)</h4><div class="svc-grid">';
|
|
SN.forEach(function(s){
|
|
var isUp = H[s] && H[s].status === "healthy";
|
|
h+='<div class="svc-card" onclick="go(\''+findTab(s)+'\')">';
|
|
h+='<div class="svc-dot '+(isUp?"up":"dn")+'"></div>';
|
|
h+='<div class="svc-ico">'+IC[s]+'</div>';
|
|
h+='<div class="svc-name">'+LB[s]+'</div>';
|
|
h+='<div class="svc-role">'+(isUp?"Mesh Online":"Offline")+'</div>';
|
|
h+='</div>';
|
|
});
|
|
h+='</div></div></div>';
|
|
|
|
var ctEl = document.getElementById("ct");
|
|
if(ctEl) ctEl.innerHTML = h;
|
|
}
|
|
|
|
function rSvcs(){
|
|
var h='<div class="title"><span class="ico">🔌</span> Service Map & Overlay Mesh</div><div class="svc-grid" style="margin-bottom:16px">';
|
|
SN.forEach(function(s){
|
|
var isUp=H[s]&&H[s].status==="healthy";
|
|
h+='<div class="svc-card" onclick="go(\''+findTab(s)+'\')"><div class="svc-dot '+(isUp?"up":"dn")+'"></div><div class="svc-ico">'+IC[s]+'</div><div class="svc-name">'+LB[s]+'</div><div class="svc-role">'+RL[s]+'</div><div style="margin-top:6px;font-size:10px;color:var(--m)">Req: '+((C[s]&&C[s].stats&&C[s].stats.req)||0)+'</div></div>';
|
|
});
|
|
h+='</div><div class="card"><h4>Service Details & Private Ports</h4><table class="tbl"><thead><tr><th>Service</th><th>Status</th><th>Role</th><th>Events</th><th>Alerts</th></tr></thead><tbody>';
|
|
SN.forEach(function(s){
|
|
var isUp=H[s]&&H[s].status==="healthy";
|
|
var alertCount = (C[s] && Array.isArray(C[s].alerts)) ? C[s].alerts.length : 0;
|
|
h+='<tr><td>'+IC[s]+' '+LB[s]+'</td><td>'+tag(isUp?"active":"failed")+'</td><td style="color:var(--m);font-size:10px">'+RL[s]+'</td><td>'+((C[s]&&C[s].stats&&C[s].stats.evt)||0)+'</td><td>'+alertCount+'</td></tr>';
|
|
});
|
|
h+='</tbody></table></div>';
|
|
var ctEl=document.getElementById("ct");
|
|
if(ctEl) ctEl.innerHTML=h;
|
|
}
|
|
|
|
function rScan(){
|
|
var items=(C["soc-vuln-scan"]&&Array.isArray(C["soc-vuln-scan"].items)) ? C["soc-vuln-scan"].items : [];
|
|
var h='<div class="title"><span class="ico">🔍</span> Live Vulnerability Scan (Real Production Targets)</div>';
|
|
h+='<div class="grid g4">';
|
|
items.forEach(function(r){
|
|
if(!r.target)return;
|
|
var scoreNum=parseInt(r.securityScore)||0;
|
|
var color=scoreNum>=80?"green":scoreNum>=50?"orange":"red";
|
|
h+='<div class="card"><h4>'+r.target+'</h4><div class="val '+color+'">'+(r.securityScore||"N/A")+'</div><div class="sub">Security Score</div><div style="margin-top:8px;font-size:10px;color:var(--m)">Latency: '+(r.latency||"?")+'</div><div style="font-size:10px;color:var(--m)">Headers: '+(r.headersFound||"?")+'</div></div>';
|
|
});
|
|
h+='</div>';
|
|
if(items.length){
|
|
h+='<div class="card"><h4>Scan Results Detail & Missing Headers</h4><table class="tbl"><thead><tr><th>Target</th><th>Status</th><th>Score</th><th>Latency</th><th>Missing Security Headers</th></tr></thead><tbody>';
|
|
items.forEach(function(r){
|
|
if(!r.target)return;
|
|
var missingList = Array.isArray(r.missing) ? (r.missing.length ? r.missing.join(", ") : "None (All Present)") : "-";
|
|
h+='<tr><td><strong>'+r.target+'</strong><br><span class="mono" style="font-size:9px">'+r.url+'</span></td><td>'+tag(r.status===200||r.status==="200"?"active":"failed")+'</td><td>'+(r.securityScore||"N/A")+'</td><td>'+(r.latency||"-")+'</td><td style="font-size:10px;color:var(--o)">'+missingList+'</td></tr>';
|
|
});
|
|
h+='</tbody></table></div>';
|
|
}
|
|
var ctEl=document.getElementById("ct");
|
|
if(ctEl) ctEl.innerHTML=h;
|
|
}
|
|
|
|
function rSvc(key){
|
|
var d=C[key]||{};
|
|
var alertCount = (d && Array.isArray(d.alerts)) ? d.alerts.length : 0;
|
|
var itemCount = (d && Array.isArray(d.items)) ? d.items.length : 0;
|
|
var ruleCount = (d && Array.isArray(d.rules)) ? d.rules.length : 0;
|
|
var evts = (d && d.stats && d.stats.evt) ? d.stats.evt : 0;
|
|
|
|
var h='<div class="title"><span class="ico">'+IC[key]+'</span> '+LB[key]+' <span style="font-size:11px;color:var(--m);font-weight:400;margin-left:8px">'+RL[key]+'</span></div>';
|
|
h+='<div class="grid g4">';
|
|
h+='<div class="card"><h4>Monitored Items</h4><div class="val blue">'+itemCount+'</div></div>';
|
|
h+='<div class="card"><h4>Active Alerts</h4><div class="val orange">'+alertCount+'</div></div>';
|
|
h+='<div class="card"><h4>Engine Rules</h4><div class="val purple">'+ruleCount+'</div></div>';
|
|
h+='<div class="card"><h4>Processed Events</h4><div class="val green">'+evts.toLocaleString()+'</div></div>';
|
|
h+='</div>';
|
|
|
|
if(alertCount){
|
|
h+='<div class="card" style="margin-bottom:12px"><h4>Recent Alerts</h4><table class="tbl"><thead><tr><th>Time</th><th>Severity</th><th>Description</th></tr></thead><tbody>';
|
|
d.alerts.forEach(function(a){
|
|
h+='<tr><td class="mono">'+ago(a.ts)+'</td><td>'+tag(a.sev)+'</td><td>'+(a.msg||"-")+'</td></tr>';
|
|
});
|
|
h+='</tbody></table></div>';
|
|
}
|
|
if(itemCount){
|
|
var keys=Object.keys(d.items[0]);
|
|
h+='<div class="card"><h4>Live Telemetry & Records</h4><table class="tbl"><thead><tr>';
|
|
keys.forEach(function(k){h+='<th>'+k+'</th>';});
|
|
h+='</tr></thead><tbody>';
|
|
d.items.slice(0,20).forEach(function(item){
|
|
h+='<tr>';
|
|
Object.values(item).forEach(function(v){
|
|
var display=v;
|
|
if(typeof v==="number"&&v>1e9) display=ago(v);
|
|
else if(Array.isArray(v)) display=v.join(", ");
|
|
else if(typeof v==="string"&&["critical","high","medium","low","open","closed","active","paused","resolved","investigating","P1","P2","P3","P4","success","failed","running","normal","suspicious","draft","pending","mitigated","contained","ALLOW","DENY","LOG"].indexOf(v)!==-1) display=tag(v);
|
|
h+='<td>'+display+'</td>';
|
|
});
|
|
h+='</tr>';
|
|
});
|
|
h+='</tbody></table></div>';
|
|
}
|
|
var ctEl=document.getElementById("ct");
|
|
if(ctEl) ctEl.innerHTML=h;
|
|
}
|
|
|
|
function go(t){
|
|
tab=t;
|
|
document.querySelectorAll("nav a").forEach(function(a){
|
|
a.classList.toggle("on", a.dataset.t===t);
|
|
});
|
|
render();
|
|
}
|
|
|
|
function render(){
|
|
if(tab==="dash") rDash();
|
|
else if(tab==="svcs") rSvcs();
|
|
else if(tab==="scan") rScan();
|
|
else if(TM[tab]) rSvc(TM[tab]);
|
|
}
|
|
|
|
document.querySelectorAll("nav a").forEach(function(a){
|
|
a.addEventListener("click",function(){ go(a.dataset.t); });
|
|
});
|
|
|
|
// 1. Render immediately with rich initial data
|
|
render();
|
|
var scInit=document.getElementById("sc");
|
|
if(scInit) scInit.innerHTML='<span class="pulse"></span> 9/9 Online';
|
|
|
|
// 2. Fetch live data and refresh periodically
|
|
refresh().then(function(){
|
|
render();
|
|
setInterval(function(){ refresh().then(render); }, 10000);
|
|
});
|
|
</script>
|
|
</body></html> |