Files
hamlab/BUGS.md
T

114 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Bug bash — 2026-09-18
Discovery only; nothing here is fixed yet. Found by a headless Chromium pass (iPhone and
1440px desktop viewports) over every page on the hub and on each remote machine, an
offline-node scenario (wyse stopped for the test, then restarted), direct API probing
with hostile inputs, and a code read. Ordered by severity within each group.
## Security / robustness
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. ✅ **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. ✅ **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. ✅ **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. ✅ **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`
or `pwrstat` writes three `pam_unix(sudo:session)` lines into the journal *under
`hamlab.service`*: 839 lines in 10 minutes at a 250 ms refresh, and the journal is
already 4 GB. It also buries Ham Lab's own log entries. Fix options: add the service
user to `systemd-journal` (and `adm`) so journal reads don't need sudo, and/or a
sudoers `Defaults!/usr/bin/journalctl !syslog, !pam_session` line in the installer.
7. **Uploading over an existing file silently overwrites it.** No confirmation, no
"replace?" prompt. Same for "+ file" when the name exists (server refuses; UI just
shows the error, fine) — upload is the gap.
8. **`/api/<anything>/../../x` falls through to the SPA** and returns `index.html` with
200 instead of a JSON 404. Harmless, but sloppy for an API prefix.
## Functional bugs
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.**
`switchMachine` only resets the view for Machines; Settings stays, so "mac" shows
core's account/token/about panels. Both are hub-only pages and should bounce to the
dashboard (or be clearly labelled as hub pages).
11. **Machines add-form `pattern` is an invalid regex in Chromium.** `[a-z0-9][a-z0-9-]*`
fails under the `v` flag ("Invalid character class"), logs a console error and the
browser skips validation. Needs `[a-z0-9][a-z0-9\-]*`.
12. **Updates tab on dnf hosts shows nonsense.** Mac: "Index refreshed 561d ago" (mtime of
`/var/cache/dnf`, which Fedora 41+ no longer uses; `libdnf5` is the live cache) and
"Last upgrade never · from apt history" (label and source are apt-specific). Needs
`dnf history` for last upgrade, metadata timestamps for refresh, and neutral labels.
13. **Cron overview is slow: 3.6 s per request on core.** At any refresh rate the page
is constantly busy. The 30-day, 20 000-line journal scan for "last run" is rebuilt
every 5 s; timers make three `systemctl` calls. Needs an incremental journal cursor
and a single `systemctl` call. Services list is 0.6 s for the same reason (two calls).
14. **Daemon memory grows.** RSS on core went 22 → 85 → 216 MB during the day (136 MB at
rest after the bash). Suspects: per-pid caches in the process table (`procInfo` only
prunes on the next walk), task output buffers, SMART cache, xterm scrollback rings
that are never freed for exited sessions. Needs a heap profile under load.
15. **Version string is always `-dirty`.** `make deploy` builds before the commit, so
every installed binary reports e.g. `v0.1.0-32-ga372378-dirty`, and the Machines page
compares with `-dirty` stripped. Build after commit, or tag releases.
16. **View resets to Dashboard on reload; no deep links; browser Back leaves the app.**
The selected machine survives a reload (sessionStorage) but the tab does not, and
there is no URL routing at all. Needs hash or path routing (`/#/grid/services`).
17. **Cron log tab lists oldest first and starts at the top.** 200 lines from Sep 10 with
the newest at the bottom, off-screen. Should show newest first or scroll to the end.
18. **Processes: empty filter result shows nothing.** No "nothing matches" message.
19. **Terminals accumulate.** Nothing ever reaps exited or idle sessions; the bash left
several "bug-bash-term" shells on the hub until deleted by hand. Exited sessions
should auto-remove after a while; idle ones at least be counted somewhere.
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. ✅ **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
22. **Tap targets under 28 px on phone:** header ⏻ (25), logout (22), customize ▲ ▼ hide
(23), terminal leaf ✎ ✕ (24), chips (25), table headers (27). Apple's guideline is
44 pt. Systemic: the small `.btn`/`.chip`/`.icon-btn` sizes need a touch variant.
23. **Power dialog on the Mac asks you to type `5ce91ee54af6`.** The confirmation uses
the node's hostname, which on the Mac is the unset default. Should offer the machine
name (`mac`) as the confirmation word on remote machines.
24. **Dashboard shows dead interfaces and empty modules.** wlp1s0 (down) gets a full
sparkline block of flat lines; "Fans: no fan sensors" is a permanent empty card on
hosts without fans. Down interfaces should collapse to one line; empty modules should
auto-hide unless customizing.
25. **Header load average is unlabelled** ("1.39 1.56 1.55" floating in the header).
26. **Files list on desktop is a narrow strip in a sea of empty space** for small
folders; the row menu opens off the right edge of the row. Cosmetic.
27. **Offline machine keeps polling every view at the refresh rate**, producing a 502
per request per page (and console errors). The UI copes — badge, message — but the
hub should back off to the prober's cadence while a node is down.
28. **Services logs are noise.** The default view of `hamlab.service` logs is the sudo
session spam from bug 6, three lines per poll.
## Confirmed working under abuse (for the record)
Path traversal in files, services, cron, ZFS, procs and updates all rejected. `/etc`
and `/` protected from delete/rename as root. Unit, pool, dataset, snapshot and
package names validated. Token issue refused with a wrong password. Oversized prefs
rejected. Duplicate machine add refused. Unicode + spaces in file names round-trip.
Upload byte-exact. Terminal scrollback survives switching. Offline node: switcher and
header show it, pages show the reason. Remote terminals, logs, tasks and files through
the proxy all work on all three nodes.