Bug bash fixes: special-file reads, per-client lockout, prefs validation, security headers, kernel-thread guard, login feedback, error text; plus journal-quiet sudoers + journal groups, upload overwrite guard, machines pattern, dnf freshness, cron/services speed, clean version

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-18 17:41:52 -07:00
co-authored by Claude Fable 5.1
parent 57c58ebffb
commit 27f9665606
18 changed files with 306 additions and 85 deletions
+7 -7
View File
@@ -7,24 +7,24 @@ with hostile inputs, and a code read. Ordered by severity within each group.
## Security / robustness
1. **Reading a special file hangs the request forever.** `GET /api/files/read?path=/dev/zero`
1. ✅ **RESOLVED 2026-09-18** — only regular files are readable; the byte cap applies to what is sent, not to `stat`. `/dev/zero`, `/dev/null` and FIFOs answer 400 in 30 ms; `/proc/cpuinfo` still reads. **Reading a special file hangs the request forever.** `GET /api/files/read?path=/dev/zero`
never ends: the `max` cap compares against `stat` size, which is 0 for device files,
so the cap never triggers and the handler streams `/dev/zero` until the client gives
up. Same for FIFOs (blocks on open) and `/proc/self/mem`. Opening one in the Files
viewer would hang the tab. Needs: refuse non-regular files for read/edit, and cap the
copy with `io.LimitReader` regardless of `stat`.
2. **Login lockout is global, not per client.** Five bad passwords from anyone on the
2. ✅ **RESOLVED** — per-client buckets (first X-Forwarded-For hop or the peer) with the same 5-strike schedule, plus a global backstop of 100 failures per 5 min → 1 min. Verified: core's public IP locked while grid logged in fine. **Login lockout is global, not per client.** Five bad passwords from anyone on the
internet lock the real user out for up to 10 minutes (`auth.Limiter` is one bucket).
The bash locked the hub for the full 10 minutes twice. Also locks `POST /api/tokens`,
so hub pairing fails during a lockout. Needs per-IP buckets with a modest global cap.
3. **UI prefs from the server are not validated.** A stored `refreshMs` of `-5` made the
3. ✅ **RESOLVED** — server rejects refreshMs outside 100–60000 and non-identifier themes (400); client ignores out-of-range rates from any source. **UI prefs from the server are not validated.** A stored `refreshMs` of `-5` made the
client call `setInterval(-5)` on every live view: a request storm that took the
browser to `ERR_INSUFFICIENT_RESOURCES` and hammered the daemon. `theme` is validated
only by falling back. Needs range checks on load (and on save server-side).
4. **No anti-framing / CSP headers.** Only HSTS (from NPM). A root-capable console should
4. ✅ **RESOLVED** — every response carries X-Frame-Options DENY, CSP frame-ancestors none, nosniff, no-referrer. **No anti-framing / CSP headers.** Only HSTS (from NPM). A root-capable console should
send `X-Frame-Options: DENY`, `Content-Security-Policy: frame-ancestors 'none'`,
`Referrer-Policy: no-referrer`, `X-Content-Type-Options: nosniff` on every response.
5. **Signals can be sent to kernel threads.** `POST /api/procs/2/signal` (kthreadd)
5. ✅ **RESOLVED** — kernel threads (empty cmdline) and the daemon's own PID are refused. **Signals can be sent to kernel threads.** `POST /api/procs/2/signal` (kthreadd)
returns 204. Harmless in practice, but the guard should refuse PIDs with an empty
cmdline (kernel threads) and our own PID.
6. **Journal spam from the daemon's own sudo calls.** Every sudo'd `journalctl`, `smartctl`
@@ -41,7 +41,7 @@ with hostile inputs, and a code read. Ordered by severity within each group.
## Functional bugs
9. **Wrong password gives no feedback at all.** The 401 is thrown as `Unauthenticated`
9. ✅ **RESOLVED** — 401 carries the server message; the form shows "wrong password" (verified headless). **Wrong password gives no feedback at all.** The 401 is thrown as `Unauthenticated`
with an empty message, and the login form only renders `err` when non-empty. The
button just re-enables. Reproduced on phone and desktop.
10. **Switching machines while on Settings or Machines keeps showing the hub's page.**
@@ -78,7 +78,7 @@ with hostile inputs, and a code read. Ordered by severity within each group.
20. **Self-update restarts without warning running work.** A push to a node kills its
open terminals and any running task (upgrade in progress). The hub should refuse or
warn when the node reports a running task or attached terminals.
21. **Error text leaks raw Go errors.** e.g. rename shows "rename /tmp/x /tmp/y: no such
21. ✅ **RESOLVED** — PathError and LinkError are unwrapped to the bare reason. **Error text leaks raw Go errors.** e.g. rename shows "rename /tmp/x /tmp/y: no such
file or directory". Fine for a lab, ugly for a product; strip the syscall prefix.
## Mobile / UX