feat: Limit comment length

Limit the amount of characters that can be entered for a comment.

Signed-off-by: moson <moson@archlinux.org>
This commit is contained in:
moson 2024-02-25 10:46:47 +01:00
parent d050b626db
commit 21a23c9abe
No known key found for this signature in database
GPG key ID: 4A4760AB4EE15296
14 changed files with 128 additions and 14 deletions

View file

@ -1,6 +1,9 @@
from http import HTTPStatus
from typing import Any
from aurweb import db
from fastapi import HTTPException
from aurweb import config, db
from aurweb.exceptions import ValidationError
from aurweb.models import PackageBase
@ -12,8 +15,8 @@ def request(
merge_into: str,
context: dict[str, Any],
) -> None:
if not comments:
raise ValidationError(["The comment field must not be empty."])
# validate comment
comment(comments)
if type == "merge":
# Perform merge-related checks.
@ -32,3 +35,21 @@ def request(
if target.ID == pkgbase.ID:
# TODO: This error needs to be translated.
raise ValidationError(["You cannot merge a package base into itself."])
def comment(comment: str):
if not comment:
raise ValidationError(["The comment field must not be empty."])
if len(comment) > config.getint("options", "max_chars_comment", 5000):
raise ValidationError(["Maximum number of characters for comment exceeded."])
def comment_raise_http_ex(comments: str):
try:
comment(comments)
except ValidationError as err:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=err.data[0],
)

View file

@ -159,6 +159,8 @@ async def pkgbase_flag_post(
request, "pkgbase/flag.html", context, status_code=HTTPStatus.BAD_REQUEST
)
validate.comment_raise_http_ex(comments)
has_cred = request.user.has_credential(creds.PKGBASE_FLAG)
if has_cred and not pkgbase.OutOfDateTS:
now = time.utcnow()
@ -185,8 +187,7 @@ async def pkgbase_comments_post(
"""Add a new comment via POST request."""
pkgbase = get_pkg_or_base(name, PackageBase)
if not comment:
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST)
validate.comment_raise_http_ex(comment)
# If the provided comment is different than the record's version,
# update the db record.
@ -304,9 +305,9 @@ async def pkgbase_comment_post(
pkgbase = get_pkg_or_base(name, PackageBase)
db_comment = get_pkgbase_comment(pkgbase, id)
if not comment:
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST)
elif request.user.ID != db_comment.UsersID:
validate.comment_raise_http_ex(comment)
if request.user.ID != db_comment.UsersID:
raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED)
# If the provided comment is different than the record's version,
@ -602,6 +603,9 @@ async def pkgbase_disown_post(
):
pkgbase = get_pkg_or_base(name, PackageBase)
if comments:
validate.comment_raise_http_ex(comments)
comaints = {c.User for c in pkgbase.comaintainers}
approved = [pkgbase.Maintainer] + list(comaints)
has_cred = request.user.has_credential(creds.PKGBASE_DISOWN, approved=approved)
@ -873,6 +877,7 @@ async def pkgbase_delete_post(
)
if comments:
validate.comment_raise_http_ex(comments)
# Update any existing deletion requests' ClosureComment.
with db.begin():
requests = pkgbase.requests.filter(
@ -966,6 +971,9 @@ async def pkgbase_merge_post(
request, "pkgbase/merge.html", context, status_code=HTTPStatus.BAD_REQUEST
)
if comments:
validate.comment_raise_http_ex(comments)
with db.begin():
update_closure_comment(pkgbase, MERGE_ID, comments, target=target)

View file

@ -70,6 +70,7 @@ def make_context(request: Request, title: str, next: str = None):
commit_url = aurweb.config.get_with_fallback("devel", "commit_url", None)
commit_hash = aurweb.config.get_with_fallback("devel", "commit_hash", None)
max_chars_comment = aurweb.config.getint("options", "max_chars_comment", 5000)
if commit_hash:
# Shorten commit_hash to a short Git hash.
commit_hash = commit_hash[:7]
@ -92,6 +93,7 @@ def make_context(request: Request, title: str, next: str = None):
"creds": aurweb.auth.creds,
"next": next if next else request.url.path,
"version": os.environ.get("COMMIT_HASH", aurweb.config.AURWEB_VERSION),
"max_chars_comment": max_chars_comment,
}

View file

@ -49,6 +49,8 @@ salt_rounds = 12
redis_address = redis://localhost
; Toggles traceback display in templates/errors/500.html.
traceback = 0
; Maximum number of characters for a comment
max_chars_comment = 5000
[ratelimit]
request_limit = 4000

View file

@ -2371,3 +2371,7 @@ msgid "Note that if you hide your email address, it'll "
"receive an email. However, replies are typically sent to the "
"mailing-list and would then be visible in the archive."
msgstr ""
#: templates/partials/packages/comment_form.html
msgid "Maximum number of characters"
msgstr ""

View file

@ -21,12 +21,15 @@ Routes:
| format('<a href="https://daringfireball.net/projects/markdown/syntax">',
"</a>")
| safe }}
<br/>
{{ "Maximum number of characters" | tr }}: {{ max_chars_comment }}.
</p>
<p>
<textarea id="id_comment"
name="comment"
cols="80"
rows="10"
maxlength="{{ max_chars_comment }}"
>{% if comment %}{{ comment.Comments or '' }}{% endif %}</textarea>
</p>
<p>

View file

