CVE-2014-0474
Description
The (1) FilePathField, (2) GenericIPAddressField, and (3) IPAddressField model field classes in Django before 1.4.11, 1.5.x before 1.5.6, 1.6.x before 1.6.3, and 1.7.x before 1.7 beta 2 do not properly perform type conversion, which allows remote attackers to have unspecified impact and vectors, related to "MySQL typecasting."
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
DjangoPyPI | < 1.4.11 | 1.4.11 |
DjangoPyPI | >= 1.5, < 1.5.6 | 1.5.6 |
DjangoPyPI | >= 1.6, < 1.6.3 | 1.6.3 |
Affected products
28cpe:2.3:a:djangoproject:django:*:*:*:*:*:*:*:*+ 22 more
- cpe:2.3:a:djangoproject:django:*:*:*:*:*:*:*:*range: <=1.4.10
- cpe:2.3:a:djangoproject:django:1.4:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.3:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.4:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.5:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.6:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.7:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.8:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.4.9:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.3:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.4:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.5.5:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6.1:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.6.2:*:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7:alpha1:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7:alpha2:*:*:*:*:*:*
- cpe:2.3:a:djangoproject:django:1.7:beta1:*:*:*:*:*:*
cpe:2.3:o:canonical:ubuntu_linux:10.04:-:lts:*:*:*:*:*+ 4 more
- cpe:2.3:o:canonical:ubuntu_linux:10.04:-:lts:*:*:*:*:*
- cpe:2.3:o:canonical:ubuntu_linux:12.04:-:lts:*:*:*:*:*
- cpe:2.3:o:canonical:ubuntu_linux:12.10:*:*:*:*:*:*:*
- cpe:2.3:o:canonical:ubuntu_linux:13.10:*:*:*:*:*:*:*
- cpe:2.3:o:canonical:ubuntu_linux:14.04:*:*:*:lts:*:*:*
Patches
3aa80f498de6d[1.4.x] Fixed queries that may return unexpected results on MySQL due to typecasting.
6 files changed · +154 −2
django/db/models/fields/__init__.py+15 −1 modified@@ -911,6 +911,12 @@ def __init__(self, verbose_name=None, name=None, path='', match=None, kwargs['max_length'] = kwargs.get('max_length', 100) Field.__init__(self, verbose_name, name, **kwargs) + def get_prep_value(self, value): + value = super(FilePathField, self).get_prep_value(value) + if value is None: + return None + return smart_unicode(value) + def formfield(self, **kwargs): defaults = { 'path': self.path, @@ -1010,6 +1016,12 @@ def __init__(self, *args, **kwargs): kwargs['max_length'] = 15 Field.__init__(self, *args, **kwargs) + def get_prep_value(self, value): + value = super(IPAddressField, self).get_prep_value(value) + if value is None: + return None + return smart_unicode(value) + def get_internal_type(self): return "IPAddressField" @@ -1047,12 +1059,14 @@ def get_db_prep_value(self, value, connection, prepared=False): return value or None def get_prep_value(self, value): + if value is None: + return value if value and ':' in value: try: return clean_ipv6_address(value, self.unpack_ipv4) except exceptions.ValidationError: pass - return value + return smart_unicode(value) def formfield(self, **kwargs): defaults = {'form_class': forms.GenericIPAddressField}
docs/howto/custom-model-fields.txt+10 −0 modified@@ -482,6 +482,16 @@ For example:: return ''.join([''.join(l) for l in (value.north, value.east, value.south, value.west)]) +.. warning:: + + If your custom field uses the ``CHAR``, ``VARCHAR`` or ``TEXT`` + types for MySQL, you must make sure that :meth:`.get_prep_value` + always returns a string type. MySQL performs flexible and unexpected + matching when a query is performed on these types and the provided + value is an integer, which can cause queries to include unexpected + objects in their results. This problem cannot occur if you always + return a string type from :meth:`.get_prep_value`. + Converting query values to database values ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
docs/ref/databases.txt+16 −0 modified@@ -432,6 +432,22 @@ MySQL does not support the ``NOWAIT`` option to the ``SELECT ... FOR UPDATE`` statement. If ``select_for_update()`` is used with ``nowait=True`` then a ``DatabaseError`` will be raised. +Automatic typecasting can cause unexpected results +-------------------------------------------------- + +When performing a query on a string type, but with an integer value, MySQL will +coerce the types of all values in the table to an integer before performing the +comparison. If your table contains the values ``'abc'``, ``'def'`` and you +query for ``WHERE mycolumn=0``, both rows will match. Similarly, ``WHERE mycolumn=1`` +will match the value ``'abc1'``. Therefore, string type fields included in Django +will always cast the value to a string before using it in a query. + +If you implement custom model fields that inherit from :class:`~django.db.models.Field` +directly, are overriding :meth:`~django.db.models.Field.get_prep_value`, or use +:meth:`extra() <django.db.models.query.QuerySet.extra>` or +:meth:`raw() <django.db.models.Manager.raw>`, you should ensure that you +perform the appropriate typecasting. + .. _sqlite-notes: SQLite notes
docs/ref/models/querysets.txt+10 −0 modified@@ -1041,6 +1041,16 @@ of the arguments is required, but you should use at least one of them. Entry.objects.extra(where=['headline=%s'], params=['Lennon']) +.. warning:: + + If you are performing queries on MySQL, note that MySQL's silent type coercion + may cause unexpected results when mixing types. If you query on a string + type column, but with an integer value, MySQL will coerce the types of all values + in the table to an integer before performing the comparison. For example, if your + table contains the values ``'abc'``, ``'def'`` and you query for ``WHERE mycolumn=0``, + both rows will match. To prevent this, perform the correct typecasting + before using the value in a query. + defer ~~~~~
docs/topics/db/sql.txt+10 −0 modified@@ -69,6 +69,16 @@ options that make it very powerful. database, but does nothing to enforce that. If the query does not return rows, a (possibly cryptic) error will result. +.. warning:: + + If you are performing queries on MySQL, note that MySQL's silent type coercion + may cause unexpected results when mixing types. If you query on a string + type column, but with an integer value, MySQL will coerce the types of all values + in the table to an integer before performing the comparison. For example, if your + table contains the values ``'abc'``, ``'def'`` and you query for ``WHERE mycolumn=0``, + both rows will match. To prevent this, perform the correct typecasting + before using the value in a query. + Mapping query fields to model fields ------------------------------------
tests/regressiontests/model_fields/tests.py+93 −1 modified@@ -6,8 +6,15 @@ from django import test from django import forms from django.core.exceptions import ValidationError +from django.db.models.fields import ( + AutoField, BigIntegerField, BooleanField, CharField, + CommaSeparatedIntegerField, DateField, DateTimeField, DecimalField, + EmailField, FilePathField, FloatField, IntegerField, IPAddressField, + GenericIPAddressField, NullBooleanField, PositiveIntegerField, + PositiveSmallIntegerField, SlugField, SmallIntegerField, TextField, + TimeField, URLField) from django.db import models -from django.db.models.fields.files import FieldFile +from django.db.models.fields.files import FileField, ImageField, FieldFile from django.utils import unittest from .models import (Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post, @@ -373,3 +380,88 @@ def test_changed(self): field = d._meta.get_field('myfile') field.save_form_data(d, 'else.txt') self.assertEqual(d.myfile, 'else.txt') + + +class PrepValueTest(test.TestCase): + def test_AutoField(self): + self.assertIsInstance(AutoField(primary_key=True).get_prep_value(1), int) + + def test_BigIntegerField(self): + self.assertIsInstance(BigIntegerField().get_prep_value(long(9999999999999999999)), long) + + def test_BooleanField(self): + self.assertIsInstance(BooleanField().get_prep_value(True), bool) + + def test_CharField(self): + self.assertIsInstance(CharField().get_prep_value(''), str) + self.assertIsInstance(CharField().get_prep_value(0), unicode) + + def test_CommaSeparatedIntegerField(self): + self.assertIsInstance(CommaSeparatedIntegerField().get_prep_value('1,2'), str) + self.assertIsInstance(CommaSeparatedIntegerField().get_prep_value(0), unicode) + + def test_DateField(self): + self.assertIsInstance(DateField().get_prep_value(datetime.date.today()), datetime.date) + + def test_DateTimeField(self): + self.assertIsInstance(DateTimeField().get_prep_value(datetime.datetime.now()), datetime.datetime) + + def test_DecimalField(self): + self.assertIsInstance(DecimalField().get_prep_value(Decimal('1.2')), Decimal) + + def test_EmailField(self): + self.assertIsInstance(EmailField().get_prep_value('mailbox@domain.com'), str) + + def test_FileField(self): + self.assertIsInstance(FileField().get_prep_value('filename.ext'), unicode) + self.assertIsInstance(FileField().get_prep_value(0), unicode) + + def test_FilePathField(self): + self.assertIsInstance(FilePathField().get_prep_value('tests.py'), unicode) + self.assertIsInstance(FilePathField().get_prep_value(0), unicode) + + def test_FloatField(self): + self.assertIsInstance(FloatField().get_prep_value(1.2), float) + + def test_ImageField(self): + self.assertIsInstance(ImageField().get_prep_value('filename.ext'), unicode) + + def test_IntegerField(self): + self.assertIsInstance(IntegerField().get_prep_value(1), int) + + def test_IPAddressField(self): + self.assertIsInstance(IPAddressField().get_prep_value('127.0.0.1'), unicode) + self.assertIsInstance(IPAddressField().get_prep_value(0), unicode) + + def test_GenericIPAddressField(self): + self.assertIsInstance(GenericIPAddressField().get_prep_value('127.0.0.1'), unicode) + self.assertIsInstance(GenericIPAddressField().get_prep_value(0), unicode) + + def test_NullBooleanField(self): + self.assertIsInstance(NullBooleanField().get_prep_value(True), bool) + + def test_PositiveIntegerField(self): + self.assertIsInstance(PositiveIntegerField().get_prep_value(1), int) + + def test_PositiveSmallIntegerField(self): + self.assertIsInstance(PositiveSmallIntegerField().get_prep_value(1), int) + + def test_SlugField(self): + self.assertIsInstance(SlugField().get_prep_value('slug'), str) + self.assertIsInstance(SlugField().get_prep_value(0), unicode) + + def test_SmallIntegerField(self): + self.assertIsInstance(SmallIntegerField().get_prep_value(1), int) + + def test_TextField(self): + self.assertIsInstance(TextField().get_prep_value('Abc'), str) + self.assertIsInstance(TextField().get_prep_value(0), unicode) + + def test_TimeField(self): + self.assertIsInstance( + TimeField().get_prep_value(datetime.datetime.now().time()), + datetime.time) + + def test_URLField(self): + self.assertIsInstance(URLField().get_prep_value('http://domain.com'), str) +
985434fb1d6b[1.5.x] Fixed queries that may return unexpected results on MySQL due to typecasting.
6 files changed · +155 −2
django/db/models/fields/__init__.py+15 −1 modified@@ -934,6 +934,12 @@ def __init__(self, verbose_name=None, name=None, path='', match=None, kwargs['max_length'] = kwargs.get('max_length', 100) Field.__init__(self, verbose_name, name, **kwargs) + def get_prep_value(self, value): + value = super(FilePathField, self).get_prep_value(value) + if value is None: + return None + return six.text_type(value) + def formfield(self, **kwargs): defaults = { 'path': self.path, @@ -1035,6 +1041,12 @@ def __init__(self, *args, **kwargs): kwargs['max_length'] = 15 Field.__init__(self, *args, **kwargs) + def get_prep_value(self, value): + value = super(IPAddressField, self).get_prep_value(value) + if value is None: + return None + return six.text_type(value) + def get_internal_type(self): return "IPAddressField" @@ -1072,12 +1084,14 @@ def get_db_prep_value(self, value, connection, prepared=False): return value or None def get_prep_value(self, value): + if value is None: + return value if value and ':' in value: try: return clean_ipv6_address(value, self.unpack_ipv4) except exceptions.ValidationError: pass - return value + return six.text_type(value) def formfield(self, **kwargs): defaults = {'form_class': forms.GenericIPAddressField}
docs/howto/custom-model-fields.txt+10 −0 modified@@ -501,6 +501,16 @@ For example:: return ''.join([''.join(l) for l in (value.north, value.east, value.south, value.west)]) +.. warning:: + + If your custom field uses the ``CHAR``, ``VARCHAR`` or ``TEXT`` + types for MySQL, you must make sure that :meth:`.get_prep_value` + always returns a string type. MySQL performs flexible and unexpected + matching when a query is performed on these types and the provided + value is an integer, which can cause queries to include unexpected + objects in their results. This problem cannot occur if you always + return a string type from :meth:`.get_prep_value`. + Converting query values to database values ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
docs/ref/databases.txt+16 −0 modified@@ -429,6 +429,22 @@ MySQL does not support the ``NOWAIT`` option to the ``SELECT ... FOR UPDATE`` statement. If ``select_for_update()`` is used with ``nowait=True`` then a ``DatabaseError`` will be raised. +Automatic typecasting can cause unexpected results +-------------------------------------------------- + +When performing a query on a string type, but with an integer value, MySQL will +coerce the types of all values in the table to an integer before performing the +comparison. If your table contains the values ``'abc'``, ``'def'`` and you +query for ``WHERE mycolumn=0``, both rows will match. Similarly, ``WHERE mycolumn=1`` +will match the value ``'abc1'``. Therefore, string type fields included in Django +will always cast the value to a string before using it in a query. + +If you implement custom model fields that inherit from :class:`~django.db.models.Field` +directly, are overriding :meth:`~django.db.models.Field.get_prep_value`, or use +:meth:`extra() <django.db.models.query.QuerySet.extra>` or +:meth:`raw() <django.db.models.Manager.raw>`, you should ensure that you +perform the appropriate typecasting. + .. _sqlite-notes: SQLite notes
docs/ref/models/querysets.txt+10 −0 modified@@ -1068,6 +1068,16 @@ of the arguments is required, but you should use at least one of them. Entry.objects.extra(where=['headline=%s'], params=['Lennon']) +.. warning:: + + If you are performing queries on MySQL, note that MySQL's silent type coercion + may cause unexpected results when mixing types. If you query on a string + type column, but with an integer value, MySQL will coerce the types of all values + in the table to an integer before performing the comparison. For example, if your + table contains the values ``'abc'``, ``'def'`` and you query for ``WHERE mycolumn=0``, + both rows will match. To prevent this, perform the correct typecasting + before using the value in a query. + defer ~~~~~
docs/topics/db/sql.txt+10 −0 modified@@ -66,6 +66,16 @@ options that make it very powerful. database, but does nothing to enforce that. If the query does not return rows, a (possibly cryptic) error will result. +.. warning:: + + If you are performing queries on MySQL, note that MySQL's silent type coercion + may cause unexpected results when mixing types. If you query on a string + type column, but with an integer value, MySQL will coerce the types of all values + in the table to an integer before performing the comparison. For example, if your + table contains the values ``'abc'``, ``'def'`` and you query for ``WHERE mycolumn=0``, + both rows will match. To prevent this, perform the correct typecasting + before using the value in a query. + Mapping query fields to model fields ------------------------------------
tests/regressiontests/model_fields/tests.py+94 −1 modified@@ -6,8 +6,15 @@ from django import test from django import forms from django.core.exceptions import ValidationError +from django.db.models.fields import ( + AutoField, BigIntegerField, BooleanField, CharField, + CommaSeparatedIntegerField, DateField, DateTimeField, DecimalField, + EmailField, FilePathField, FloatField, IntegerField, IPAddressField, + GenericIPAddressField, NullBooleanField, PositiveIntegerField, + PositiveSmallIntegerField, SlugField, SmallIntegerField, TextField, + TimeField, URLField) from django.db import models -from django.db.models.fields.files import FieldFile +from django.db.models.fields.files import FileField, ImageField, FieldFile from django.utils import six from django.utils import unittest @@ -414,3 +421,89 @@ def test_changed(self): field = d._meta.get_field('myfile') field.save_form_data(d, 'else.txt') self.assertEqual(d.myfile, 'else.txt') + + +class PrepValueTest(test.TestCase): + def test_AutoField(self): + self.assertIsInstance(AutoField(primary_key=True).get_prep_value(1), int) + + @unittest.skipIf(six.PY3, "Python 3 has no `long` type.") + def test_BigIntegerField(self): + self.assertIsInstance(BigIntegerField().get_prep_value(long(9999999999999999999)), long) + + def test_BooleanField(self): + self.assertIsInstance(BooleanField().get_prep_value(True), bool) + + def test_CharField(self): + self.assertIsInstance(CharField().get_prep_value(''), six.text_type) + self.assertIsInstance(CharField().get_prep_value(0), six.text_type) + + def test_CommaSeparatedIntegerField(self): + self.assertIsInstance(CommaSeparatedIntegerField().get_prep_value('1,2'), six.text_type) + self.assertIsInstance(CommaSeparatedIntegerField().get_prep_value(0), six.text_type) + + def test_DateField(self): + self.assertIsInstance(DateField().get_prep_value(datetime.date.today()), datetime.date) + + def test_DateTimeField(self): + self.assertIsInstance(DateTimeField().get_prep_value(datetime.datetime.now()), datetime.datetime) + + def test_DecimalField(self): + self.assertIsInstance(DecimalField().get_prep_value(Decimal('1.2')), Decimal) + + def test_EmailField(self): + self.assertIsInstance(EmailField().get_prep_value('mailbox@domain.com'), six.text_type) + + def test_FileField(self): + self.assertIsInstance(FileField().get_prep_value('filename.ext'), six.text_type) + self.assertIsInstance(FileField().get_prep_value(0), six.text_type) + + def test_FilePathField(self): + self.assertIsInstance(FilePathField().get_prep_value('tests.py'), six.text_type) + self.assertIsInstance(FilePathField().get_prep_value(0), six.text_type) + + def test_FloatField(self): + self.assertIsInstance(FloatField().get_prep_value(1.2), float) + + def test_ImageField(self): + self.assertIsInstance(ImageField().get_prep_value('filename.ext'), six.text_type) + + def test_IntegerField(self): + self.assertIsInstance(IntegerField().get_prep_value(1), int) + + def test_IPAddressField(self): + self.assertIsInstance(IPAddressField().get_prep_value('127.0.0.1'), six.text_type) + self.assertIsInstance(IPAddressField().get_prep_value(0), six.text_type) + + def test_GenericIPAddressField(self): + self.assertIsInstance(GenericIPAddressField().get_prep_value('127.0.0.1'), six.text_type) + self.assertIsInstance(GenericIPAddressField().get_prep_value(0), six.text_type) + + def test_NullBooleanField(self): + self.assertIsInstance(NullBooleanField().get_prep_value(True), bool) + + def test_PositiveIntegerField(self): + self.assertIsInstance(PositiveIntegerField().get_prep_value(1), int) + + def test_PositiveSmallIntegerField(self): + self.assertIsInstance(PositiveSmallIntegerField().get_prep_value(1), int) + + def test_SlugField(self): + self.assertIsInstance(SlugField().get_prep_value('slug'), six.text_type) + self.assertIsInstance(SlugField().get_prep_value(0), six.text_type) + + def test_SmallIntegerField(self): + self.assertIsInstance(SmallIntegerField().get_prep_value(1), int) + + def test_TextField(self): + self.assertIsInstance(TextField().get_prep_value('Abc'), six.text_type) + self.assertIsInstance(TextField().get_prep_value(0), six.text_type) + + def test_TimeField(self): + self.assertIsInstance( + TimeField().get_prep_value(datetime.datetime.now().time()), + datetime.time) + + def test_URLField(self): + self.assertIsInstance(URLField().get_prep_value('http://domain.com'), six.text_type) +
5f0829a27e85[1.6.x] Fixed queries that may return unexpected results on MySQL due to typecasting.
6 files changed · +157 −2
django/db/models/fields/__init__.py+15 −1 modified@@ -1013,6 +1013,12 @@ def __init__(self, verbose_name=None, name=None, path='', match=None, kwargs['max_length'] = kwargs.get('max_length', 100) Field.__init__(self, verbose_name, name, **kwargs) + def get_prep_value(self, value): + value = super(FilePathField, self).get_prep_value(value) + if value is None: + return None + return six.text_type(value) + def formfield(self, **kwargs): defaults = { 'path': self.path, @@ -1120,6 +1126,12 @@ def __init__(self, *args, **kwargs): kwargs['max_length'] = 15 Field.__init__(self, *args, **kwargs) + def get_prep_value(self, value): + value = super(IPAddressField, self).get_prep_value(value) + if value is None: + return None + return six.text_type(value) + def get_internal_type(self): return "IPAddressField" @@ -1158,12 +1170,14 @@ def get_db_prep_value(self, value, connection, prepared=False): return value or None def get_prep_value(self, value): + if value is None: + return value if value and ':' in value: try: return clean_ipv6_address(value, self.unpack_ipv4) except exceptions.ValidationError: pass - return value + return six.text_type(value) def formfield(self, **kwargs): defaults = {
docs/howto/custom-model-fields.txt+10 −0 modified@@ -501,6 +501,16 @@ For example:: return ''.join([''.join(l) for l in (value.north, value.east, value.south, value.west)]) +.. warning:: + + If your custom field uses the ``CHAR``, ``VARCHAR`` or ``TEXT`` + types for MySQL, you must make sure that :meth:`.get_prep_value` + always returns a string type. MySQL performs flexible and unexpected + matching when a query is performed on these types and the provided + value is an integer, which can cause queries to include unexpected + objects in their results. This problem cannot occur if you always + return a string type from :meth:`.get_prep_value`. + Converting query values to database values ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
docs/ref/databases.txt+16 −0 modified@@ -504,6 +504,22 @@ MySQL does not support the ``NOWAIT`` option to the ``SELECT ... FOR UPDATE`` statement. If ``select_for_update()`` is used with ``nowait=True`` then a ``DatabaseError`` will be raised. +Automatic typecasting can cause unexpected results +-------------------------------------------------- + +When performing a query on a string type, but with an integer value, MySQL will +coerce the types of all values in the table to an integer before performing the +comparison. If your table contains the values ``'abc'``, ``'def'`` and you +query for ``WHERE mycolumn=0``, both rows will match. Similarly, ``WHERE mycolumn=1`` +will match the value ``'abc1'``. Therefore, string type fields included in Django +will always cast the value to a string before using it in a query. + +If you implement custom model fields that inherit from :class:`~django.db.models.Field` +directly, are overriding :meth:`~django.db.models.Field.get_prep_value`, or use +:meth:`extra() <django.db.models.query.QuerySet.extra>` or +:meth:`raw() <django.db.models.Manager.raw>`, you should ensure that you +perform the appropriate typecasting. + .. _sqlite-notes: SQLite notes
docs/ref/models/querysets.txt+10 −0 modified@@ -1132,6 +1132,16 @@ of the arguments is required, but you should use at least one of them. Entry.objects.extra(where=['headline=%s'], params=['Lennon']) +.. warning:: + + If you are performing queries on MySQL, note that MySQL's silent type coercion + may cause unexpected results when mixing types. If you query on a string + type column, but with an integer value, MySQL will coerce the types of all values + in the table to an integer before performing the comparison. For example, if your + table contains the values ``'abc'``, ``'def'`` and you query for ``WHERE mycolumn=0``, + both rows will match. To prevent this, perform the correct typecasting + before using the value in a query. + defer ~~~~~
docs/topics/db/sql.txt+10 −0 modified@@ -66,6 +66,16 @@ options that make it very powerful. database, but does nothing to enforce that. If the query does not return rows, a (possibly cryptic) error will result. +.. warning:: + + If you are performing queries on MySQL, note that MySQL's silent type coercion + may cause unexpected results when mixing types. If you query on a string + type column, but with an integer value, MySQL will coerce the types of all values + in the table to an integer before performing the comparison. For example, if your + table contains the values ``'abc'``, ``'def'`` and you query for ``WHERE mycolumn=0``, + both rows will match. To prevent this, perform the correct typecasting + before using the value in a query. + Mapping query fields to model fields ------------------------------------
tests/model_fields/tests.py+96 −1 modified@@ -7,7 +7,14 @@ from django import forms from django.core.exceptions import ValidationError from django.db import connection, models, IntegrityError -from django.db.models.fields.files import FieldFile +from django.db.models.fields import ( + AutoField, BigIntegerField, BinaryField, BooleanField, CharField, + CommaSeparatedIntegerField, DateField, DateTimeField, DecimalField, + EmailField, FilePathField, FloatField, IntegerField, IPAddressField, + GenericIPAddressField, NullBooleanField, PositiveIntegerField, + PositiveSmallIntegerField, SlugField, SmallIntegerField, TextField, + TimeField, URLField) +from django.db.models.fields.files import FileField, ImageField from django.utils import six from django.utils import unittest @@ -494,6 +501,94 @@ def test_genericipaddressfield_formfield_protocol(self): self.assertRaises(ValidationError, form_field.clean, '127.0.0.1') +class PrepValueTest(test.TestCase): + def test_AutoField(self): + self.assertIsInstance(AutoField(primary_key=True).get_prep_value(1), int) + + @unittest.skipIf(six.PY3, "Python 3 has no `long` type.") + def test_BigIntegerField(self): + self.assertIsInstance(BigIntegerField().get_prep_value(long(9999999999999999999)), long) + + def test_BinaryField(self): + self.assertIsInstance(BinaryField().get_prep_value(b''), bytes) + + def test_BooleanField(self): + self.assertIsInstance(BooleanField().get_prep_value(True), bool) + + def test_CharField(self): + self.assertIsInstance(CharField().get_prep_value(''), six.text_type) + self.assertIsInstance(CharField().get_prep_value(0), six.text_type) + + def test_CommaSeparatedIntegerField(self): + self.assertIsInstance(CommaSeparatedIntegerField().get_prep_value('1,2'), six.text_type) + self.assertIsInstance(CommaSeparatedIntegerField().get_prep_value(0), six.text_type) + + def test_DateField(self): + self.assertIsInstance(DateField().get_prep_value(datetime.date.today()), datetime.date) + + def test_DateTimeField(self): + self.assertIsInstance(DateTimeField().get_prep_value(datetime.datetime.now()), datetime.datetime) + + def test_DecimalField(self): + self.assertIsInstance(DecimalField().get_prep_value(Decimal('1.2')), Decimal) + + def test_EmailField(self): + self.assertIsInstance(EmailField().get_prep_value('mailbox@domain.com'), six.text_type) + + def test_FileField(self): + self.assertIsInstance(FileField().get_prep_value('filename.ext'), six.text_type) + self.assertIsInstance(FileField().get_prep_value(0), six.text_type) + + def test_FilePathField(self): + self.assertIsInstance(FilePathField().get_prep_value('tests.py'), six.text_type) + self.assertIsInstance(FilePathField().get_prep_value(0), six.text_type) + + def test_FloatField(self): + self.assertIsInstance(FloatField().get_prep_value(1.2), float) + + def test_ImageField(self): + self.assertIsInstance(ImageField().get_prep_value('filename.ext'), six.text_type) + + def test_IntegerField(self): + self.assertIsInstance(IntegerField().get_prep_value(1), int) + + def test_IPAddressField(self): + self.assertIsInstance(IPAddressField().get_prep_value('127.0.0.1'), six.text_type) + self.assertIsInstance(IPAddressField().get_prep_value(0), six.text_type) + + def test_GenericIPAddressField(self): + self.assertIsInstance(GenericIPAddressField().get_prep_value('127.0.0.1'), six.text_type) + self.assertIsInstance(GenericIPAddressField().get_prep_value(0), six.text_type) + + def test_NullBooleanField(self): + self.assertIsInstance(NullBooleanField().get_prep_value(True), bool) + + def test_PositiveIntegerField(self): + self.assertIsInstance(PositiveIntegerField().get_prep_value(1), int) + + def test_PositiveSmallIntegerField(self): + self.assertIsInstance(PositiveSmallIntegerField().get_prep_value(1), int) + + def test_SlugField(self): + self.assertIsInstance(SlugField().get_prep_value('slug'), six.text_type) + self.assertIsInstance(SlugField().get_prep_value(0), six.text_type) + + def test_SmallIntegerField(self): + self.assertIsInstance(SmallIntegerField().get_prep_value(1), int) + + def test_TextField(self): + self.assertIsInstance(TextField().get_prep_value('Abc'), six.text_type) + self.assertIsInstance(TextField().get_prep_value(0), six.text_type) + + def test_TimeField(self): + self.assertIsInstance( + TimeField().get_prep_value(datetime.datetime.now().time()), + datetime.time) + + def test_URLField(self): + self.assertIsInstance(URLField().get_prep_value('http://domain.com'), six.text_type) + + class CustomFieldTests(unittest.TestCase): def test_14786(self):
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
14- github.com/advisories/GHSA-wqjj-hx84-v449ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2014-0474ghsaADVISORY
- www.djangoproject.com/weblog/2014/apr/21/security/nvdVendor Advisory
- lists.opensuse.org/opensuse-updates/2014-09/msg00023.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2014-0456.htmlnvdWEB
- rhn.redhat.com/errata/RHSA-2014-0457.htmlnvdWEB
- www.debian.org/security/2014/dsa-2934nvdWEB
- www.ubuntu.com/usn/USN-2169-1nvdWEB
- github.com/django/django/commit/5f0829a27e85d89ad8c433f5c6a7a7d17c9e9292ghsaWEB
- github.com/django/django/commit/985434fb1d6bf2335bf96c6ebf91c3674f1f399fghsaWEB
- github.com/django/django/commit/aa80f498de6d687e613860933ac58433ab71ea4bghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/django/PYSEC-2014-3.yamlghsaWEB
- www.djangoproject.com/weblog/2014/apr/21/securityghsaWEB
- secunia.com/advisories/61281nvd
News mentions
0No linked articles in our index yet.