From 11dcc177637a234fd624db5d766746dea992bcd4 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 31 Jul 2020 04:14:04 +0200 Subject: [PATCH] Improve HTML escaping helper (#12383) 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. Co-authored-by: techknowlogick --- 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 d14500932..415fb3801 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4372,6 +4372,11 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==" }, + "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 3597ed6a5..d2980e930 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "css-loader": "4.0.0", "cssnano-webpack-plugin": "1.0.3", "dropzone": "5.7.2", + "escape-goat": "3.0.0", "fast-glob": "3.2.4", "file-loader": "6.0.0", "fomantic-ui": "2.8.6", diff --git a/web_src/js/index.js b/web_src/js/index.js index 30a3d26ce..32fb340dc 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -4,6 +4,7 @@ import './publicpath.js'; import Vue from 'vue'; +import {htmlEscape} from 'escape-goat'; import 'jquery.are-you-sure'; import './vendor/semanticdropdown.js'; @@ -25,10 +26,6 @@ import {svg, svgs} from './svg.js'; const {AppSubUrl, StaticUrlPrefix, csrf} = window.config; -function htmlEncode(text) { - return jQuery('
').text(text).html(); -} - let previewFileModes; const commentMDEditors = {}; @@ -528,12 +525,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')); @@ -1944,7 +1941,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, @@ -2220,7 +2217,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 }); });