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).
285 lines
14 KiB
HTML
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>
|