mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-04 20:40:14 +03:00
raise possible problem and a possible (but probably not the correct) solution.
This commit is contained in:
parent
411511622d
commit
7ff5e79e83
|
@ -814,6 +814,7 @@ class IntegerField(WritableField):
|
||||||
type_label = 'integer'
|
type_label = 'integer'
|
||||||
form_field_class = forms.IntegerField
|
form_field_class = forms.IntegerField
|
||||||
empty = 0
|
empty = 0
|
||||||
|
max_digits = 39 # len(str(2**128))
|
||||||
|
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _('Enter a whole number.'),
|
'invalid': _('Enter a whole number.'),
|
||||||
|
@ -835,7 +836,9 @@ class IntegerField(WritableField):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
value = int(str(value))
|
str_value = str(value)
|
||||||
|
validators.MaxLengthValidator(self.max_digits)(str_value)
|
||||||
|
value = int(str_value)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'])
|
||||||
return value
|
return value
|
||||||
|
@ -846,6 +849,7 @@ class FloatField(WritableField):
|
||||||
type_label = 'float'
|
type_label = 'float'
|
||||||
form_field_class = forms.FloatField
|
form_field_class = forms.FloatField
|
||||||
empty = 0
|
empty = 0
|
||||||
|
max_digits = 17 # IEEE-754 double can get at most 17 significant digits.
|
||||||
|
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid': _("'%s' value must be a float."),
|
'invalid': _("'%s' value must be a float."),
|
||||||
|
@ -856,6 +860,7 @@ class FloatField(WritableField):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
validators.MaxLengthValidator(self.max_digits)(str(value))
|
||||||
return float(value)
|
return float(value)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
msg = self.error_messages['invalid'] % value
|
msg = self.error_messages['invalid'] % value
|
||||||
|
@ -898,6 +903,9 @@ class DecimalField(WritableField):
|
||||||
return None
|
return None
|
||||||
value = smart_text(value).strip()
|
value = smart_text(value).strip()
|
||||||
try:
|
try:
|
||||||
|
# to not change behavior something similar to
|
||||||
|
# .validate(self, value) should be run
|
||||||
|
# validators.MaxLengthValidator(self.max_digits)(str(value))
|
||||||
value = Decimal(value)
|
value = Decimal(value)
|
||||||
except DecimalException:
|
except DecimalException:
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'])
|
||||||
|
|
|
@ -965,6 +965,53 @@ class FieldCallableDefault(TestCase):
|
||||||
self.assertEqual(into, {'field': 'foo bar'})
|
self.assertEqual(into, {'field': 'foo bar'})
|
||||||
|
|
||||||
|
|
||||||
|
class CanHangOnHugeNumbers(TestCase):
|
||||||
|
"""
|
||||||
|
Test that number fields will not hang on a very large input. The
|
||||||
|
test and one approach to handling it is not intended as a final solution
|
||||||
|
but a starting point for a discussion.
|
||||||
|
|
||||||
|
The main concern is when using a serializer in the context of the rest
|
||||||
|
framework. A malicious user could send a large number as a string
|
||||||
|
representation (here we use a small example, str(2**256), that is big
|
||||||
|
enough for demonstration). Since the number is converted to an integer
|
||||||
|
in fields.IntegerField.from_native function without checking the length
|
||||||
|
of the input, the system can hang. One solution is to use a length
|
||||||
|
validation before converting, however this seems a little messy.
|
||||||
|
Similar concerns exist in FloatField and DecimalField where a similar
|
||||||
|
solution could be used be used.
|
||||||
|
"""
|
||||||
|
def test_huge_numbers_do_not_cause_a_hang(self):
|
||||||
|
|
||||||
|
class NumberModel(models.Model):
|
||||||
|
integer = models.IntegerField(
|
||||||
|
validators=[validators.MaxValueValidator(2 ** 32)])
|
||||||
|
decimal = models.DecimalField(max_digits=17)
|
||||||
|
double = models.FloatField()
|
||||||
|
|
||||||
|
class NumberSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = NumberModel
|
||||||
|
|
||||||
|
number = NumberModel(integer=1, decimal=3.14, double=2.7182818284)
|
||||||
|
|
||||||
|
serializer = NumberSerializer(
|
||||||
|
number, data={"integer": 2, "decimal": 3.0, "double": 2.7})
|
||||||
|
self.assertTrue(serializer.is_valid())
|
||||||
|
|
||||||
|
serializer = NumberSerializer(
|
||||||
|
number, data={"integer": str(2 ** 256)}, partial=True)
|
||||||
|
self.assertFalse(serializer.is_valid())
|
||||||
|
|
||||||
|
serializer = NumberSerializer(
|
||||||
|
number, data={"decimal": '3.14159265358979323846264'}, partial=True)
|
||||||
|
self.assertFalse(serializer.is_valid())
|
||||||
|
|
||||||
|
serializer = NumberSerializer(
|
||||||
|
number, data={"double": '2.718281828459045235360287'}, partial=True)
|
||||||
|
self.assertFalse(serializer.is_valid())
|
||||||
|
|
||||||
|
|
||||||
class CustomIntegerField(TestCase):
|
class CustomIntegerField(TestCase):
|
||||||
"""
|
"""
|
||||||
Test that custom fields apply min_value and max_value constraints
|
Test that custom fields apply min_value and max_value constraints
|
||||||
|
|
Loading…
Reference in New Issue
Block a user