Antivirus/gui/dist/index.html
johnmcafee 6ef296caa1 Full feature set: realtime monitor, quarantine vault, rootkit scan, settings, events
Daemon (houndd)
- realtime.rs: inotify monitor over watched dirs (default ~/Downloads,
  ~/Documents, ~/Desktop), ClamAV scan on touch, on_detect action
  (quarantine/rename/remove), ransomware heuristic (writes/renames per
  minute above threshold -> 'watching'/'alarm' + critical event)
- quarantine.rs: SHA-256-keyed vault under ~/.local/share/hound/quarantine,
  add/list/restore/remove with original-path metadata
- rootkit.rs: setuid anomaly detection (allowlisted stock binaries),
  deleted-but-executing inodes, world-writable /usr /bin; 3 severity levels
- settings.rs: persisted ~/.config/hound/settings.json, hot-reload on set
- events.rs: ring buffer of severity-tagged events, query + clear

API (hound-api): Settings, Event, QuarantineEntry, RootkitScan/
RootkitFinding, RealtimeStatus types + 10 client methods; Status gains
engine field (engine-agnostic seam)

CLI (hound): events, quarantine list|add|restore|remove, settings
[show|paused|auto-update|notify|realtime on|off|watch|on-detect|
max-size|exclude], rootkit, realtime [status|on|off] — color human
output, --json everywhere

GUI (Tauri 2):
- 16 backend commands bridging every client method
- tray watcher: 1s poll loop, 4-state icon ladder (green/amber/red/gray),
  desktop notification on fresh critical events
- 6-tab frontend: Protection (hero + scan + update), Quarantine (vault
  manager + manual add), Realtime (stats + watch list + toggle), Rootkit
  (on-demand scan), Alerts (event log + clear), Settings (full editor)
- capabilities/default.json for dialog/notification/event permissions

Verified: 27/27 workspace tests, live E2E — EICAR dropped in ~/Downloads
auto-quarantined by the running daemon (critical event logged, file
removed from origin).
2026-08-20 20:33:44 -05:00

