VYPR
High severityNVD Advisory· Published Apr 23, 2014· Updated May 6, 2026

CVE-2014-0474

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.

PackageAffected versionsPatched versions
DjangoPyPI
< 1.4.111.4.11
DjangoPyPI
>= 1.5, < 1.5.61.5.6
DjangoPyPI
>= 1.6, < 1.6.31.6.3

Affected products

28
  • cpe: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

3
aa80f498de6d

[1.4.x] Fixed queries that may return unexpected results on MySQL due to typecasting.

https://github.com/django/djangoErik RomijnApr 20, 2014via ghsa
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.

https://github.com/django/djangoErik RomijnApr 20, 2014via ghsa
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.

https://github.com/django/djangoErik RomijnApr 20, 2014via ghsa
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

News mentions

0

No linked articles in our index yet.