Prevent URLs from being added to ratings text (#22604)

This commit is contained in:
Mathieu Pillard 2024-08-28 18:08:03 +02:00 коммит произвёл GitHub
Родитель 3351d7febe
Коммит 132be7de2e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
5 изменённых файлов: 32 добавлений и 48 удалений

Просмотреть файл

@ -32,6 +32,7 @@ from olympia.api.fields import (
)
from olympia.api.serializers import AMOModelSerializer, BaseESSerializer
from olympia.api.utils import is_gate_active
from olympia.api.validators import NoURLsValidator
from olympia.applications.models import AppVersion
from olympia.bandwagon.models import Collection
from olympia.constants.applications import APP_IDS, APPS_ALL
@ -85,7 +86,6 @@ from .validators import (
MatchingGuidValidator,
NoFallbackDefaultLocaleValidator,
NoThemesValidator,
NoURLsValidator,
ReviewedSourceFileValidator,
VerifyMozillaTrademark,
VersionAddonMetadataValidator,

Просмотреть файл

@ -5,7 +5,7 @@ from django.utils.translation import gettext
from rest_framework import exceptions, fields
from olympia import amo
from olympia.amo.utils import find_language, verify_no_urls
from olympia.amo.utils import find_language
from olympia.versions.models import License
from .models import Addon
@ -318,14 +318,6 @@ class CanSetCompatibilityValidator:
)
class NoURLsValidator:
def __call__(self, value):
try:
verify_no_urls(value)
except forms.ValidationError as exc:
raise exceptions.ValidationError(exc.message)
class NoThemesValidator:
requires_context = True

Просмотреть файл

@ -1,7 +1,8 @@
from django.core.exceptions import ValidationError as DjangoValidationError
from rest_framework import serializers
from rest_framework import exceptions
from olympia.amo.utils import verify_no_urls
from olympia.amo.validators import OneOrMorePrintableCharacterValidator
@ -13,4 +14,12 @@ class OneOrMorePrintableCharacterAPIValidator(OneOrMorePrintableCharacterValidat
try:
return super().__call__(value)
except DjangoValidationError:
raise serializers.ValidationError(self.message)
raise exceptions.ValidationError(self.message)
class NoURLsValidator:
def __call__(self, value):
try:
verify_no_urls(value)
except DjangoValidationError as exc:
raise exceptions.ValidationError(exc.message)

Просмотреть файл

@ -1,11 +1,9 @@
import re
from collections import OrderedDict
from urllib.parse import unquote
from django.conf import settings
from django.utils.translation import gettext, ngettext
from bleach.linkifier import TLDS
from rest_framework import serializers
from rest_framework.relations import PrimaryKeyRelatedField
@ -13,23 +11,13 @@ from olympia.accounts.serializers import BaseUserSerializer
from olympia.addons.serializers import SimpleAddonSerializer, SimpleVersionSerializer
from olympia.api.serializers import AMOModelSerializer
from olympia.api.utils import is_gate_active
from olympia.api.validators import NoURLsValidator
from olympia.users.utils import RestrictionChecker
from olympia.versions.models import Version
from .models import DeniedRatingWord, Rating, RatingFlag
# This matches the following three types of patterns:
# http://... or https://..., generic domain names, and IPv4 octets. It does not
# match IPv6 addresses or long strings such as "example dot com".
link_pattern = re.compile(
r'((://)|' # Protocols (e.g.: http://)
r'((\d{1,3}\.){3}(\d{1,3}))|'
r'([0-9a-z\-%%]+\.(%s)))' % '|'.join(TLDS),
(re.I | re.U | re.M),
)
class RatingAddonSerializer(SimpleAddonSerializer):
def get_attribute(self, obj):
# Avoid database queries if possible by re-using the addon object from
@ -39,6 +27,12 @@ class RatingAddonSerializer(SimpleAddonSerializer):
class BaseRatingSerializer(AMOModelSerializer):
addon = RatingAddonSerializer(read_only=True)
body = serializers.CharField(
allow_blank=True,
allow_null=True,
required=False,
validators=[NoURLsValidator()],
)
is_deleted = serializers.BooleanField(read_only=True, source='deleted')
is_developer_reply = serializers.SerializerMethodField()
is_latest = serializers.BooleanField(read_only=True)
@ -149,12 +143,10 @@ class BaseRatingSerializer(AMOModelSerializer):
f'{user.email} or {{{ip_address}}} matched a UserRestriction',
)
# Flag the review if there was a word match or a URL was in it.
# Unquote when searching for links, in case someone tries 'example%2ecom'.
if (
hasattr(self, '_rating_flag_to_save')
or link_pattern.search(unquote(data.get('body') or '')) is not None
):
if hasattr(self, '_rating_flag_to_save'):
# If we have the _rating_flag_to_save set, we should be set the
# flag and editorreview attributes on the instance we're about to
# save.
data['flag'] = True
data['editorreview'] = True
@ -188,7 +180,12 @@ class BaseRatingSerializer(AMOModelSerializer):
class RatingSerializerReply(BaseRatingSerializer):
"""Serializer used for replies only."""
body = serializers.CharField(allow_null=False, required=True, allow_blank=False)
body = serializers.CharField(
allow_null=False,
required=True,
allow_blank=False,
validators=[NoURLsValidator()],
)
def to_representation(self, obj):
should_access_deleted = getattr(

Просмотреть файл

@ -1824,7 +1824,6 @@ class TestRatingViewSetPost(TestCase):
self.client.login_api(self.user)
assert not Rating.objects.exists()
body = 'Trying to spam <br> http://éxample.com'
cleaned_body = 'Trying to spam \n http://éxample.com'
response = self.client.post(
self.url,
{
@ -1834,21 +1833,8 @@ class TestRatingViewSetPost(TestCase):
'version': self.addon.current_version.pk,
},
)
assert response.status_code == 201
review = Rating.objects.latest('pk')
assert review.pk == response.data['id']
assert str(review.body) == response.data['body'] == cleaned_body
assert review.rating == response.data['score'] == 5
assert review.user == self.user
assert review.reply_to is None
assert review.addon == self.addon
assert review.version == self.addon.current_version
assert response.data['version'] == {
'id': review.version.id,
'version': review.version.version,
}
assert review.flag
assert review.editorreview
assert response.status_code == 400
assert response.data['body'] == ['URLs are not allowed.']
def test_post_rating_float(self):
self.user = user_factory()