Replace client-side session table filtering with server-side filtering via a new /fragments/recent-sessions htmx endpoint. Add InputBytes column to session tables, Human score > 0 checkbox filter, and Sort by Input Bytes option to help identify sessions with actual shell interaction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
167 lines
5.2 KiB
HTML
167 lines
5.2 KiB
HTML
{{define "content"}}
|
|
<section id="stats-section" hx-get="/fragments/stats" hx-trigger="every 30s" hx-swap="innerHTML">
|
|
{{template "stats" .Stats}}
|
|
</section>
|
|
|
|
<details>
|
|
<summary>Filters</summary>
|
|
<form id="filter-form">
|
|
<div class="grid">
|
|
<label>Since <input type="date" name="since"></label>
|
|
<label>Until <input type="date" name="until"></label>
|
|
<label>IP <input type="text" name="ip" placeholder="10.0.0.1"></label>
|
|
<label>Country <input type="text" name="country" placeholder="CN" maxlength="2"></label>
|
|
<label>Username <input type="text" name="username" placeholder="root"></label>
|
|
</div>
|
|
<div class="grid">
|
|
<label><input type="checkbox" name="human_score" value="1"> Human score > 0</label>
|
|
<label>Sort by <select name="sort"><option value="connected_at">Recent</option><option value="input_bytes">Input Bytes</option></select></label>
|
|
</div>
|
|
<button type="submit">Apply</button>
|
|
<button type="button" class="secondary" onclick="clearFilters()">Clear</button>
|
|
</form>
|
|
</details>
|
|
|
|
<section>
|
|
<h3>Attack Trends</h3>
|
|
<div class="grid">
|
|
<article>
|
|
<header>Attempts Over Time</header>
|
|
<canvas id="chart-attempts"></canvas>
|
|
</article>
|
|
<article>
|
|
<header>Hourly Pattern (UTC)</header>
|
|
<canvas id="chart-hourly"></canvas>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h3>Attack Origins</h3>
|
|
<article>
|
|
<div id="world-map"></div>
|
|
</article>
|
|
</section>
|
|
|
|
<div id="dashboard-content">
|
|
{{template "dashboard_content" .}}
|
|
</div>
|
|
|
|
<section>
|
|
<h3>Active Sessions</h3>
|
|
<div id="active-sessions" hx-get="/fragments/active-sessions" hx-trigger="every 10s" hx-swap="innerHTML">
|
|
{{template "active_sessions" .ActiveSessions}}
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h3>Recent Sessions</h3>
|
|
<table id="recent-sessions-table">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>IP</th>
|
|
<th>Country</th>
|
|
<th>Username</th>
|
|
<th>Type</th>
|
|
<th>Score</th>
|
|
<th>Input</th>
|
|
<th>Connected</th>
|
|
<th>Disconnected</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{template "recent_sessions" .RecentSessions}}
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
{{end}}
|
|
|
|
{{define "scripts"}}
|
|
<script src="/static/chart.min.js"></script>
|
|
<script src="/static/dashboard.js"></script>
|
|
{{end}}
|
|
|
|
{{define "dashboard_content"}}
|
|
<section>
|
|
<h3>Top Credentials & IPs</h3>
|
|
<div class="top-grid">
|
|
<article>
|
|
<header>Top Usernames</header>
|
|
<table>
|
|
<thead>
|
|
<tr><th>Username</th><th>Attempts</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .TopUsernames}}
|
|
<tr><td>{{.Value}}</td><td>{{.Count}}</td></tr>
|
|
{{else}}
|
|
<tr><td colspan="2">No data</td></tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</article>
|
|
<article>
|
|
<header>Top Passwords</header>
|
|
<table>
|
|
<thead>
|
|
<tr><th>Password</th><th>Attempts</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .TopPasswords}}
|
|
<tr><td>{{.Value}}</td><td>{{.Count}}</td></tr>
|
|
{{else}}
|
|
<tr><td colspan="2">No data</td></tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</article>
|
|
<article>
|
|
<header>Top IPs</header>
|
|
<table>
|
|
<thead>
|
|
<tr><th>IP</th><th>Country</th><th>Attempts</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .TopIPs}}
|
|
<tr><td>{{.Value}}</td><td>{{.Country}}</td><td>{{.Count}}</td></tr>
|
|
{{else}}
|
|
<tr><td colspan="3">No data</td></tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</article>
|
|
<article>
|
|
<header>Top Countries</header>
|
|
<table>
|
|
<thead>
|
|
<tr><th>Country</th><th>Attempts</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .TopCountries}}
|
|
<tr><td>{{.Value}}</td><td>{{.Count}}</td></tr>
|
|
{{else}}
|
|
<tr><td colspan="2">No data</td></tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</article>
|
|
<article>
|
|
<header>Top Exec Commands</header>
|
|
<table>
|
|
<thead>
|
|
<tr><th>Command</th><th>Count</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .TopExecCommands}}
|
|
<tr><td><code>{{truncateCommand .Value}}</code></td><td>{{.Count}}</td></tr>
|
|
{{else}}
|
|
<tr><td colspan="2">No data</td></tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
{{end}}
|