Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
b7698123
Commit
b7698123
authored
Nov 04, 2015
by
Robert Raposa
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #10484 from edx/robrap/TNL-2646
TNL-2646: Escape json in Studio
parents
acf5bb57
3682fac8
Hide whitespace changes
Inline
Side-by-side
Showing
34 changed files
with
132 additions
and
187 deletions
+132
-187
cms/djangoapps/contentstore/views/certificates.py
+1
-1
cms/djangoapps/contentstore/views/component.py
+1
-62
cms/djangoapps/contentstore/views/course.py
+4
-6
cms/djangoapps/contentstore/views/entrance_exam.py
+2
-10
cms/djangoapps/contentstore/views/error.py
+2
-2
cms/djangoapps/contentstore/views/item.py
+1
-1
cms/djangoapps/contentstore/views/library.py
+1
-1
cms/djangoapps/contentstore/views/tests/test_item.py
+1
-1
cms/static/js/spec/views/pages/course_outline_spec.js
+1
-1
cms/static/js/views/modals/course_outline_modals.js
+1
-1
cms/templates/base.html
+6
-5
cms/templates/certificates.html
+2
-2
cms/templates/container.html
+4
-4
cms/templates/course_info.html
+2
-2
cms/templates/course_outline.html
+2
-2
cms/templates/export.html
+4
-4
cms/templates/group_configurations.html
+2
-2
cms/templates/import.html
+2
-2
cms/templates/library.html
+3
-4
cms/templates/manage_users.html
+2
-2
cms/templates/manage_users_lib.html
+2
-2
cms/templates/settings.html
+2
-2
cms/templates/settings_advanced.html
+1
-1
cms/templates/settings_graders.html
+3
-1
cms/templates/studio_xblock_wrapper.html
+3
-3
cms/templates/textbooks.html
+2
-2
common/djangoapps/third_party_auth/tests/specs/test_testshib.py
+3
-4
lms/djangoapps/student_account/test/test_views.py
+2
-2
lms/djangoapps/teams/templates/teams/teams.html
+5
-5
lms/templates/courseware/courses.html
+2
-2
lms/templates/student_account/login_and_register.html
+2
-2
lms/templates/student_profile/learner_profile.html
+2
-2
openedx/core/lib/js_utils.py
+42
-29
openedx/core/lib/tests/test_js_utils.py
+17
-15
No files found.
cms/djangoapps/contentstore/views/certificates.py
View file @
b7698123
...
...
@@ -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
,
...
...
cms/djangoapps/contentstore/views/component.py
View file @
b7698123
from
__future__
import
absolute_import
import
json
import
logging
from
django.http
import
HttpResponseBadRequest
,
Http404
...
...
@@ -74,66 +73,6 @@ def _advanced_component_types():
return
[
c_type
for
c_type
in
ADVANCED_COMPONENT_TYPES
if
c_type
not
in
settings
.
DEPRECATED_ADVANCED_COMPONENT_TYPES
]
@require_GET
@login_required
def
subsection_handler
(
request
,
usage_key_string
):
"""
The restful handler for subsection-specific requests.
GET
html: return html page for editing a subsection
json: not currently supported
"""
if
'text/html'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'text/html'
):
usage_key
=
UsageKey
.
from_string
(
usage_key_string
)
try
:
course
,
item
,
lms_link
,
preview_link
=
_get_item_in_course
(
request
,
usage_key
)
except
ItemNotFoundError
:
return
HttpResponseBadRequest
()
# make sure that location references a 'sequential', otherwise return
# BadRequest
if
item
.
location
.
category
!=
'sequential'
:
return
HttpResponseBadRequest
()
parent
=
get_parent_xblock
(
item
)
# remove all metadata from the generic dictionary that is presented in a
# more normalized UI. We only want to display the XBlocks fields, not
# the fields from any mixins that have been added
fields
=
getattr
(
item
,
'unmixed_class'
,
item
.
__class__
)
.
fields
policy_metadata
=
dict
(
(
field
.
name
,
field
.
read_from
(
item
))
for
field
in
fields
.
values
()
if
field
.
name
not
in
[
'display_name'
,
'start'
,
'due'
,
'format'
]
and
field
.
scope
==
Scope
.
settings
)
can_view_live
=
False
subsection_units
=
item
.
get_children
()
can_view_live
=
any
([
modulestore
()
.
has_published_version
(
unit
)
for
unit
in
subsection_units
])
return
render_to_response
(
'edit_subsection.html'
,
{
'subsection'
:
item
,
'context_course'
:
course
,
'new_unit_category'
:
'vertical'
,
'lms_link'
:
lms_link
,
'preview_link'
:
preview_link
,
'course_graders'
:
json
.
dumps
(
CourseGradingModel
.
fetch
(
item
.
location
.
course_key
)
.
graders
),
'parent_item'
:
parent
,
'locator'
:
item
.
location
,
'policy_metadata'
:
policy_metadata
,
'subsection_units'
:
subsection_units
,
'can_view_live'
:
can_view_live
}
)
else
:
return
HttpResponseBadRequest
(
"Only supports html requests"
)
def
_load_mixed_class
(
category
):
"""
Load an XBlock by category name, and apply all defined mixins
...
...
@@ -213,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
,
...
...
cms/djangoapps/contentstore/views/course.py
View file @
b7698123
...
...
@@ -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
),
})
...
...
cms/djangoapps/contentstore/views/entrance_exam.py
View file @
b7698123
...
...
@@ -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.
...
...
cms/djangoapps/contentstore/views/error.py
View file @
b7698123
...
...
@@ -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
:
...
...
cms/djangoapps/contentstore/views/item.py
View file @
b7698123
...
...
@@ -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
,
...
...
cms/djangoapps/contentstore/views/library.py
View file @
b7698123
...
...
@@ -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
,
})
...
...
cms/djangoapps/contentstore/views/tests/test_item.py
View file @
b7698123
...
...
@@ -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
)
...
...
cms/static/js/spec/views/pages/course_outline_spec.js
View file @
b7698123
...
...
@@ -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'
,
...
...
cms/static/js/views/modals/course_outline_modals.js
View file @
b7698123
...
...
@@ -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'
)
};
}
});
...
...
cms/templates/base.html
View file @
b7698123
...
...
@@ -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
)
%
>
<!doctype html>
<!--[if lte IE 9]><html class="ie9 lte9" lang="${LANGUAGE_CODE}"><![endif]-->
...
...
@@ -41,7 +42,7 @@ import json
<a
class=
"nav-skip"
href=
"#content"
>
${_("Skip to main content")}
</a>
<script
type=
"text/javascript"
>
window
.
baseUrl
=
$
{
json
.
dumps
(
settings
.
STATIC_URL
)}
;
window
.
baseUrl
=
"${escape_js_string(settings.STATIC_URL) | n}"
;
var
require
=
{
baseUrl
:
window
.
baseUrl
};
</script>
<script
type=
"text/javascript"
src=
"${static.url("
js
/
vendor
/
require
.
js
")}"
></script>
...
...
@@ -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
...
...
cms/templates/certificates.html
View file @
b7698123
...
...
@@ -2,9 +2,9 @@
<
%
def
name=
"online_help_token()"
><
%
return
"
certificates
"
%
></
%
def>
<
%
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")}
</
%
block>
...
...
@@ -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
} );
});
</
%
block>
...
...
cms/templates/container.html
View file @
b7698123
...
...
@@ -8,10 +8,9 @@ else:
%
>
</
%
def>
<
%!
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>
<
%
block
name=
"bodyclass"
>
is-signedin course container view-container
</
%
block>
...
...
@@ -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
}
);
...
...
cms/templates/course_info.html
View file @
b7698123
...
...
@@ -2,9 +2,9 @@
<
%
def
name=
"online_help_token()"
><
%
return
"
updates
"
%
></
%
def>
<
%
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
}
);
});
</
%
block>
...
...
cms/templates/course_outline.html
View file @
b7698123
<
%
inherit
file=
"base.html"
/>
<
%
def
name=
"online_help_token()"
><
%
return
"
outline
"
%
></
%
def>
<
%!
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
from
openedx
.
core
.
djangoapps
.
self_paced
.
models
import
SelfPacedConfiguration
...
...
@@ -16,7 +16,7 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
<
%
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});
});
</
%
block>
...
...
cms/templates/export.html
View file @
b7698123
...
...
@@ -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);
...
...
cms/templates/group_configurations.html
View file @
b7698123
...
...
@@ -3,9 +3,9 @@
<
%
def
name=
"experiment_group_configurations_help_token()"
><
%
return
"
group_configurations
"
%
></
%
def>
<
%
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")}
</
%
block>
...
...
@@ -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}");
});
</
%
block>
...
...
cms/templates/import.html
View file @
b7698123
...
...
@@ -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
});
});
</
%
block>
cms/templates/library.html
View file @
b7698123
<
%
inherit
file=
"base.html"
/>
<
%
def
name=
"online_help_token()"
><
%
return
"
content_libraries
"
%
></
%
def>
<
%!
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>
<
%
block
name=
"bodyclass"
>
is-signedin course container view-container view-library
</
%
block>
...
...
@@ -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,
...
...
cms/templates/manage_users.html
View file @
b7698123
<
%
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
"
%
></
%
def>
<
%
block
name=
"title"
>
${_("Course Team Settings")}
</
%
block>
...
...
@@ -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()}
...
...
cms/templates/manage_users_lib.html
View file @
b7698123
<
%
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
"
%
></
%
def>
<
%
block
name=
"title"
>
${_("Library User Access")}
</
%
block>
...
...
@@ -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()}
...
...
cms/templates/settings.html
View file @
b7698123
...
...
@@ -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
});
});
</
%
block>
...
...
cms/templates/settings_advanced.html
View file @
b7698123
...
...
@@ -4,7 +4,7 @@
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
from
contentstore
import
utils
from
openedx
.
core
.
lib
.
js
on
_utils
import
escape_json_dumps
from
openedx
.
core
.
lib
.
js_utils
import
escape_json_dumps
%
>
<
%
block
name=
"title"
>
${_("Advanced Settings")}
</
%
block>
<
%
block
name=
"bodyclass"
>
is-signedin course advanced view-settings
</
%
block>
...
...
cms/templates/settings_graders.html
View file @
b7698123
...
...
@@ -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>
<
%
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}");
});
</
%
block>
...
...
cms/templates/studio_xblock_wrapper.html
View file @
b7698123
...
...
@@ -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())
<script>
require
([
"jquery"
,
"js/factories/xblock_validation"
],
function
(
$
,
XBlockValidationFactory
)
{
XBlockValidationFactory
(
$
{
messages
},
$
{
escape_json_dumps
(
messages
)
|
n
},
$
.
parseJSON
(
"${bool(xblock_url)}"
.
toLowerCase
()),
// xblock_url will be None or a string
$
.
parseJSON
(
"${bool(is_root)}"
.
toLowerCase
()),
// is_root will be None or a boolean
$
(
'div.xblock-validation-messages[data-locator="${xblock.location | h}"]'
)
...
...
cms/templates/textbooks.html
View file @
b7698123
...
...
@@ -2,8 +2,8 @@
<
%
def
name=
"online_help_token()"
><
%
return
"
textbooks
"
%
></
%
def>
<
%
namespace
name=
'static'
file=
'static_content.html'
/>
<
%!
import
json
from
django
.
utils
.
translation
import
ugettext
as
_
from
openedx
.
core
.
lib
.
js_utils
import
escape_json_dumps
%
>
<
%
block
name=
"title"
>
${_("Textbooks")}
</
%
block>
...
...
@@ -28,7 +28,7 @@ CMS.URL.LMS_BASE = "${settings.LMS_BASE}"
</
%
block>
<
%
block
name=
"requirejs"
>
require(["js/factories/textbooks"], function(TextbooksFactory) {
TextbooksFactory(${
json.dumps(textbooks)
});
TextbooksFactory(${
escape_json_dumps(textbooks) | n
});
});
</
%
block>
...
...
common/djangoapps/third_party_auth/tests/specs/test_testshib.py
View file @
b7698123
...
...
@@ -9,11 +9,10 @@ from mock import patch
from
django.core.urlresolvers
import
reverse
from
openedx.core.lib.json_utils
import
EscapedEdxJSONEncoder
from
student.tests.factories
import
UserFactory
from
third_party_auth.tasks
import
fetch_saml_metadata
from
third_party_auth.tests
import
testutil
from
openedx.core.lib.js_utils
import
escape_json_dumps
TESTSHIB_ENTITY_ID
=
'https://idp.testshib.org/idp/shibboleth'
...
...
@@ -190,7 +189,7 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
response
=
self
.
client
.
get
(
self
.
login_page_url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertIn
(
"TestShib"
,
response
.
content
)
self
.
assertIn
(
json
.
dumps
(
TPA_TESTSHIB_LOGIN_URL
,
cls
=
EscapedEdxJSONEncoder
),
response
.
content
)
self
.
assertIn
(
escape_json_dumps
(
TPA_TESTSHIB_LOGIN_URL
),
response
.
content
)
return
response
def
_check_register_page
(
self
):
...
...
@@ -198,7 +197,7 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
response
=
self
.
client
.
get
(
self
.
register_page_url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertIn
(
"TestShib"
,
response
.
content
)
self
.
assertIn
(
json
.
dumps
(
TPA_TESTSHIB_REGISTER_URL
,
cls
=
EscapedEdxJSONEncoder
),
response
.
content
)
self
.
assertIn
(
escape_json_dumps
(
TPA_TESTSHIB_REGISTER_URL
),
response
.
content
)
return
response
def
_configure_testshib_provider
(
self
,
**
kwargs
):
...
...
lms/djangoapps/student_account/test/test_views.py
View file @
b7698123
...
...
@@ -19,7 +19,7 @@ from django.test.client import RequestFactory
from
openedx.core.djangoapps.user_api.accounts.api
import
activate_account
,
create_account
from
openedx.core.djangoapps.user_api.accounts
import
EMAIL_MAX_LENGTH
from
openedx.core.lib.js
on_utils
import
EscapedEdxJSONEncoder
from
openedx.core.lib.js
_utils
import
escape_json_dumps
from
student.tests.factories
import
UserFactory
from
student_account.views
import
account_settings_context
from
third_party_auth.tests.testutil
import
simulate_running_pipeline
,
ThirdPartyAuthTestMixin
...
...
@@ -385,7 +385,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
"finishAuthUrl"
:
finish_auth_url
,
"errorMessage"
:
None
,
}
auth_info
=
json
.
dumps
(
auth_info
,
cls
=
EscapedEdxJSONEncoder
)
auth_info
=
escape_json_dumps
(
auth_info
)
expected_data
=
'"third_party_auth": {auth_info}'
.
format
(
auth_info
=
auth_info
...
...
lms/djangoapps/teams/templates/teams/teams.html
View file @
b7698123
## mako
<
%!
import
json
%
>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%!
from
openedx
.
core
.
lib
.
js
on_utils
import
EscapedEdxJSONEncoder
%
>
<
%!
from
openedx
.
core
.
lib
.
js
_utils
import
escape_json_dumps
%
>
<
%
namespace
name=
'static'
file=
'/static_content.html'
/>
<
%
inherit
file=
"/main.html"
/>
...
...
@@ -34,8 +34,8 @@
<
%
static:require_module
module_name=
"teams/js/teams_tab_factory"
class_name=
"TeamsTabFactory"
>
TeamsTabFactory({
courseID: '${ unicode(course.id) }',
topics: ${
json.dumps(topics, cls=EscapedEdxJSONEncoder)
},
userInfo: ${
json.dumps(user_info, cls=EscapedEdxJSONEncoder)
},
topics: ${
escape_json_dumps(topics) | n
},
userInfo: ${
escape_json_dumps(user_info) | n
},
topicUrl: '${ topic_url }',
topicsUrl: '${ topics_url }',
teamsUrl: '${ teams_url }',
...
...
@@ -44,8 +44,8 @@
teamMembershipDetailUrl: '${ team_membership_detail_url }',
myTeamsUrl: '${ my_teams_url }',
maxTeamSize: ${ course.teams_max_size },
languages: ${
json.dumps(languages, cls=EscapedEdxJSONEncoder)
},
countries: ${
json.dumps(countries, cls=EscapedEdxJSONEncoder)
},
languages: ${
escape_json_dumps(languages) | n
},
countries: ${
escape_json_dumps(countries) | n
},
teamsBaseUrl: '${ teams_base_url }'
});
</
%
static:require
_module
>
...
...
lms/templates/courseware/courses.html
View file @
b7698123
...
...
@@ -2,7 +2,7 @@
import
json
from
django
.
utils
.
translation
import
ugettext
as
_
from
microsite_configuration
import
microsite
from
openedx
.
core
.
lib
.
js
on_utils
import
EscapedEdxJSONEncoder
from
openedx
.
core
.
lib
.
js
_utils
import
escape_json_dumps
%
>
<
%
inherit
file=
"../main.html"
/>
<
%
...
...
@@ -20,7 +20,7 @@
% endfor
<
%
static:require_module
module_name=
"js/discovery/discovery_factory"
class_name=
"DiscoveryFactory"
>
DiscoveryFactory(
${
json.dumps(course_discovery_meanings, cls=EscapedEdxJSONEncoder)
},
${
escape_json_dumps(course_discovery_meanings) | n
},
getParameterByName('search_query')
);
</
%
static:require
_module
>
...
...
lms/templates/student_account/login_and_register.html
View file @
b7698123
<
%!
import
json
from
django
.
utils
.
translation
import
ugettext
as
_
from
openedx
.
core
.
lib
.
js
on_utils
import
EscapedEdxJSONEncoder
from
openedx
.
core
.
lib
.
js
_utils
import
escape_json_dumps
%
>
<
%
namespace
name=
'static'
file=
'/static_content.html'
/>
...
...
@@ -11,7 +11,7 @@
<
%
block
name=
"js_extra"
>
<
%
static:require_module
module_name=
"js/student_account/logistration_factory"
class_name=
"LogistrationFactory"
>
var options = ${
json.dumps(data, cls=EscapedEdxJSONEncoder)
};
var options = ${
escape_json_dumps(data) | n
};
LogistrationFactory(options);
</
%
static:require
_module
>
</
%
block>
...
...
lms/templates/student_profile/learner_profile.html
View file @
b7698123
...
...
@@ -4,7 +4,7 @@
import
json
from
django
.
core
.
urlresolvers
import
reverse
from
django
.
utils
.
translation
import
ugettext
as
_
from
openedx
.
core
.
lib
.
js
on_utils
import
EscapedEdxJSONEncoder
from
openedx
.
core
.
lib
.
js
_utils
import
escape_json_dumps
%
>
<
%
block
name=
"pagetitle"
>
${_("Learner Profile")}
</
%
block>
...
...
@@ -24,7 +24,7 @@ from openedx.core.lib.json_utils import EscapedEdxJSONEncoder
<
%
block
name=
"js_extra"
>
<
%
static:require_module
module_name=
"js/student_profile/views/learner_profile_factory"
class_name=
"LearnerProfileFactory"
>
var options = ${
json.dumps(data, cls=EscapedEdxJSONEncoder)
};
var options = ${
escape_json_dumps(data) | n
};
LearnerProfileFactory(options);
</
%
static:require
_module
>
</
%
block>
openedx/core/lib/js
on
_utils.py
→
openedx/core/lib/js_utils.py
View file @
b7698123
"""
Utilities for dealing with JSON.
Utilities for dealing with J
avascript and J
SON.
"""
import
json
import
simplejson
from
django.template.defaultfilters
import
escapejs
from
mako.filters
import
decode
from
xmodule.modulestore
import
EdxJSONEncoder
class
EscapedEdxJSONEncoder
(
EdxJSONEncoder
):
"""
Class for encoding edx JSON which will be printed inline into HTML
templates.
"""
def
encode
(
self
,
obj
):
"""
Encodes JSON that is safe to be embedded in HTML.
"""
return
simplejson
.
dumps
(
simplejson
.
loads
(
super
(
EscapedEdxJSONEncoder
,
self
)
.
encode
(
obj
)),
cls
=
simplejson
.
JSONEncoderForHTML
)
def
_escape_json_for_html
(
json_str
):
def
_escape_json_for_html
(
json_string
):
"""
Escape JSON that is safe to be embedded in HTML.
This implementation is based on escaping performed in simplejson.JSONEncoderForHTML.
Arguments:
json_str
(str
): The JSON string to be escaped
json_str
ing (string
): The JSON string to be escaped
Returns:
(str) Escaped JSON that is safe to be embedded in HTML.
(str
ing
) Escaped JSON that is safe to be embedded in HTML.
"""
json_str
=
json_str
.
replace
(
"&"
,
"
\\
u0026"
)
json_str
=
json_str
.
replace
(
">"
,
"
\\
u003e"
)
json_str
=
json_str
.
replace
(
"<"
,
"
\\
u003c"
)
return
json_str
json_str
ing
=
json_string
.
replace
(
"&"
,
"
\\
u0026"
)
json_str
ing
=
json_string
.
replace
(
">"
,
"
\\
u003e"
)
json_str
ing
=
json_string
.
replace
(
"<"
,
"
\\
u003c"
)
return
json_str
ing
def
escape_json_dumps
(
obj
,
cls
=
EdxJSONEncoder
):
"""
JSON dumps
encoded
JSON that is safe to be embedded in HTML.
JSON dumps
and escapes
JSON that is safe to be embedded in HTML.
Usage:
Can be used inside a Mako template inside a <SCRIPT> as follows:
var my_json = ${escape_json_dumps(my_object) | n}
Use the "n" Mako filter above. It is possible that the
default filter may include html e
ncod
ing in the future, and
default filter may include html e
scap
ing in the future, and
we must make sure to get the proper escaping.
Ensure ascii in json.dumps (ensure_ascii=True) allows safe skipping of Mako's
...
...
@@ -62,9 +46,38 @@ def escape_json_dumps(obj, cls=EdxJSONEncoder):
cls (class): The JSON encoder class (defaults to EdxJSONEncoder)
Returns:
str:
Escaped encoded JSON
(string)
Escaped encoded JSON
"""
encoded_json
=
json
.
dumps
(
obj
,
ensure_ascii
=
True
,
cls
=
cls
)
encoded_json
=
_escape_json_for_html
(
encoded_json
)
return
encoded_json
def
escape_js_string
(
js_string
):
"""
Escape a javascript string that is safe to be embedded in HTML.
Usage:
Can be used inside a Mako template inside a <SCRIPT> as follows:
var my_js_string = "${escape_js_string(my_js_string) | n}"
Must include the surrounding quotes for the string.
Use the "n" Mako filter above. It is possible that the
default filter may include html escaping in the future, and
we must make sure to get the proper escaping.
Mako's default filter decode.utf8 is applied here since this default
filter is skipped in the Mako template with "n".
Arguments:
js_string (string): The javascript string to be escaped
Returns:
(string) Escaped javascript as unicode
"""
js_string
=
decode
.
utf8
(
js_string
)
js_string
=
escapejs
(
js_string
)
return
js_string
openedx/core/lib/tests/test_js
on
_utils.py
→
openedx/core/lib/tests/test_js_utils.py
View file @
b7698123
"""
Tests for js
on
_utils.py
Tests for js_utils.py
"""
import
json
from
unittest
import
TestCase
from
openedx.core.lib.js
on
_utils
import
(
escape_json_dumps
,
EscapedEdxJSONEncoder
from
openedx.core.lib.js_utils
import
(
escape_json_dumps
,
escape_js_string
)
class
TestJ
son
Utils
(
TestCase
):
class
TestJ
S
Utils
(
TestCase
):
"""
Test JS
ON U
tils
Test JS
u
tils
"""
class
NoDefaultEncoding
(
object
):
...
...
@@ -28,16 +28,6 @@ class TestJsonUtils(TestCase):
def
default
(
self
,
noDefaultEncodingObj
):
return
noDefaultEncodingObj
.
value
.
replace
(
"<script>"
,
"sample-encoder-was-here"
)
def
test_escapes_forward_slashes
(
self
):
"""
Verify that we escape forward slashes with backslashes.
"""
malicious_json
=
{
'</script><script>alert("hello, ");</script>'
:
'</script><script>alert("world!");</script>'
}
self
.
assertNotIn
(
'</script>'
,
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
=
"</script><script>alert('hello, ');</script>"
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
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment