Merge pull request #20926 from edx/adeel/part1a_xss_vulnerability
Fix Template Issues
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%!
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from six import text_type
|
||||
%>
|
||||
|
||||
<%
|
||||
def _message(reqm, message):
|
||||
return message.format(link="<a href={url}>{url_name}</a>".format(
|
||||
return Text(message).format(link=HTML("<a href={url}>{url_name}</a>").format(
|
||||
url = reverse('jump_to', kwargs=dict(course_id=text_type(reqm.course_id),
|
||||
location=text_type(reqm.location))),
|
||||
url_name = reqm.display_name_with_default_escaped))
|
||||
url_name = reqm.display_name_with_default))
|
||||
%>
|
||||
% if message:
|
||||
% for reqm in module.required_modules:
|
||||
|
||||
@@ -11,7 +11,7 @@ from third_party_auth import provider, pipeline
|
||||
%>
|
||||
|
||||
<%!
|
||||
from openedx.core.djangolib.js_utils import js_escaped_string
|
||||
from openedx.core.djangolib.js_utils import js_escaped_string, dump_js_escaped_json
|
||||
%>
|
||||
|
||||
<%block name="pagetitle">${_("Log into your {platform_name} Account").format(platform_name=platform_name)}</%block>
|
||||
@@ -60,7 +60,7 @@ from openedx.core.djangolib.js_utils import js_escaped_string
|
||||
if (request.status === 403) {
|
||||
$('.message.submission-error').removeClass('is-shown');
|
||||
$('.third-party-signin.message').addClass('is-shown').focus();
|
||||
$('.third-party-signin.message .instructions').HtmlUtils.setHtml(request.responseText);
|
||||
$('.third-party-signin.message .instructions').text(request.responseText);
|
||||
} else {
|
||||
$('.third-party-signin.message').removeClass('is-shown');
|
||||
$('.message.submission-error').addClass('is-shown').focus();
|
||||
@@ -70,7 +70,7 @@ from openedx.core.djangolib.js_utils import js_escaped_string
|
||||
|
||||
$('#login-form').on('ajax:success', function(event, json, xhr) {
|
||||
if(json.success) {
|
||||
var nextUrl = "${login_redirect_url | n}"; // xss-lint: disable=mako-invalid-js-filter
|
||||
var nextUrl = "${login_redirect_url | n, js_escaped_string}";
|
||||
if (json.redirect_url) {
|
||||
nextUrl = json.redirect_url; // Most likely third party auth completion. This trumps 'nextUrl' above.
|
||||
}
|
||||
@@ -89,7 +89,7 @@ from openedx.core.djangolib.js_utils import js_escaped_string
|
||||
} else {
|
||||
toggleSubmitButton(true);
|
||||
$('.message.submission-error').addClass('is-shown').focus();
|
||||
$('.message.submission-error .message-copy').HtmlUtils.setHtml(json.value);
|
||||
$('.message.submission-error .message-copy').text(json.value);
|
||||
}
|
||||
});
|
||||
$("#forgot-password-link").click(function() {
|
||||
@@ -101,15 +101,22 @@ from openedx.core.djangolib.js_utils import js_escaped_string
|
||||
|
||||
function toggleSubmitButton(enable) {
|
||||
var $submitButton = $('form .form-actions #submit');
|
||||
var $var1 = '${_('Log into My {platform_name} Account').format(platform_name=platform_name) | n, js_escaped_string}'
|
||||
var $var2 = '${_('Access My Courses') | n, js_escaped_string}'
|
||||
|
||||
if(enable) {
|
||||
var platform = "${_('Log into My {platform_name} Account').format(platform_name=platform_name) | n, js_escaped_string}";
|
||||
var msg = "${_('Access My Courses') | n, js_escaped_string}";
|
||||
var content = edx.HtmlUtils.interpolateHtml(
|
||||
edx.HtmlUtils.HTML("{platform}<span class='orn-plus'>+</span>{msg}"),
|
||||
{
|
||||
platform:platform,
|
||||
msg:msg
|
||||
});
|
||||
|
||||
$submitButton.
|
||||
removeClass('is-disabled').
|
||||
attr('aria-disabled', false).
|
||||
prop('disabled', false).
|
||||
HtmlUtils.setHtml("$var1 <span class='orn-plus'>+</span> $var2");
|
||||
html(HtmlUtils.ensureHtml(content).toString());
|
||||
}
|
||||
else {
|
||||
$submitButton.
|
||||
@@ -133,7 +140,7 @@ from openedx.core.djangolib.js_utils import js_escaped_string
|
||||
if (pipeline_running) {
|
||||
$('#login-form').submit();
|
||||
}
|
||||
})('${pipeline_running | n, js_escaped_string}')
|
||||
})(${pipeline_running | n, dump_js_escaped_json})
|
||||
</script>
|
||||
</%block>
|
||||
|
||||
@@ -178,7 +185,10 @@ from openedx.core.djangolib.js_utils import js_escaped_string
|
||||
% endif
|
||||
|
||||
<p class="instructions sr">
|
||||
${HTML(_('Please provide the following information to log into your {platform_name} account. Required fields are noted by <strong class="indicator">bold text and an asterisk (*)</strong>.')).format(platform_name=platform_name)}
|
||||
${Text(_('Please provide the following information to log into your {platform_name} account. Required fields are noted by {strong_start}bold text and an asterisk (*){strong_end}.')).format(
|
||||
strong_start=HTML('<strong class="indicator">'),
|
||||
strong_end=HTML('</strong>'),
|
||||
platform_name=platform_name)}
|
||||
</p>
|
||||
|
||||
<div class="group group-form group-form-requiredinformation">
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%inherit file="main.html" />
|
||||
|
||||
<%!
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from openedx.core.djangolib.js_utils import js_escaped_string
|
||||
%>
|
||||
|
||||
<h2>${_("Manage student accounts")}</h2>
|
||||
@@ -62,14 +64,14 @@ PLACEHOLDER_USERNAME = '__PLACEHOLDER_USERNAME'
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
var form = $(".manage-accounts-form"),
|
||||
profileUrl = "${reverse('accounts_api', kwargs={'username': PLACEHOLDER_USERNAME})}",
|
||||
removeProfileUrl = "${reverse('profile_image_remove', kwargs={'username': PLACEHOLDER_USERNAME})}",
|
||||
profileUrl = "${reverse('accounts_api', kwargs={'username': PLACEHOLDER_USERNAME}) | n, js_escaped_string}",
|
||||
removeProfileUrl = "${reverse('profile_image_remove', kwargs={'username': PLACEHOLDER_USERNAME}) | n, js_escaped_string}",
|
||||
refreshProfile;
|
||||
|
||||
refreshProfile = function(username) {
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
url: profileUrl.replace('${PLACEHOLDER_USERNAME}', username),
|
||||
url: profileUrl.replace('${PLACEHOLDER_USERNAME | n, js_escaped_string}', username),
|
||||
success: function(response) {
|
||||
var imageUrl = response["profile_image"]["image_url_medium"];
|
||||
$("#profile-image", form).attr("src", imageUrl);
|
||||
@@ -86,10 +88,10 @@ $(function() {
|
||||
var username = $('#username', form).val(),
|
||||
action = $("input:radio[name=account_action]:checked", form).val();
|
||||
if (action === 'remove_profile_image') {
|
||||
$(".account-change-status").text("${_('working')}");
|
||||
$(".account-change-status").text("${_('working') | n, js_escaped_string}");
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: removeProfileUrl.replace('${PLACEHOLDER_USERNAME}', username),
|
||||
url: removeProfileUrl.replace('${PLACEHOLDER_USERNAME | n, js_escaped_string}', username),
|
||||
success: function(response) {
|
||||
refreshProfile(username).always(function() {
|
||||
$("#profile-image", form).focus();
|
||||
@@ -98,13 +100,13 @@ $(function() {
|
||||
}
|
||||
});
|
||||
} else if (action) {
|
||||
$(".account-change-status").text("${_('working')}");
|
||||
$(".account-change-status").text("${_('working') | n, js_escaped_string}");
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: form.attr('action'),
|
||||
data: form.serialize(),
|
||||
success: function(response) {
|
||||
$(".account-change-status").html(response.message);
|
||||
$(".account-change-status").text(response.message);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%inherit file="shopping_cart_flow.html" />
|
||||
<%!
|
||||
from openedx.core.djangolib.js_utils import js_escaped_string
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.urls import reverse
|
||||
%>
|
||||
@@ -44,7 +46,7 @@ from django.urls import reverse
|
||||
<div class="col-two">
|
||||
|
||||
<div class="col-2">
|
||||
${form_html}
|
||||
${form_html | n, decode.utf8}
|
||||
<p>
|
||||
${_('If no additional billing details are populated the payment confirmation will be sent to the user making the purchase.')}
|
||||
</p>
|
||||
@@ -74,7 +76,7 @@ from django.urls import reverse
|
||||
var recipient_email = $('input[name="recipient_email"]').val();
|
||||
var company_contact_email = $('input[name="company_contact_email"]').val();
|
||||
if ( recipient_email != '' && !(validateEmail(recipient_email))) {
|
||||
$('span#recipient_email_error').html('Please enter valid email address');
|
||||
$('span#recipient_email_error').text('Please enter valid email address');
|
||||
$('input[name="recipient_email"]').addClass('error');
|
||||
is_valid_email = false;
|
||||
}
|
||||
@@ -83,7 +85,7 @@ from django.urls import reverse
|
||||
$('span#recipient_email_error').html('');
|
||||
}
|
||||
if ( company_contact_email != '' && !(validateEmail(company_contact_email))) {
|
||||
$('span#company_contact_email_error').html('Please enter valid email address');
|
||||
$('span#company_contact_email_error').text('Please enter valid email address');
|
||||
$('input[name="company_contact_email"]').addClass('error');
|
||||
is_valid_email = false;
|
||||
}
|
||||
@@ -97,7 +99,7 @@ from django.urls import reverse
|
||||
event.preventDefault();
|
||||
// Disable the submit button to prevent duplicate submissions
|
||||
$(this).addClass("disabled");
|
||||
var post_url = "${reverse('billing_details')}";
|
||||
var post_url = "${reverse('billing_details') | n, js_escaped_string}";
|
||||
var data = {
|
||||
"company_name" : $('input[name="company_name"]').val(),
|
||||
"company_contact_name" : $('input[name="company_contact_name"]').val(),
|
||||
@@ -109,7 +111,7 @@ from django.urls import reverse
|
||||
$.post(post_url, data)
|
||||
.success(function(data) {
|
||||
if (data.is_course_enrollment_closed == true) {
|
||||
location.href = "${reverse('shoppingcart.views.show_cart')}";
|
||||
location.href = "${reverse('shoppingcart.views.show_cart') | n, js_escaped_string}";
|
||||
}
|
||||
else {
|
||||
payment_form.submit();
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%inherit file="shopping_cart_flow.html" />
|
||||
<%block name="review_highlight">class="active"</%block>
|
||||
|
||||
<%!
|
||||
from openedx.core.djangolib.js_utils import js_escaped_string
|
||||
from django.urls import reverse
|
||||
from edxmako.shortcuts import marketing_link
|
||||
from django.utils.translation import ugettext as _
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from django.utils.translation import ungettext
|
||||
from openedx.core.lib.courses import course_image_url
|
||||
%>
|
||||
@@ -66,7 +69,7 @@ from openedx.core.lib.courses import course_image_url
|
||||
<div class="clearfix">
|
||||
<div class="image">
|
||||
<img class="item-image" src="${course_image_url(course)}"
|
||||
alt="${course.display_number_with_default | h} ${course.display_name_with_default_escaped} ${_('Cover Image')}" />
|
||||
alt="${course.display_number_with_default} ${course.display_name_with_default} ${_('Cover Image')}" />
|
||||
</div>
|
||||
<div class="data-input">
|
||||
## Translators: "Registration for:" is followed by a course name
|
||||
@@ -169,17 +172,19 @@ from openedx.core.lib.courses import course_image_url
|
||||
</div>
|
||||
<div name="payment" class="hidden">
|
||||
<div id="processor_form" aria-describedby="payment_business_helper_text">
|
||||
${form_html}
|
||||
${form_html | n, decode.utf8}
|
||||
</div>
|
||||
<p id="payment_business_helper_text">
|
||||
${_('After this purchase is complete, {username} will be enrolled in this course.').format(username=u'<br/><b>{username}</b>'.format(username=order.user.username))}
|
||||
${Text(_('After this purchase is complete, {username} will be enrolled in this course.')).format( \
|
||||
username=HTML(u'<br/><b>{username}</b>').format(username=order.user.username))}
|
||||
</p>
|
||||
</div>
|
||||
% else:
|
||||
<div name="payment" aria-describedby="payment_helper_text">
|
||||
${form_html}
|
||||
${form_html | n, decode.utf8}
|
||||
<p id="payment_helper_text">
|
||||
${_('After this purchase is complete, {username} will be enrolled in this course.').format(username=u'<br/><b>{username}</b>'.format(username=order.user.username))}
|
||||
${Text(_('After this purchase is complete, {username} will be enrolled in this course.')).format( \
|
||||
username=HTML(u'<br/><b>{username}</b>').format(username=order.user.username))}
|
||||
</p>
|
||||
</div>
|
||||
<div name="billing" class="hidden">
|
||||
@@ -235,7 +240,7 @@ from openedx.core.lib.courses import course_image_url
|
||||
|
||||
$('button.btn-remove').click(function(event) {
|
||||
event.preventDefault();
|
||||
var post_url = "${reverse('shoppingcart.views.remove_item')}";
|
||||
var post_url = "${reverse('shoppingcart.views.remove_item') | n, js_escaped_string}";
|
||||
$.post(post_url, {id:$(this).data('item-id')})
|
||||
.always(function(data){
|
||||
location.reload(true);
|
||||
@@ -244,7 +249,7 @@ from openedx.core.lib.courses import course_image_url
|
||||
|
||||
$('#submit-code').click(function(event){
|
||||
event.preventDefault();
|
||||
var post_url = "${reverse('shoppingcart.views.use_code')}";
|
||||
var post_url = "${reverse('shoppingcart.views.use_code') | n, js_escaped_string}";
|
||||
if($('#input_code').val() == "") {
|
||||
showErrorMsgs('Must enter a valid code','code');
|
||||
return;
|
||||
@@ -273,7 +278,7 @@ from openedx.core.lib.courses import course_image_url
|
||||
|
||||
$('#submit-reset-redemption').click(function(event){
|
||||
event.preventDefault();
|
||||
var post_url = "${reverse('shoppingcart.views.reset_code_redemption')}";
|
||||
var post_url = "${reverse('shoppingcart.views.reset_code_redemption') | n, js_escaped_string}";
|
||||
$.post(post_url)
|
||||
.success(function(data) {
|
||||
location.reload(true);
|
||||
@@ -297,7 +302,7 @@ from openedx.core.lib.courses import course_image_url
|
||||
return false;
|
||||
}
|
||||
event.preventDefault();
|
||||
location.href = "${reverse('billing_details')}";
|
||||
location.href = "${reverse('billing_details') | n, js_escaped_string}";
|
||||
});
|
||||
|
||||
|
||||
@@ -353,7 +358,7 @@ from openedx.core.lib.courses import course_image_url
|
||||
function showErrorMsgs(msg, msg_area){
|
||||
|
||||
$( "span.error-text#"+ msg_area +"" ).removeClass("hidden");
|
||||
$( "span.error-text#"+ msg_area +"" ).html(msg).show();
|
||||
$( "span.error-text#"+ msg_area +"" ).text(msg).show();
|
||||
|
||||
if(msg_area=='code'){
|
||||
$("#input_code").addClass('error');
|
||||
@@ -375,11 +380,11 @@ from openedx.core.lib.courses import course_image_url
|
||||
|
||||
function update_user_cart(ItemId, newQty, prevQty, unit_cost, wasbusinessType, isbusinessType){
|
||||
|
||||
var post_url = "${reverse('shoppingcart.views.update_user_cart')}";
|
||||
var post_url = "${reverse('shoppingcart.views.update_user_cart') | n, js_escaped_string}";
|
||||
var typeChanged = false;
|
||||
var prevTotal = $('#total-amount').data('amount');
|
||||
var newTotal = getNewTotal(prevQty, newQty, unit_cost, prevTotal);
|
||||
$('#total-amount').html('$'+newTotal.toFixed(2)+' USD');
|
||||
$('#total-amount').text('$'+newTotal.toFixed(2)+' USD');
|
||||
$('#total-amount').data('amount', newTotal);
|
||||
|
||||
if(isbusinessType != wasbusinessType){
|
||||
@@ -401,7 +406,7 @@ from openedx.core.lib.courses import course_image_url
|
||||
var prevTotal = data['total_cost'];
|
||||
$('html').css({'cursor':'default'});
|
||||
$(".button").css({'cursor':'default'});
|
||||
$("#processor_form").html(data['form_html']);
|
||||
$("#processor_form").html(edx.HtmlUtils.HTML(data['form_html']).toString());
|
||||
if(typeChanged){
|
||||
var submit_button = $('.col-2.relative').find("button[type='submit']");
|
||||
submit_button.removeAttr('disabled');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%inherit file="/main.html" />
|
||||
<%!
|
||||
from django.urls import reverse
|
||||
@@ -21,9 +22,9 @@
|
||||
duration: 200
|
||||
});
|
||||
if (self.html() === "[ + ]") {
|
||||
self.html("[ − ]");
|
||||
self.html(edx.HtmlUtils.HTML("[ − ]").toString());
|
||||
} else {
|
||||
self.html("[ + ]");
|
||||
self.text("[ + ]");
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
@@ -130,7 +131,7 @@ textarea {
|
||||
|
||||
%if course_id is not None:
|
||||
## Translators: Git is a version-control system; see http://git-scm.com/about
|
||||
<h2>${_('Recent git load activity for {course_id}').format(course_id=course_id) | h}</h2>
|
||||
<h2>${_('Recent git load activity for {course_id}').format(course_id=course_id)}</h2>
|
||||
%if error_msg:
|
||||
<h3>${_('Error')}:</h3>
|
||||
<p>${error_msg}</p>
|
||||
@@ -160,7 +161,7 @@ textarea {
|
||||
<td>${date}</td>
|
||||
<td>
|
||||
<a href="${reverse('gitlogs_detail', kwargs={'course_id': unicode(cil.course_id)})}">
|
||||
${cil.course_id | h}
|
||||
${cil.course_id}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
@@ -176,7 +177,7 @@ textarea {
|
||||
<tr class="import-log" id="import-log-${index}">
|
||||
<td colspan="3">
|
||||
<pre>
|
||||
${cil.import_log | h}
|
||||
${cil.import_log}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -150,7 +150,7 @@ class LoginFormTest(ThirdPartyAuthTestMixin, UrlResetMixin, SharedModuleStoreTes
|
||||
|
||||
# Verify that the parameters are sent on to the next page correctly
|
||||
post_login_handler = _finish_auth_url(params)
|
||||
js_success_var = u'var nextUrl = "{}";'.format(post_login_handler)
|
||||
js_success_var = u'var nextUrl = "{}";'.format(js_escaped_string(post_login_handler))
|
||||
self.assertContains(response, js_success_var)
|
||||
|
||||
# Verify that the login link preserves the querystring params
|
||||
|
||||
Reference in New Issue
Block a user