fix(wave-3): audit fixes — ACL guards, presence, moderation, theming perf

Wave-3 bug-hunt fixes (findings in LOTUS_TODO), reviewed + gate-green:
- 🔴 ACL editor [H1–H4]: block saving an empty allow-list (was a one-click
  federation brick), warn on self-ban (case-insensitive glob match of
  mx.getDomain() vs allow/deny), accept real globs (1.2.3.*, *.evil.*), and
  gate Save behind a confirm dialog.
- 🔴 [P1] room context menu no longer acts on the wrong room after a live
  reorder (key by roomId, not list index). 🔴 [P2] status writes no longer
  force presence to online over Invisible/DND (shared presenceStateFromSetting).
- 🟠 [P3] timed mutes restored on boot; [P4] custom-status auto-clear now fires
  (always-mounted StatusExpiryMonitor); [P5] timezone also PUT to the m.tz
  profile field so it's visible to others; [H6] RoomInsights single-pass
  min/max (was Math.min(...spread) stack overflow); [H7/H8] mod-log labels.
- 🟡 [P6/P7] favorites collapse+filter, [P8] charCount reset, [P9] DM preview
  refresh on decrypt; theming [T-P1] lazy decorations, [T-P2] drop the redundant
  always-on body animation, [T-P4] live useReducedMotion, [T-P5] decoration key.
- NATIVE-CINNY LAW: notification presets + Powers permissions use folds icons.

DEFERRED: [H5] invite-QR is fetched from api.qrserver.com (third-party leak);
local generation needs a bundled QR lib (not added). tsc/eslint/prettier clean,
build OK, 677 tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 21:40:07 -04:00
parent 41149db685
commit dcd8201e16
22 changed files with 527 additions and 116 deletions
+25
View File
@@ -96,6 +96,31 @@ Tier-2 bug-hunt (desktop/native, crypto/session/infra, messaging data) by 3 para
---
## 🔎 Audit findings — Wave 3 (2026-07)
Tier-3 bug-hunt (theming/visual, presence/UX/composer, rooms-customization/moderation) by 3 parallel agents. Higher-severity than expected in the non-theming areas. `[P#]`=presence/UX, `[H#]`=rooms/moderation, `[T#]`=theming.
**✅ FIXED (2026-07), reviewed + gate-green (677 tests):** the ACL cluster [H1H4] (empty-allow block, self-ban warning w/ case-insensitive glob match, glob validation, confirm dialog), [P1] wrong-room menu, [P2] presence override, [H6] insights overflow, [H7/H8] mod-log labels, [P3/P4/P5] mute-restore + status-expiry + timezone-`m.tz`, [P6P9] favorites/charCount/DM-preview, and theming [T-P1/P2/P4/P5]. **DEFERRED:** [H5] invite-QR local generation (needs a bundled QR lib — not added).
**🔴 High (fixing/fixed this pass):**
- **[H1H4] Server ACL editor can brick a room's federation in one click** — no guard against saving an **empty allow-list** (denies every server → room partitioned, unrecoverable), no warning on **denying/omitting your own homeserver**, glob validation **rejects valid patterns** (`1.2.3.*`, `*.evil.*`), and a single Save writes `m.room.server_acl` with no confirmation. → adding empty-allow block, self-ban warning (`mx.getDomain()`), glob validation, and a confirm dialog.
- **[P1] Room context menu acts on the WRONG room after a live reorder** — `RoomNavItem` keyed by list `index`, so an open menu rebinds to a different room on activity-sort reorder → Leave/Mute/Favorite hits the wrong room. → key by `roomId`.
- **[P2] Setting a status message force-flips presence to `online`** — overrides Invisible/DND/Idle (an Invisible user is outed as online). → derive presence from the `presenceStatus` setting.
- **[H5] Invite QR leaks room identity to a third party** — the QR is fetched from `api.qrserver.com` (`RoomShareInvite.tsx`). **DEFERRED — needs a bundled QR lib** (none in deps); generate locally instead of a remote call.
**🟠 Medium (fixing/fixed):**
- **[H6] RoomInsights `Math.min(...allTs)`** spread overflowed the call stack on a large timeline → **FIXED** (single-pass min/max). [H7] policy-list mislabels `org.matrix.mjolnir.ban` + empty recommendation badge; [H8] activity-log mislabels knock→join and invite-retraction.
- **[P3] Timed-mute timers never restored on startup** → a mute set before a reload stays stuck forever; re-arm/expire persisted timers on client init. **[P4]** custom-status auto-clear **never fires** (timer lives in the Settings modal) → move to an always-mounted watcher. **[P5]** timezone written to `im.lotus.timezone` but read from the `m.tz` profile field → invisible to other users despite the "visible to others" copy; also PUT `m.tz`.
- **[T-P1] Decoration picker eager-loads ~100 animated PNGs** (jank/CPU) → `loading="lazy"`. **[T-P2]** a redundant always-on animated `<body>` compositor layer when glassmorphism is off → gate on `glassmorphismSidebar`. **[T-P4]** `prefers-reduced-motion` sampled once, never re-subscribed → a `useReducedMotion()` hook.
**🟡 Low (fixing/open):** [P6/P7] favorites collapse chevron doesn't hide + filter ignores favorites; [P8] `charCount` not reset on send; [P9] encrypted DM preview stale until next event (listen for `Decrypted`); [P10] presence badge not seeded when the User appears late; [T-P5] decoration `<img>` stuck hidden on a recycled node; [H10] room-name setter fire-and-forget/silent length reject; theming [T-P3/P6/P7/P8] preview-grid perf + seasonal-swatch viewport-units + mutual-exclusion UX asymmetry (mostly acceptable); `App.tsx` mention-color assumes 6-digit hex.
**Verified sound (spot-checks):** NO theming leaks (all backgrounds/overlays pure-CSS; `lotus-boot`/`LotusDecorationPusher` timers self-clean; NightLight unmounts + `pointerEvents:none`; reduced-motion honored on load); favorites use per-room `m.tag` (**no** account-data race); bookmarks serialization intact; toast queue self-dismiss + dedup; composer-toolbar config; CollapsibleBody ResizeObserver; syntax highlighter renders React children (**no XSS**); Report Room endpoint (MSC4151); knock badge gated on PL; ACL event wire shape.
---
## ✅ Done — Awaiting Verification
Built and gate-green; verify per [LOTUS_TESTING.md](./LOTUS_TESTING.md), then they graduate to LOTUS_FEATURES.md. (Open bugs + the verification backlog now live in this file and LOTUS_TESTING.md.)