Skip to content

Commit 77a68c0

Browse files
committed
Fix a XSS vulnerability with bad input to json_dumps.
Django's JSON serialization does not handle escaping of any characters to make them safe for injecting into HTML. This allows an attacker who can provide part of a JSON-serializable object to craft a string that can break out of a <script> tag and create its own, injecting a custom script. To fix this, we escape '<', '>', and '&' characters in the resulting string, preventing a </script> from executing.
1 parent 57ee1a0 commit 77a68c0

File tree

2 files changed

+27
-1
lines changed

2 files changed

+27
-1
lines changed

Diff for: djblets/util/templatetags/djblets_js.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,20 @@
3131
from django.core.serializers import serialize
3232
from django.db.models.query import QuerySet
3333
from django.utils import six
34+
from django.utils.encoding import force_text
3435
from django.utils.safestring import mark_safe
3536

3637
from djblets.util.serializers import DjbletsJSONEncoder
3738

3839

3940
register = template.Library()
4041

42+
_safe_js_escapes = {
43+
ord('&'): '\\u0026',
44+
ord('<'): '\\u003C',
45+
ord('>'): '\\u003E',
46+
}
47+
4148

4249
@register.simple_tag
4350
def form_dialog_fields(form):
@@ -75,7 +82,7 @@ def json_dumps(value, indent=None):
7582
else:
7683
result = json.dumps(value, indent=indent, cls=DjbletsJSONEncoder)
7784

78-
return mark_safe(result)
85+
return mark_safe(force_text(result).translate(_safe_js_escapes))
7986

8087

8188
@register.filter

Diff for: djblets/util/templatetags/tests.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from __future__ import unicode_literals
2+
3+
from djblets.testing.testcases import TestCase
4+
from djblets.util.templatetags.djblets_js import json_dumps
5+
6+
7+
class JSTagTests(TestCase):
8+
"""Unit tests for djblets_js template tags."""
9+
def test_json_dumps_xss(self):
10+
"""Testing json_dumps doesn't allow XSS injection"""
11+
# This is bug 3406.
12+
obj = {
13+
'xss': '</script><script>alert(1);</script>'
14+
}
15+
16+
self.assertEqual(
17+
json_dumps(obj),
18+
'{"xss": "\\u003C/script\\u003E\\u003Cscript\\u003E'
19+
'alert(1);\\u003C/script\\u003E"}')

0 commit comments

Comments
 (0)