Release / Plan (reuse the published image?) (push) Successful in 4s
CI / Docker image (build and smoke test) (push) Skipped
CI / Format and clippy (push) Successful in 16s
CI / Dependency audit (push) Successful in 17s
CI / Tests (Linux) (push) Successful in 34s
Release / ci (push) Successful in 35s
Release / Build image (arm64) (push) Successful in 4m55s
Release / Build image (amd64) (push) Failing after 11m10s
Release / Publish tags (push) Skipped
Release / Gitea release (push) Skipped
The sidebar nav (Overview/Alerts/Analysis) only did scrollIntoView on a
single stacked page, so clicking it produced no visible change. The
Alerts/Analysis panels were also placeholder shells with no real data
exploration despite the API already exposing rich per-pillar evidence.
- app.js/app.css/index.html: real panel switching via showView()/currentView,
toggling an .active class with `.view:not(.active) { display: none }`;
settings keeps opening its own dialog without disturbing the active view.
- New Hosts panel: table of observed hosts ranked by fusion score, with
per-pillar (P1/P2/P3) columns, search, and drill-down into each pillar's
evidence. Documents the known limitation that /api/alerts only lists hosts
with at least one alert (no /api/hosts endpoint exists yet), so a benign
host with no alert won't appear.
- Alerts panel: filterable by pillar and minimum score, sortable, with
full evidence rendered as a formatted key/value table instead of raw JSON.
- Overview panel: KPI tiles (alert count, hosts with alerts, max score,
dataset records) plus two interactive SVG charts (top hosts by fusion
score, alert distribution by pillar) with hover tooltips.
- i18n-en.js: English strings for all new UI text.
No Rust files changed; this is purely apps/gui/ui/ consuming the existing
/api/alerts, /api/analyze and /api/status endpoints.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
335 lines
13 KiB
HTML
335 lines
13 KiB
HTML
<!doctype html>
|
||
<html lang="pt-BR">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Acauã</title>
|
||
<link rel="icon" href="/logo.png">
|
||
<link rel="stylesheet" href="/app.css">
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
<aside class="sidebar">
|
||
<div class="brand">
|
||
<img class="logo" src="/logo.png" alt="">
|
||
<div>
|
||
<strong>Acauã</strong>
|
||
<small>caça a ameaças em rede</small>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="side-title">Navegação</div>
|
||
<nav class="nav" aria-label="Navegação principal">
|
||
<button type="button" class="nav-item active" data-target="overview" aria-current="page">
|
||
<span class="nav-icon">◈</span>
|
||
<span><span class="nav-label">Visão geral</span><span class="nav-sub">estado e prioridades</span></span>
|
||
</button>
|
||
<button type="button" class="nav-item" data-target="alerts">
|
||
<span class="nav-icon">◎</span>
|
||
<span><span class="nav-label">Alertas</span><span class="nav-sub">filtros e evidências</span></span>
|
||
</button>
|
||
<button type="button" class="nav-item" data-target="hosts">
|
||
<span class="nav-icon">▤</span>
|
||
<span><span class="nav-label">Hosts</span><span class="nav-sub">fusão por evidência</span></span>
|
||
</button>
|
||
<button type="button" class="nav-item" data-target="analysis">
|
||
<span class="nav-icon">⌁</span>
|
||
<span><span class="nav-label">Análise</span><span class="nav-sub">dataset Zeek</span></span>
|
||
</button>
|
||
<button type="button" class="nav-item" data-target="settings">
|
||
<span class="nav-icon">⚙</span>
|
||
<span><span class="nav-label">Configurações</span><span class="nav-sub">idioma e tema</span></span>
|
||
</button>
|
||
</nav>
|
||
|
||
<div class="side-foot">
|
||
<div class="status-row">
|
||
<span id="connection" class="pill"><span class="dot"></span><span id="connectionText">Conectando…</span></span>
|
||
<span class="pill muted">pré-lançamento</span>
|
||
</div>
|
||
<div class="status-row">
|
||
<span id="version" class="mono">—</span>
|
||
<button id="updateBtn" type="button" class="link-btn">checar atualização</button>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<main class="main">
|
||
<header class="topbar">
|
||
<div>
|
||
<p class="eyebrow">Acauã</p>
|
||
<h1 id="viewTitle">Visão geral</h1>
|
||
<p id="viewSub" class="sub">Estado, prioridades e distribuição dos alertas</p>
|
||
</div>
|
||
<div class="top-actions">
|
||
<span id="lastUpdated" class="mono last-updated">—</span>
|
||
<button id="refreshBtn" type="button" class="btn">Atualizar status</button>
|
||
<button id="settingsBtn" type="button" class="icon-btn" aria-label="Configurações" title="Configurações">⚙</button>
|
||
</div>
|
||
</header>
|
||
|
||
<section class="view active" id="overview">
|
||
<div class="kpi-grid">
|
||
<article class="kpi">
|
||
<span class="kpi-label">Alertas</span>
|
||
<strong id="kpiAlerts" class="kpi-value">—</strong>
|
||
<span id="kpiAlertsSub" class="kpi-sub">carregando…</span>
|
||
</article>
|
||
<article class="kpi">
|
||
<span class="kpi-label">Hosts com alerta</span>
|
||
<strong id="kpiHosts" class="kpi-value">—</strong>
|
||
<span id="kpiHostsSub" class="kpi-sub">ranqueados por fusão</span>
|
||
</article>
|
||
<article class="kpi">
|
||
<span class="kpi-label">Maior score</span>
|
||
<strong id="kpiScore" class="kpi-value">—</strong>
|
||
<span id="kpiScoreSub" class="kpi-sub">melhor host observado</span>
|
||
</article>
|
||
<article class="kpi">
|
||
<span class="kpi-label">Registros no dataset</span>
|
||
<strong id="kpiRecords" class="kpi-value">—</strong>
|
||
<span id="kpiRecordsSub" class="kpi-sub">armazenamento local</span>
|
||
</article>
|
||
</div>
|
||
|
||
<div class="charts-grid">
|
||
<section class="panel chart-panel">
|
||
<div class="panel-head">
|
||
<div>
|
||
<h2>Top hosts por fusão</h2>
|
||
<p class="hint">Score ponderado de evidências (0 a 1). Passe o mouse para ver os pilares.</p>
|
||
</div>
|
||
<span class="panel-tag">m1 · m2 · m3</span>
|
||
</div>
|
||
<div class="chart-wrap">
|
||
<svg id="hostChart" class="chart" role="img" aria-label="Hosts por score de fusão"></svg>
|
||
<div id="hostTip" class="chart-tip" hidden></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel chart-panel">
|
||
<div class="panel-head">
|
||
<div>
|
||
<h2>Alertas por pilar</h2>
|
||
<p class="hint">Distribuição observada. Clique em uma barra para filtrar os alertas.</p>
|
||
</div>
|
||
<span class="panel-tag">P1 · P2 · P3</span>
|
||
</div>
|
||
<div class="chart-wrap">
|
||
<svg id="pillarChart" class="chart" role="img" aria-label="Distribuição de alertas por pilar"></svg>
|
||
<div id="pillarTip" class="chart-tip" hidden></div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<section class="panel">
|
||
<div class="panel-head">
|
||
<div>
|
||
<h2>Alertas prioritários</h2>
|
||
<p class="hint">Os alertas com maior score individual. Abra a lista completa para filtrar.</p>
|
||
</div>
|
||
<button id="seeAllAlerts" type="button" class="btn">Ver todos os alertas</button>
|
||
</div>
|
||
<div id="overviewAlerts" class="overview-alerts"></div>
|
||
</section>
|
||
|
||
<section class="pillars" aria-label="Pilares de detecção">
|
||
<article class="card" data-pillar-card="1" tabindex="0" role="button">
|
||
<span class="card-kicker">Pilar 1</span>
|
||
<h2>Beaconing <span class="card-count" data-pillar-count="1">0</span></h2>
|
||
<p>Periodograma de Rayleigh/Schuster com grade limitada ao intervalo observado.</p>
|
||
<span class="tag">versão inicial</span>
|
||
</article>
|
||
<article class="card" data-pillar-card="2" tabindex="0" role="button">
|
||
<span class="card-kicker">Pilar 2</span>
|
||
<h2>Sessões persistentes <span class="card-count" data-pillar-count="2">0</span></h2>
|
||
<p>Sessões TCP longas, inclusive as que o Zeek divide por inatividade.</p>
|
||
<span class="tag">versão inicial</span>
|
||
</article>
|
||
<article class="card" data-pillar-card="3" tabindex="0" role="button">
|
||
<span class="card-kicker">Pilar 3</span>
|
||
<h2>Anomalias de protocolo <span class="card-count" data-pillar-count="3">0</span></h2>
|
||
<p>Volume de informação (entropia de Shannon) em subdomínios DNS.</p>
|
||
<span class="tag">versão inicial</span>
|
||
</article>
|
||
<article class="card" data-pillar-card="4" tabindex="0" role="button">
|
||
<span class="card-kicker">Pilar 4</span>
|
||
<h2>Fusão de evidências</h2>
|
||
<p>Prioridade por host com evidência por alerta.</p>
|
||
<span class="tag" id="pillarFusionTag">sem hosts</span>
|
||
</article>
|
||
</section>
|
||
|
||
<section class="notice">
|
||
<strong>Uso autorizado.</strong>
|
||
<span>Use somente em redes próprias ou com autorização explícita. Nenhum pacote é enviado para fora.</span>
|
||
</section>
|
||
</section>
|
||
|
||
<section class="view" id="alerts" hidden>
|
||
<section class="panel">
|
||
<div class="panel-head">
|
||
<div>
|
||
<h2>Alertas</h2>
|
||
<p class="hint">Filtre, ordene e expanda cada alerta para ver a evidência completa como tabela.</p>
|
||
</div>
|
||
<span id="alertCount" class="panel-tag">—</span>
|
||
</div>
|
||
|
||
<div class="toolbar">
|
||
<div class="chips" id="pillarFilters" role="group" aria-label="Filtrar por pilar">
|
||
<button type="button" class="chip active" data-pillar="all">Todos</button>
|
||
<button type="button" class="chip" data-pillar="1">Pilar 1</button>
|
||
<button type="button" class="chip" data-pillar="2">Pilar 2</button>
|
||
<button type="button" class="chip" data-pillar="3">Pilar 3</button>
|
||
</div>
|
||
<label class="field compact">
|
||
<span>Ordenar</span>
|
||
<select id="alertSort">
|
||
<option value="score-desc">Maior score</option>
|
||
<option value="score-asc">Menor score</option>
|
||
<option value="pillar">Pilar</option>
|
||
<option value="newest">Mais recentes</option>
|
||
</select>
|
||
</label>
|
||
<label class="field compact range-field">
|
||
<span>Score mínimo <output id="minScoreOut">0,00</output></span>
|
||
<input id="alertMin" type="range" min="0" max="1" step="0.01" value="0">
|
||
</label>
|
||
<label class="field compact search-field">
|
||
<span>Buscar</span>
|
||
<input id="alertSearch" type="search" placeholder="IP, host, domínio ou ID" autocomplete="off">
|
||
</label>
|
||
</div>
|
||
|
||
<div id="alertList" class="alert-cards">
|
||
<p class="empty">Nenhum alerta ainda. Analise um diretório de logs do Zeek.</p>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
|
||
<section class="view" id="hosts" hidden>
|
||
<section class="panel">
|
||
<div class="panel-head">
|
||
<div>
|
||
<h2>Hosts observados</h2>
|
||
<p class="hint">Ranking por score de fusão (desc). Clique em uma linha para ver a evidência de cada pilar.</p>
|
||
</div>
|
||
<span id="hostCount" class="panel-tag">—</span>
|
||
</div>
|
||
|
||
<div class="toolbar toolbar-slim">
|
||
<label class="field compact search-field">
|
||
<span>Buscar host</span>
|
||
<input id="hostSearch" type="search" placeholder="192.168.1.50" autocomplete="off" spellcheck="false">
|
||
</label>
|
||
<div class="legend">
|
||
<span class="legend-item"><i class="swatch p1"></i>Pilar 1</span>
|
||
<span class="legend-item"><i class="swatch p2"></i>Pilar 2</span>
|
||
<span class="legend-item"><i class="swatch p3"></i>Pilar 3</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="table-wrap">
|
||
<table class="host-table">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col">#</th>
|
||
<th scope="col">Host</th>
|
||
<th scope="col">Fusão</th>
|
||
<th scope="col">P1</th>
|
||
<th scope="col">P2</th>
|
||
<th scope="col">P3</th>
|
||
<th scope="col">Alertas</th>
|
||
<th scope="col"><span class="sr-only">Detalhes</span></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="hostRows">
|
||
<tr><td colspan="8" class="empty">Nenhum host carregado.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<p class="footnote">A API de fusão lista apenas hosts com pelo menos um alerta. Hosts sem alerta não aparecem nesta tabela.</p>
|
||
</section>
|
||
</section>
|
||
|
||
<section class="view" id="analysis" hidden>
|
||
<section class="panel">
|
||
<div class="panel-head">
|
||
<div>
|
||
<h2>Analisar dataset</h2>
|
||
<p class="hint">Aponte para um diretório de logs Zeek ou para um dataset local.</p>
|
||
</div>
|
||
<span id="reportStatus" class="panel-tag">—</span>
|
||
</div>
|
||
|
||
<div class="field-row">
|
||
<label class="field">
|
||
<span>Diretório de logs</span>
|
||
<input id="datasetPath" type="text" placeholder="/var/log/zeek/current" autocomplete="off" spellcheck="false">
|
||
</label>
|
||
<button id="analyzeBtn" type="button" class="btn primary">Analisar</button>
|
||
</div>
|
||
|
||
<div id="reportSummary" class="summary-grid" hidden></div>
|
||
|
||
<div class="report-detail">
|
||
<pre id="output" class="output">Nenhuma análise executada.</pre>
|
||
<div class="report-actions">
|
||
<button id="gotoAlerts" type="button" class="btn" hidden>Ver alertas</button>
|
||
<button id="gotoHosts" type="button" class="btn" hidden>Ver hosts</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="panel">
|
||
<div class="panel-head">
|
||
<div>
|
||
<h2>Estado do serviço</h2>
|
||
<p class="hint">Versão, dataset e armazenamento informados pela API local.</p>
|
||
</div>
|
||
</div>
|
||
<div id="serviceSummary" class="summary-grid summary-grid-wide"></div>
|
||
</section>
|
||
</section>
|
||
|
||
<footer class="foot">
|
||
<span>Acauã · pré-lançamento</span>
|
||
<span class="mono">MIT OR Apache-2.0</span>
|
||
</footer>
|
||
</main>
|
||
</div>
|
||
|
||
<dialog id="settingsDlg" aria-labelledby="settingsTitle">
|
||
<form method="dialog">
|
||
<div class="dialog-head">
|
||
<h2 id="settingsTitle">Configurações</h2>
|
||
<button type="button" id="closeSettings" class="icon-btn" aria-label="Fechar configurações" title="Fechar">×</button>
|
||
</div>
|
||
<label class="field">
|
||
<span>Idioma</span>
|
||
<select id="langSel">
|
||
<option value="auto">Auto</option>
|
||
<option value="pt">Português</option>
|
||
<option value="en">English</option>
|
||
</select>
|
||
</label>
|
||
<div class="field">
|
||
<span>Tema</span>
|
||
<div class="segmented" id="themePick">
|
||
<button type="button" data-theme-pick="auto">Auto</button>
|
||
<button type="button" data-theme-pick="light">Claro</button>
|
||
<button type="button" data-theme-pick="dark">Escuro</button>
|
||
</div>
|
||
</div>
|
||
<menu>
|
||
<button type="button" id="doneSettings" class="btn primary">Fechar</button>
|
||
</menu>
|
||
</form>
|
||
</dialog>
|
||
|
||
<script type="module" src="/app.js"></script>
|
||
</body>
|
||
</html>
|