Skip to content

Commit

Permalink
Make CharField prohibit surrogate characters (#7026) (#7067)
Browse files Browse the repository at this point in the history
* CharField: Detect and prohibit surrogate characters

* CharField: Cover handling of surrogate characters
  • Loading branch information
hartwork authored and tomchristie committed Jan 6, 2020
1 parent 165da5b commit 373e521
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 0 deletions.
2 changes: 2 additions & 0 deletions rest_framework/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from rest_framework.settings import api_settings
from rest_framework.utils import html, humanize_datetime, json, representation
from rest_framework.utils.formatting import lazy_format
from rest_framework.validators import ProhibitSurrogateCharactersValidator


class empty:
Expand Down Expand Up @@ -818,6 +819,7 @@ def __init__(self, **kwargs):
# ProhibitNullCharactersValidator is None on Django < 2.0
if ProhibitNullCharactersValidator is not None:
self.validators.append(ProhibitNullCharactersValidator())
self.validators.append(ProhibitSurrogateCharactersValidator())

def run_validation(self, data=empty):
# Test for the empty string here so that it does not get validated,
Expand Down
11 changes: 11 additions & 0 deletions rest_framework/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,17 @@ def __repr__(self):
)


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:
message = None
missing_message = _('This field is required.')
Expand Down
15 changes: 15 additions & 0 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,21 @@ def test_null_bytes(self):
'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):
"""
Ensure `validators` parameter is compatible with reasonable iterables.
Expand Down

0 comments on commit 373e521

Please sign in to comment.