raise possible problem and a possible (but probably not the correct) solution.

This commit is contained in:
David Millman 2014-09-24 20:38:54 -04:00
parent 411511622d
commit 7ff5e79e83
2 changed files with 56 additions and 1 deletions

View File

@ -814,6 +814,7 @@ class IntegerField(WritableField):
type_label = 'integer'
form_field_class = forms.IntegerField
empty = 0
max_digits = 39 # len(str(2**128))
default_error_messages = {
'invalid': _('Enter a whole number.'),
@ -835,7 +836,9 @@ class IntegerField(WritableField):
return None
try:
value = int(str(value))
str_value = str(value)
validators.MaxLengthValidator(self.max_digits)(str_value)
value = int(str_value)
except (ValueError, TypeError):
raise ValidationError(self.error_messages['invalid'])
return value
@ -846,6 +849,7 @@ class FloatField(WritableField):
type_label = 'float'
form_field_class = forms.FloatField
empty = 0
max_digits = 17 # IEEE-754 double can get at most 17 significant digits.
default_error_messages = {
'invalid': _("'%s' value must be a float."),
@ -856,6 +860,7 @@ class FloatField(WritableField):
return None
try:
validators.MaxLengthValidator(self.max_digits)(str(value))
return float(value)
except (TypeError, ValueError):
msg = self.error_messages['invalid'] % value
@ -898,6 +903,9 @@ class DecimalField(WritableField):
return None
value = smart_text(value).strip()
try:
# to not change behavior something similar to
# .validate(self, value) should be run
# validators.MaxLengthValidator(self.max_digits)(str(value))
value = Decimal(value)
except DecimalException:
raise ValidationError(self.error_messages['invalid'])

View File

@ -965,6 +965,53 @@ class FieldCallableDefault(TestCase):
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):
"""
Test that custom fields apply min_value and max_value constraints