fix(security): tab-nabbing hardening + /acl self-lockout guard (SEC-3/4)

SEC-3: add `noopener,noreferrer` to the 5 `window.open(_blank)` sites that
don't use the returned handle (UserChips, OidcManageAccount, OtherDevices x2,
Verification), closing reverse tab-nabbing. SSOStage is intentionally excluded —
it needs the window handle + intact opener for its origin-checked SSO
postMessage handshake.

SEC-4: guard the `/acl` slash command against bricking the room.
- Extract the ACL glob helpers (isValidServerPattern/globToRegExp/matchesAnyGlob)
  from RoomServerACL into a shared utils/serverAcl.ts (+ unit test) so the
  command and the settings editor validate identically.
- Default a MISSING allow list to `*` only when the room has NO existing ACL
  (a first `/acl -d x` otherwise sent `allow: []`, which bricks the room); an
  existing ACL's absent/empty allow is preserved, not silently widened.
- Reject invalid globs; fail CLOSED on the universally-catastrophic cases
  (empty allow, or a `*` deny) even when the local domain is unknown; and reject
  any change that would ban this homeserver (self-lockout).

Guard hardened per two review passes (fail-closed on unknown domain; no silent
federation widening).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 21:23:35 -04:00
co-authored by Claude Opus 4.8
parent 1b8f554584
commit 3e1106b2d9
8 changed files with 149 additions and 54 deletions
@@ -114,7 +114,7 @@ export function ServerChip({ server }: { server: string }) {
size="300"
radii="300"
onClick={() => {
window.open(`https://${server}`, '_blank');
window.open(`https://${server}`, '_blank', 'noopener,noreferrer');
close();
}}
>