VYPR
High severityNVD Advisory· Published Jan 16, 2015· Updated May 6, 2026

CVE-2015-0222

CVE-2015-0222

Description

ModelMultipleChoiceField in Django 1.6.x before 1.6.10 and 1.7.x before 1.7.3, when show_hidden_initial is set to True, allows remote attackers to cause a denial of service by submitting duplicate values, which triggers a large number of SQL queries.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
DjangoPyPI
>= 1.6, < 1.6.101.6.10
DjangoPyPI
>= 1.7, < 1.7.31.7.3

Affected products

18
  • cpe:2.3:o:canonical:ubuntu_linux:10.04:*:lts:*:*:*:*:*+ 3 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:14.04:*:*:*:lts:*:*:*
    • cpe:2.3:o:canonical:ubuntu_linux:14.10:*:*:*:*:*:*:*
  • cpe:2.3:a:djangoproject:django:1.6.8:*:*:*:*:*:*:*+ 13 more
    • cpe:2.3:a:djangoproject:django:1.6.8:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.9:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7.1:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:*:*:*:*:*:*:*:*range: <=1.4.17
    • 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.6.3:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.4:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.5:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.6:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.6.7:*:*:*:*:*:*:*
    • cpe:2.3:a:djangoproject:django:1.7.2:*:*:*:*:*:*:*

Patches

2
bcfb47780ce7

[1.7.x] Fixed DoS possibility in ModelMultipleChoiceField.

https://github.com/django/djangoTim GrahamDec 11, 2014via ghsa
5 files changed · +63 5
  • django/forms/models.py+23 5 modified
    @@ -1221,8 +1221,7 @@ def __init__(self, queryset, cache_choices=False, required=True,
         def to_python(self, value):
             if not value:
                 return []
    -        to_py = super(ModelMultipleChoiceField, self).to_python
    -        return [to_py(val) for val in value]
    +        return list(self._check_values(value))
     
         def clean(self, value):
             if self.required and not value:
    @@ -1231,7 +1230,29 @@ def clean(self, value):
                 return self.queryset.none()
             if not isinstance(value, (list, tuple)):
                 raise ValidationError(self.error_messages['list'], code='list')
    +        qs = self._check_values(value)
    +        # Since this overrides the inherited ModelChoiceField.clean
    +        # we run custom validators here
    +        self.run_validators(value)
    +        return qs
    +
    +    def _check_values(self, value):
    +        """
    +        Given a list of possible PK values, returns a QuerySet of the
    +        corresponding objects. Raises a ValidationError if a given value is
    +        invalid (not a valid PK, not in the queryset, etc.)
    +        """
             key = self.to_field_name or 'pk'
    +        # deduplicate given values to avoid creating many querysets or
    +        # requiring the database backend deduplicate efficiently.
    +        try:
    +            value = frozenset(value)
    +        except TypeError:
    +            # list of lists isn't hashable, for example
    +            raise ValidationError(
    +                self.error_messages['list'],
    +                code='list',
    +            )
             for pk in value:
                 try:
                     self.queryset.filter(**{key: pk})
    @@ -1250,9 +1271,6 @@ def clean(self, value):
                         code='invalid_choice',
                         params={'value': val},
                     )
    -        # Since this overrides the inherited ModelChoiceField.clean
    -        # we run custom validators here
    -        self.run_validators(value)
             return qs
     
         def prepare_value(self, value):
    
  • docs/releases/1.6.10.txt+9 0 modified
    @@ -58,3 +58,12 @@ Note, however, that this view has always carried a warning that it is not
     hardened for production use and should be used only as a development aid. Now
     may be a good time to audit your project and serve your files in production
     using a real front-end web server if you are not doing so.
    +
    +Database denial-of-service with ``ModelMultipleChoiceField``
    +============================================================
    +
    +Given a form that uses ``ModelMultipleChoiceField`` and
    +``show_hidden_initial=True`` (not a documented API), it was possible for a user
    +to cause an unreasonable number of SQL queries by submitting duplicate values
    +for the field's data. The validation logic in ``ModelMultipleChoiceField`` now
    +deduplicates submitted values to address this issue.
    
  • docs/releases/1.7.3.txt+9 0 modified
    @@ -59,6 +59,15 @@ hardened for production use and should be used only as a development aid. Now
     may be a good time to audit your project and serve your files in production
     using a real front-end web server if you are not doing so.
     
    +Database denial-of-service with ``ModelMultipleChoiceField``
    +============================================================
    +
    +Given a form that uses ``ModelMultipleChoiceField`` and
    +``show_hidden_initial=True`` (not a documented API), it was possible for a user
    +to cause an unreasonable number of SQL queries by submitting duplicate values
    +for the field's data. The validation logic in ``ModelMultipleChoiceField`` now
    +deduplicates submitted values to address this issue.
    +
     Bugfixes
     ========
     
    
  • docs/spelling_wordlist+1 0 modified
    @@ -134,6 +134,7 @@ dbshell
     de
     deconstruct
     deconstructing
    +deduplicates
     deepcopy
     deserialization
     deserialize
    
  • tests/model_forms/tests.py+21 0 modified
    @@ -1573,6 +1573,27 @@ class WriterForm(forms.Form):
             self.assertTrue(form.is_valid())
             self.assertTrue(form.has_changed())
     
    +    def test_show_hidden_initial_changed_queries_efficiently(self):
    +        class WriterForm(forms.Form):
    +            persons = forms.ModelMultipleChoiceField(
    +                show_hidden_initial=True, queryset=Writer.objects.all())
    +
    +        writers = (Writer.objects.create(name=str(x)) for x in range(0, 50))
    +        writer_pks = tuple(x.pk for x in writers)
    +        form = WriterForm(data={'initial-persons': writer_pks})
    +        with self.assertNumQueries(1):
    +            self.assertTrue(form.has_changed())
    +
    +    def test_clean_does_deduplicate_values(self):
    +        class WriterForm(forms.Form):
    +            persons = forms.ModelMultipleChoiceField(queryset=Writer.objects.all())
    +
    +        person1 = Writer.objects.create(name="Person 1")
    +        form = WriterForm(data={})
    +        queryset = form.fields['persons'].clean([str(person1.pk)] * 50)
    +        sql, params = queryset.query.sql_with_params()
    +        self.assertEqual(len(params), 1)
    +
     
     class ModelOneToOneFieldTests(TestCase):
         def test_modelform_onetoonefield(self):
    
