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>
9 files changed,
+30 insertions,
-11 deletions
| 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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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>
|