285 lines
14 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hound Antivirus</title>
<link rel="stylesheet" href="styles.css" />
<link rel="icon" type="image/png" href="favicon-32.png" />
</head>
<body>
<div id="app">
<!-- ── Header ─────────────────────────────────────────────── -->
<header class="header">
<div class="brand">
<img src="hound-32.png" alt="" class="brand-mark" />
<div class="brand-text">
<h1>Hound</h1>
<span class="tagline">Antivirus for Linux</span>
</div>
</div>
<div class="header-status">
<span class="engine-dot" id="engine-dot" title="Engine status"></span>
<span class="engine-label" id="engine-label">connecting…</span>
</div>
</header>
<!-- ── Tabs ───────────────────────────────────────────────── -->
<nav class="tabs" role="tablist">
<button class="tab active" data-tab="protection" role="tab">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
Protection
</button>
<button class="tab" data-tab="quarantine" role="tab">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 8v13H3V8"/><path d="M1 3h22v5H1z"/><path d="M10 12h4"/></svg>
Quarantine
<span class="tab-badge hidden" id="badge-quarantine"></span>
</button>
<button class="tab" data-tab="realtime" role="tab">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M2 12h4l3-9 4 18 3-9h6"/></svg>
Realtime
</button>
<button class="tab" data-tab="rootkit" role="tab">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
Rootkit
</button>
<button class="tab" data-tab="alerts" role="tab">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg>
Alerts
<span class="tab-badge hidden" id="badge-alerts"></span>
</button>
<button class="tab" data-tab="settings" role="tab">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
Settings
</button>
</nav>
<!-- ══ Protection ═══════════════════════════════════════════ -->
<section class="tab-panel active" id="panel-protection">
<section class="hero" id="hero">
<div class="shield" id="shield">
<img id="shield-icon" src="state-protected-48.png" alt="" width="48" height="48" />
</div>
<div class="hero-copy">
<h2 id="hero-title">Protected</h2>
<p id="hero-sub">Your system looks healthy.</p>
<div class="hero-meta">
<span class="pill" id="pill-db">signatures: —</span>
<span class="pill" id="pill-os"></span>
<span class="pill" id="pill-engine">engine: —</span>
</div>
</div>
</section>
<section class="actions">
<button class="btn primary" id="btn-scan-home">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
Scan Home
</button>
<button class="btn primary" id="btn-scan-custom">Scan Folder…</button>
<button class="btn" id="btn-update">Update Signatures</button>
<button class="btn" id="btn-pause">Pause Protection</button>
</section>
<section class="panel hidden" id="scan-panel">
<div class="panel-head">
<h3 id="scan-title">Scanning…</h3>
<span class="scan-target" id="scan-target"></span>
</div>
<div class="progress">
<div class="progress-bar" id="progress-bar"></div>
</div>
<p class="muted" id="scan-note">ClamAV is working the queue. This can take a while on large folders.</p>
</section>
<section class="panel" id="results-panel">
<div class="panel-head">
<h3>Last Scan Results</h3>
<span class="muted" id="results-meta"></span>
</div>
<div id="results-body" class="results">
<p class="muted empty">No scans yet. Hit <strong>Scan Home</strong> to start.</p>
</div>
</section>
<section class="panel hidden" id="update-panel">
<div class="panel-head">
<h3>Signature Update</h3>
</div>
<pre class="log" id="update-log"></pre>
</section>
</section>
<!-- ══ Quarantine ═══════════════════════════════════════════ -->
<section class="tab-panel" id="panel-quarantine">
<section class="panel">
<div class="panel-head">
<h3>Quarantine Vault</h3>
<div class="row-actions">
<span class="muted" id="qt-count"></span>
<button class="btn small" id="btn-qt-refresh">Refresh</button>
</div>
</div>
<div id="qt-body" class="qt-list">
<p class="muted empty">Loading…</p>
</div>
</section>
<section class="panel">
<div class="panel-head"><h3>Add Manually</h3></div>
<div class="form-row">
<input type="text" id="qt-add-path" class="input" placeholder="/path/to/suspicious/file" />
<input type="text" id="qt-add-virus" class="input" placeholder="label (e.g. manual-suspicious)" value="manual" />
<button class="btn" id="btn-qt-add">Quarantine</button>
</div>
<p class="muted" style="margin-top:8px">The file is moved into the vault; its original location is left empty.</p>
</section>
</section>
<!-- ══ Realtime ═════════════════════════════════════════════ -->
<section class="tab-panel" id="panel-realtime">
<section class="panel">
<div class="panel-head">
<h3>Realtime Monitor</h3>
<label class="switch-row">
<span id="rt-enabled-label" class="muted">loading…</span>
<input type="checkbox" id="rt-toggle" />
<span class="switch"></span>
</label>
</div>
<div class="stat-grid" id="rt-stats">
<div class="stat"><span class="stat-val" id="rt-seen"></span><span class="stat-key">file events seen</span></div>
<div class="stat"><span class="stat-val" id="rt-quar"></span><span class="stat-key">auto-quarantined</span></div>
<div class="stat"><span class="stat-val" id="rt-uptime"></span><span class="stat-key">monitor uptime</span></div>
<div class="stat"><span class="stat-val" id="rt-ransom"></span><span class="stat-key">ransomware guard</span></div>
</div>
<div class="kv"><span class="kv-k">watching</span><span class="kv-v" id="rt-watch"></span></div>
<div class="kv"><span class="kv-k">last file event</span><span class="kv-v" id="rt-last"></span></div>
<div class="kv"><span class="kv-k">on detection</span><span class="kv-v" id="rt-action"></span></div>
</section>
<section class="panel">
<div class="panel-head"><h3>Watch Folders</h3></div>
<div id="rt-watch-list" class="watch-list"></div>
<p class="muted" style="margin-top:8px">Edit in Settings → Realtime. Restart required for the monitor to pick up new folders.</p>
</section>
</section>
<!-- ══ Rootkit ══════════════════════════════════════════════ -->
<section class="tab-panel" id="panel-rootkit">
<section class="panel">
<div class="panel-head">
<h3>Rootkit Hunter</h3>
<button class="btn primary" id="btn-rootkit">Run Scan</button>
</div>
<div id="rootkit-banner" class="verdict hidden"></div>
<div id="rootkit-body" class="finding-list">
<p class="muted empty">Runs userspace heuristics: deleted-but-open executables, world-writable binaries, unexpected setuid, hidden /proc entries.</p>
</div>
</section>
</section>
<!-- ══ Alerts ═══════════════════════════════════════════════ -->
<section class="tab-panel" id="panel-alerts">
<section class="panel">
<div class="panel-head">
<h3>Event Log</h3>
<div class="row-actions">
<span class="muted" id="alerts-count"></span>
<button class="btn small" id="btn-alerts-refresh">Refresh</button>
<button class="btn small danger" id="btn-alerts-clear">Clear Log</button>
</div>
</div>
<div id="alerts-body" class="event-list">
<p class="muted empty">Loading…</p>
</div>
</section>
</section>
<!-- ══ Settings ═════════════════════════════════════════════ -->
<section class="tab-panel" id="panel-settings">
<section class="panel">
<div class="panel-head"><h3>Protection</h3></div>
<label class="switch-row setting">
<span><strong>Pause all protection</strong><small>Suspends realtime and grays the tray.</small></span>
<input type="checkbox" id="set-paused" /><span class="switch"></span>
</label>
<label class="switch-row setting">
<span><strong>Auto-update signatures</strong><small>Daemon runs freshclam on a schedule.</small></span>
<input type="checkbox" id="set-autoupdate" /><span class="switch"></span>
</label>
<label class="switch-row setting">
<span><strong>Desktop notifications</strong><small>Pop an alert for critical events.</small></span>
<input type="checkbox" id="set-notify" /><span class="switch"></span>
</label>
</section>
<section class="panel">
<div class="panel-head"><h3>Realtime</h3></div>
<label class="switch-row setting">
<span><strong>Realtime monitor</strong><small>Watch folders and quarantine on the fly.</small></span>
<input type="checkbox" id="set-realtime" /><span class="switch"></span>
</label>
<div class="field">
<label for="set-watch">Watch folders (one per line)</label>
<textarea id="set-watch" class="input mono" rows="4" spellcheck="false"></textarea>
</div>
<div class="field">
<label for="set-ondetect">Action on detection</label>
<select id="set-ondetect" class="input">
<option value="quarantine">Quarantine automatically</option>
<option value="alert">Alert only (leave file in place)</option>
</select>
</div>
</section>
<section class="panel">
<div class="panel-head"><h3>Scanning</h3></div>
<div class="field">
<label for="set-maxsize">Max file size (MB)</label>
<input type="number" id="set-maxsize" class="input" min="1" />
</div>
<div class="field">
<label for="set-excludes">Excluded paths (one per line, exact or prefix)</label>
<textarea id="set-excludes" class="input mono" rows="4" spellcheck="false"></textarea>
</div>
<label class="switch-row setting">
<span><strong>Recursive by default</strong></span>
<input type="checkbox" id="set-recursive" /><span class="switch"></span>
</label>
</section>
<section class="panel">
<div class="panel-head"><h3>Ransomware Guard</h3></div>
<label class="switch-row setting">
<span><strong>Enable guard</strong><small>Write-burst heuristic trips an alarm under attack.</small></span>
<input type="checkbox" id="set-ransom" /><span class="switch"></span>
</label>
<div class="field">
<label for="set-ransom-thresh">Threshold (file-writes / minute)</label>
<input type="number" id="set-ransom-thresh" class="input" min="10" />
</div>
</section>
<section class="panel">
<div class="panel-head"><h3>Rootkit</h3></div>
<label class="switch-row setting">
<span><strong>Include in scans</strong><small>Run rootkit heuristics as part of the health check.</small></span>
<input type="checkbox" id="set-rootkit" /><span class="switch"></span>
</label>
</section>
<div class="actions">
<button class="btn primary" id="btn-settings-save">Save Settings</button>
<span class="muted" id="settings-msg"></span>
</div>
</section>
<footer class="footer">
<span id="foot-ver">houndd —</span>
<span class="muted">engine: ClamAV via Unix socket</span>
</footer>
</div>
<script type="module" src="app.js"></script>
</body>
</html>