Make CharField prohibit surrogate characters (#7026) (#7067)

* CharField: Detect and prohibit surrogate characters

* CharField: Cover handling of surrogate characters
This commit is contained in:
Sebastian Pipping 2020-01-06 15:12:21 +01:00 committed by Tom Christie
parent 165da5be0c
commit 373e521f36
3 changed files with 28 additions and 0 deletions

View File

@ -36,6 +36,7 @@ from rest_framework.exceptions import ErrorDetail, ValidationError
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.utils import html, humanize_datetime, json, representation from rest_framework.utils import html, humanize_datetime, json, representation
from rest_framework.utils.formatting import lazy_format from rest_framework.utils.formatting import lazy_format
from rest_framework.validators import ProhibitSurrogateCharactersValidator
class empty: class empty:
@ -818,6 +819,7 @@ class CharField(Field):
# ProhibitNullCharactersValidator is None on Django < 2.0 # ProhibitNullCharactersValidator is None on Django < 2.0
if ProhibitNullCharactersValidator is not None: if ProhibitNullCharactersValidator is not None:
self.validators.append(ProhibitNullCharactersValidator()) self.validators.append(ProhibitNullCharactersValidator())
self.validators.append(ProhibitSurrogateCharactersValidator())
def run_validation(self, data=empty): def run_validation(self, data=empty):
# Test for the empty string here so that it does not get validated, # Test for the empty string here so that it does not get validated,

View File

@ -167,6 +167,17 @@ class UniqueTogetherValidator:
) )
class ProhibitSurrogateCharactersValidator:
message = _('Surrogate characters are not allowed: U+{code_point:X}.')
code = 'surrogate_characters_not_allowed'
def __call__(self, value):
for surrogate_character in (ch for ch in str(value)
if 0xD800 <= ord(ch) <= 0xDFFF):
message = self.message.format(code_point=ord(surrogate_character))
raise ValidationError(message, code=self.code)
class BaseUniqueForValidator: class BaseUniqueForValidator:
message = None message = None
missing_message = _('This field is required.') missing_message = _('This field is required.')

View File

@ -758,6 +758,21 @@ class TestCharField(FieldValues):
'Null characters are not allowed.' 'Null characters are not allowed.'
] ]
def test_surrogate_characters(self):
field = serializers.CharField()
for code_point, expected_message in (
(0xD800, 'Surrogate characters are not allowed: U+D800.'),
(0xDFFF, 'Surrogate characters are not allowed: U+DFFF.'),
):
with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation(chr(code_point))
assert exc_info.value.detail[0].code == 'surrogate_characters_not_allowed'
assert str(exc_info.value.detail[0]) == expected_message
for code_point in (0xD800 - 1, 0xDFFF + 1):
field.run_validation(chr(code_point))
def test_iterable_validators(self): def test_iterable_validators(self):
""" """
Ensure `validators` parameter is compatible with reasonable iterables. Ensure `validators` parameter is compatible with reasonable iterables.