diff --git a/.eslintrc.js b/.eslintrc.js index 2650c58..748b27b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,7 +15,8 @@ module.exports = { "Services": true }, "plugins": [ - "promise" + "promise", + "unsafe-property-assignment" ], "root": true, "rules": { @@ -28,6 +29,8 @@ module.exports = { "promise/no-promise-in-callback": "warn", "promise/no-return-wrap": "error", "promise/param-names": "error", + "unsafe-property-assignment/no-key-assignment": ["error"], + "unsafe-property-assignment/enforce-tagged-template-protection": ["error"], "eqeqeq": "error", "indent": ["error", 2], diff --git a/package.json b/package.json index 94d011e..75f6de1 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "testpilot-containers", "title": "Containers Experiment", "description": "Containers works by isolating cookie jars using separate origin-attributes defined visually by colored ‘Container Tabs’. This add-on is a modified version of the containers feature for Firefox Test Pilot.", - "version": "1.1.0", + "version": "1.1.1", "author": "Andrea Marchesini, Luke Crouch and Jonathan Kingston", "bugs": { "url": "https://github.com/mozilla/testpilot-containers/issues" @@ -11,8 +11,9 @@ "devDependencies": { "addons-linter": "^0.15.14", "deploy-txp": "^1.0.7", - "eslint": "^3.12.2", + "eslint": "^3.17.1", "eslint-plugin-promise": "^3.4.0", + "eslint-plugin-unsafe-property-assign": "^1.0.2", "htmllint-cli": "^0.0.5", "jpm": "^1.2.2", "npm-run-all": "^4.0.0", diff --git a/webextension/js/popup.js b/webextension/js/popup.js index abb1c64..c108558 100644 --- a/webextension/js/popup.js +++ b/webextension/js/popup.js @@ -18,6 +18,42 @@ const P_CONTAINER_INFO = "containerInfo"; const P_CONTAINER_EDIT = "containerEdit"; const P_CONTAINER_DELETE = "containerDelete"; +/** + * Escapes any occurances of &, ", < or > with XML entities. + * + * @param {string} str + * The string to escape. + * @return {string} The escaped string. + */ +function escapeXML(str) { + const replacements = {"&": "&", "\"": """, "'": "'", "<": "<", ">": ">"}; + return String(str).replace(/[&"''<>]/g, m => replacements[m]); +} + +/** + * A tagged template function which escapes any XML metacharacters in + * interpolated values. + * + * @param {Array} strings + * An array of literal strings extracted from the templates. + * @param {Array} values + * An array of interpolated values extracted from the template. + * @returns {string} + * The result of the escaped values interpolated with the literal + * strings. + */ +function escaped(strings, ...values) { + const result = []; + + for (const [i, string] of strings.entries()) { + result.push(string); + if (i < values.length) + result.push(escapeXML(values[i])); + } + + return result.join(""); +} + // This object controls all the panels, identities and many other things. const Logic = { _identities: [], @@ -235,7 +271,7 @@ Logic.registerPanel(P_CONTAINERS_LIST, { tr.classList.add("container-panel-row"); context.classList.add("userContext-wrapper", "open-newtab", "clickable"); manage.classList.add("show-tabs", "pop-button"); - context.innerHTML = ` + context.innerHTML = escaped`
${tab.title}`; @@ -422,7 +458,7 @@ Logic.registerPanel(P_CONTAINERS_EDIT, { const tr = document.createElement("tr"); fragment.appendChild(tr); tr.classList.add("container-panel-row"); - tr.innerHTML = ` + tr.innerHTML = escaped`
{ - return ` + return escaped`