Merge pull request #20926 from edx/adeel/part1a_xss_vulnerability

Fix Template Issues
This commit is contained in:
adeel khan
2019-07-03 20:32:06 +05:00
committed by GitHub
7 changed files with 64 additions and 42 deletions

View File

@@ -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:

View File

@@ -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">

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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');

View File

@@ -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("[ &#8722; ]");
self.html(edx.HtmlUtils.HTML("[ &#8722; ]").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>

View File

@@ -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