Skip to main content

max / multithreaded

Add input length limits and graceful shutdown for session cleanup Client-side maxlength on all inputs/textareas (12 fields across 6 templates). Server-side limits for flag detail and ban/mute reason (1024 bytes). Await deletion_task after abort for clean shutdown. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-03-22 05:59 UTC
Commit: 39362253d0d3bc55a47452789d31372809e0e9a9
Parent: 5f0d45b
9 files changed, +30 insertions, -11 deletions
@@ -126,6 +126,7 @@ async fn main() {
126 126 .expect("server error");
127 127
128 128 deletion_task.abort();
129 + let _ = deletion_task.await;
129 130 }
130 131
131 132 async fn shutdown_signal() {
@@ -61,6 +61,12 @@ pub(super) async fn flag_post_handler(
61 61
62 62 let detail = form.detail.as_deref().filter(|d| !d.trim().is_empty());
63 63
64 + if let Some(d) = detail {
65 + if d.len() > 1024 {
66 + return Err((StatusCode::UNPROCESSABLE_ENTITY, "Flag detail too long (max 1024 bytes).").into_response());
67 + }
68 + }
69 +
64 70 mt_db::mutations::insert_flag(&state.db, post_id, user.user_id, &form.reason, detail)
65 71 .await
66 72 .map_err(|e| {
@@ -247,6 +247,12 @@ pub(super) async fn ban_user_handler(
247 247 let expires_at = parse_duration(&form.duration);
248 248 let reason = form.reason.as_deref().filter(|r| !r.trim().is_empty());
249 249
250 + if let Some(r) = reason {
251 + if r.len() > 1024 {
252 + return Err((StatusCode::UNPROCESSABLE_ENTITY, "Reason too long (max 1024 bytes).").into_response());
253 + }
254 + }
255 +
250 256 mt_db::mutations::create_community_ban(
251 257 &state.db, community.id, target_id, user.user_id,
252 258 BanType::Ban, reason, expires_at,
@@ -326,6 +332,12 @@ pub(super) async fn mute_user_handler(
326 332 let expires_at = parse_duration(&form.duration);
327 333 let reason = form.reason.as_deref().filter(|r| !r.trim().is_empty());
328 334
335 + if let Some(r) = reason {
336 + if r.len() > 1024 {
337 + return Err((StatusCode::UNPROCESSABLE_ENTITY, "Reason too long (max 1024 bytes).").into_response());
338 + }
339 + }
340 +
329 341 mt_db::mutations::create_community_ban(
330 342 &state.db, community.id, target_id, user.user_id,
331 343 BanType::Mute, reason, expires_at,
@@ -27,7 +27,7 @@
27 27 </div>
28 28 <div class="form-group">
29 29 <label for="description">Description</label>
30 - <textarea id="description" name="description" class="textarea-medium">{{ community_description.as_deref().unwrap_or("") }}</textarea>
30 + <textarea id="description" name="description" class="textarea-medium" maxlength="2048">{{ community_description.as_deref().unwrap_or("") }}</textarea>
31 31 </div>
32 32 <div class="form-group">
33 33 <label for="auto_hide_threshold">Auto-hide flag threshold</label>
@@ -102,7 +102,7 @@
102 102 </div>
103 103 <div class="form-group">
104 104 <label for="cat-description">Description (optional)</label>
105 - <textarea id="cat-description" name="description" class="textarea-short"></textarea>
105 + <textarea id="cat-description" name="description" class="textarea-short" maxlength="1024"></textarea>
106 106 </div>
107 107 <div class="form-actions">
108 108 <button type="submit" class="primary">Add Category</button>
@@ -26,7 +26,7 @@
26 26 </div>
27 27 <div class="form-group">
28 28 <label for="description">Description</label>
29 - <textarea id="description" name="description" class="textarea-medium">{{ category_description.as_deref().unwrap_or("") }}</textarea>
29 + <textarea id="description" name="description" class="textarea-medium" maxlength="1024">{{ category_description.as_deref().unwrap_or("") }}</textarea>
30 30 </div>
31 31 <div class="form-actions">
32 32 <button type="submit" class="primary">Save</button>
@@ -24,7 +24,7 @@
24 24 <form method="post" action="/p/{{ community_slug }}/{{ category_slug }}/{{ thread_id }}/edit">
25 25 <div class="form-group">
26 26 <label for="title">Title</label>
27 - <input type="text" id="title" name="title" value="{{ current_title }}" required>
27 + <input type="text" id="title" name="title" value="{{ current_title }}" required maxlength="256">
28 28 </div>
29 29 <div class="form-actions">
30 30 <button type="submit" class="primary">Save</button>
@@ -41,7 +41,7 @@
41 41 </div>
42 42 <div class="form-group">
43 43 <label for="ban-reason">Reason (optional)</label>
44 - <textarea id="ban-reason" name="reason" rows="2" class="textarea-short"></textarea>
44 + <textarea id="ban-reason" name="reason" rows="2" class="textarea-short" maxlength="1024"></textarea>
45 45 </div>
46 46 <div class="form-actions">
47 47 <button type="submit" class="primary">Ban User</button>
@@ -70,7 +70,7 @@
70 70 </div>
71 71 <div class="form-group">
72 72 <label for="mute-reason">Reason (optional)</label>
73 - <textarea id="mute-reason" name="reason" rows="2" class="textarea-short"></textarea>
73 + <textarea id="mute-reason" name="reason" rows="2" class="textarea-short" maxlength="1024"></textarea>
74 74 </div>
75 75 <div class="form-actions">
76 76 <button type="submit" class="primary">Mute User</button>
@@ -22,11 +22,11 @@
22 22 <form method="post" action="/p/{{ community_slug }}/{{ category_slug }}/new">
23 23 <div class="form-group">
24 24 <label for="title">Title</label>
25 - <input type="text" id="title" name="title" placeholder="Thread title" required>
25 + <input type="text" id="title" name="title" placeholder="Thread title" required maxlength="256">
26 26 </div>
27 27 <div class="form-group">
28 28 <label for="body">Body (Markdown supported)</label>
29 - <textarea id="body" name="body" placeholder="Write your post..." required></textarea>
29 + <textarea id="body" name="body" placeholder="Write your post..." required maxlength="65536"></textarea>
30 30 </div>
31 31 {% if !available_tags.is_empty() %}
32 32 <fieldset class="form-group tag-fieldset">
@@ -80,7 +80,7 @@
80 80 <label><input type="radio" name="reason" value="spam" required> Spam</label>
81 81 <label><input type="radio" name="reason" value="rule_breaking"> Rule-breaking</label>
82 82 <label><input type="radio" name="reason" value="off_topic"> Off-topic</label>
83 - <textarea name="detail" placeholder="Optional details..." class="textarea-short"></textarea>
83 + <textarea name="detail" placeholder="Optional details..." class="textarea-short" maxlength="1024"></textarea>
84 84 <button type="submit" class="primary flag-btn">Submit Flag</button>
85 85 </form>
86 86 </details>
@@ -122,7 +122,7 @@
122 122 <details class="footnote-form-toggle">
123 123 <summary class="post-action-link">add footnote</summary>
124 124 <form method="post" action="/p/{{ community_slug }}/{{ category_slug }}/{{ thread_id }}/posts/{{ post.id }}/footnote">
125 - <textarea name="body" placeholder="Add a correction or clarification..." required></textarea>
125 + <textarea name="body" placeholder="Add a correction or clarification..." required maxlength="65536"></textarea>
126 126 <button type="submit" class="primary footnote-btn">Add Footnote</button>
127 127 </form>
128 128 </details>
@@ -140,7 +140,7 @@
140 140 <form method="post" action="/p/{{ community_slug }}/{{ category_slug }}/{{ thread_id }}/reply">
141 141 <div class="form-group">
142 142 <label for="reply-body">Your reply (Markdown supported)</label>
143 - <textarea id="reply-body" name="body" placeholder="Write your reply..." required></textarea>
143 + <textarea id="reply-body" name="body" placeholder="Write your reply..." required maxlength="65536"></textarea>
144 144 </div>
145 145 <div class="form-actions">
146 146 <button type="submit" class="primary">Post Reply</button>