diff --git a/package.json b/package.json index 56535501..60d040b9 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "test": "npm run lint-errors && npm run just-test-in-node", "test-in-node": "npm run lint-errors && npm run just-test-in-node", "just-test": "karma start --config karma.conf.js", - "just-test-in-node": "mocha --recursive --compilers js:babel-core/register test/core test/components test/bugs test/swagger-ui-dist-package test/xss", + "just-test-in-node": "mocha --require test/setup.js --recursive --compilers js:babel-core/register test/core test/components test/bugs test/swagger-ui-dist-package test/xss", "just-check-coverage": "nyc npm run just-test-in-node", "test-e2e": "sleep 3 && nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json", "e2e-initial-render": "nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json --group initial-render", @@ -48,6 +48,7 @@ "core-js": "^2.5.1", "css.escape": "1.5.1", "deep-extend": "0.4.1", + "dompurify": "^1.0.4", "expect": "1.20.2", "getbase": "^2.8.2", "ieee754": "^1.1.8", @@ -80,7 +81,6 @@ "redux-logger": "*", "remarkable": "^1.7.1", "reselect": "2.5.3", - "sanitize-html": "^1.14.1", "scroll-to-element": "^2.0.0", "serialize-error": "2.0.0", "shallowequal": "0.2.2", @@ -119,6 +119,7 @@ "file-loader": "0.11.2", "git-describe": "^4.0.1", "imports-loader": "0.7.1", + "jsdom": "^11.10.0", "json-loader": "0.5.4", "json-server": "^0.11.0", "karma": "^1.7.0", diff --git a/src/core/components/providers/markdown.jsx b/src/core/components/providers/markdown.jsx index 4952a326..aac227fa 100644 --- a/src/core/components/providers/markdown.jsx +++ b/src/core/components/providers/markdown.jsx @@ -1,7 +1,7 @@ import React from "react" import PropTypes from "prop-types" import Remarkable from "remarkable" -import sanitize from "sanitize-html" +import DomPurify from "dompurify" import cx from "classnames" // eslint-disable-next-line no-useless-escape @@ -40,20 +40,8 @@ Markdown.propTypes = { export default Markdown -const sanitizeOptions = { - allowedTags: sanitize.defaults.allowedTags.concat([ "h1", "h2", "img", "span" ]), - allowedAttributes: { - ...sanitize.defaults.allowedAttributes, - "img": sanitize.defaults.allowedAttributes.img.concat(["title"]), - "td": [ "colspan" ], - "*": [ "class" ] - }, - allowedSchemesByTag: { img: [ "http", "https", "data" ] }, - textFilter: function(text) { - return text.replace(/"/g, "\"") - } -} - export function sanitizer(str) { - return sanitize(str, sanitizeOptions) + return DomPurify.sanitize(str, { + ADD_ATTR: ["target"] + }) } diff --git a/test/components/markdown.js b/test/components/markdown.js index cf208510..ebf765bc 100644 --- a/test/components/markdown.js +++ b/test/components/markdown.js @@ -16,19 +16,19 @@ describe("Markdown component", function() { it("allows td elements with colspan attrib", function() { const str = `
ABC
` const el = render() - expect(el.html()).toEqual(`
ABC
`) + expect(el.html()).toEqual(`
ABC
`) }) it("allows image elements", function() { const str = `![Image alt text](http://image.source "Image title")` const el = render() - expect(el.html()).toEqual(`

\n
`) + expect(el.html()).toEqual(`

Image alt text

\n
`) }) - + it("allows image elements with https scheme", function() { const str = `![Image alt text](https://image.source "Image title")` const el = render() - expect(el.html()).toEqual(`

\n
`) + expect(el.html()).toEqual(`

Image alt text

\n
`) }) it("allows image elements with data scheme", function() { @@ -52,7 +52,7 @@ describe("Markdown component", function() { it("allows links", function() { const str = `[Link](https://example.com/)` const el = render() - expect(el.html()).toEqual(``) + expect(el.html()).toEqual(``) }) }) @@ -60,13 +60,13 @@ describe("Markdown component", function() { it("allows image elements", function() { const str = `![Image alt text](http://image.source "Image title")` const el = render() - expect(el.html()).toEqual(`

`) + expect(el.html()).toEqual(`

Image alt text

`) }) it("allows image elements with https scheme", function() { const str = `![Image alt text](https://image.source "Image title")` const el = render() - expect(el.html()).toEqual(`

`) + expect(el.html()).toEqual(`

Image alt text

`) }) it("allows image elements with data scheme", function() { diff --git a/test/setup.js b/test/setup.js new file mode 100644 index 00000000..49d8d1d1 --- /dev/null +++ b/test/setup.js @@ -0,0 +1,23 @@ +const { JSDOM } = require("jsdom") +const win = require("core/window") + +const jsdom = new JSDOM("") +const { window } = jsdom + +function copyProps(src, target) { + const props = Object.getOwnPropertyNames(src) + .filter(prop => typeof target[prop] === "undefined") + .reduce((result, prop) => ({ + ...result, + [prop]: Object.getOwnPropertyDescriptor(src, prop), + }), {}) + Object.defineProperties(target, props) +} + +global.window = window +global.document = window.document +global.navigator = { + userAgent: "node.js", +} +copyProps(win, window) // use UI's built-in window wrapper +copyProps(window, global)