Two mechanisms Joe asked for, neither of which existed. **Definitions now update themselves.** `auto_update_signatures` shipped from the start and nothing ever read it — definitions only moved when somebody typed `hound update`, which for a security product means most installations were running whatever they were installed with. There is now a scheduler thread: definitions hourly, release check daily, and a 90-second startup delay so a fleet that reboots together does not arrive at the CDN in one wave. It calls the same install path as `hound update` rather than a second implementation that could drift. **Release notifications, signed.** dl.houndav.com/latest.json carries an Ed25519 signature from the same key as the definition packs, for the same reason: whoever serves that host must not be able to invent a version and point our users at a binary of their choosing. A manifest that does not verify is discarded, and no manifest means "nothing newer known" — never "update available". The safe default is silence. The signature covers a canonical byte form defined identically in release.rs and tools/publish-release.py. Signing a re-serialisation of a parsed struct is the classic way to verify one thing and act on another, so a test runs a real manifest produced by the Python tool through the Rust verifier against the production key. If those drift, manifests verify nowhere and the only symptom is that nobody ever hears about an update. Version comparison is numeric, not lexical, because "0.9.0" > "0.10.0" as strings and that is precisely the pair where it would first be noticed. Downgrades are never offered — a signed-but-old manifest must not become a way to reintroduce a fixed vulnerability. The app does not replace its own binary. A root daemon that can rewrite itself is the mechanism a supply-chain attacker most wants. Installing goes through apt with the user present and authenticating: the package is downloaded, checked against the manifest's SHA-256, staged into a root-owned 0700 directory so nothing can substitute it between the check and the read, and handed over. Two guards beyond the signature — the URL must be on our own download host, because a signature proves publisher intent and not that the publisher got the URL right. Tray, per Joe's design: a new amber "attention" state ranked below a threat and an in-flight scan, above protected. Deeper amber than "scanning" so the two are distinguishable — scanning lasts seconds and the user started it, attention persists. The tooltip carries the reason rather than only that there is one, and "Install Hound X…" appears enabled only when there is something to install; a permanently greyed item teaches people the menu is decorative. Definition staleness is a first-class signal, not just update availability: amber at 7 days, and at 30 the wording stops pretending — "this machine is not currently protected against anything found since then". An antivirus showing green on month-old definitions is lying in the same way a dead front-end showing "Protected" was. Also: the four files that declare a version now have a test asserting they agree. Drift there means a release looks older than what is installed and the update silently never offers, or offers forever. 377 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
29 lines
843 B
TOML
29 lines
843 B
TOML
[package]
|
|
name = "hound-gui"
|
|
description = "Hound Antivirus desktop app (Tauri 2)"
|
|
version = "0.1.1"
|
|
edition = "2021"
|
|
license = "MIT"
|
|
repository = "https://git.joelovestech.com/Hound/Antivirus"
|
|
|
|
# Standalone crate: the GUI has its own workspace (different dep set from
|
|
# the CLI/daemon) and is built via `cargo tauri build`, not the root build.
|
|
[workspace]
|
|
|
|
[dependencies]
|
|
hound-api = { path = "../../crates/hound-api" }
|
|
tauri = { version = "2", features = ["tray-icon", "image-png"] }
|
|
tauri-plugin-dialog = "2"
|
|
tauri-plugin-notification = "2"
|
|
tauri-plugin-opener = "2"
|
|
serde = { version = "1", features = ["derive"] }
|
|
sha2 = "0.10"
|
|
ureq = { version = "2", default-features = false, features = ["tls", "gzip"] }
|
|
serde_json = "1"
|
|
anyhow = "1"
|
|
|
|
[build-dependencies]
|
|
tauri-build = { version = "2", features = [] }
|
|
|
|
[profile.release]
|
|
strip = true
|