@ -24,13 +24,17 @@
"</a>"
) | safe
}}
<br/>
{{ "Maximum number of characters" | tr }}: {{ max_chars_comment }}.
</p>
<p>
<textarea id="id_comment"
name="comment"
cols="80"
rows="10">{{ comment.Comments }}</textarea>
rows="10"
maxlength="{{ max_chars_comment }}"
>{{ comment.Comments }}</textarea>
</p>
<p>

View file

@ -50,7 +50,7 @@
<p>
<label for="id_comments">{{ "Comments" | tr }}:</label>
<textarea id="id_comments" name="comments"
rows="5" cols="50"
rows="5" cols="50" maxlength="{{ max_chars_comment }}"
placeholder="Related package request closure comments..."
></textarea>
</p>

View file

@ -55,6 +55,7 @@
<textarea id="id_comments"
name="comments"
rows="5" cols="50"
maxlength="{{ max_chars_comment }}"
placeholder="Related package request closure comments..."
></textarea>
</p>

View file

@ -60,7 +60,8 @@
<textarea id="id_comments"
name="comments"
rows="5"
cols="50"></textarea>
cols="50"
maxlength="{{ max_chars_comment }}"></textarea>
</p>
<p>
<input class="button" type="submit" value="{{ 'Flag' | tr }}" />

View file

@ -52,6 +52,7 @@
<textarea id="id_comments"
name="comments"
rows="5" cols="50"
maxlength="{{ max_chars_comment }}"
placeholder="Related package request closure comments..."
></textarea>
</p>

View file

@ -64,7 +64,10 @@
<p>
<label for="id_comments">{{ "Comments" | tr }}:</label>
<textarea id="id_comments" name="comments"
rows="5" cols="50">{{ comments or '' }}</textarea>
rows="5" cols="50"
maxlength="{{ max_chars_comment }}"
>{{ comments or '' }}
</textarea>
</p>
<p id="deletion_hint">

View file

@ -26,7 +26,8 @@
<p>
<label for="id_comments">{{ "Comments" | tr }}:</label>
<textarea id="id_comments" name="comments"
rows="5" cols="50"></textarea>
rows="5" cols="50" maxlength="{{ max_chars_comment }}"
></textarea>
</p>
<p>

View file

@ -6,7 +6,7 @@ import pytest
from fastapi.testclient import TestClient
from sqlalchemy import and_
from aurweb import asgi, db, time
from aurweb import asgi, config, db, time
from aurweb.models.account_type import USER_ID, AccountType
from aurweb.models.dependency_type import DependencyType
from aurweb.models.package import Package
@ -25,6 +25,8 @@ from aurweb.testing.email import Email
from aurweb.testing.html import get_errors, get_successes, parse_root
from aurweb.testing.requests import Request
max_chars_comment = config.getint("options", "max_chars_comment", 5000)
def package_endpoint(package: Package) -> str:
return f"/packages/{package.Name}"
@ -572,6 +574,38 @@ def test_pkgbase_comments(
assert "form" in data
def test_pkgbase_comment_exceed_character_limit(
client: TestClient,
user: User,
package: Package,
comment: PackageComment,
):
# Post new comment
cookies = {"AURSID": user.login(Request(), "testPassword")}
pkgbasename = package.PackageBase.Name
endpoint = f"/pkgbase/{pkgbasename}/comments"
with client as request:
request.cookies = cookies
resp = request.post(
endpoint,
data={"comment": "x" * (max_chars_comment + 1)},
)
assert resp.status_code == int(HTTPStatus.BAD_REQUEST)
assert "Maximum number of characters for comment exceeded." in resp.text
# Edit existing
cookies = {"AURSID": user.login(Request(), "testPassword")}
with client as request:
request.cookies = cookies
endp = f"/pkgbase/{pkgbasename}/comments/{comment.ID}"
response = request.post(
endp,
data={"comment": "x" * (max_chars_comment + 1)},
)
assert response.status_code == HTTPStatus.BAD_REQUEST
assert "Maximum number of characters for comment exceeded." in resp.text
def test_pkgbase_comment_edit_unauthorized(
client: TestClient,
user: User,
@ -935,6 +969,28 @@ def test_pkgbase_request_post_no_comment_error(
assert error.text.strip() == expected
def test_pkgbase_request_post_comment_exceed_character_limit(
client: TestClient, user: User, package: Package
):
endpoint = f"/pkgbase/{package.PackageBase.Name}/request"
cookies = {"AURSID": user.login(Request(), "testPassword")}
with client as request:
request.cookies = cookies
resp = request.post(
endpoint,
data={
"type": "deletion",
"comments": "x" * (max_chars_comment + 1),
},
)
assert resp.status_code == int(HTTPStatus.OK)
root = parse_root(resp.text)
error = root.xpath('//ul[@class="errorlist"]/li')[0]
expected = "Maximum number of characters for comment exceeded."
assert error.text.strip() == expected
def test_pkgbase_request_post_merge_not_found_error(
client: TestClient, user: User, package: Package
):
@ -1087,6 +1143,13 @@ def test_pkgbase_flag(
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
assert pkgbase.Flagger is None
# Try flagging with a comment that exceeds our character limit.
with client as request:
request.cookies = cookies
data = {"comments": "x" * (max_chars_comment + 1)}
resp = request.post(f"/pkgbase/{pkgbase.Name}/flag", data=data)
assert resp.status_code == int(HTTPStatus.BAD_REQUEST)
# Flag it again.
with client as request:
request.cookies = cookies