Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #21962 -- Added escape_html flag to ErrorDict.as_json()

  • Loading branch information...
commit c23b3717be71e4b2e5a32f156ef0a7b4703d012d 1 parent 7b47435
@vvojvoda vvojvoda authored timgraham committed
View
11 django/forms/utils.py
@@ -5,7 +5,7 @@
import warnings
from django.conf import settings
-from django.utils.html import format_html, format_html_join
+from django.utils.html import format_html, format_html_join, escape
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
@@ -55,8 +55,8 @@ class ErrorDict(dict):
def as_data(self):
return {f: e.as_data() for f, e in self.items()}
- def as_json(self):
- errors = {f: json.loads(e.as_json()) for f, e in self.items()}
+ def as_json(self, escape_html=False):
+ errors = {f: json.loads(e.as_json(escape_html=escape_html)) for f, e in self.items()}
return json.dumps(errors)
def as_ul(self):
@@ -86,11 +86,12 @@ class ErrorList(UserList, list):
def as_data(self):
return self.data
- def as_json(self):
+ def as_json(self, escape_html=False):
errors = []
for error in ValidationError(self.data).error_list:
+ message = list(error)[0]
errors.append({
- 'message': list(error)[0],
+ 'message': escape(message) if escape_html else message,
'code': error.code or '',
})
return json.dumps(errors)
View
13 docs/ref/forms/api.txt
@@ -142,7 +142,7 @@ and methods with an ``as_`` prefix could render them, but it had to be done
the other way around in order not to break code that expects rendered error
messages in ``Form.errors``.
-.. method:: Form.errors.as_json()
+.. method:: Form.errors.as_json(escape_html=False)
.. versionadded:: 1.7
@@ -152,6 +152,17 @@ Returns the errors serialized as JSON.
{"sender": [{"message": "Enter a valid email address.", "code": "invalid"}],
"subject": [{"message": "This field is required.", "code": "required"}]}
+By default, ``as_json()`` does not escape its output. If you are using it for
+something like AJAX requests to a form view where the client interprets the
+response and inserts errors into the page, you'll want to be sure to escape the
+results on the client-side to avoid the possibility of a cross-site scripting
+attack. It's trivial to do so using a JavaScript library like jQuery - simply
+use ``$(el).text(errorText)`` rather than ``.html()``.
+
+If for some reason you don't want to use client-side escaping, you can also
+set ``escape_html=True`` and error messages will be escaped so you can use them
+directly in HTML.
+
.. method:: Form.add_error(field, error)
.. versionadded:: 1.7
View
27 tests/forms_tests/tests/test_forms.py
@@ -2071,6 +2071,33 @@ def clean(self):
}
self.assertEqual(errors, control)
+ def test_error_dict_as_json_escape_html(self):
+ """#21962 - adding html escape flag to ErrorDict"""
+ class MyForm(Form):
+ foo = CharField()
+ bar = CharField()
+
+ def clean(self):
+ raise ValidationError('<p>Non-field error.</p>',
+ code='secret',
+ params={'a': 1, 'b': 2})
+
+ control = {
+ 'foo': [{'code': 'required', 'message': 'This field is required.'}],
+ 'bar': [{'code': 'required', 'message': 'This field is required.'}],
+ '__all__': [{'code': 'secret', 'message': '<p>Non-field error.</p>'}]
+ }
+
+ form = MyForm({})
+ self.assertFalse(form.is_valid())
+
+ errors = json.loads(form.errors.as_json())
+ self.assertEqual(errors, control)
+
+ errors = json.loads(form.errors.as_json(escape_html=True))
+ control['__all__'][0]['message'] = '&lt;p&gt;Non-field error.&lt;/p&gt;'
+ self.assertEqual(errors, control)
+
def test_error_list(self):
e = ErrorList()
e.append('Foo')

0 comments on commit c23b371

Please sign in to comment.
Something went wrong with that request. Please try again.