From ee5e5a50934906df476c3b898c2fe8e0df984f3c Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 22 Aug 2020 14:36:56 +0200 Subject: [PATCH] Improve HTML escaping helper (#12562) The previous method did not escape single quotes which under some circumstances can lead to XSS vulnerabilites and the fact that it depends on jQuery is also not ideal. Replace it with a lightweight module. --- package-lock.json | 5 +++++ package.json | 1 + web_src/js/index.js | 17 +++++++---------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2f1c58073..87e6991dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4007,6 +4007,11 @@ "es6-symbol": "^3.1.1" } }, + "escape-goat": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", + "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", diff --git a/package.json b/package.json index ce14631d7..fe2c6193c 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "cssnano": "4.1.10", "domino": "2.1.5", "dropzone": "5.7.0", + "escape-goat": "3.0.0", "fast-glob": "3.2.2", "file-loader": "6.0.0", "fomantic-ui": "2.8.4", diff --git a/web_src/js/index.js b/web_src/js/index.js index 886520db6..6fe6d3777 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -6,6 +6,7 @@ import './publicpath.js'; import './polyfills.js'; import Vue from 'vue'; +import {htmlEscape} from 'escape-goat'; import 'jquery.are-you-sure'; import './vendor/semanticdropdown.js'; import {svg} from './utils.js'; @@ -25,10 +26,6 @@ import {createCodeEditor} from './features/codeeditor.js'; const {AppSubUrl, StaticUrlPrefix, csrf} = window.config; -function htmlEncode(text) { - return jQuery('
').text(text).html(); -} - let previewFileModes; const commentMDEditors = {}; @@ -532,12 +529,12 @@ function initCommentForm() { switch (input_id) { case '#milestone_id': $list.find('.selected').html(`${ - htmlEncode($(this).text())}`); + htmlEscape($(this).text())}`); break; case '#assignee_id': $list.find('.selected').html(`` + `${ - htmlEncode($(this).text())}`); + htmlEscape($(this).text())}`); } $(`.ui${select_id}.list .no-select`).addClass('hide'); $(input_id).val($(this).data('id')); @@ -1942,7 +1939,7 @@ function searchUsers() { $.each(response.data, (_i, item) => { let title = item.login; if (item.full_name && item.full_name.length > 0) { - title += ` (${htmlEncode(item.full_name)})`; + title += ` (${htmlEscape(item.full_name)})`; } items.push({ title, @@ -2223,7 +2220,7 @@ function initTemplateSearch() { // Parse the response from the api to work with our dropdown $.each(response.data, (_r, repo) => { filteredResponse.results.push({ - name: htmlEncode(repo.full_name), + name: htmlEscape(repo.full_name), value: repo.id }); }); @@ -3500,8 +3497,8 @@ function initIssueList() { return; } filteredResponse.results.push({ - name: `#${issue.number} ${htmlEncode(issue.title) - }
${htmlEncode(issue.repository.full_name)}
`, + name: `#${issue.number} ${htmlEscape(issue.title) + }
${htmlEscape(issue.repository.full_name)}
`, value: issue.id }); });