Commit graph

2 commits

Author SHA1 Message Date
Hound
85234842f9 houndd: gate covers writes too; inotify becomes the fallback
Completes Phase 1. The gate now asks for FAN_CLOSE_WRITE alongside the
permission events, so a threat written to disk is quarantined and a
threat being executed is refused — one mechanism, one mark, no
watch-descriptor ceiling and no blind spots outside a configured list.

Verified live. The nicest evidence is an error message:

  $ chmod +x /tmp/hound-live/malware.sh
  chmod: cannot access '/tmp/hound-live/malware.sh': No such file or directory

Hound had already quarantined it. `hound quarantine list` shows the
entry, the clean binary beside it still runs, and CapPrm/CapEff/CapBnd
read 000000000020000e.

Three bugs, each of which looked like working code:

* A file descriptor number is not an identity. The kernel allocates an
  fd per event and recycles the number the moment we close it, so one
  write arrives as FAN_OPEN_PERM on fd 6 and then FAN_CLOSE_WRITE on fd
  6 again. Idempotency keyed on the fd treated the second as a duplicate
  of the first and dropped it — detection ran, matched EICAR, and threw
  the result away. Events now carry a monotonic seq that is never reused.

* rename(2) fails EXDEV across filesystems, and for quarantine that is
  the common case rather than the exotic one: the vault is under
  /var/lib while threats land on /home, in a tmpfs, on a USB stick or
  in a container overlay. Quarantine now falls back to copy-then-unlink,
  unlinking only once the copy is safely down, and seals the stored file
  at 0600 with every execute bit cleared.

* The capability set was too small to do the job. CAP_DAC_READ_SEARCH
  lets us read a threat but not unlink it, so quarantine failed EACCES
  as root. The set is now four capabilities — SYS_ADMIN, DAC_READ_SEARCH,
  DAC_OVERRIDE, FOWNER. DAC_OVERRIDE is close to "write anywhere" and
  that is worth being honest about; an antivirus that quarantines cannot
  avoid it, because the threat is by definition in a directory somebody
  else owns. What the reduction still buys is what it excludes, and
  there is a test asserting SYS_MODULE, SYS_BOOT, SYS_PTRACE, NET_ADMIN,
  NET_RAW, AUDIT_CONTROL and SETUID never creep back in. Narrowing
  further means a separate privileged helper for quarantine.

realtime.rs is now documented as the unprivileged fallback and does not
start when the gate is armed — running both would scan everything twice
and quarantine the same file from two threads.

99 tests pass. HOUNDD_GATE_DEBUG=1 dumps every event and decision.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 23:27:34 -05:00
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