Render markdown on the client side and use DOMPurify to prevent XSS.,

The parsedown parser has problems with links in <>, so we use marked.js now which is more conform with (GFM) CommonMark and offers more feautures. Also with the usage of DOMPurify you can now use every HTML tag in Markdown without need to worry about XSS.
This commit is contained in:
Jan Böhmer 2019-10-12 17:41:13 +02:00
parent 7ec406d4a1
commit be8f074ca5
9 changed files with 93 additions and 32 deletions

View file

@ -795,3 +795,10 @@ table.dataTable {
max-width: 35%;
vertical-align: middle;
}
.markdown blockquote {
padding: 10px 10px;
margin: 0 0 10px;
font-size: 17.5px;
border-left: 5px solid #eee;
}

View file

@ -52,7 +52,10 @@ require('bootstrap-fileinput');
require('./datatables.js');
window.bootbox = require('bootbox')
window.bootbox = require('bootbox');
require("marked");
window.DOMPurify = require("dompurify");
// Includes required for tag input
require('./tagsinput.js');

View file

@ -30,6 +30,8 @@
import {ajaxUI} from "./ajax_ui";
import "bootbox";
import "marked";
import * as marked from "marked";
/************************************
*
@ -202,11 +204,11 @@ $(document).on("ajaxUI:start ajaxUI:reload", function() {
message: message,
title: title,
callback: function(result) {
//If the dialog was confirmed, then submit the form.
if(result) {
ajaxUI.submitForm(form);
}
}});
//If the dialog was confirmed, then submit the form.
if(result) {
ajaxUI.submitForm(form);
}
}});
return false;
});
@ -275,7 +277,7 @@ $(document).on("ajaxUI:reload ajaxUI:start", function () {
if (location.hash) {
$activeTab = $('a[href=\'' + location.hash + '\']');
} else if(localStorage.getItem('activeTab')) {
$activeTab = $('a[href="' + localStorage.getItem('activeTab') + '"]');
$activeTab = $('a[href="' + localStorage.getItem('activeTab') + '"]');
}
if($activeTab) {
@ -372,20 +374,49 @@ $(document).on("ajaxUI:reload ajaxUI:start attachment:create", function () {
});
$(this).typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
name: 'states',
source: engine
});
hint: true,
highlight: true,
minLength: 1
},
{
name: 'states',
source: engine
});
//Make the typeahead input fill the container (remove block-inline attr)
$(this).parent(".twitter-typeahead").css('display', 'block');
})
});
$(document).on("ajaxUI:start", function () {
function decodeHTML(html) {
var txt = document.createElement('textarea');
txt.innerHTML = html;
return txt.value;
};
function parseMarkdown() {
$('.markdown').each(function() {
let unescaped = marked(decodeHTML( $(this).data('markdown')));
//@ts-ignore
let escaped = DOMPurify.sanitize(unescaped);
$(this).html(escaped);
//Remove markdown from DOM
$(this).removeAttr('data-markdown');
//Make all links external
$('a', this).addClass('link-external').attr('target', '_blank');
});
}
//Configure markdown
marked.setOptions({
gfm: true,
});
parseMarkdown();
$(document).on("ajaxUI:reload", parseMarkdown);
});
//Need for proper body padding, with every navbar height
$(window).resize(function () {

View file

@ -4,7 +4,7 @@
"target": "es5",
"sourceMap": true,
"typeRoots": ["../node_modules"],
"types": ["jquery", "bootstrap", "jquery.form", "bootstrap-treeview", "bootbox", "typeahead"]
"types": ["jquery", "bootstrap", "jquery.form", "bootstrap-treeview", "bootbox", "typeahead", "marked"]
},
"exclude": [
"node_modules"