LC-153-WIKI-DESC-CAP: cap room description + wiki markdown body length (share the LC-153 cap) #283

Closed
opened 2026-05-31 04:38:00 +02:00 by longjacksonle · 0 comments

Problem

Room description and wiki bodies are rendered through the markdown + LaTeX pipeline with no length cap, unlike every other markdown surface. Chat messages are capped at MAX_MESSAGE_CHARS = 16_000 (server/src/routes/room.rs:24, enforced at :440/:1486/:1848 via check_message_length, the LC-153 cap); email-ingress bodies are capped at 64 KiB. The two room-doc handlers have no equivalent check, so the only bound is Axum's framework-default DefaultBodyLimit (~2 MiB) with no per-route override.

Where

  • server/src/routes/room_info.rs::patch_description (:400): takes form.body, stores it, renders via render_markdown_body. No check_message_length, no per-route body limit.
  • server/src/routes/room_info.rs::patch_wiki (:459): same shape.
  • Both are mod+-gated (require_can_edit_description / require_can_edit_wiki), so the actor is a room moderator, not an anonymous user.
  • Possible secondary issue: the chat-message markdown render is spawn_blocking-wrapped (per main.rs commentary); confirm whether these two room_info render sites are also off the async worker thread, or wrap them.

Failure mode / cost

A 2 MiB body fed to the markdown + pulldown-latex pipeline is a ~128x larger render unit than the 16K-char chat cap. A privileged-but-not-trusted room moderator can spike CPU or stall a worker thread on render. Independently, the inconsistent input contract invites the next markdown-surface author to copy the unbounded handler.

Proposed scope

  • Add a shared length cap to patch_description and patch_wiki (a MAX_DOC_CHARS constant, or reuse/relax check_message_length if 16K is acceptable for docs; pick a doc-appropriate bound).
  • Confirm the render runs on a blocking thread (spawn_blocking / block_in_place); wrap if not.
  • Test: an over-cap description/wiki POST is rejected (same shape as the chat-message length test).
  • Fold the "every markdown surface must share the length cap" gotcha into CLAUDE.md (the M2 item from the sweep) so the rule outlives this ticket.

Acceptance

patch_description and patch_wiki reject over-length bodies and render on a blocking thread; a regression test covers it; CLAUDE.md records the markdown-surface cap convention.

Anchored to LC-153 (message length cap). Severity: correctness, low-privilege DoS, high confidence (the missing cap is confirmed by reading the handlers).

## Problem Room **description** and **wiki** bodies are rendered through the markdown + LaTeX pipeline with no length cap, unlike every other markdown surface. Chat messages are capped at `MAX_MESSAGE_CHARS = 16_000` (`server/src/routes/room.rs:24`, enforced at `:440/:1486/:1848` via `check_message_length`, the LC-153 cap); email-ingress bodies are capped at 64 KiB. The two room-doc handlers have no equivalent check, so the only bound is Axum's framework-default `DefaultBodyLimit` (~2 MiB) with no per-route override. ## Where - `server/src/routes/room_info.rs::patch_description` (`:400`): takes `form.body`, stores it, renders via `render_markdown_body`. No `check_message_length`, no per-route body limit. - `server/src/routes/room_info.rs::patch_wiki` (`:459`): same shape. - Both are mod+-gated (`require_can_edit_description` / `require_can_edit_wiki`), so the actor is a room moderator, not an anonymous user. - Possible secondary issue: the chat-message markdown render is `spawn_blocking`-wrapped (per `main.rs` commentary); confirm whether these two `room_info` render sites are also off the async worker thread, or wrap them. ## Failure mode / cost A 2 MiB body fed to the markdown + pulldown-latex pipeline is a ~128x larger render unit than the 16K-char chat cap. A privileged-but-not-trusted room moderator can spike CPU or stall a worker thread on render. Independently, the inconsistent input contract invites the next markdown-surface author to copy the unbounded handler. ## Proposed scope - Add a shared length cap to `patch_description` and `patch_wiki` (a `MAX_DOC_CHARS` constant, or reuse/relax `check_message_length` if 16K is acceptable for docs; pick a doc-appropriate bound). - Confirm the render runs on a blocking thread (`spawn_blocking` / `block_in_place`); wrap if not. - Test: an over-cap description/wiki POST is rejected (same shape as the chat-message length test). - Fold the "every markdown surface must share the length cap" gotcha into CLAUDE.md (the M2 item from the sweep) so the rule outlives this ticket. ## Acceptance `patch_description` and `patch_wiki` reject over-length bodies and render on a blocking thread; a regression test covers it; CLAUDE.md records the markdown-surface cap convention. Anchored to LC-153 (message length cap). Severity: correctness, low-privilege DoS, high confidence (the missing cap is confirmed by reading the handlers).
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat#283
No description provided.