Skip to content

Commit f3a79ce

Browse files
committed
Update ManagementForm to validate current_step
Previously, bots could and would stuff this form value with all kinds of nonsense, triggering a server error (and subsequent alert emails). Supersedes jazzband#47
1 parent 97441f1 commit f3a79ce

File tree

3 files changed

+32
-9
lines changed

3 files changed

+32
-9
lines changed

formtools/wizard/forms.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
from django import forms
2+
from django.core.exceptions import ValidationError
23

34

45
class ManagementForm(forms.Form):
56
"""
67
``ManagementForm`` is used to keep track of the current wizard step.
78
"""
9+
def __init__(self, steps, **kwargs):
10+
self.steps = steps
11+
super().__init__(**kwargs)
12+
813
template_name = "django/forms/p.html" # Remove when Django 5.0 is minimal version.
914
current_step = forms.CharField(widget=forms.HiddenInput)
15+
16+
def clean_current_step(self):
17+
data = self.cleaned_data['current_step']
18+
if data not in self.steps:
19+
raise ValidationError("Invalid step name.")
20+
return data

formtools/wizard/views.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ def __repr__(self):
4545

4646
@property
4747
def all(self):
48-
"Returns the names of all steps/forms."
48+
"""Returns the names of all steps/forms."""
4949
return list(self._wizard.get_form_list())
5050

5151
@property
5252
def count(self):
53-
"Returns the total number of steps/forms in this the wizard."
53+
"""Returns the total number of steps/forms in this the wizard."""
5454
return len(self.all)
5555

5656
@property
@@ -63,27 +63,27 @@ def current(self):
6363

6464
@property
6565
def first(self):
66-
"Returns the name of the first step."
66+
"""Returns the name of the first step."""
6767
return self.all[0]
6868

6969
@property
7070
def last(self):
71-
"Returns the name of the last step."
71+
"""Returns the name of the last step."""
7272
return self.all[-1]
7373

7474
@property
7575
def next(self):
76-
"Returns the next step."
76+
"""Returns the next step."""
7777
return self._wizard.get_next_step()
7878

7979
@property
8080
def prev(self):
81-
"Returns the previous step."
81+
"""Returns the previous step."""
8282
return self._wizard.get_prev_step()
8383

8484
@property
8585
def index(self):
86-
"Returns the index for the current step."
86+
"""Returns the index for the current step."""
8787
return self._wizard.get_step_index()
8888

8989
@property
@@ -277,7 +277,7 @@ def post(self, *args, **kwargs):
277277
return self.render_goto_step(wizard_goto_step)
278278

279279
# Check if form was refreshed
280-
management_form = ManagementForm(self.request.POST, prefix=self.prefix)
280+
management_form = ManagementForm(steps=self.steps.all, data=self.request.POST, prefix=self.prefix)
281281
if not management_form.is_valid():
282282
raise SuspiciousOperation(_('ManagementForm data is missing or has been tampered.'))
283283

@@ -576,7 +576,7 @@ def get_context_data(self, form, **kwargs):
576576
context['wizard'] = {
577577
'form': form,
578578
'steps': self.steps,
579-
'management_form': ManagementForm(prefix=self.prefix, initial={
579+
'management_form': ManagementForm(steps=self.steps.all, prefix=self.prefix, initial={
580580
'current_step': self.steps.current,
581581
}),
582582
}

tests/wizard/wizardtests/tests.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ def test_form_post_mgmt_data_missing(self):
7373
# view should return HTTP 400 Bad Request
7474
self.assertEqual(response.status_code, 400)
7575

76+
def test_invalid_step_data(self):
77+
wizard_step_data = self.wizard_step_data[0].copy()
78+
79+
# Replace the current step with invalid data
80+
for key in list(wizard_step_data.keys()):
81+
if "current_step" in key:
82+
wizard_step_data[key] = "not-a-valid-step"
83+
84+
response = self.client.post(self.wizard_url, wizard_step_data)
85+
# view should return HTTP 400 Bad Request
86+
self.assertEqual(response.status_code, 400)
87+
7688
def test_form_post_success(self):
7789
response = self.client.post(self.wizard_url, self.wizard_step_data[0])
7890
wizard = response.context['wizard']

0 commit comments

Comments
 (0)