Expand rootkit monitoring and Claude docs
This commit is contained in:
parent
a7129e5666
commit
3e5f8fc3f7
16 changed files with 524 additions and 32 deletions
|
|
@ -40,11 +40,12 @@
|
|||
.section-title{display:flex;align-items:center;justify-content:space-between;
|
||||
padding:13px 14px 8px;color:var(--muted);font-size:11px;letter-spacing:.12em;
|
||||
text-transform:uppercase}
|
||||
.metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:16px}
|
||||
.metrics{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-bottom:16px}
|
||||
.metric{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:12px}
|
||||
.metric b{display:block;font-size:24px;line-height:1;color:#fff}
|
||||
.metric span{display:block;color:var(--muted);font-size:11px;margin-top:8px;text-transform:uppercase}
|
||||
.metric.crit b{color:var(--red)} .metric.high b{color:var(--amber)} .metric.med b{color:var(--blue)}
|
||||
.metric.posture b{color:var(--accent)}
|
||||
.list{padding:0 8px 14px}
|
||||
.item{border:1px solid transparent;border-bottom-color:var(--line);padding:10px;
|
||||
cursor:pointer;border-radius:7px;margin-bottom:4px}
|
||||
|
|
@ -118,10 +119,12 @@
|
|||
<div class="tabs">
|
||||
<button class="active" data-tab="incident">Incident</button>
|
||||
<button data-tab="alerts">Alerts</button>
|
||||
<button data-tab="posture">Posture</button>
|
||||
<button data-tab="events">Events</button>
|
||||
</div>
|
||||
<section id="incidentTab"></section>
|
||||
<section id="alertsTab" hidden></section>
|
||||
<section id="postureTab" hidden></section>
|
||||
<section id="eventsTab" hidden></section>
|
||||
</main>
|
||||
|
||||
|
|
@ -134,7 +137,7 @@
|
|||
<script>
|
||||
const TOKEN = new URLSearchParams(location.search).get("token") || "";
|
||||
const HDR = TOKEN ? {Authorization:"Bearer "+TOKEN} : {};
|
||||
const state = {incidents:[], alerts:[], selected:null, incident:null, plan:null, tab:"incident"};
|
||||
const state = {incidents:[], alerts:[], posture:null, selected:null, incident:null, plan:null, tab:"incident"};
|
||||
|
||||
async function api(path){
|
||||
const r = await fetch(path,{headers:HDR});
|
||||
|
|
@ -150,10 +153,10 @@ function fmt(t){return (t||"?").replace("T"," ").slice(0,19)}
|
|||
|
||||
async function refresh(){
|
||||
try{
|
||||
const [status, incidents, alerts, events] = await Promise.all([
|
||||
api("/api/status"), api("/api/incidents"), api("/api/alerts"), api("/api/events")
|
||||
const [status, incidents, alerts, posture, events] = await Promise.all([
|
||||
api("/api/status"), api("/api/incidents"), api("/api/alerts"), api("/api/posture"), api("/api/events")
|
||||
]);
|
||||
state.incidents = incidents; state.alerts = alerts; state.events = events.events || [];
|
||||
state.incidents = incidents; state.alerts = alerts; state.posture = posture; state.events = events.events || [];
|
||||
renderStatus(status); renderLists();
|
||||
if(!state.selected && incidents.length) await openIncident(incidents[0].id);
|
||||
else if(state.selected) await openIncident(state.selected, false);
|
||||
|
|
@ -169,9 +172,11 @@ function renderStatus(st){
|
|||
document.getElementById("ebpf").textContent = "eBPF "+(st.ebpf || "unknown");
|
||||
const counts = st.counts || {};
|
||||
const metrics = document.getElementById("metrics"); metrics.innerHTML = "";
|
||||
[["CRITICAL","crit"],["HIGH","high"],["MEDIUM","med"],["TOTAL",""]].forEach(([k,c])=>{
|
||||
const m=el("div","metric "+c); m.append(el("b",null,String(k==="TOTAL" ? st.total_alerts||0 : counts[k]||0)));
|
||||
m.append(el("span",null,k==="TOTAL" ? "alerts" : k)); metrics.append(m);
|
||||
[["CRITICAL","crit"],["HIGH","high"],["MEDIUM","med"],["TOTAL",""],["POSTURE","posture"]].forEach(([k,c])=>{
|
||||
const value = k==="TOTAL" ? st.total_alerts||0 : k==="POSTURE" ? state.posture?.count||0 : counts[k]||0;
|
||||
const label = k==="TOTAL" ? "alerts" : k==="POSTURE" ? "findings" : k;
|
||||
const m=el("div","metric "+c); m.append(el("b",null,String(value)));
|
||||
m.append(el("span",null,label)); metrics.append(m);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -212,7 +217,8 @@ async function openIncident(id, mark=true){
|
|||
async function openAlert(name){
|
||||
state.tab = "alerts"; setTabButtons();
|
||||
const box = document.getElementById("alertsTab");
|
||||
box.hidden = false; document.getElementById("incidentTab").hidden = true; document.getElementById("eventsTab").hidden = true;
|
||||
box.hidden = false; document.getElementById("incidentTab").hidden = true;
|
||||
document.getElementById("postureTab").hidden = true; document.getElementById("eventsTab").hidden = true;
|
||||
box.innerHTML='<div class="empty">loading snapshot</div>';
|
||||
try{
|
||||
const a = await api("/api/alerts/"+encodeURIComponent(name));
|
||||
|
|
@ -261,8 +267,10 @@ function renderTab(){
|
|||
setTabButtons();
|
||||
document.getElementById("incidentTab").hidden = state.tab !== "incident";
|
||||
document.getElementById("alertsTab").hidden = state.tab !== "alerts";
|
||||
document.getElementById("postureTab").hidden = state.tab !== "posture";
|
||||
document.getElementById("eventsTab").hidden = state.tab !== "events";
|
||||
if(state.tab === "incident") renderIncident();
|
||||
if(state.tab === "posture") renderPosture();
|
||||
if(state.tab === "events") renderEvents();
|
||||
}
|
||||
function setTabButtons(){
|
||||
|
|
@ -273,8 +281,37 @@ function renderEvents(){
|
|||
box.innerHTML = '<div class="card"><h3>Event tail</h3><pre></pre></div>';
|
||||
box.querySelector("pre").textContent = (state.events||[]).join("\n") || "No events.";
|
||||
}
|
||||
function renderPosture(){
|
||||
const box = document.getElementById("postureTab");
|
||||
const report = state.posture || {findings:[], counts:{}};
|
||||
box.innerHTML = "";
|
||||
const summary = el("div","card");
|
||||
summary.append(el("h2",null,"Host posture"));
|
||||
const counts = report.counts || {};
|
||||
summary.append(tags([
|
||||
"critical "+(counts.CRITICAL||0),
|
||||
"high "+(counts.HIGH||0),
|
||||
"medium "+(counts.MEDIUM||0)
|
||||
]));
|
||||
if(!report.findings || !report.findings.length){
|
||||
summary.append(el("div","fine","No SSH, sudo, PATH, sensitive-file, or package-signature posture findings."));
|
||||
box.append(summary);
|
||||
return;
|
||||
}
|
||||
summary.append(el("div","fine",String(report.count)+" advisory finding(s). These are read-only hygiene checks, not live containment actions."));
|
||||
box.append(summary);
|
||||
report.findings.forEach(f=>{
|
||||
const row = el("div","card");
|
||||
const top = el("div","top"); top.append(sev(f.severity)); top.append(el("span","id",f.signature));
|
||||
row.append(top);
|
||||
row.append(tags(["sid "+(f.sid||0), f.classtype||"host-posture"]));
|
||||
row.append(el("code","cmd",f.detail || ""));
|
||||
box.append(row);
|
||||
});
|
||||
}
|
||||
function showError(msg){
|
||||
document.getElementById("incidentTab").innerHTML = '<div class="err">'+msg+'</div>';
|
||||
document.getElementById("postureTab").innerHTML = '<div class="err">'+msg+'</div>';
|
||||
document.getElementById("plan").innerHTML = '<div class="err">'+msg+'</div>';
|
||||
}
|
||||
document.querySelectorAll(".tabs button").forEach(b=>b.onclick=()=>{state.tab=b.dataset.tab; renderTab();});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue