(function(){
var tabs=document.querySelectorAll('#sec-tabs .sub-tab');
var panels=document.querySelectorAll('#ws-security .sub-panel');
function getCSRF(){var m=document.querySelector('meta[name="csrf-token"]');return m?m.content:''}
tabs.forEach(function(t){
t.addEventListener('click',function(){
tabs.forEach(function(b){b.classList.remove('active')});
panels.forEach(function(p){p.classList.remove('active')});
t.classList.add('active');
var p=document.getElementById(t.dataset.panel);
if(p)p.classList.add('active');
loadPanel(t.dataset.panel);
});
});
function secFetch(action,cb){
var fd=new FormData();fd.append('_sec_action',action);fd.append('_csrf',getCSRF());
fetch('',{method:'POST',body:fd,credentials:'same-origin'}).then(function(r){return r.json()}).then(cb).catch(function(e){void 0;});
}
function loadPanel(id){
if(id==='sec-overview')loadOverview();
else if(id==='sec-ghostwall')gwLoadStatus();
else if(id==='sec-honeypots')loadHoneypots();
else if(id==='sec-trust')loadTrustMonitor();
else if(id==='sec-master')loadMasterPanel();
else if(id==='sec-scraping')loadScraping();
else if(id==='sec-integrity')loadIntegrity();
else if(id==='sec-access')loadAccess();
else if(id==='sec-audit')loadAudit();
}
function kpi(color,val,label,sub,icon){return '<div class="cc-kpi '+color+'"><span class="cc-kpi-val">'+val+'</span><span class="cc-kpi-lbl">'+label+'</span><span class="cc-kpi-sub">'+sub+'</span></div>';}
function esc(s){var d=document.createElement('div');d.textContent=s||'';return d.innerHTML;}
function timeAgo(dt){if(!dt)return '';var d=new Date(dt),n=Date.now(),s=Math.floor((n-d.getTime())/1000);if(s<60)return s+'s ago';if(s<3600)return Math.floor(s/60)+'m ago';if(s<86400)return Math.floor(s/3600)+'h ago';return Math.floor(s/86400)+'d ago';}
function loadOverview(){
secFetch('load_overview',function(d){
if(!d.ok)return;
var h='<div class="cc-kpi-ribbon">';
h+=kpi('red',d.secToday,'SECURITY EVENTS','Today','');
h+=kpi('amber',d.wdToday,'WATCHDOG ALERTS','Today','');
h+=kpi('purple',d.ipChanges,'IP CHANGES','Today','');
h+=kpi('cyan',d.hpToday,'HONEYPOT TRIPS',d.hpTotal+' total','');
h+=kpi('blue',d.vsActive,'ACTIVE SESSIONS',d.vsTotal+' total','');
h+=kpi(d.highRisk>0?'red':'green',d.highRisk,'HIGH RISK','score >=70','');
h+='</div>';
if(d.threats&&d.threats.length){
h+='<div class="cc-grid"><div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">Threat Classifications</span></div>';
d.threats.forEach(function(t){h+='<div class="cc-sec-row"><span class="cc-sec-action">'+esc(t.classification)+'</span><span class="cc-sec-time" style="color:#e74c3c;font-weight:900">'+t.cnt+'</span></div>';});
h+='</div>';
}else{
h+='<div class="cc-grid"><div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">Threat Classifications</span></div><div style="color:var(--text-dim);text-align:center;padding:20px;font-size:11px">No threats classified</div></div>';
}
h+='<div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">Recent Security Events</span></div><div style="max-height:300px;overflow-y:auto">';
if(d.events&&d.events.length){
d.events.forEach(function(e){
h+='<div class="cc-sec-row"><div class="cc-sec-dot security"></div><span class="cc-sec-action">'+esc(e.action)+'</span><span style="font-size:9px;color:var(--text-dim);font-family:monospace;margin:0 8px">'+esc(e.ip_address||'')+'</span><span class="cc-sec-time">'+timeAgo(e.created_at)+'</span></div>';
});
}else{h+='<div style="color:var(--text-dim);text-align:center;padding:20px;font-size:11px">No security events</div>';}
h+='</div></div></div>';
document.getElementById('sec-overview').innerHTML=h;
});
}
function loadHoneypots(){
var p=document.getElementById('sec-honeypots');
if(!p)return;
p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Loading honeypot data...</div>';
secFetch('load_honeypots',function(d){
if(!d.ok){p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Failed to load honeypot data.</div>';return;}
var h='<div class="cc-grid">';
h+='<div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">Top Trap Routes</span></div>';
if(d.byRoute&&d.byRoute.length){d.byRoute.forEach(function(r){h+='<div class="cc-sec-row"><span class="cc-sec-action" style="font-family:monospace;font-size:11px">'+esc(r.trap_route)+'</span><span class="cc-sec-time" style="color:#e74c3c;font-weight:900;font-size:14px">'+r.cnt+'</span></div>';});}
else{h+='<div class="mkt-empty">No honeypot hits yet. Deploy trap routes to detect scanners.</div>';}
h+='</div>';
h+='<div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">Top Offending IPs</span></div>';
if(d.byIp&&d.byIp.length){d.byIp.forEach(function(r){h+='<div class="cc-sec-row"><span class="cc-sec-action" style="font-family:monospace">'+esc(r.ip_address)+'</span><span class="cc-sec-time" style="color:#f39c12;font-weight:900">'+r.cnt+' hits</span></div>';});}
else{h+='<div style="color:var(--text-dim);text-align:center;padding:20px;font-size:11px">No IP data</div>';}
h+='</div></div>';
h+='<div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">Recent Hits</span><span class="cc-panel-badge ok">'+(d.hits?d.hits.length:0)+' SHOWN</span></div>';
if(d.hits&&d.hits.length){
h+='<table class="mkt-table"><thead><tr><th>Route</th><th>Method</th><th>IP Hash</th><th>Time</th></tr></thead><tbody>';
d.hits.forEach(function(r){h+='<tr><td style="font-family:monospace;font-size:10px">'+esc(r.trap_route)+'</td><td>'+esc(r.request_method)+'</td><td style="font-family:monospace;font-size:10px;color:var(--text-dim)">'+esc(r.ip_address||'').substring(0,16)+'\u2026</td><td style="font-size:10px">'+timeAgo(r.created_at)+'</td></tr>';});
h+='</tbody></table>';
}else{h+='<div class="mkt-empty">No honeypot hits recorded</div>';}
h+='</div>';
p.innerHTML=h;
});
}
function loadScraping(){
var p=document.getElementById('sec-scraping');
if(!p)return;
p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Loading anti-scraping data...</div>';
secFetch('load_scraping',function(d){
if(!d.ok){p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Failed to load.</div>';return;}
var h='<div class="cc-grid">';
h+='<div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">Detected Scraping Attempts</span></div>';
if(d.scrapers&&d.scrapers.length){
h+='<table class="mkt-table"><thead><tr><th>Action</th><th>IP</th><th>Time</th></tr></thead><tbody>';
d.scrapers.forEach(function(s){h+='<tr><td>'+esc(s.action)+'</td><td style="font-family:monospace;font-size:10px">'+esc(s.ip_address||'')+'</td><td style="font-size:10px">'+timeAgo(s.created_at)+'</td></tr>';});
h+='</tbody></table>';
}else{h+='<div class="mkt-empty">No scraping attempts detected</div>';}
h+='</div>';
h+='<div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">IP Frequency Analysis</span></div>';
if(d.ipLogs&&d.ipLogs.length){
d.ipLogs.forEach(function(ip){h+='<div class="cc-sec-row"><span class="cc-sec-action" style="font-family:monospace;font-size:10px">'+esc(ip.ip_address)+'</span><span class="cc-sec-time" style="font-weight:900">'+ip.cnt+' requests</span></div>';});
}else{h+='<div style="color:var(--text-dim);text-align:center;padding:20px;font-size:11px">No IP logs</div>';}
h+='</div></div>';
p.innerHTML=h;
});
}
function loadIntegrity(){
var p=document.getElementById('sec-integrity');
if(!p)return;
p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Loading code integrity data...</div>';
secFetch('load_integrity',function(d){
if(!d.ok){p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Failed to load.</div>';return;}
var h='<div class="cc-grid">';
h+='<div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">CSP Violations & Manipulation</span></div>';
if(d.cspViolations&&d.cspViolations.length){
h+='<table class="mkt-table"><thead><tr><th>Action</th><th>IP</th><th>Time</th></tr></thead><tbody>';
d.cspViolations.forEach(function(v){h+='<tr><td>'+esc(v.action)+'</td><td style="font-family:monospace;font-size:10px">'+esc(v.ip_address||'')+'</td><td style="font-size:10px">'+timeAgo(v.created_at)+'</td></tr>';});
h+='</tbody></table>';
}else{h+='<div class="mkt-empty">No integrity violations detected</div>';}
h+='</div>';
h+='<div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">Source Code Viewers</span></div>';
if(d.srcViews&&d.srcViews.length){
d.srcViews.forEach(function(s){h+='<div class="cc-sec-row"><span class="cc-sec-action" style="font-family:monospace;font-size:10px">'+esc(s.session_hash).substring(0,16)+'\u2026</span><span style="font-size:10px;color:'+(s.risk_score>=70?'#e74c3c':'var(--text-dim)')+'">Risk: '+s.risk_score+'</span><span class="cc-sec-time">'+s.source_views+' views / '+Math.round(s.source_view_time_ms/1000)+'s</span></div>';});
}else{h+='<div style="color:var(--text-dim);text-align:center;padding:20px;font-size:11px">No source code viewing detected</div>';}
h+='</div></div>';
p.innerHTML=h;
});
}
function loadAccess(){
var p=document.getElementById('sec-access');
if(!p)return;
p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Loading access logs...</div>';
secFetch('load_access',function(d){
if(!d.ok){p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Failed to load.</div>';return;}
var h='<div class="cc-grid">';
h+='<div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">Watchdog Events</span><span class="cc-panel-badge '+(d.watchdog&&d.watchdog.length>0?'warn':'ok')+'">'+(d.watchdog?d.watchdog.length:0)+'</span></div>';
if(d.watchdog&&d.watchdog.length){
h+='<table class="mkt-table"><thead><tr><th>Type</th><th>Source IP</th><th>Severity</th><th>Time</th></tr></thead><tbody>';
d.watchdog.forEach(function(w){var sc=w.severity==='critical'?'color:#e74c3c':w.severity==='warning'?'color:#f39c12':'';h+='<tr><td>'+esc(w.event_type)+'</td><td style="font-family:monospace;font-size:10px">'+esc(w.ip_address||'')+'</td><td style="font-weight:900;'+sc+'">'+esc(w.severity||'info')+'</td><td style="font-size:10px">'+timeAgo(w.created_at)+'</td></tr>';});
h+='</tbody></table>';
}else{h+='<div class="mkt-empty">No watchdog events</div>';}
h+='</div>';
h+='<div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">IP Lockdowns</span></div>';
if(d.lockdowns&&d.lockdowns.length){
d.lockdowns.forEach(function(l){h+='<div class="cc-sec-row"><span class="cc-sec-action" style="font-family:monospace;font-size:10px">'+esc(l.ip_address)+'</span><span style="font-size:10px;color:#e74c3c">'+esc(l.reason)+'</span><span class="cc-sec-time">'+timeAgo(l.locked_at)+'</span></div>';});
}else{h+='<div style="color:var(--text-dim);text-align:center;padding:20px;font-size:11px">No lockdowns</div>';}
h+='</div></div>';
p.innerHTML=h;
});
}
function loadAudit(){
var p=document.getElementById('sec-audit');
if(!p)return;
p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Loading audit log...</div>';
secFetch('load_audit',function(d){
if(!d.ok){p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Failed to load.</div>';return;}
var h='<div class="cc-panel"><div class="cc-panel-hdr"><span class="cc-panel-title">Admin Audit Log</span><span class="cc-panel-badge ok">'+(d.audit?d.audit.length:0)+' ENTRIES</span></div>';
if(d.audit&&d.audit.length){
h+='<table class="mkt-table"><thead><tr><th>Admin</th><th>Action</th><th>Target</th><th>IP</th><th>Time</th></tr></thead><tbody>';
d.audit.forEach(function(a){h+='<tr><td>'+esc(a.admin_id||'system')+'</td><td>'+esc(a.action)+'</td><td>'+esc((a.target_type||'')+' '+(a.target_id||''))+'</td><td style="font-family:monospace;font-size:10px">'+esc(a.ip_address||'')+'</td><td style="font-size:10px">'+timeAgo(a.created_at)+'</td></tr>';});
h+='</tbody></table>';
}else{h+='<div class="mkt-empty">No audit entries</div>';}
h+='</div>';
p.innerHTML=h;
});
}
function loadMasterPanel(){
var p=document.getElementById('sec-master');
if(!p)return;
p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Loading master panel...</div>';
var fd=new FormData();
fd.append('_sec_action','load_master');
fd.append('_csrf',getCSRF());
fetch('',{method:'POST',body:fd,credentials:'same-origin'})
.then(function(r){return r.json()})
.then(function(d){
if(d&&d.html)p.innerHTML=d.html;
else p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Master panel unavailable.</div>';
}).catch(function(e){p.innerHTML='<div style="text-align:center;padding:40px;color:#e74c3c">Error: '+e.message+'</div>';});
}
function loadTrustMonitor(){
var p=document.getElementById('sec-trust');
if(!p)return;
p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">Loading trust data...</div>';
var fd=new FormData();
fd.append('_sec_action','load_trust');
fd.append('_csrf',getCSRF());
fetch('',{method:'POST',body:fd,credentials:'same-origin'})
.then(function(r){return r.json()})
.then(function(d){
if(d&&d.html)p.innerHTML=d.html;
else p.innerHTML='<div style="text-align:center;padding:40px;color:var(--text-dim)">No trust data available.</div>';
}).catch(function(e){p.innerHTML='<div style="text-align:center;padding:40px;color:#e74c3c">Error: '+e.message+'</div>';});
}
loadOverview();
var GW_NC={};
window.gwLoadStatus=function(){
var fd=new FormData();fd.append('_gw_action','load_status');fd.append('_csrf',getCSRF());
fetch('',{method:'POST',body:fd,credentials:'same-origin'}).then(function(r){return r.json()}).then(function(d){
if(!d.ok||!d.nodes)return;
var body=document.getElementById('gw-matrix-body');
if(!body)return;
var hardened=0,pending=0,h='';
Object.keys(d.nodes).forEach(function(k){
var n=d.nodes[k];
if(!n||!n.node_id)return;
var nid=n.node_id;
var checks=0,passed=0;
function chk(val,good){
checks++;
if(val===null||val===undefined)return '<span style="color:var(--text-dim)">\u2014</span>';
var ok=(val===good||val===true||val==='yes'||val==='active'||val==='enforcing');
if(ok)passed++;
return ok?'<span style="color:#2ecc71">\u2713</span>':'<span style="color:#e74c3c">\u2717</span>';
}
var sshOk=chk(n.ssh_password,'no');
var fwOk=chk(n.firewall,'active');
var f2bOk=chk(n.fail2ban,'active');
var wgOk=chk(n.wireguard,'active');
var stOk=chk(n.ssh_root,'no');
var upOk=chk(n.auto_updates,'yes');
var statusLabel='\u2014',statusClass='';
if(!n.last_heartbeat){statusLabel='OFFLINE';statusClass='color:#e74c3c';pending++;}
else{
var ago=(Date.now()-new Date(n.last_heartbeat).getTime())/1000;
if(ago<120){statusLabel='ONLINE';statusClass='color:#2ecc71';if(checks>0&&passed>=checks*0.5)hardened++;else pending++;}
else if(ago<600){statusLabel='STALE';statusClass='color:#f39c12';pending++;}
else{statusLabel='DOWN';statusClass='color:#e74c3c';pending++;}
}
h+='<tr>';
h+='<td style="font-weight:700;font-family:monospace">'+esc(nid)+'</td>';
h+='<td>'+esc(n.node_name||'Node')+'</td>';
h+='<td style="text-align:center">'+sshOk+'</td>';
h+='<td style="text-align:center">'+fwOk+'</td>';
h+='<td style="text-align:center">'+f2bOk+'</td>';
h+='<td style="text-align:center">'+wgOk+'</td>';
h+='<td style="text-align:center">'+stOk+'</td>';
h+='<td style="text-align:center">'+upOk+'</td>';
h+='<td style="font-weight:900;font-size:10px;letter-spacing:0.5px;'+statusClass+'">'+statusLabel+'</td>';
h+='<td><button class="cc-btn sm" data-nc="gw-select" data-node="'+nid+'">CFG</button></td>';
h+='</tr>';
});
body.innerHTML=h;
var hEl=document.getElementById('gw-hardened');if(hEl)hEl.textContent=hardened;
var pEl=document.getElementById('gw-pending');if(pEl)pEl.textContent=pending;
}).catch(function(e){void 0;});
};
window.gwGenerate=function(){
var nid=document.getElementById('gw-gen-node').value;
var port=document.getElementById('gw-gen-port').value||'33022';
var fd=new FormData();
fd.append('_gw_action','generate_config');fd.append('node_id',nid);fd.append('ssh_port',port);fd.append('_csrf',getCSRF());
fetch('',{method:'POST',body:fd,credentials:'same-origin'}).then(function(r){return r.json()}).then(function(d){
if(!d.ok)return;
document.getElementById('gw-config-output').style.display='block';
document.getElementById('gw-cfg-sysctl').textContent=d.config.sysctl||'';
document.getElementById('gw-cfg-ssh').textContent=d.config.ssh||'';
document.getElementById('gw-cfg-fail2ban').textContent=d.config.fail2ban||'';
document.getElementById('gw-cfg-firewall').textContent=d.config.firewall||'';
document.getElementById('gw-cfg-stealth').textContent=d.config.stealth||'';
}).catch(function(e){void 0;});
};
window.gwPush=function(){
var nid=document.getElementById('gw-push-node').value;
var comp=document.getElementById('gw-push-component').value;
if(!confirm('Queue '+comp+' hardening command to node '+nid+'?'))return;
var fd=new FormData();
fd.append('_gw_action','push_harden');fd.append('node_id',nid);fd.append('component',comp);fd.append('_csrf',getCSRF());
fetch('',{method:'POST',body:fd,credentials:'same-origin'}).then(function(r){return r.json()}).then(function(d){
var el=document.getElementById('gw-push-result');
el.style.display='block';
if(d.ok){
el.innerHTML='<div style="padding:10px;border-radius:6px;background:rgba(46,204,113,.08);border:1px solid rgba(46,204,113,.25);color:#2ecc71;font-size:12px">Queued <strong>'+esc(comp)+'</strong> to node <strong>'+esc(nid)+'</strong></div>';
}else{
el.innerHTML='<div style="padding:10px;border-radius:6px;background:rgba(231,76,60,.08);border:1px solid rgba(231,76,60,.25);color:#e74c3c;font-size:12px">'+esc(d.error||'Failed')+'</div>';
}
}).catch(function(e){void 0;});
};
window.gwCopy=function(id){
var el=document.getElementById(id);
if(el){navigator.clipboard.writeText(el.textContent).then(function(){el.style.borderColor='#2ecc71';setTimeout(function(){el.style.borderColor='';},1000);});}
};
window.gwSelectNode=function(nid){
document.getElementById('gw-gen-node').value=nid;
document.getElementById('gw-push-node').value=nid;
document.getElementById('gw-gen-node').scrollIntoView({behavior:'smooth',block:'center'});
};
document.addEventListener('click',function(e){
var el=e.target.closest('[data-nc]');
if(!el)return;
var nc=el.dataset.nc;
if(nc==='gw-select')gwSelectNode(el.dataset.node);
else if(nc==='gw-refresh')gwLoadStatus();
else if(nc==='gw-generate')gwGenerate();
else if(nc==='gw-copy')gwCopy(el.dataset.target);
else if(nc==='gw-push')gwPush();
});
})();
