Skip to content

Commit

Permalink
[1.0.X] Fixed #6160, #9111 -- Consistently apply conditional_escape t…
Browse files Browse the repository at this point in the history
…o form errors and labels when outputing them as HTML.

[9365] from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@9366 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
kmtracey committed Nov 6, 2008
1 parent 675ee5c commit 335f4ac
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 13 deletions.
6 changes: 3 additions & 3 deletions django/contrib/auth/tests/views.py
Expand Up @@ -16,7 +16,7 @@ def test_email_not_found(self):
response = self.client.get('/password_reset/') response = self.client.get('/password_reset/')
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'}) response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'})
self.assertContains(response, "That e-mail address doesn't have an associated user account") self.assertContains(response, "That e-mail address doesn't have an associated user account")
self.assertEquals(len(mail.outbox), 0) self.assertEquals(len(mail.outbox), 0)


def test_email_found(self): def test_email_found(self):
Expand Down Expand Up @@ -87,7 +87,7 @@ def test_confirm_different_passwords(self):
response = self.client.post(path, {'new_password1': 'anewpassword', response = self.client.post(path, {'new_password1': 'anewpassword',
'new_password2':' x'}) 'new_password2':' x'})
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
self.assert_("The two password fields didn't match" in response.content) self.assert_("The two password fields didn't match" in response.content)




class ChangePasswordTest(TestCase): class ChangePasswordTest(TestCase):
Expand Down Expand Up @@ -147,7 +147,7 @@ def test_password_change_fails_with_mismatched_passwords(self):
} }
) )
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
self.assert_("The two password fields didn't match." in response.content) self.assert_("The two password fields didn't match." in response.content)


def test_password_change_succeeds(self): def test_password_change_succeeds(self):
self.login() self.login()
Expand Down
8 changes: 4 additions & 4 deletions django/forms/forms.py
Expand Up @@ -5,7 +5,7 @@
from copy import deepcopy from copy import deepcopy


from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.utils.html import escape from django.utils.html import conditional_escape
from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe


Expand Down Expand Up @@ -140,7 +140,7 @@ def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_
output, hidden_fields = [], [] output, hidden_fields = [], []
for name, field in self.fields.items(): for name, field in self.fields.items():
bf = BoundField(self, field, name) bf = BoundField(self, field, name)
bf_errors = self.error_class([escape(error) for error in bf.errors]) # Escape and cache in local variable. bf_errors = self.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable.
if bf.is_hidden: if bf.is_hidden:
if bf_errors: if bf_errors:
top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors]) top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors])
Expand All @@ -149,7 +149,7 @@ def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_
if errors_on_separate_row and bf_errors: if errors_on_separate_row and bf_errors:
output.append(error_row % force_unicode(bf_errors)) output.append(error_row % force_unicode(bf_errors))
if bf.label: if bf.label:
label = escape(force_unicode(bf.label)) label = conditional_escape(force_unicode(bf.label))
# Only add the suffix if the label does not end in # Only add the suffix if the label does not end in
# punctuation. # punctuation.
if self.label_suffix: if self.label_suffix:
Expand Down Expand Up @@ -395,7 +395,7 @@ def label_tag(self, contents=None, attrs=None):
If attrs are given, they're used as HTML attributes on the <label> tag. If attrs are given, they're used as HTML attributes on the <label> tag.
""" """
contents = contents or escape(self.label) contents = contents or conditional_escape(self.label)
widget = self.field.widget widget = self.field.widget
id_ = widget.attrs.get('id') or self.auto_id id_ = widget.attrs.get('id') or self.auto_id
if id_: if id_:
Expand Down
2 changes: 1 addition & 1 deletion django/forms/util.py
Expand Up @@ -39,7 +39,7 @@ def __unicode__(self):
def as_ul(self): def as_ul(self):
if not self: return u'' if not self: return u''
return mark_safe(u'<ul class="errorlist">%s</ul>' return mark_safe(u'<ul class="errorlist">%s</ul>'
% ''.join([u'<li>%s</li>' % force_unicode(e) for e in self])) % ''.join([u'<li>%s</li>' % conditional_escape(force_unicode(e)) for e in self]))


