fix(rooms): cap room description + wiki body length like chat messages (LC-153-WIKI-DESC-CAP) #287

Merged
longjacksonle merged 1 commit from fix/lc-153-wiki-desc-cap into main 2026-05-31 04:45:41 +02:00

Closes #283 (LC-153-WIKI-DESC-CAP). Room description and wiki bodies rendered through the markdown + LaTeX pipeline with no length bound - the only limit was Axum's ~2 MiB DefaultBodyLimit, vs the 16K-char cap chat messages get (LC-153). A privileged-but-untrusted room moderator could feed a multi-MiB body to the renderer on every save and to every viewer on every page load.

Fix

  • routes::room_info::patch_description and patch_wiki now reject a body over MAX_DOC_CHARS = 64_000 chars before any DB write or render. Counted as characters (not bytes), matching room::check_message_length, so multibyte (CJK / emoji) docs are not penalized. 64K is generous for a wiki page and ~32x below the framework body limit.
  • The check runs after the mod+ gate, so a non-moderator still gets 403, not 400.
  • Render is NOT offloaded: markdown::render is synchronous and called inline on the request thread everywhere (the chat post path at room.rs:1953/1962 included; only the one-time warm_syntect warm-up is spawn_blocking-offloaded). Bounding the input is the mitigation and matches the established chat convention, so this stays consistent with every other markdown surface.

Tests

over_long_wiki_rejected / over_long_description_rejected (64001 chars -> 400, nothing stored) and wiki_at_cap_accepted (64000 chars -> 200, stored; boundary is inclusive). 9/9 in routes_room_info, passing in both standalone and saas.

Docs

CLAUDE.md gains a "Markdown surfaces share a length cap" note (the M2 gotcha from the tech-debt sweep, folded into this ticket) so the next contributor adding a markdown-backed field caps it on the write handler.

No [operator-action]: input-validation hardening, no new env/config/operator step.

🤖 Generated with Claude Code

Closes #283 (LC-153-WIKI-DESC-CAP). Room description and wiki bodies rendered through the markdown + LaTeX pipeline with no length bound - the only limit was Axum's ~2 MiB `DefaultBodyLimit`, vs the 16K-char cap chat messages get (LC-153). A privileged-but-untrusted room moderator could feed a multi-MiB body to the renderer on every save and to every viewer on every page load. ## Fix - `routes::room_info::patch_description` and `patch_wiki` now reject a body over `MAX_DOC_CHARS = 64_000` chars before any DB write or render. Counted as characters (not bytes), matching `room::check_message_length`, so multibyte (CJK / emoji) docs are not penalized. 64K is generous for a wiki page and ~32x below the framework body limit. - The check runs after the mod+ gate, so a non-moderator still gets 403, not 400. - Render is NOT offloaded: `markdown::render` is synchronous and called inline on the request thread everywhere (the chat post path at `room.rs:1953/1962` included; only the one-time `warm_syntect` warm-up is `spawn_blocking`-offloaded). Bounding the input is the mitigation and matches the established chat convention, so this stays consistent with every other markdown surface. ## Tests `over_long_wiki_rejected` / `over_long_description_rejected` (64001 chars -> 400, nothing stored) and `wiki_at_cap_accepted` (64000 chars -> 200, stored; boundary is inclusive). 9/9 in `routes_room_info`, passing in both standalone and saas. ## Docs CLAUDE.md gains a "Markdown surfaces share a length cap" note (the M2 gotcha from the tech-debt sweep, folded into this ticket) so the next contributor adding a markdown-backed field caps it on the write handler. No `[operator-action]`: input-validation hardening, no new env/config/operator step. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(rooms): cap room description + wiki body length like chat messages (LC-153-WIKI-DESC-CAP, #283)
All checks were successful
check-secrets / TruffleHog (push) Successful in 3s
check-secrets / Nosey parker (pull_request) Successful in 4s
check-secrets / Kingfisher (push) Successful in 5s
check-secrets / Nosey parker (push) Successful in 5s
check-secrets / Kingfisher (pull_request) Successful in 5s
check-secrets / TruffleHog (pull_request) Successful in 6s
Create release / Create release from merged PR (pull_request) Has been skipped
Check / clippy + fmt + tests (pull_request) Successful in 3m15s
235de2b089
Room description and wiki bodies render through the same markdown + LaTeX pipeline as chat messages but had no length bound: the only limit was Axum's ~2 MiB DefaultBodyLimit. A privileged-but-untrusted room moderator could feed a multi-MiB body to the renderer on every save and to every viewer on every page load. Chat messages are capped at MAX_MESSAGE_CHARS = 16_000 (LC-153, routes::room::check_message_length); these two handlers had no equivalent.

`markdown::render` is synchronous and called inline on the request thread everywhere (the chat post path at room.rs included; only the one-time warm_syntect warm-up is spawn_blocking-offloaded), so the fix is to bound the INPUT, not offload the render - matching the established chat convention. routes::room_info::patch_description and patch_wiki now reject a body over MAX_DOC_CHARS = 64_000 chars (counted as chars, not bytes, so multibyte docs are not penalized; generous for a wiki page, ~32x below the framework body limit) before any DB write or render. The check runs after the mod+ gate, so a non-moderator still gets 403, not 400.

Tests: over-length wiki and description are rejected with 400 and nothing is stored; a body exactly at the 64_000 boundary is accepted (inclusive). Pass in both standalone and saas.

CLAUDE.md gains a "Markdown surfaces share a length cap" note (the M2 gotcha folded into this ticket) so the next contributor adding a markdown-backed field caps it on the write handler instead of relying on the framework body limit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch fix/lc-153-wiki-desc-cap 2026-05-31 04:45:41 +02:00
Sign in to join this conversation.
No reviewers
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!287
No description provided.