Merge pull request #12335 from edx/christina/tnl-4193
HTML-encode string values in keyword substitution.
This commit is contained in:
@@ -12,6 +12,7 @@ file and check it in at the same time as your model changes. To do that,
|
||||
|
||||
"""
|
||||
import logging
|
||||
import markupsafe
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
@@ -176,7 +177,7 @@ class CourseEmailTemplate(models.Model):
|
||||
which is rendered using format() with the provided `context` dict.
|
||||
|
||||
Any keywords encoded in the form %%KEYWORD%% found in the message
|
||||
body are subtituted with user data before the body is inserted into
|
||||
body are substituted with user data before the body is inserted into
|
||||
the template.
|
||||
|
||||
Output is returned as a unicode string. It is not encoded as utf-8.
|
||||
@@ -215,6 +216,10 @@ class CourseEmailTemplate(models.Model):
|
||||
Convert HTML text body (`htmltext`) into HTML email message using the
|
||||
stored HTML template and the provided `context` dict.
|
||||
"""
|
||||
# HTML-escape string values in the context (used for keyword substitution).
|
||||
for key, value in context.iteritems():
|
||||
if isinstance(value, basestring):
|
||||
context[key] = markupsafe.escape(value)
|
||||
return CourseEmailTemplate._render(self.html_template, htmltext, context)
|
||||
|
||||
|
||||
|
||||
@@ -97,6 +97,15 @@ class CourseEmailTemplateTest(TestCase):
|
||||
context['course_image_url'] = "/location/of/course/image/url"
|
||||
return context
|
||||
|
||||
def _add_xss_fields(self, context):
|
||||
""" Add fields to the context for XSS testing. """
|
||||
context['course_title'] = "<script>alert('Course Title!');</alert>"
|
||||
context['name'] = "<script>alert('Profile Name!');</alert>"
|
||||
# Must have user_id and course_id present in order to do keyword substitution
|
||||
context['user_id'] = 12345
|
||||
context['course_id'] = "course-v1:edx+100+1"
|
||||
return context
|
||||
|
||||
def test_get_template(self):
|
||||
# Get the default template, which has name=None
|
||||
template = CourseEmailTemplate.get_template()
|
||||
@@ -134,11 +143,31 @@ class CourseEmailTemplateTest(TestCase):
|
||||
context = self._get_sample_html_context()
|
||||
template.render_htmltext("My new html text.", context)
|
||||
|
||||
def test_render_html_xss(self):
|
||||
template = CourseEmailTemplate.get_template()
|
||||
context = self._add_xss_fields(self._get_sample_html_context())
|
||||
message = template.render_htmltext(
|
||||
"Dear %%USER_FULLNAME%%, thanks for enrolling in %%COURSE_DISPLAY_NAME%%.", context
|
||||
)
|
||||
self.assertNotIn("<script>", message)
|
||||
self.assertIn("<script>alert('Course Title!');</alert>", message)
|
||||
self.assertIn("<script>alert('Profile Name!');</alert>", message)
|
||||
|
||||
def test_render_plain(self):
|
||||
template = CourseEmailTemplate.get_template()
|
||||
context = self._get_sample_plain_context()
|
||||
template.render_plaintext("My new plain text.", context)
|
||||
|
||||
def test_render_plain_no_escaping(self):
|
||||
template = CourseEmailTemplate.get_template()
|
||||
context = self._add_xss_fields(self._get_sample_plain_context())
|
||||
message = template.render_plaintext(
|
||||
"Dear %%USER_FULLNAME%%, thanks for enrolling in %%COURSE_DISPLAY_NAME%%.", context
|
||||
)
|
||||
self.assertNotIn("<script>", message)
|
||||
self.assertIn(context['course_title'], message)
|
||||
self.assertIn(context['name'], message)
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
class CourseAuthorizationTest(TestCase):
|
||||
|
||||
Reference in New Issue
Block a user