Ticket #15075: 15075_fix.patch

File 15075_fix.patch, 5.7 KB (added by lukeplant, 4 years ago)

Patch by stas

  • django/contrib/formtools/tests/__init__.py

     
    233233class WizardPageTwoForm(forms.Form):
    234234    field = forms.CharField()
    235235
     236class WizardPageTwoAlternativeForm(forms.Form):
     237    field = forms.CharField()
    236238
    237239class WizardPageThreeForm(forms.Form):
    238240    field = forms.CharField()
     
    349351        response = self.client.post('/wizard/', data)
    350352        self.assertEquals(2, response.context['step0'])
    351353
     354    def test_14498_formlist(self):
     355        """
     356        Regression test for ticket #14498.  Allow modifying wizard's form_list
     357        in process_step.
     358        """
     359        that = self
     360
     361        class WizardWithProcessStep(WizardClass):
     362            def process_step(self, request, form, step):
     363                if step == 0:
     364                    self.form_list[1] = WizardPageTwoAlternativeForm
     365                if step == 1:
     366                    that.assertTrue(isinstance(form, WizardPageTwoAlternativeForm))
     367
     368        wizard = WizardWithProcessStep([WizardPageOneForm,
     369                                        WizardPageTwoForm,
     370                                        WizardPageThreeForm])
     371        data = {"0-field": "test",
     372                "1-field": "test2",
     373                "hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
     374                "wizard_step": "1"}
     375        wizard(DummyRequest(POST=data))
     376
    352377    def test_14498(self):
    353378        """
    354         Regression test for ticket #14498.
     379        Regression test for ticket #14498.  All previous steps' forms should be
     380        validated.
    355381        """
    356382        that = self
    357383
  • django/contrib/formtools/wizard.py

     
    9090        if current_step >= self.num_steps():
    9191            raise Http404('Step %s does not exist' % current_step)
    9292
     93        # Validate and process all the previous forms before instantiating the
     94        # current step's form in case self.process_step makes changes to
     95        # self.form_list.
     96
     97        # If any of them fails validation, that must mean the validator relied
     98        # on some other input, such as an external Web site.
     99
     100        # It is also possible that alidation might fail under certain attack
     101        # situations: an attacker might be able to bypass previous stages, and
     102        # generate correct security hashes for all the skipped stages by virtue
     103        # of:
     104        #  1) having filled out an identical form which doesn't have the
     105        #     validation (and does something different at the end),
     106        #  2) or having filled out a previous version of the same form which
     107        #     had some validation missing,
     108        #  3) or previously having filled out the form when they had more
     109        #     privileges than they do now.
     110        #
     111        # Since the hashes only take into account values, and not other other
     112        # validation the form might do, we must re-do validation now for
     113        # security reasons.
     114        previous_form_list = []
     115        for i in range(current_step):
     116            f = self.get_form(i, request.POST)
     117            if not self._check_security_hash(request.POST.get("hash_%d" % i, ''),
     118                                             request, f):
     119                return self.render_hash_failure(request, i)
     120
     121            if not f.is_valid():
     122                return self.render_revalidation_failure(request, i, f)
     123            else:
     124                self.process_step(request, f, i)
     125                previous_form_list.append(f)
     126
    93127        # Process the current step. If it's valid, go to the next step or call
    94128        # done(), depending on whether any steps remain.
    95129        if request.method == 'POST':
     
    98132            form = self.get_form(current_step)
    99133
    100134        if form.is_valid():
    101             # Validate all the forms. If any of them fail validation, that
    102             # must mean the validator relied on some other input, such as
    103             # an external Web site.
    104 
    105             # It is also possible that validation might fail under certain
    106             # attack situations: an attacker might be able to bypass previous
    107             # stages, and generate correct security hashes for all the
    108             # skipped stages by virtue of:
    109             #  1) having filled out an identical form which doesn't have the
    110             #     validation (and does something different at the end),
    111             #  2) or having filled out a previous version of the same form
    112             #     which had some validation missing,
    113             #  3) or previously having filled out the form when they had
    114             #     more privileges than they do now.
    115             #
    116             # Since the hashes only take into account values, and not other
    117             # other validation the form might do, we must re-do validation
    118             # now for security reasons.
    119             previous_form_list = [self.get_form(i, request.POST) for i in range(current_step)]
    120 
    121             for i, f in enumerate(previous_form_list):
    122                 if not self._check_security_hash(request.POST.get("hash_%d" % i, ''), request, f):
    123                     return self.render_hash_failure(request, i)
    124 
    125                 if not f.is_valid():
    126                     return self.render_revalidation_failure(request, i, f)
    127                 else:
    128                     self.process_step(request, f, i)
    129 
    130             # Now progress to processing this step:
    131135            self.process_step(request, form, current_step)
    132136            next_step = current_step + 1
    133137
Back to Top