3 Commits
Author SHA1 Message Date
Paul LütznerandClaude Opus 5.5 75750e3ebc fix(security): publish every Mercure update as private
All notifiers published public updates, which the hub delivers to any
subscriber that names the topic, so the per-user topic grant in the
subscriber cookie protected nothing: a signed-in user could read another
user's mail/user/<id> stream. Updates now go through UserUpdate::create(),
which always sets private; the cookie already grants exactly that topic.
Corrects CODE-REVIEW.md S-06, which claimed the isolation held.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:36:59 +02:00
Paul LütznerandClaude Opus 5 d3e4eced03 fix: three corrections the suite caught, one of them a recipient bug
**The shared dropdown escape was too broad.** Sending every ui--dropdown menu to
the top layer fixed the ones trapped in a pane and broke the ones deliberately
anchored to the box they live in — the compose window's emoji picker is centred
ON the window and asserted to stay inside it. It is opt-in now
(`data-ui--dropdown-escapes-value`), and the snooze menu on a mail row asks for
it because it opens inside the mail pane.

**The recipient dropdown is NOT reparented to <body>, and that is now written
down.** `dropdownParent: 'body'` fixes the reported symptom in one line — the
compose window grows to make room for the suggestion panel instead of the panel
overlaying it — and it reintroduces something worse. Subject sits directly under
To, so an overlaid panel covers it, and a click aimed at Subject lands on
whichever suggestion is under the pointer and enrols it. That is a real report:
it silently added a third party to a message addressed to someone else, and
three specs in compose-recipients.spec.ts exist to keep it fixed. They caught
this.

So the panel stays in flow and the window growing is the price, with the
reasoning in the code so the next person does not spend the same hour. The way
out is a layout change — the panel opening where it cannot cover the next field
— not a parent change.

**The session guard swallowed too much.** It cancelled every 401 and 403, not
just the redirect that would have swapped a login form into the shell, so the
compose dock's own "your session has expired, reload and sign in" — said in the
place the user just clicked — never rendered. Only the redirect is cancelled
now; a bare 401 travels on to whoever asked for it, who usually has something
more specific to say.

compose-safety's held-shape test sets its own setting and restores it in an
afterEach rather than an afterAll, so a failure there cannot leave the worker's
user on a setting the rest of the file does not expect.

Full Playwright suite: 563 passed, 0 failed. That is 563 rather than the 517 a
green run used to report — the exclusive project depends on the main one, so
every flake was also skipping those 19 specs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 14:39:14 +02:00
Paul LütznerandClaude Opus 5 7f80e1268c docs(review): track the code review's findings where the findings live
CODE-REVIEW.md arrives as a report and becomes a worklist, so it gets a status
table that says which findings are closed and by which commit — and a note on
S-01 recording the two things the recommended fix would have broken, because
that is the part a reader six months from now cannot recover from the diff.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 11:17:15 +02:00