From 0eb2dc1137189027cc8d638630fb1754b02d6cfa Mon Sep 17 00:00:00 2001 From: Jerome Leclanche Date: Tue, 2 Oct 2018 17:54:15 +0300 Subject: [PATCH] Prohibit null characters in CharField by default (#6073) * Implement an allow_null_bytes argument to CharField (default True) * Switch to using native ProhibitNullCharactersValidator instead --- rest_framework/compat.py | 5 +++++ rest_framework/fields.py | 9 +++++++-- tests/test_fields.py | 12 ++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index d6559d5f51..d28055a3ac 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -29,6 +29,11 @@ RegexURLResolver as URLResolver, ) +try: + from django.core.validators import ProhibitNullCharactersValidator # noqa +except ImportError: + ProhibitNullCharactersValidator = None + def get_original_route(urlpattern): """ diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 61f3451b0a..70c210c162 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -34,7 +34,8 @@ from rest_framework import ISO_8601 from rest_framework.compat import ( MaxLengthValidator, MaxValueValidator, MinLengthValidator, - MinValueValidator, unicode_repr, unicode_to_repr + MinValueValidator, ProhibitNullCharactersValidator, unicode_repr, + unicode_to_repr ) from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.settings import api_settings @@ -755,7 +756,7 @@ class CharField(Field): 'invalid': _('Not a valid string.'), 'blank': _('This field may not be blank.'), 'max_length': _('Ensure this field has no more than {max_length} characters.'), - 'min_length': _('Ensure this field has at least {min_length} characters.') + 'min_length': _('Ensure this field has at least {min_length} characters.'), } initial = '' @@ -778,6 +779,10 @@ def __init__(self, **kwargs): self.validators.append( MinLengthValidator(self.min_length, message=message)) + # ProhibitNullCharactersValidator is None on Django < 2.0 + if ProhibitNullCharactersValidator is not None: + self.validators.append(ProhibitNullCharactersValidator()) + def run_validation(self, data=empty): # Test for the empty string here so that it does not get validated, # and so that subclasses do not need to handle it explicitly diff --git a/tests/test_fields.py b/tests/test_fields.py index 1b8f596e29..9a1d049797 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -15,6 +15,7 @@ import rest_framework from rest_framework import exceptions, serializers +from rest_framework.compat import ProhibitNullCharactersValidator from rest_framework.fields import DjangoImageField, is_simple_callable try: @@ -728,6 +729,17 @@ def test_disallow_blank_with_trim_whitespace(self): field.run_validation(' ') assert exc_info.value.detail == ['This field may not be blank.'] + @pytest.mark.skipif(ProhibitNullCharactersValidator is None, reason="Skipped on Django < 2.0") + def test_null_bytes(self): + field = serializers.CharField() + + for value in ('\0', 'foo\0', '\0foo', 'foo\0foo'): + with pytest.raises(serializers.ValidationError) as exc_info: + field.run_validation(value) + assert exc_info.value.detail == [ + 'Null characters are not allowed.' + ] + class TestEmailField(FieldValues): """