def as_text(self): def as_text(self):
if not self: return u'' if not self: return u''
Expand Down
18 changes: 13 additions & 5 deletions tests/regressiontests/forms/forms.py
Expand Up @@ -593,17 +593,25 @@
u'Yesterday' u'Yesterday'
Validation errors are HTML-escaped when output as HTML. Validation errors are HTML-escaped when output as HTML.
>>> from django.utils.safestring import mark_safe
>>> class EscapingForm(Form): >>> class EscapingForm(Form):
... special_name = CharField() ... special_name = CharField(label="<em>Special</em> Field")
... special_safe_name = CharField(label=mark_safe("<em>Special</em> Field"))
... def clean_special_name(self): ... def clean_special_name(self):
... raise ValidationError("Something's wrong with '%s'" % self.cleaned_data['special_name']) ... raise ValidationError("Something's wrong with '%s'" % self.cleaned_data['special_name'])
... def clean_special_safe_name(self):
... raise ValidationError(mark_safe("'<b>%s</b>' is a safe string" % self.cleaned_data['special_safe_name']))
>>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False) >>> f = EscapingForm({'special_name': "Nothing to escape", 'special_safe_name': "Nothing to escape"}, auto_id=False)
>>> print f >>> print f
<tr><th>Special name:</th><td><ul class="errorlist"><li>Something&#39;s wrong with &#39;Nothing to escape&#39;</li></ul><input type="text" name="special_name" value="Nothing to escape" /></td></tr> <tr><th>&lt;em&gt;Special&lt;/em&gt; Field:</th><td><ul class="errorlist"><li>Something&#39;s wrong with &#39;Nothing to escape&#39;</li></ul><input type="text" name="special_name" value="Nothing to escape" /></td></tr>
>>> f = EscapingForm({'special_name': "Should escape < & > and <script>alert('xss')</script>"}, auto_id=False) <tr><th><em>Special</em> Field:</th><td><ul class="errorlist"><li>'<b>Nothing to escape</b>' is a safe string</li></ul><input type="text" name="special_safe_name" value="Nothing to escape" /></td></tr>
>>> f = EscapingForm(
... {'special_name': "Should escape < & > and <script>alert('xss')</script>",
... 'special_safe_name': "<i>Do not escape</i>"}, auto_id=False)
>>> print f >>> print f
<tr><th>Special name:</th><td><ul class="errorlist"><li>Something&#39;s wrong with &#39;Should escape &lt; &amp; &gt; and &lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;&#39;</li></ul><input type="text" name="special_name" value="Should escape &lt; &amp; &gt; and &lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;" /></td></tr> <tr><th>&lt;em&gt;Special&lt;/em&gt; Field:</th><td><ul class="errorlist"><li>Something&#39;s wrong with &#39;Should escape &lt; &amp; &gt; and &lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;&#39;</li></ul><input type="text" name="special_name" value="Should escape &lt; &amp; &gt; and &lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;" /></td></tr>
<tr><th><em>Special</em> Field:</th><td><ul class="errorlist"><li>'<b><i>Do not escape</i></b>' is a safe string</li></ul><input type="text" name="special_safe_name" value="&lt;i&gt;Do not escape&lt;/i&gt;" /></td></tr>
""" + \ """ + \
r""" # [This concatenation is to keep the string below the jython's 32K limit]. r""" # [This concatenation is to keep the string below the jython's 32K limit].
Expand Down
7 changes: 7 additions & 0 deletions tests/regressiontests/forms/util.py
Expand Up @@ -49,4 +49,11 @@
# Can take a non-string. # Can take a non-string.
>>> print ValidationError(VeryBadError()).messages >>> print ValidationError(VeryBadError()).messages
<ul class="errorlist"><li>A very bad error.</li></ul> <ul class="errorlist"><li>A very bad error.</li></ul>
# Escapes non-safe input but not input marked safe.
>>> example = 'Example of link: <a href="http://www.example.com/">example</a>'
>>> print ValidationError(example).messages
<ul class="errorlist"><li>Example of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>
>>> print ValidationError(mark_safe(example)).messages
<ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></li></ul>
""" """

0 comments on commit 335f4ac

Please sign in to comment.