max title (200) and body (10k) length limits, fix unescaped body in edit form

This commit is contained in:
user 2026-06-05 01:04:17 +00:00
parent d86349292a
commit a1af7eb64f

View file

@ -5,7 +5,8 @@ from datetime import datetime
from urllib.parse import unquote from urllib.parse import unquote
PER_PAGE = 20 MAX_TITLE_LENGTH = 200
MAX_BODY_LENGTH = 10000
RECENT_SECONDS = 86400 * 7 # "new" = within last 7 days RECENT_SECONDS = 86400 * 7 # "new" = within last 7 days
@ -222,9 +223,11 @@ class ForumHandlers:
f"<h1>new thread</h1>" f"<h1>new thread</h1>"
f'<form method="post" action="/forum/new">' f'<form method="post" action="/forum/new">'
f'{self._csrf_field()}' f'{self._csrf_field()}'
f'<input name="title" placeholder="title" size="50" required><br><br>' f'<input name="title" placeholder="title" size="50" required><br>'
f"<small>max {MAX_TITLE_LENGTH} characters</small><br><br>"
f'<input name="url" placeholder="URL you want to share (optional)" size="50"><br><br>' f'<input name="url" placeholder="URL you want to share (optional)" size="50"><br><br>'
f'<textarea name="body" rows="6" cols="50" placeholder="details or context (optional)"></textarea><br><br>' f'<textarea name="body" rows="6" cols="50" placeholder="details or context (optional)"></textarea><br>'
f"<small>max {MAX_BODY_LENGTH} characters</small><br><br>"
f'<input name="tags" placeholder="tags, comma-separated (optional)" size="50"><br><br>' f'<input name="tags" placeholder="tags, comma-separated (optional)" size="50"><br><br>'
f'<button type="submit">post</button>' f'<button type="submit">post</button>'
f"</form>" f"</form>"
@ -239,6 +242,10 @@ class ForumHandlers:
tags = body.get("tags", [""])[0].strip() tags = body.get("tags", [""])[0].strip()
if not title: if not title:
return self.handle_new_form("Title is required.") return self.handle_new_form("Title is required.")
if len(title) > MAX_TITLE_LENGTH:
return self.handle_new_form(f"Title too long (max {MAX_TITLE_LENGTH} characters).")
if len(body_text) > MAX_BODY_LENGTH:
return self.handle_new_form(f"Body too long (max {MAX_BODY_LENGTH} characters).")
thread_id = secrets.token_hex(16) thread_id = secrets.token_hex(16)
author_instance = self.identity.hash.hex() if self.identity else "local" author_instance = self.identity.hash.hex() if self.identity else "local"
author_name = self.site_name author_name = self.site_name
@ -308,7 +315,8 @@ class ForumHandlers:
reply_form = ( reply_form = (
f'<form method="post" action="/forum/t/{thread["id"]}/reply">' f'<form method="post" action="/forum/t/{thread["id"]}/reply">'
f'{self._csrf_field()}' f'{self._csrf_field()}'
f'<textarea name="body" rows="4" cols="50" placeholder="share a URL or reply..." required></textarea><br><br>' f'<textarea name="body" rows="4" cols="50" placeholder="share a URL or reply..." required></textarea><br>'
f"<small>max {MAX_BODY_LENGTH} characters</small><br><br>"
f'<button type="submit">reply</button>' f'<button type="submit">reply</button>'
f"</form>" f"</form>"
) )
@ -366,9 +374,11 @@ class ForumHandlers:
f"<h1>edit thread</h1>" f"<h1>edit thread</h1>"
f'<form method="post" action="/forum/t/{thread_id}/edit">' f'<form method="post" action="/forum/t/{thread_id}/edit">'
f'{self._csrf_field()}' f'{self._csrf_field()}'
f'<input name="title" value="{esc(thread["title"])}" size="50" required><br><br>' f'<input name="title" value="{esc(thread["title"])}" size="50" required><br>'
f"<small>max {MAX_TITLE_LENGTH} characters</small><br><br>"
f'<input name="url" value="{esc(thread["url"] or "")}" placeholder="URL" size="50"><br><br>' f'<input name="url" value="{esc(thread["url"] or "")}" placeholder="URL" size="50"><br><br>'
f'<textarea name="body" rows="6" cols="50" placeholder="details or context">{(thread["body"] or "")}</textarea><br><br>' f'<textarea name="body" rows="6" cols="50" placeholder="details or context">{esc(thread["body"] or "")}</textarea><br>'
f"<small>max {MAX_BODY_LENGTH} characters</small><br><br>"
f'<input name="tags" value="{esc(thread["tags"] or "")}" placeholder="tags, comma-separated" size="50"><br><br>' f'<input name="tags" value="{esc(thread["tags"] or "")}" placeholder="tags, comma-separated" size="50"><br><br>'
f'<button type="submit">save</button>' f'<button type="submit">save</button>'
f"</form>" f"</form>"
@ -386,8 +396,12 @@ class ForumHandlers:
title = body.get("title", [""])[0].strip() title = body.get("title", [""])[0].strip()
if not title: if not title:
return self.handle_edit_form(thread_id, "Title is required.") return self.handle_edit_form(thread_id, "Title is required.")
if len(title) > MAX_TITLE_LENGTH:
return self.handle_edit_form(thread_id, f"Title too long (max {MAX_TITLE_LENGTH} characters).")
url = body.get("url", [""])[0].strip() url = body.get("url", [""])[0].strip()
body_text = body.get("body", [""])[0].strip() body_text = body.get("body", [""])[0].strip()
if len(body_text) > MAX_BODY_LENGTH:
return self.handle_edit_form(thread_id, f"Body too long (max {MAX_BODY_LENGTH} characters).")
tags = body.get("tags", [""])[0].strip() tags = body.get("tags", [""])[0].strip()
now = self._now() now = self._now()
self.fdb.update_thread(thread_id, title, url, body_text, tags, now) self.fdb.update_thread(thread_id, title, url, body_text, tags, now)
@ -397,6 +411,8 @@ class ForumHandlers:
body_text = body.get("body", [""])[0].strip() body_text = body.get("body", [""])[0].strip()
if not body_text: if not body_text:
return self._redirect(f"/forum/t/{thread_id}") return self._redirect(f"/forum/t/{thread_id}")
if len(body_text) > MAX_BODY_LENGTH:
return self._respond(f"<p>Body too long (max {MAX_BODY_LENGTH} characters). <a href=\"/forum/t/{esc(thread_id)}\">back</a></p>")
parent_id = body.get("parent_id", [""])[0].strip() parent_id = body.get("parent_id", [""])[0].strip()
author_instance = self.identity.hash.hex() if self.identity else "local" author_instance = self.identity.hash.hex() if self.identity else "local"
author_name = self.site_name author_name = self.site_name