diff --git a/package.json b/package.json --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "clipboard": "^2.0.4", "d3": "^5.9.2", "datatables.net-bs4": "^1.10.19", + "dompurify": "^1.0.10", "elementsfrompoint-polyfill": "^1.0.0", "font-awesome": "^4.7.0", "highlight.js": "^9.15.6", @@ -34,7 +35,6 @@ "pdfjs-dist": "^2.0.943", "popper.js": "^1.15.0", "showdown": "^1.9.0", - "showdown-xss-filter": "^0.2.0", "typeface-alegreya": "0.0.69", "typeface-alegreya-sans": "^0.0.72", "url-search-params-polyfill": "^5.1.0", diff --git a/swh/web/assets/src/bundles/webapp/readme-rendering.js b/swh/web/assets/src/bundles/webapp/readme-rendering.js --- a/swh/web/assets/src/bundles/webapp/readme-rendering.js +++ b/swh/web/assets/src/bundles/webapp/readme-rendering.js @@ -1,25 +1,39 @@ /** - * Copyright (C) 2018 The Software Heritage developers + * Copyright (C) 2018-2019 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ +import DOMPurify from 'dompurify'; + import {handleFetchError} from 'utils/functions'; +DOMPurify.addHook('uponSanitizeAttribute', function(node, data) { + if (node.nodeName === 'IMG' && data.attrName === 'src') { + // remove leading slash from image src to fix rendering + if (data.attrValue.startsWith('/')) { + data.attrValue = data.attrValue.slice(1); + } + } +}); + +export function filterXSS(html) { + return DOMPurify.sanitize(html); +} + export async function renderMarkdown(domElt, markdownDocUrl) { let showdown = await import(/* webpackChunkName: "showdown" */ 'utils/showdown'); - let xssFilter = require('showdown-xss-filter'); $(document).ready(() => { - let converter = new showdown.Converter({tables: true, extensions: [xssFilter]}); + let converter = new showdown.Converter({tables: true}); fetch(markdownDocUrl) .then(handleFetchError) .then(response => response.text()) .then(data => { $(domElt).addClass('swh-showdown'); - $(domElt).html(converter.makeHtml(data)); + $(domElt).html(filterXSS(converter.makeHtml(data))); }) .catch(() => { $(domElt).text('Readme bytes are not available'); @@ -36,7 +50,7 @@ let orgDocument = parser.parse(orgDocData, {toc: false}); let orgHTMLDocument = orgDocument.convert(org.ConverterHTML, {}); $(domElt).addClass('swh-org'); - $(domElt).html(orgHTMLDocument.toString()); + $(domElt).html(filterXSS(orgHTMLDocument.toString())); // remove toc and section numbers to get consistent // with other readme renderings $('.swh-org ul').first().remove(); diff --git a/swh/web/templates/includes/readme-display.html b/swh/web/templates/includes/readme-display.html --- a/swh/web/templates/includes/readme-display.html +++ b/swh/web/templates/includes/readme-display.html @@ -19,7 +19,7 @@ {% if readme_html %} {% elif readme_name.lower == 'readme' or readme_name.lower == 'readme.txt' %}