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' %}