From 3682fac83294f6f397829bea730b58a3c08ccf28 Mon Sep 17 00:00:00 2001 From: Robert Raposa Date: Tue, 6 Oct 2015 17:06:00 -0400 Subject: [PATCH] Add json escaping to Studio Make escaping for json simpler and more consistent in Mako templates - add escape_json_dumps to escape and json.dumps - add escape_js_str to escape javascript string - refactor Studio to use escape_json_dumps in Mako templates TNL-2646: Escape json.dumps --- .../contentstore/views/certificates.py | 2 +- .../contentstore/views/component.py | 3 +- cms/djangoapps/contentstore/views/course.py | 10 +-- .../contentstore/views/entrance_exam.py | 12 +-- cms/djangoapps/contentstore/views/error.py | 4 +- cms/djangoapps/contentstore/views/item.py | 2 +- cms/djangoapps/contentstore/views/library.py | 2 +- .../contentstore/views/tests/test_item.py | 2 +- .../spec/views/pages/course_outline_spec.js | 2 +- .../js/views/modals/course_outline_modals.js | 2 +- cms/templates/base.html | 11 +-- cms/templates/certificates.html | 4 +- cms/templates/container.html | 8 +- cms/templates/course_info.html | 4 +- cms/templates/course_outline.html | 4 +- cms/templates/export.html | 8 +- cms/templates/group_configurations.html | 4 +- cms/templates/import.html | 4 +- cms/templates/library.html | 7 +- cms/templates/manage_users.html | 4 +- cms/templates/manage_users_lib.html | 4 +- cms/templates/settings.html | 4 +- cms/templates/settings_advanced.html | 2 +- cms/templates/settings_graders.html | 4 +- cms/templates/studio_xblock_wrapper.html | 6 +- cms/templates/textbooks.html | 4 +- .../tests/specs/test_testshib.py | 7 +- .../student_account/test/test_views.py | 4 +- .../teams/templates/teams/teams.html | 10 +-- lms/templates/courseware/courses.html | 4 +- .../student_account/login_and_register.html | 4 +- .../student_profile/learner_profile.html | 4 +- openedx/core/lib/js_utils.py | 83 +++++++++++++++++++ openedx/core/lib/json_utils.py | 70 ---------------- .../{test_json_utils.py => test_js_utils.py} | 32 +++---- 35 files changed, 173 insertions(+), 168 deletions(-) create mode 100644 openedx/core/lib/js_utils.py delete mode 100644 openedx/core/lib/json_utils.py rename openedx/core/lib/tests/{test_json_utils.py => test_js_utils.py} (77%) diff --git a/cms/djangoapps/contentstore/views/certificates.py b/cms/djangoapps/contentstore/views/certificates.py index d515489f95..ca453c9f7d 100644 --- a/cms/djangoapps/contentstore/views/certificates.py +++ b/cms/djangoapps/contentstore/views/certificates.py @@ -376,7 +376,7 @@ def certificates_list_handler(request, course_key_string): 'certificate_url': certificate_url, 'course_outline_url': course_outline_url, 'upload_asset_url': upload_asset_url, - 'certificates': json.dumps(certificates), + 'certificates': certificates, 'course_modes': course_modes, 'certificate_web_view_url': certificate_web_view_url, 'is_active': is_active, diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index b85900efdc..3e131378b1 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -1,6 +1,5 @@ from __future__ import absolute_import -import json import logging from django.http import HttpResponseBadRequest, Http404 @@ -153,7 +152,7 @@ def container_handler(request, usage_key_string): 'section': section, 'new_unit_category': 'vertical', 'ancestor_xblocks': ancestor_xblocks, - 'component_templates': json.dumps(component_templates), + 'component_templates': component_templates, 'xblock_info': xblock_info, 'draft_preview_link': preview_lms_link, 'published_preview_link': lms_link, diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 8d3ebe08be..6d4dba3649 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -36,6 +36,7 @@ from opaque_keys.edx.locations import Location from opaque_keys.edx.keys import CourseKey from django.views.decorators.csrf import ensure_csrf_cookie +from openedx.core.lib.js_utils import escape_json_dumps from contentstore.course_info_model import get_course_updates, update_course_updates, delete_course_update from contentstore.course_group_config import ( GroupConfiguration, @@ -317,10 +318,10 @@ def course_search_index_handler(request, course_key_string): try: reindex_course_and_check_access(course_key, request.user) except SearchIndexingError as search_err: - return HttpResponse(json.dumps({ + return HttpResponse(escape_json_dumps({ "user_message": search_err.error_list }), content_type=content_type, status=500) - return HttpResponse(json.dumps({ + return HttpResponse(escape_json_dumps({ "user_message": _("Course has been successfully reindexed.") }), content_type=content_type, status=200) @@ -554,9 +555,6 @@ def course_index(request, course_key): 'sections': sections, 'course_structure': course_structure, 'initial_state': course_outline_initial_state(locator_to_show, course_structure) if locator_to_show else None, - 'course_graders': json.dumps( - CourseGradingModel.fetch(course_key).graders - ), 'rerun_notification_id': current_action.id if current_action else None, 'course_release_date': course_release_date, 'settings_url': settings_url, @@ -1056,7 +1054,7 @@ def grading_handler(request, course_key_string, grader_index=None): return render_to_response('settings_graders.html', { 'context_course': course_module, 'course_locator': course_key, - 'course_details': json.dumps(course_details, cls=CourseSettingsEncoder), + 'course_details': course_details, 'grading_url': reverse_course_url('grading_handler', course_key), 'is_credit_course': is_credit_course(course_key), }) diff --git a/cms/djangoapps/contentstore/views/entrance_exam.py b/cms/djangoapps/contentstore/views/entrance_exam.py index 8087b5a02a..aaf311ba4f 100644 --- a/cms/djangoapps/contentstore/views/entrance_exam.py +++ b/cms/djangoapps/contentstore/views/entrance_exam.py @@ -10,6 +10,7 @@ from django.contrib.auth.decorators import login_required from django.views.decorators.csrf import ensure_csrf_cookie from django.http import HttpResponse, HttpResponseBadRequest +from openedx.core.lib.js_utils import escape_json_dumps from contentstore.views.helpers import create_xblock, remove_entrance_exam_graders from contentstore.views.item import delete_item from models.settings.course_metadata import CourseMetadata @@ -185,7 +186,7 @@ def _get_entrance_exam(request, course_key): # pylint: disable=W0613 try: exam_descriptor = modulestore().get_item(exam_key) return HttpResponse( - _serialize_entrance_exam(exam_descriptor), + escape_json_dumps({'locator': unicode(exam_descriptor.location)}), status=200, mimetype='application/json') except ItemNotFoundError: return HttpResponse(status=404) @@ -241,15 +242,6 @@ def _delete_entrance_exam(request, course_key): return HttpResponse(status=204) -def _serialize_entrance_exam(entrance_exam_module): - """ - Internal helper to convert an entrance exam module/object into JSON - """ - return json.dumps({ - 'locator': unicode(entrance_exam_module.location) - }) - - def add_entrance_exam_milestone(course_id, x_block): # Add an entrance exam milestone if one does not already exist for given xBlock # As this is a standalone method for entrance exam, We should check that given xBlock should be an entrance exam. diff --git a/cms/djangoapps/contentstore/views/error.py b/cms/djangoapps/contentstore/views/error.py index c67a559175..af4e6c1e27 100644 --- a/cms/djangoapps/contentstore/views/error.py +++ b/cms/djangoapps/contentstore/views/error.py @@ -4,7 +4,7 @@ from django.http import (HttpResponse, HttpResponseServerError, HttpResponseNotFound) from edxmako.shortcuts import render_to_string, render_to_response import functools -import json +from openedx.core.lib.js_utils import escape_json_dumps __all__ = ['not_found', 'server_error', 'render_404', 'render_500'] @@ -18,7 +18,7 @@ def jsonable_error(status=500, message="The Studio servers encountered an error" @functools.wraps(func) def inner(request, *args, **kwargs): if request.is_ajax(): - content = json.dumps({"error": message}) + content = escape_json_dumps({"error": message}) return HttpResponse(content, content_type="application/json", status=status) else: diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index cbc1e28cbc..0eddbcb9eb 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -853,7 +853,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F "due_date": get_default_time_display(xblock.due), "due": xblock.fields['due'].to_json(xblock.due), "format": xblock.format, - "course_graders": json.dumps([grader.get('type') for grader in graders]), + "course_graders": [grader.get('type') for grader in graders], "has_changes": has_changes, "actions": xblock_actions, "explanatory_message": explanatory_message, diff --git a/cms/djangoapps/contentstore/views/library.py b/cms/djangoapps/contentstore/views/library.py index 1323aa4f09..9cf459a8a2 100644 --- a/cms/djangoapps/contentstore/views/library.py +++ b/cms/djangoapps/contentstore/views/library.py @@ -191,7 +191,7 @@ def library_blocks_view(library, user, response_format): return render_to_response('library.html', { 'can_edit': can_edit, 'context_library': library, - 'component_templates': json.dumps(component_templates), + 'component_templates': component_templates, 'xblock_info': xblock_info, 'templates': CONTAINER_TEMPLATES, }) diff --git a/cms/djangoapps/contentstore/views/tests/test_item.py b/cms/djangoapps/contentstore/views/tests/test_item.py index c2dfb7bee7..ce923e47d1 100644 --- a/cms/djangoapps/contentstore/views/tests/test_item.py +++ b/cms/djangoapps/contentstore/views/tests/test_item.py @@ -1642,7 +1642,7 @@ class TestXBlockInfo(ItemTest): self.assertEqual(xblock_info['display_name'], 'Week 1') self.assertTrue(xblock_info['published']) self.assertIsNone(xblock_info.get('edited_by', None)) - self.assertEqual(xblock_info['course_graders'], '["Homework", "Lab", "Midterm Exam", "Final Exam"]') + self.assertEqual(xblock_info['course_graders'], ['Homework', 'Lab', 'Midterm Exam', 'Final Exam']) self.assertEqual(xblock_info['start'], '2030-01-01T00:00:00Z') self.assertEqual(xblock_info['graded'], False) self.assertEqual(xblock_info['due'], None) diff --git a/cms/static/js/spec/views/pages/course_outline_spec.js b/cms/static/js/spec/views/pages/course_outline_spec.js index c4d59a50c8..56ee53b2a5 100644 --- a/cms/static/js/spec/views/pages/course_outline_spec.js +++ b/cms/static/js/spec/views/pages/course_outline_spec.js @@ -65,7 +65,7 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/components/u published: true, edited_on: 'Jul 02, 2014 at 20:56 UTC', edited_by: 'MockUser', - course_graders: '["Lab", "Howework"]', + course_graders: ["Lab", "Howework"], has_explicit_staff_lock: false, child_info: { category: 'vertical', diff --git a/cms/static/js/views/modals/course_outline_modals.js b/cms/static/js/views/modals/course_outline_modals.js index 7dc43d65f0..fb7dc29604 100644 --- a/cms/static/js/views/modals/course_outline_modals.js +++ b/cms/static/js/views/modals/course_outline_modals.js @@ -410,7 +410,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', getContext: function () { return { - graderTypes: JSON.parse(this.model.get('course_graders')) + graderTypes: this.model.get('course_graders') }; } }); diff --git a/cms/templates/base.html b/cms/templates/base.html index c0d06ec080..3feec0ad3c 100644 --- a/cms/templates/base.html +++ b/cms/templates/base.html @@ -2,8 +2,9 @@ <%namespace name='static' file='static_content.html'/> <%! from django.utils.translation import ugettext as _ -from django.template.defaultfilters import escapejs -import json +from openedx.core.lib.js_utils import ( + escape_json_dumps, escape_js_string +) %> @@ -41,7 +42,7 @@ import json ${_("Skip to main content")} @@ -79,14 +80,14 @@ import json % if context_course: require(['js/factories/course'], function(CourseFactory) { CourseFactory({ - id: "${context_course.id | escapejs}", + id: "${escape_js_string(context_course.id) | n}", name: "${context_course.display_name_with_default | h}", url_name: "${context_course.location.name | h}", org: "${context_course.location.org | h}", num: "${context_course.location.course | h}", display_course_number: "${_(context_course.display_coursenumber)}", revision: "${context_course.location.revision | h}", - self_paced: ${json.dumps(context_course.self_paced)} + self_paced: ${escape_json_dumps(context_course.self_paced) | n} }); }); % endif diff --git a/cms/templates/certificates.html b/cms/templates/certificates.html index c39f994864..2b144c80d1 100644 --- a/cms/templates/certificates.html +++ b/cms/templates/certificates.html @@ -2,9 +2,9 @@ <%def name="online_help_token()"><% return "certificates" %> <%namespace name='static' file='static_content.html'/> <%! -import json from contentstore import utils from django.utils.translation import ugettext as _ +from openedx.core.lib.js_utils import escape_json_dumps %> <%block name="title">${_("Course Certificates")} @@ -30,7 +30,7 @@ CMS.User.isGlobalStaff = '${is_global_staff}'=='True' ? true : false; <%block name="requirejs"> require(["js/certificates/factories/certificates_page_factory"], function(CertificatesPageFactory) { - CertificatesPageFactory(${json.dumps(certificates)}, "${certificate_url}", "${course_outline_url}", ${json.dumps(course_modes)}, ${json.dumps(certificate_web_view_url)}, ${json.dumps(is_active)}, ${json.dumps(certificate_activation_handler_url)} ); + CertificatesPageFactory(${escape_json_dumps(certificates) | n}, "${certificate_url}", "${course_outline_url}", ${escape_json_dumps(course_modes) | n}, ${escape_json_dumps(certificate_web_view_url) | n}, ${escape_json_dumps(is_active) | n}, ${escape_json_dumps(certificate_activation_handler_url) | n} ); }); diff --git a/cms/templates/container.html b/cms/templates/container.html index 75b11c8241..c44ac74b84 100644 --- a/cms/templates/container.html +++ b/cms/templates/container.html @@ -8,10 +8,9 @@ else: %> <%! -import json - from contentstore.views.helpers import xblock_studio_url, xblock_type_display_name from django.utils.translation import ugettext as _ +from openedx.core.lib.js_utils import escape_json_dumps %> <%block name="title">${xblock.display_name_with_default} ${xblock_type_display_name(xblock) | h} <%block name="bodyclass">is-signedin course container view-container @@ -33,10 +32,11 @@ from django.utils.translation import ugettext as _ <%block name="requirejs"> require(["js/factories/container"], function(ContainerFactory) { ContainerFactory( - ${component_templates | n}, ${json.dumps(xblock_info) | n}, + ${ escape_json_dumps(component_templates) | n }, + ${ escape_json_dumps(xblock_info) | n }, "${action | h}", { - isUnitPage: ${json.dumps(is_unit_page)}, + isUnitPage: ${ escape_json_dumps(is_unit_page) | n }, canEdit: true } ); diff --git a/cms/templates/course_info.html b/cms/templates/course_info.html index 14eac181f2..7fa84c264e 100644 --- a/cms/templates/course_info.html +++ b/cms/templates/course_info.html @@ -2,9 +2,9 @@ <%def name="online_help_token()"><% return "updates" %> <%namespace name='static' file='static_content.html'/> <%! -import json from django.utils.translation import ugettext as _ from django.template.defaultfilters import escapejs +from openedx.core.lib.js_utils import escape_json_dumps %> ## TODO decode course # from context_course into title. @@ -26,7 +26,7 @@ from django.template.defaultfilters import escapejs "${updates_url}", "${handouts_locator | escapejs}", "${base_asset_url}", - ${json.dumps(push_notification_enabled)} + ${escape_json_dumps(push_notification_enabled) | n} ); }); diff --git a/cms/templates/course_outline.html b/cms/templates/course_outline.html index 839689d6ef..2a43fdea5a 100644 --- a/cms/templates/course_outline.html +++ b/cms/templates/course_outline.html @@ -1,10 +1,10 @@ <%inherit file="base.html" /> <%def name="online_help_token()"><% return "outline" %> <%! -import json import logging from util.date_utils import get_default_time_display from django.utils.translation import ugettext as _ +from openedx.core.lib.js_utils import escape_json_dumps from contentstore.utils import reverse_usage_url from microsite_configuration import microsite %> @@ -15,7 +15,7 @@ from microsite_configuration import microsite <%block name="requirejs"> require(["js/factories/outline"], function (OutlineFactory) { - OutlineFactory(${json.dumps(course_structure) | n}, ${json.dumps(initial_state) | n}); + OutlineFactory(${escape_json_dumps(course_structure) | n}, ${escape_json_dumps(initial_state) | n}); }); diff --git a/cms/templates/export.html b/cms/templates/export.html index f8534f73d2..6a8168b4e4 100644 --- a/cms/templates/export.html +++ b/cms/templates/export.html @@ -11,7 +11,7 @@ else: <%! from django.utils.translation import ugettext as _ - import json + from openedx.core.lib.js_utils import escape_json_dumps %> <%block name="title"> %if library: @@ -24,11 +24,11 @@ else: <%block name="requirejs"> % if in_err: - var hasUnit = ${json.dumps(bool(unit))}, + var hasUnit = ${escape_json_dumps(bool(unit)) | n}, editUnitUrl = "${edit_unit_url or ""}", courselikeHomeUrl = "${courselike_home_url or ""}", - is_library = ${json.dumps(library)} - errMsg = ${json.dumps(raw_err_msg or "")}; + is_library = ${escape_json_dumps(library) | n} + errMsg = ${escape_json_dumps(raw_err_msg or "") | n}; require(["js/factories/export"], function(ExportFactory) { ExportFactory(hasUnit, editUnitUrl, courselikeHomeUrl, is_library, errMsg); diff --git a/cms/templates/group_configurations.html b/cms/templates/group_configurations.html index 865c8959d6..1fa5cc6131 100644 --- a/cms/templates/group_configurations.html +++ b/cms/templates/group_configurations.html @@ -3,9 +3,9 @@ <%def name="experiment_group_configurations_help_token()"><% return "group_configurations" %> <%namespace name='static' file='static_content.html'/> <%! -import json from contentstore import utils from django.utils.translation import ugettext as _ +from openedx.core.lib.js_utils import escape_json_dumps %> <%block name="title">${_("Group Configurations")} @@ -21,7 +21,7 @@ from django.utils.translation import ugettext as _ <%block name="requirejs"> require(["js/factories/group_configurations"], function(GroupConfigurationsFactory) { - GroupConfigurationsFactory(${json.dumps(should_show_experiment_groups)}, ${json.dumps(experiment_group_configurations)}, ${json.dumps(content_group_configuration)}, "${group_configuration_url}", "${course_outline_url}"); + GroupConfigurationsFactory(${escape_json_dumps(should_show_experiment_groups) | n}, ${escape_json_dumps(experiment_group_configurations) | n}, ${escape_json_dumps(content_group_configuration) | n}, "${group_configuration_url}", "${course_outline_url}"); }); diff --git a/cms/templates/import.html b/cms/templates/import.html index 61fcbb5f25..989c2cbc12 100644 --- a/cms/templates/import.html +++ b/cms/templates/import.html @@ -10,7 +10,7 @@ else: <%namespace name='static' file='static_content.html'/> <%! from django.utils.translation import ugettext as _ - import json + from openedx.core.lib.js_utils import escape_json_dumps %> <%block name="title"> %if library: @@ -239,6 +239,6 @@ else: <%block name="requirejs"> require(["js/factories/import"], function(ImportFactory) { - ImportFactory("${import_status_url}", ${json.dumps(library)}); + ImportFactory("${import_status_url}", ${escape_json_dumps(library) | n}); }); diff --git a/cms/templates/library.html b/cms/templates/library.html index 864c69c518..f55c745ec3 100644 --- a/cms/templates/library.html +++ b/cms/templates/library.html @@ -1,10 +1,9 @@ <%inherit file="base.html" /> <%def name="online_help_token()"><% return "content_libraries" %> <%! -import json - from contentstore.views.helpers import xblock_studio_url, xblock_type_display_name from django.utils.translation import ugettext as _ +from openedx.core.lib.js_utils import escape_json_dumps %> <%block name="title">${context_library.display_name_with_default} ${xblock_type_display_name(context_library)} <%block name="bodyclass">is-signedin course container view-container view-library @@ -25,8 +24,8 @@ from django.utils.translation import ugettext as _ <%block name="requirejs"> require(["js/factories/library"], function(LibraryFactory) { LibraryFactory( - ${component_templates | n}, - ${json.dumps(xblock_info) | n}, + ${escape_json_dumps(component_templates) | n}, + ${escape_json_dumps(xblock_info) | n}, { isUnitPage: false, page_size: 10, diff --git a/cms/templates/manage_users.html b/cms/templates/manage_users.html index 815d635409..687247c590 100644 --- a/cms/templates/manage_users.html +++ b/cms/templates/manage_users.html @@ -1,8 +1,8 @@ <%inherit file="base.html" /> <%! -import json from django.utils.translation import ugettext as _ from django.core.urlresolvers import reverse +from openedx.core.lib.js_utils import escape_json_dumps %> <%def name="online_help_token()"><% return "team_course" %> <%block name="title">${_("Course Team Settings")} @@ -115,7 +115,7 @@ from django.core.urlresolvers import reverse require(["js/factories/manage_users"], function(ManageCourseUsersFactory) { ManageCourseUsersFactory( "${context_course.display_name | h}", - ${json.dumps(users)}, + ${escape_json_dumps(users) | n}, "${reverse('contentstore.views.course_team_handler', kwargs={'course_key_string': unicode(context_course.id), 'email': '@@EMAIL@@'})}", ${ request.user.id }, ${str(allow_actions).lower()} diff --git a/cms/templates/manage_users_lib.html b/cms/templates/manage_users_lib.html index e4dd12d3cf..b080c619f2 100644 --- a/cms/templates/manage_users_lib.html +++ b/cms/templates/manage_users_lib.html @@ -1,8 +1,8 @@ <%inherit file="base.html" /> <%! -import json from django.utils.translation import ugettext as _ from django.core.urlresolvers import reverse +from openedx.core.lib.js_utils import escape_json_dumps %> <%def name="online_help_token()"><% return "team_library" %> <%block name="title">${_("Library User Access")} @@ -108,7 +108,7 @@ from django.core.urlresolvers import reverse require(["js/factories/manage_users_lib"], function(ManageLibraryUsersFactory) { ManageLibraryUsersFactory( "${context_library.display_name_with_default | h}", - ${json.dumps(users)}, + ${escape_json_dumps(users) | n}, "${reverse('contentstore.views.course_team_handler', kwargs={'course_key_string': library_key, 'email': '@@EMAIL@@'})}", ${ request.user.id }, ${str(allow_actions).lower()} diff --git a/cms/templates/settings.html b/cms/templates/settings.html index b9456eb036..306cd32daa 100644 --- a/cms/templates/settings.html +++ b/cms/templates/settings.html @@ -5,10 +5,10 @@ <%namespace name='static' file='static_content.html'/> <%! - import json import urllib from django.utils.translation import ugettext as _ from contentstore import utils + from openedx.core.lib.js_utils import escape_json_dumps %> <%block name="header_extras"> @@ -31,7 +31,7 @@ CMS.URL.UPLOAD_ASSET = '${upload_asset_url}'; <%block name="requirejs"> require(["js/factories/settings"], function(SettingsFactory) { - SettingsFactory("${details_url}", ${json.dumps(show_min_grade_warning)}); + SettingsFactory("${details_url}", ${escape_json_dumps(show_min_grade_warning) | n}); }); diff --git a/cms/templates/settings_advanced.html b/cms/templates/settings_advanced.html index 40f347ca30..5bfb09679e 100644 --- a/cms/templates/settings_advanced.html +++ b/cms/templates/settings_advanced.html @@ -4,7 +4,7 @@ <%! from django.utils.translation import ugettext as _ from contentstore import utils - from openedx.core.lib.json_utils import escape_json_dumps + from openedx.core.lib.js_utils import escape_json_dumps %> <%block name="title">${_("Advanced Settings")} <%block name="bodyclass">is-signedin course advanced view-settings diff --git a/cms/templates/settings_graders.html b/cms/templates/settings_graders.html index 6deac5c679..580e831c14 100644 --- a/cms/templates/settings_graders.html +++ b/cms/templates/settings_graders.html @@ -8,6 +8,8 @@ import json from contentstore import utils from django.utils.translation import ugettext as _ + from openedx.core.lib.js_utils import escape_json_dumps + from models.settings.course_details import CourseSettingsEncoder %> <%block name="header_extras"> @@ -23,7 +25,7 @@ <%block name="requirejs"> require(["js/factories/settings_graders"], function(SettingsGradersFactory) { - SettingsGradersFactory(_.extend(${course_details|n}, {is_credit_course: ${json.dumps(is_credit_course)}}), "${grading_url}"); + SettingsGradersFactory(_.extend(${escape_json_dumps(course_details, cls=CourseSettingsEncoder) | n}, {is_credit_course: ${escape_json_dumps(is_credit_course) | n}}), "${grading_url}"); }); diff --git a/cms/templates/studio_xblock_wrapper.html b/cms/templates/studio_xblock_wrapper.html index f947bf1038..129f5775b2 100644 --- a/cms/templates/studio_xblock_wrapper.html +++ b/cms/templates/studio_xblock_wrapper.html @@ -2,7 +2,7 @@ from django.utils.translation import ugettext as _ from contentstore.views.helpers import xblock_studio_url from contentstore.utils import is_visible_to_specific_content_groups -import json +from openedx.core.lib.js_utils import escape_json_dumps %> <% xblock_url = xblock_studio_url(xblock) @@ -10,7 +10,7 @@ show_inline = xblock.has_children and not xblock_url section_class = "level-nesting" if show_inline else "level-element" collapsible_class = "is-collapsible" if xblock.has_children else "" label = xblock.display_name_with_default or xblock.scope_ids.block_type -messages = json.dumps(xblock.validate().to_json()) +messages = xblock.validate().to_json() %> <%namespace name='static' file='static_content.html'/> @@ -24,7 +24,7 @@ messages = json.dumps(xblock.validate().to_json()) ': ''} - self.assertNotIn( - '', - json.dumps(malicious_json, cls=EscapedEdxJSONEncoder) - ) - def test_escape_json_dumps_escapes_unsafe_html(self): """ Test escape_json_dumps properly escapes &, <, and >. @@ -70,3 +60,15 @@ class TestJsonUtils(TestCase): encoded_json = escape_json_dumps(malicious_json, cls=self.SampleJSONEncoder) self.assertEquals(expected_custom_encoded_json, encoded_json) + + def test_escape_js_string_escapes_unsafe_html(self): + """ + Test escape_js_string escapes &, <, and >, as well as returns a unicode type + """ + malicious_js_string = "" + + expected_escaped_js_string = unicode( + r"\u003C/script\u003E\u003Cscript\u003Ealert(\u0027hello, \u0027)\u003B\u003C/script\u003E" + ) + escaped_js_string = escape_js_string(malicious_js_string) + self.assertEquals(expected_escaped_js_string, escaped_js_string)