Skip to content

Commit 373e521

Browse files
hartworklovelydinosaur
authored andcommitted
Make CharField prohibit surrogate characters (#7026) (#7067)
* CharField: Detect and prohibit surrogate characters * CharField: Cover handling of surrogate characters
1 parent 165da5b commit 373e521

File tree

3 files changed

+28
-0
lines changed

3 files changed

+28
-0
lines changed

rest_framework/fields.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from rest_framework.settings import api_settings
3737
from rest_framework.utils import html, humanize_datetime, json, representation
3838
from rest_framework.utils.formatting import lazy_format
39+
from rest_framework.validators import ProhibitSurrogateCharactersValidator
3940

4041

4142
class empty:
@@ -818,6 +819,7 @@ def __init__(self, **kwargs):
818819
# ProhibitNullCharactersValidator is None on Django < 2.0
819820
if ProhibitNullCharactersValidator is not None:
820821
self.validators.append(ProhibitNullCharactersValidator())
822+
self.validators.append(ProhibitSurrogateCharactersValidator())
821823

822824
def run_validation(self, data=empty):
823825
# Test for the empty string here so that it does not get validated,

rest_framework/validators.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,17 @@ def __repr__(self):
167167
)
168168

169169

170+
class ProhibitSurrogateCharactersValidator:
171+
message = _('Surrogate characters are not allowed: U+{code_point:X}.')
172+
code = 'surrogate_characters_not_allowed'
173+
174+
def __call__(self, value):
175+
for surrogate_character in (ch for ch in str(value)
176+
if 0xD800 <= ord(ch) <= 0xDFFF):
177+
message = self.message.format(code_point=ord(surrogate_character))
178+
raise ValidationError(message, code=self.code)
179+
180+
170181
class BaseUniqueForValidator:
171182
message = None
172183
missing_message = _('This field is required.')

tests/test_fields.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,21 @@ def test_null_bytes(self):
758758
'Null characters are not allowed.'
759759
]
760760

761+
def test_surrogate_characters(self):
762+
field = serializers.CharField()
763+
764+
for code_point, expected_message in (
765+
(0xD800, 'Surrogate characters are not allowed: U+D800.'),
766+
(0xDFFF, 'Surrogate characters are not allowed: U+DFFF.'),
767+
):
768+
with pytest.raises(serializers.ValidationError) as exc_info:
769+
field.run_validation(chr(code_point))
770+
assert exc_info.value.detail[0].code == 'surrogate_characters_not_allowed'
771+
assert str(exc_info.value.detail[0]) == expected_message
772+
773+
for code_point in (0xD800 - 1, 0xDFFF + 1):
774+
field.run_validation(chr(code_point))
775+
761776
def test_iterable_validators(self):
762777
"""
763778
Ensure `validators` parameter is compatible with reasonable iterables.

0 commit comments

Comments
 (0)