From f42d2edd29d420672a963a6d3fd4f1041d8667b0 Mon Sep 17 00:00:00 2001
From: Alex Wang <awang@edx.org>
Date: Tue, 26 Nov 2019 20:15:33 -0500
Subject: [PATCH] Fix xss warnings (#22408)

---
 .../teams/static/teams/js/views/edit_team.js  | 18 +++--
 .../static/teams/js/views/instructor_tools.js |  8 +--
 .../teams/static/teams/js/views/my_teams.js   |  8 +--
 .../teams/static/teams/js/views/team_card.js  | 67 +++++++++++--------
 .../js/views/team_profile_header_actions.js   | 19 +++---
 .../teams/static/teams/js/views/team_utils.js | 11 ++-
 .../teams/static/teams/js/views/topic_card.js | 30 ++++++---
 7 files changed, 91 insertions(+), 70 deletions(-)

diff --git a/lms/djangoapps/teams/static/teams/js/views/edit_team.js b/lms/djangoapps/teams/static/teams/js/views/edit_team.js
index 9dd906940f0..c2fd38c144f 100644
--- a/lms/djangoapps/teams/static/teams/js/views/edit_team.js
+++ b/lms/djangoapps/teams/static/teams/js/views/edit_team.js
@@ -7,8 +7,9 @@
         'js/views/fields',
         'teams/js/models/team',
         'common/js/components/utils/view_utils',
-        'text!teams/templates/edit-team.underscore'],
-        function(Backbone, _, gettext, FieldViews, TeamModel, ViewUtils, editTeamTemplate) {
+        'text!teams/templates/edit-team.underscore',
+        'edx-ui-toolkit/js/utils/html-utils'],
+        function(Backbone, _, gettext, FieldViews, TeamModel, ViewUtils, editTeamTemplate, HtmlUtils) {
             return Backbone.View.extend({
 
                 maxTeamNameLength: 255,
@@ -81,11 +82,14 @@
                 },
 
                 render: function() {
-                    this.$el.html(_.template(editTeamTemplate)({ // xss-lint: disable=javascript-jquery-html
-                        primaryButtonTitle: this.primaryButtonTitle,
-                        action: this.action,
-                        totalMembers: _.isUndefined(this.teamModel) ? 0 : this.teamModel.get('membership').length
-                    }));
+                    HtmlUtils.setHtml(
+                        this.$el,
+                        HtmlUtils.template(editTeamTemplate)({
+                            primaryButtonTitle: this.primaryButtonTitle,
+                            action: this.action,
+                            totalMembers: _.isUndefined(this.teamModel) ? 0 : this.teamModel.get('membership').length
+                        })
+                    );
                     this.set(this.teamNameField, '.team-required-fields');
                     this.set(this.teamDescriptionField, '.team-required-fields');
                     this.set(this.teamLanguageField, '.team-optional-fields');
diff --git a/lms/djangoapps/teams/static/teams/js/views/instructor_tools.js b/lms/djangoapps/teams/static/teams/js/views/instructor_tools.js
index 27df329ae85..55c287a4fbd 100644
--- a/lms/djangoapps/teams/static/teams/js/views/instructor_tools.js
+++ b/lms/djangoapps/teams/static/teams/js/views/instructor_tools.js
@@ -7,8 +7,9 @@
         'edx-ui-toolkit/js/utils/string-utils',
         'teams/js/views/team_utils',
         'common/js/components/utils/view_utils',
-        'text!teams/templates/instructor-tools.underscore'],
-        function(Backbone, _, gettext, StringUtils, TeamUtils, ViewUtils, instructorToolbarTemplate) {
+        'text!teams/templates/instructor-tools.underscore',
+        'edx-ui-toolkit/js/utils/html-utils'],
+        function(Backbone, _, gettext, StringUtils, TeamUtils, ViewUtils, instructorToolbarTemplate, HtmlUtils) {
             return Backbone.View.extend({
 
                 events: {
@@ -17,13 +18,12 @@
                 },
 
                 initialize: function(options) {
-                    this.template = _.template(instructorToolbarTemplate);
                     this.team = options.team;
                     this.teamEvents = options.teamEvents;
                 },
 
                 render: function() {
-                    this.$el.html(this.template); // xss-lint: disable=javascript-jquery-html
+                    HtmlUtils.setHtml(this.$el, HtmlUtils.template(instructorToolbarTemplate)({}));
                     return this;
                 },
 
diff --git a/lms/djangoapps/teams/static/teams/js/views/my_teams.js b/lms/djangoapps/teams/static/teams/js/views/my_teams.js
index 35b520f9623..f95819a20fe 100644
--- a/lms/djangoapps/teams/static/teams/js/views/my_teams.js
+++ b/lms/djangoapps/teams/static/teams/js/views/my_teams.js
@@ -1,8 +1,8 @@
 (function(define) {
     'use strict';
 
-    define(['backbone', 'gettext', 'teams/js/views/teams'],
-        function(Backbone, gettext, TeamsView) {
+    define(['backbone', 'gettext', 'teams/js/views/teams', 'edx-ui-toolkit/js/utils/html-utils'],
+        function(Backbone, gettext, TeamsView, HtmlUtils) {
             var MyTeamsView = TeamsView.extend({
                 render: function() {
                     var view = this;
@@ -13,9 +13,7 @@
                         .done(function() {
                             TeamsView.prototype.render.call(view);
                             if (view.collection.length === 0) {
-                                view.$el.append( // xss-lint: disable=javascript-jquery-append
-                                    // eslint-disable-next-line max-len
-                                    '<p>' + gettext('You are not currently a member of any team.') + '</p>'); // xss-lint: disable=javascript-concat-html
+                                HtmlUtils.append(view.$el, gettext('You are not currently a member of any team.'));
                             }
                         });
                     return this;
diff --git a/lms/djangoapps/teams/static/teams/js/views/team_card.js b/lms/djangoapps/teams/static/teams/js/views/team_card.js
index 65ca6941899..35dcebb8043 100644
--- a/lms/djangoapps/teams/static/teams/js/views/team_card.js
+++ b/lms/djangoapps/teams/static/teams/js/views/team_card.js
@@ -10,7 +10,9 @@
         'teams/js/views/team_utils',
         'text!teams/templates/team-membership-details.underscore',
         'text!teams/templates/team-country-language.underscore',
-        'text!teams/templates/date.underscore'
+        'text!teams/templates/date.underscore',
+        'edx-ui-toolkit/js/utils/html-utils',
+        'edx-ui-toolkit/js/utils/string-utils'
     ], function(
         $,
         Backbone,
@@ -21,14 +23,15 @@
         TeamUtils,
         teamMembershipDetailsTemplate,
         teamCountryLanguageTemplate,
-        dateTemplate
+        dateTemplate,
+        HtmlUtils,
+        StringUtils
     ) {
         var TeamMembershipView, TeamCountryLanguageView, TeamActivityView, TeamCardView;
 
         TeamMembershipView = Backbone.View.extend({
             tagName: 'div',
             className: 'team-members',
-            template: _.template(teamMembershipDetailsTemplate),
 
             initialize: function(options) {
                 this.maxTeamSize = options.maxTeamSize;
@@ -41,21 +44,23 @@
                     }).reverse(),
                     displayableMemberships = allMemberships.slice(0, 5),
                     maxMemberCount = this.maxTeamSize;
-                this.$el.html(this.template({ // xss-lint: disable=javascript-jquery-html
-                    membership_message: TeamUtils.teamCapacityText(allMemberships.length, maxMemberCount),
-                    memberships: displayableMemberships,
-                    has_additional_memberships: displayableMemberships.length < allMemberships.length,
-                    /* Translators: "and others" refers to fact that additional
-                     * members of a team exist that are not displayed. */
-                    sr_message: gettext('and others')
-                }));
+                HtmlUtils.setHtml(
+                    this.$el,
+                    HtmlUtils.template(teamMembershipDetailsTemplate)({
+                        membership_message: TeamUtils.teamCapacityText(allMemberships.length, maxMemberCount),
+                        memberships: displayableMemberships,
+                        has_additional_memberships: displayableMemberships.length < allMemberships.length,
+                        /* Translators: "and others" refers to fact that additional
+                         * members of a team exist that are not displayed. */
+                        sr_message: gettext('and others')
+
+                    })
+                );
                 return this;
             }
         });
 
         TeamCountryLanguageView = Backbone.View.extend({
-            template: _.template(teamCountryLanguageTemplate),
-
             initialize: function(options) {
                 this.countries = options.countries;
                 this.languages = options.languages;
@@ -63,10 +68,13 @@
 
             render: function() {
                 // this.$el should be the card meta div
-                this.$el.append(this.template({ // xss-lint: disable=javascript-jquery-append
-                    country: this.countries[this.model.get('country')],
-                    language: this.languages[this.model.get('language')]
-                }));
+                HtmlUtils.append(
+                    this.$el,
+                    HtmlUtils.template(teamCountryLanguageTemplate)({
+                        country: this.countries[this.model.get('country')],
+                        language: this.languages[this.model.get('language')]
+                    })
+                );
             }
         });
 
@@ -83,15 +91,17 @@
                 var lastActivity = moment(this.date),
                     currentLanguage = $('html').attr('lang');
                 lastActivity.locale(currentLanguage);
-                this.$el.html( // xss-lint: disable=javascript-jquery-html
-                    // eslint-disable-next-line no-undef
-                    interpolate( // xss-lint: disable=javascript-interpolate
-                        /* Translators: 'date' is a placeholder for a fuzzy,
-                         * relative timestamp (see: http://momentjs.com/)
-                         */
-                        gettext('Last activity %(date)s'),
-                        {date: this.template({date: lastActivity.format('MMMM Do YYYY, h:mm:ss a')})},
-                        true
+                HtmlUtils.setHtml(
+                    this.$el,
+                    HtmlUtils.HTML(
+                        StringUtils.interpolate(
+                            /* Translators: 'date' is a placeholder for a fuzzy,
+                             * relative timestamp (see: http://momentjs.com/)
+                             */
+                            gettext('Last activity {date}'),
+                            {date: this.template({date: lastActivity.format('MMMM Do YYYY, h:mm:ss a')})},
+                            true
+                        )
                     )
                 );
                 this.$('abbr').text(lastActivity.fromNow());
@@ -123,9 +133,8 @@
             details: function() { return this.detailViews; },
             actionClass: 'action-view',
             actionContent: function() {
-                // eslint-disable-next-line no-undef
-                return interpolate( // xss-lint: disable=javascript-interpolate
-                    gettext('View %(span_start)s %(team_name)s %(span_end)s'),
+                return StringUtils.interpolate(
+                    gettext('View {span_start} {team_name} {span_end}'),
                     {span_start: '<span class="sr">', team_name: _.escape(this.model.get('name')), span_end: '</span>'},
                     true
                 );
diff --git a/lms/djangoapps/teams/static/teams/js/views/team_profile_header_actions.js b/lms/djangoapps/teams/static/teams/js/views/team_profile_header_actions.js
index 1a4bfbbeff4..66780676fa4 100644
--- a/lms/djangoapps/teams/static/teams/js/views/team_profile_header_actions.js
+++ b/lms/djangoapps/teams/static/teams/js/views/team_profile_header_actions.js
@@ -6,8 +6,9 @@
         'underscore',
         'gettext',
         'teams/js/views/team_utils',
-        'text!teams/templates/team-profile-header-actions.underscore'],
-        function(Backbone, $, _, gettext, TeamUtils, teamProfileHeaderActionsTemplate) {
+        'text!teams/templates/team-profile-header-actions.underscore',
+        'edx-ui-toolkit/js/utils/html-utils'],
+        function(Backbone, $, _, gettext, TeamUtils, teamProfileHeaderActionsTemplate, HtmlUtils) {
             return Backbone.View.extend({
 
                 errorMessage: gettext('An error occurred. Try again.'),
@@ -53,12 +54,14 @@
                                 showJoinButton = true;
                             }
                         }
-
-                        view.$el.html(view.template({ // xss-lint: disable=javascript-jquery-html
-                            showJoinButton: showJoinButton,
-                            message: message,
-                            showEditButton: view.showEditButton
-                        }));
+                        HtmlUtils.setHtml(
+                            view.$el,
+                            HtmlUtils.template(teamProfileHeaderActionsTemplate)({
+                                showJoinButton: showJoinButton,
+                                message: message,
+                                showEditButton: view.showEditButton
+                            })
+                        );
                     });
                     return view;
                 },
diff --git a/lms/djangoapps/teams/static/teams/js/views/team_utils.js b/lms/djangoapps/teams/static/teams/js/views/team_utils.js
index 4c3e09ca8ba..723b583abdc 100644
--- a/lms/djangoapps/teams/static/teams/js/views/team_utils.js
+++ b/lms/djangoapps/teams/static/teams/js/views/team_utils.js
@@ -1,8 +1,8 @@
 /*  Team utility methods*/
 (function(define) {
     'use strict';
-    define(['jquery', 'underscore'],
-    function($, _) {
+    define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/string-utils'],
+    function($, _, StringUtils) {
         return {
 
             /**
@@ -20,12 +20,11 @@
             },
 
             teamCapacityText: function(memberCount, maxMemberCount) {
-                // eslint-disable-next-line no-undef
-                return interpolate( // xss-lint: disable=javascript-interpolate
+                return StringUtils.interpolate(
                     // Translators: The following message displays the number of members on a team.
                     ngettext(
-                        '%(memberCount)s / %(maxMemberCount)s Member',
-                        '%(memberCount)s / %(maxMemberCount)s Members',
+                        '{memberCount} / {maxMemberCount} Member',
+                        '{memberCount} / {maxMemberCount} Members',
                         maxMemberCount
                     ),
                     {memberCount: memberCount, maxMemberCount: maxMemberCount}, true
diff --git a/lms/djangoapps/teams/static/teams/js/views/topic_card.js b/lms/djangoapps/teams/static/teams/js/views/topic_card.js
index a65ffe1b457..34734773b3f 100644
--- a/lms/djangoapps/teams/static/teams/js/views/topic_card.js
+++ b/lms/djangoapps/teams/static/teams/js/views/topic_card.js
@@ -3,8 +3,14 @@
  */
 (function(define) {
     'use strict';
-    define(['backbone', 'underscore', 'gettext', 'js/components/card/views/card'],
-        function(Backbone, _, gettext, CardView) {
+    define([
+        'backbone',
+        'underscore',
+        'gettext',
+        'js/components/card/views/card',
+        'edx-ui-toolkit/js/utils/html-utils',
+        'edx-ui-toolkit/js/utils/string-utils'],
+        function(Backbone, _, gettext, CardView, HtmlUtils, StringUtils) {
             var TeamCountDetailView = Backbone.View.extend({
                 tagName: 'p',
                 className: 'team-count',
@@ -15,12 +21,15 @@
 
                 render: function() {
                     var team_count = this.model.get('team_count'); // eslint-disable-line camelcase
-                    // eslint-disable-next-line no-undef, max-len
-                    this.$el.html(_.escape(interpolate( // xss-lint: disable=javascript-jquery-html,javascript-interpolate
-                        ngettext('%(team_count)s Team', '%(team_count)s Teams', team_count),
-                        {team_count: team_count},
-                        true
-                    )));
+                    HtmlUtils.setHtml(
+                        this.$el,
+                        HtmlUtils.HTML(_.escape(StringUtils.interpolate(
+                            ngettext('{team_count} Team', '{team_count} Teams', team_count),
+                            {team_count: team_count},
+                            true
+                        )))
+                    );
+
                     return this;
                 }
             });
@@ -43,9 +52,8 @@
                 details: function() { return this.detailViews; },
                 actionClass: 'action-view',
                 actionContent: function() {
-                    // eslint-disable-next-line no-undef
-                    var screenReaderText = _.escape(interpolate( // xss-lint: disable=javascript-interpolate
-                        gettext('View Teams in the %(topic_name)s Topic'),
+                    var screenReaderText = _.escape(StringUtils.interpolate(
+                        gettext('View Teams in the {topic_name} Topic'),
                         {topic_name: this.model.get('name')}, true
                     ));
                     // eslint-disable-next-line max-len
-- 
GitLab