Confirmed-security quick-wins from a full re-audit (6-agent subsystem fan-out,
Codex-reviewed). Each item ships with a regression test proven to fail pre-fix.
- files: gate file CONTENT reads (download / edit / FileEditor::load) behind the
`operate` ability so a read-only viewer keeps LISTING/browse access but can no
longer pull file bytes over the server credential (root -> /etc/shadow, keys,
.env). Hide the download/edit/delete controls from viewers in the blade, and
basename() Files::open() for parity with the other path builders.
- fail2ban: reject a leading dash/dot in validJail(). A jail name like "-h" or
"--help" was parsed by fail2ban-client as an OPTION (argument-injection) rather
than a positional jail; internal dashes ("nginx-http-auth") still pass.
- bruteforce: release the atomically-claimed ban-audit dedup slot when the
AuditEvent write throws, so a transient DB failure no longer suppresses the
auth.ip_banned audit for the whole 60s window (the ban itself is unaffected).
- update path: verify_update_request now FAILS CLOSED when UPDATE_HMAC_KEY is
unset (was fail-open -> any ./run marker could drive a root update). Pin the
self-update `git pull` to the recorded CLUSEV_BUILD_BRANCH and refuse a diverged
checkout, so a `git checkout` in the tree cannot redirect the next root update.
- wireguard: normalise the DNS input to a single comma list and QUOTE the WG_DNS
assignment in wg.env; an unquoted space previously split the assignment and
broke the next `. "$WG_ENV"`.
13 new regression tests. 614 tests, Pint, shellcheck, Codex review all green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>