mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
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:
parent
d050b626db
commit
21a23c9abe
14 changed files with 128 additions and 14 deletions
|
@ -1,6 +1,9 @@
|
||||||
|
from http import HTTPStatus
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aurweb import db
|
from fastapi import HTTPException
|
||||||
|
|
||||||
|
from aurweb import config, db
|
||||||
from aurweb.exceptions import ValidationError
|
from aurweb.exceptions import ValidationError
|
||||||
from aurweb.models import PackageBase
|
from aurweb.models import PackageBase
|
||||||
|
|
||||||
|
@ -12,8 +15,8 @@ def request(
|
||||||
merge_into: str,
|
merge_into: str,
|
||||||
context: dict[str, Any],
|
context: dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
if not comments:
|
# validate comment
|
||||||
raise ValidationError(["The comment field must not be empty."])
|
comment(comments)
|
||||||
|
|
||||||
if type == "merge":
|
if type == "merge":
|
||||||
# Perform merge-related checks.
|
# Perform merge-related checks.
|
||||||
|
@ -32,3 +35,21 @@ def request(
|
||||||
if target.ID == pkgbase.ID:
|
if target.ID == pkgbase.ID:
|
||||||
# TODO: This error needs to be translated.
|
# TODO: This error needs to be translated.
|
||||||
raise ValidationError(["You cannot merge a package base into itself."])
|
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],
|
||||||
|
)
|
||||||
|
|
|
@ -159,6 +159,8 @@ async def pkgbase_flag_post(
|
||||||
request, "pkgbase/flag.html", context, status_code=HTTPStatus.BAD_REQUEST
|
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)
|
has_cred = request.user.has_credential(creds.PKGBASE_FLAG)
|
||||||
if has_cred and not pkgbase.OutOfDateTS:
|
if has_cred and not pkgbase.OutOfDateTS:
|
||||||
now = time.utcnow()
|
now = time.utcnow()
|
||||||
|
@ -185,8 +187,7 @@ async def pkgbase_comments_post(
|
||||||
"""Add a new comment via POST request."""
|
"""Add a new comment via POST request."""
|
||||||
pkgbase = get_pkg_or_base(name, PackageBase)
|
pkgbase = get_pkg_or_base(name, PackageBase)
|
||||||
|
|
||||||
if not comment:
|
validate.comment_raise_http_ex(comment)
|
||||||
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST)
|
|
||||||
|
|
||||||
# If the provided comment is different than the record's version,
|
# If the provided comment is different than the record's version,
|
||||||
# update the db record.
|
# update the db record.
|
||||||
|
@ -304,9 +305,9 @@ async def pkgbase_comment_post(
|
||||||
pkgbase = get_pkg_or_base(name, PackageBase)
|
pkgbase = get_pkg_or_base(name, PackageBase)
|
||||||
db_comment = get_pkgbase_comment(pkgbase, id)
|
db_comment = get_pkgbase_comment(pkgbase, id)
|
||||||
|
|
||||||
if not comment:
|
validate.comment_raise_http_ex(comment)
|
||||||
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST)
|
|
||||||
elif request.user.ID != db_comment.UsersID:
|
if request.user.ID != db_comment.UsersID:
|
||||||
raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED)
|
raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED)
|
||||||
|
|
||||||
# If the provided comment is different than the record's version,
|
# 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)
|
pkgbase = get_pkg_or_base(name, PackageBase)
|
||||||
|
|
||||||
|
if comments:
|
||||||
|
validate.comment_raise_http_ex(comments)
|
||||||
|
|
||||||
comaints = {c.User for c in pkgbase.comaintainers}
|
comaints = {c.User for c in pkgbase.comaintainers}
|
||||||
approved = [pkgbase.Maintainer] + list(comaints)
|
approved = [pkgbase.Maintainer] + list(comaints)
|
||||||
has_cred = request.user.has_credential(creds.PKGBASE_DISOWN, approved=approved)
|
has_cred = request.user.has_credential(creds.PKGBASE_DISOWN, approved=approved)
|
||||||
|
@ -873,6 +877,7 @@ async def pkgbase_delete_post(
|
||||||
)
|
)
|
||||||
|
|
||||||
if comments:
|
if comments:
|
||||||
|
validate.comment_raise_http_ex(comments)
|
||||||
# Update any existing deletion requests' ClosureComment.
|
# Update any existing deletion requests' ClosureComment.
|
||||||
with db.begin():
|
with db.begin():
|
||||||
requests = pkgbase.requests.filter(
|
requests = pkgbase.requests.filter(
|
||||||
|
@ -966,6 +971,9 @@ async def pkgbase_merge_post(
|
||||||
request, "pkgbase/merge.html", context, status_code=HTTPStatus.BAD_REQUEST
|
request, "pkgbase/merge.html", context, status_code=HTTPStatus.BAD_REQUEST
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if comments:
|
||||||
|
validate.comment_raise_http_ex(comments)
|
||||||
|
|
||||||
with db.begin():
|
with db.begin():
|
||||||
update_closure_comment(pkgbase, MERGE_ID, comments, target=target)
|
update_closure_comment(pkgbase, MERGE_ID, comments, target=target)
|
||||||
|
|
||||||
|
|
|
@ -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_url = aurweb.config.get_with_fallback("devel", "commit_url", None)
|
||||||
commit_hash = aurweb.config.get_with_fallback("devel", "commit_hash", 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:
|
if commit_hash:
|
||||||
# Shorten commit_hash to a short Git hash.
|
# Shorten commit_hash to a short Git hash.
|
||||||
commit_hash = commit_hash[:7]
|
commit_hash = commit_hash[:7]
|
||||||
|
@ -92,6 +93,7 @@ def make_context(request: Request, title: str, next: str = None):
|
||||||
"creds": aurweb.auth.creds,
|
"creds": aurweb.auth.creds,
|
||||||
"next": next if next else request.url.path,
|
"next": next if next else request.url.path,
|
||||||
"version": os.environ.get("COMMIT_HASH", aurweb.config.AURWEB_VERSION),
|
"version": os.environ.get("COMMIT_HASH", aurweb.config.AURWEB_VERSION),
|
||||||
|
"max_chars_comment": max_chars_comment,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,8 @@ salt_rounds = 12
|
||||||
redis_address = redis://localhost
|
redis_address = redis://localhost
|
||||||
; Toggles traceback display in templates/errors/500.html.
|
; Toggles traceback display in templates/errors/500.html.
|
||||||
traceback = 0
|
traceback = 0
|
||||||
|
; Maximum number of characters for a comment
|
||||||
|
max_chars_comment = 5000
|
||||||
|
|
||||||
[ratelimit]
|
[ratelimit]
|
||||||
request_limit = 4000
|
request_limit = 4000
|
||||||
|
|
|
@ -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 "
|
"receive an email. However, replies are typically sent to the "
|
||||||
"mailing-list and would then be visible in the archive."
|
"mailing-list and would then be visible in the archive."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/partials/packages/comment_form.html
|
||||||
|
msgid "Maximum number of characters"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -21,12 +21,15 @@ Routes:
|
||||||
| format('<a href="https://daringfireball.net/projects/markdown/syntax">',
|
| format('<a href="https://daringfireball.net/projects/markdown/syntax">',
|
||||||
"</a>")
|
"</a>")
|
||||||
| safe }}
|
| safe }}
|
||||||
|
<br/>
|
||||||
|
{{ "Maximum number of characters" | tr }}: {{ max_chars_comment }}.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<textarea id="id_comment"
|
<textarea id="id_comment"
|
||||||
name="comment"
|
name="comment"
|
||||||
cols="80"
|
cols="80"
|
||||||
rows="10"
|
rows="10"
|
||||||
|
maxlength="{{ max_chars_comment }}"
|
||||||
>{% if comment %}{{ comment.Comments or '' }}{% endif %}</textarea>
|
>{% if comment %}{{ comment.Comments or '' }}{% endif %}</textarea>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -24,13 +24,17 @@
|
||||||
"</a>"
|
"</a>"
|
||||||
) | safe
|
) | safe
|
||||||
}}
|
}}
|
||||||
|
<br/>
|
||||||
|
{{ "Maximum number of characters" | tr }}: {{ max_chars_comment }}.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<textarea id="id_comment"
|
<textarea id="id_comment"
|
||||||
name="comment"
|
name="comment"
|
||||||
cols="80"
|
cols="80"
|
||||||
rows="10">{{ comment.Comments }}</textarea>
|
rows="10"
|
||||||
|
maxlength="{{ max_chars_comment }}"
|
||||||
|
>{{ comment.Comments }}</textarea>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
<p>
|
<p>
|
||||||
<label for="id_comments">{{ "Comments" | tr }}:</label>
|
<label for="id_comments">{{ "Comments" | tr }}:</label>
|
||||||
<textarea id="id_comments" name="comments"
|
<textarea id="id_comments" name="comments"
|
||||||
rows="5" cols="50"
|
rows="5" cols="50" maxlength="{{ max_chars_comment }}"
|
||||||
placeholder="Related package request closure comments..."
|
placeholder="Related package request closure comments..."
|
||||||
></textarea>
|
></textarea>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
<textarea id="id_comments"
|
<textarea id="id_comments"
|
||||||
name="comments"
|
name="comments"
|
||||||
rows="5" cols="50"
|
rows="5" cols="50"
|
||||||
|
maxlength="{{ max_chars_comment }}"
|
||||||
placeholder="Related package request closure comments..."
|
placeholder="Related package request closure comments..."
|
||||||
></textarea>
|
></textarea>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -60,7 +60,8 @@
|
||||||
<textarea id="id_comments"
|
<textarea id="id_comments"
|
||||||
name="comments"
|
name="comments"
|
||||||
rows="5"
|
rows="5"
|
||||||
cols="50"></textarea>
|
cols="50"
|
||||||
|
maxlength="{{ max_chars_comment }}"></textarea>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<input class="button" type="submit" value="{{ 'Flag' | tr }}" />
|
<input class="button" type="submit" value="{{ 'Flag' | tr }}" />
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
<textarea id="id_comments"
|
<textarea id="id_comments"
|
||||||
name="comments"
|
name="comments"
|
||||||
rows="5" cols="50"
|
rows="5" cols="50"
|
||||||
|
maxlength="{{ max_chars_comment }}"
|
||||||
placeholder="Related package request closure comments..."
|
placeholder="Related package request closure comments..."
|
||||||
></textarea>
|
></textarea>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -64,7 +64,10 @@
|
||||||
<p>
|
<p>
|
||||||
<label for="id_comments">{{ "Comments" | tr }}:</label>
|
<label for="id_comments">{{ "Comments" | tr }}:</label>
|
||||||
<textarea id="id_comments" name="comments"
|
<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>
|
||||||
|
|
||||||
<p id="deletion_hint">
|
<p id="deletion_hint">
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
<p>
|
<p>
|
||||||
<label for="id_comments">{{ "Comments" | tr }}:</label>
|
<label for="id_comments">{{ "Comments" | tr }}:</label>
|
||||||
<textarea id="id_comments" name="comments"
|
<textarea id="id_comments" name="comments"
|
||||||
rows="5" cols="50"></textarea>
|
rows="5" cols="50" maxlength="{{ max_chars_comment }}"
|
||||||
|
></textarea>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlalchemy import and_
|
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.account_type import USER_ID, AccountType
|
||||||
from aurweb.models.dependency_type import DependencyType
|
from aurweb.models.dependency_type import DependencyType
|
||||||
from aurweb.models.package import Package
|
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.html import get_errors, get_successes, parse_root
|
||||||
from aurweb.testing.requests import Request
|
from aurweb.testing.requests import Request
|
||||||
|
|
||||||
|
max_chars_comment = config.getint("options", "max_chars_comment", 5000)
|
||||||
|
|
||||||
|
|
||||||
def package_endpoint(package: Package) -> str:
|
def package_endpoint(package: Package) -> str:
|
||||||
return f"/packages/{package.Name}"
|
return f"/packages/{package.Name}"
|
||||||
|
@ -572,6 +574,38 @@ def test_pkgbase_comments(
|
||||||
assert "form" in data
|
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(
|
def test_pkgbase_comment_edit_unauthorized(
|
||||||
client: TestClient,
|
client: TestClient,
|
||||||
user: User,
|
user: User,
|
||||||
|
@ -935,6 +969,28 @@ def test_pkgbase_request_post_no_comment_error(
|
||||||
assert error.text.strip() == expected
|
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(
|
def test_pkgbase_request_post_merge_not_found_error(
|
||||||
client: TestClient, user: User, package: Package
|
client: TestClient, user: User, package: Package
|
||||||
):
|
):
|
||||||
|
@ -1087,6 +1143,13 @@ def test_pkgbase_flag(
|
||||||
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||||
assert pkgbase.Flagger is None
|
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.
|
# Flag it again.
|
||||||
with client as request:
|
with client as request:
|
||||||
request.cookies = cookies
|
request.cookies = cookies
|
||||||
|
|
Loading…
Add table
Reference in a new issue