summaryrefslogtreecommitdiff
path: root/django/forms/formsets.py
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2013-02-12 11:22:41 +0100
committerCarl Meyer <carl@oddbird.net>2013-02-19 11:23:30 -0700
commit35c991aa06aa34fa458f01eac49275ff4c2d76f9 (patch)
tree176feb553b823a20fadc21f084df5d8cb463ef92 /django/forms/formsets.py
parent1f39eafd60761bf6a60b74d9e9859621da1b9363 (diff)
Added a default limit to the maximum number of forms in a formset.
This is a security fix. Disclosure and advisory coming shortly.
Diffstat (limited to 'django/forms/formsets.py')
-rw-r--r--django/forms/formsets.py23
1 files changed, 15 insertions, 8 deletions
diff --git a/django/forms/formsets.py b/django/forms/formsets.py
index 1addbc617b..81b75f2796 100644
--- a/django/forms/formsets.py
+++ b/django/forms/formsets.py
@@ -21,6 +21,9 @@ MAX_NUM_FORM_COUNT = 'MAX_NUM_FORMS'
ORDERING_FIELD_NAME = 'ORDER'
DELETION_FIELD_NAME = 'DELETE'
+# default maximum number of forms in a formset, to prevent memory exhaustion
+DEFAULT_MAX_NUM = 1000
+
class ManagementForm(Form):
"""
``ManagementForm`` is used to keep track of how many form instances
@@ -97,11 +100,10 @@ class BaseFormSet(object):
total_forms = initial_forms + self.extra
# Allow all existing related objects/inlines to be displayed,
# but don't allow extra beyond max_num.
- if self.max_num is not None:
- if initial_forms > self.max_num >= 0:
- total_forms = initial_forms
- elif total_forms > self.max_num >= 0:
- total_forms = self.max_num
+ if initial_forms > self.max_num >= 0:
+ total_forms = initial_forms
+ elif total_forms > self.max_num >= 0:
+ total_forms = self.max_num
return total_forms
def initial_form_count(self):
@@ -111,14 +113,14 @@ class BaseFormSet(object):
else:
# Use the length of the inital data if it's there, 0 otherwise.
initial_forms = self.initial and len(self.initial) or 0
- if self.max_num is not None and (initial_forms > self.max_num >= 0):
+ if initial_forms > self.max_num >= 0:
initial_forms = self.max_num
return initial_forms
def _construct_forms(self):
# instantiate all the forms and put them in self.forms
self.forms = []
- for i in xrange(self.total_form_count()):
+ for i in xrange(min(self.total_form_count(), self.absolute_max)):
self.forms.append(self._construct_form(i))
def _construct_form(self, i, **kwargs):
@@ -367,9 +369,14 @@ class BaseFormSet(object):
def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False,
can_delete=False, max_num=None):
"""Return a FormSet for the given form class."""
+ if max_num is None:
+ max_num = DEFAULT_MAX_NUM
+ # hard limit on forms instantiated, to prevent memory-exhaustion attacks
+ # limit defaults to DEFAULT_MAX_NUM, but developer can increase it via max_num
+ absolute_max = max(DEFAULT_MAX_NUM, max_num)
attrs = {'form': form, 'extra': extra,
'can_order': can_order, 'can_delete': can_delete,
- 'max_num': max_num}
+ 'max_num': max_num, 'absolute_max': absolute_max}
return type(form.__name__ + str('FormSet'), (formset,), attrs)
def all_valid(formsets):