d7a06ee7e571

[1.6.x] Fixed DoS possibility in ModelMultipleChoiceField.

https://github.com/django/djangoTim GrahamDec 11, 2014via ghsa
3 files changed · +53 5
  • django/forms/models.py+23 5 modified
    @@ -1174,8 +1174,7 @@ def __init__(self, queryset, cache_choices=False, required=True,
         def to_python(self, value):
             if not value:
                 return []
    -        to_py = super(ModelMultipleChoiceField, self).to_python
    -        return [to_py(val) for val in value]
    +        return list(self._check_values(value))
     
         def clean(self, value):
             if self.required and not value:
    @@ -1184,7 +1183,29 @@ def clean(self, value):
                 return self.queryset.none()
             if not isinstance(value, (list, tuple)):
                 raise ValidationError(self.error_messages['list'], code='list')
    +        qs = self._check_values(value)
    +        # Since this overrides the inherited ModelChoiceField.clean
    +        # we run custom validators here
    +        self.run_validators(value)
    +        return qs
    +
    +    def _check_values(self, value):
    +        """
    +        Given a list of possible PK values, returns a QuerySet of the
    +        corresponding objects. Raises a ValidationError if a given value is
    +        invalid (not a valid PK, not in the queryset, etc.)
    +        """
             key = self.to_field_name or 'pk'
    +        # deduplicate given values to avoid creating many querysets or
    +        # requiring the database backend deduplicate efficiently.
    +        try:
    +            value = frozenset(value)
    +        except TypeError:
    +            # list of lists isn't hashable, for example
    +            raise ValidationError(
    +                self.error_messages['list'],
    +                code='list',
    +            )
             for pk in value:
                 try:
                     self.queryset.filter(**{key: pk})
    @@ -1203,9 +1224,6 @@ def clean(self, value):
                         code='invalid_choice',
                         params={'value': val},
                     )
    -        # Since this overrides the inherited ModelChoiceField.clean
    -        # we run custom validators here
    -        self.run_validators(value)
             return qs
     
         def prepare_value(self, value):
    
  • docs/releases/1.6.10.txt+9 0 modified
    @@ -58,3 +58,12 @@ Note, however, that this view has always carried a warning that it is not
     hardened for production use and should be used only as a development aid. Now
     may be a good time to audit your project and serve your files in production
     using a real front-end web server if you are not doing so.
    +
    +Database denial-of-service with ``ModelMultipleChoiceField``
    +============================================================
    +
    +Given a form that uses ``ModelMultipleChoiceField`` and
    +``show_hidden_initial=True`` (not a documented API), it was possible for a user
    +to cause an unreasonable number of SQL queries by submitting duplicate values
    +for the field's data. The validation logic in ``ModelMultipleChoiceField`` now
    +deduplicates submitted values to address this issue.
    
  • tests/model_forms/tests.py+21 0 modified
    @@ -1381,6 +1381,27 @@ def test_with_data(self):
     </select></p>
     <p><label for="id_age">Age:</label> <input type="number" name="age" value="65" id="id_age" min="0" /></p>''' % (w_woodward.pk, w_bernstein.pk, bw.pk, w_royko.pk))
     
    +    def test_show_hidden_initial_changed_queries_efficiently(self):
    +        class WriterForm(forms.Form):
    +            persons = forms.ModelMultipleChoiceField(
    +                show_hidden_initial=True, queryset=Writer.objects.all())
    +
    +        writers = (Writer.objects.create(name=str(x)) for x in range(0, 50))
    +        writer_pks = tuple(x.pk for x in writers)
    +        form = WriterForm(data={'initial-persons': writer_pks})
    +        with self.assertNumQueries(1):
    +            self.assertTrue(form.has_changed())
    +
    +    def test_clean_does_deduplicate_values(self):
    +        class WriterForm(forms.Form):
    +            persons = forms.ModelMultipleChoiceField(queryset=Writer.objects.all())
    +
    +        person1 = Writer.objects.create(name="Person 1")
    +        form = WriterForm(data={})
    +        queryset = form.fields['persons'].clean([str(person1.pk)] * 50)
    +        sql, params = queryset.query.sql_with_params()
    +        self.assertEqual(len(params), 1)
    +
         def test_file_field(self):
             # Test conditions when files is either not given or empty.
     
    

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

19

News mentions

0

No linked articles in our index yet.