From 18ea61dc9a75a378c81810703f4775f2a13b434c Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 19 Aug 2016 15:43:14 +0200 Subject: [PATCH] use diffdom for sanitation and better redraws --- www/slide/slide.js | 77 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/www/slide/slide.js b/www/slide/slide.js index 0f5501c85..098cad315 100644 --- a/www/slide/slide.js +++ b/www/slide/slide.js @@ -1,8 +1,10 @@ define([ '/bower_components/marked/marked.min.js', + '/bower_components/diff-dom/diffDOM.js', '/bower_components/jquery/dist/jquery.min.js', ],function (Marked) { var $ = window.jQuery; + var DiffDOM = window.diffDOM; var truthy = function (x) { return x; }; @@ -17,13 +19,78 @@ define([ $modal = Slide.$modal = $m; $content = Slide.$content = $c; }; + + var forbiddenTags = Slide.forbiddenTags = [ + 'SCRIPT', + 'IFRAME', + 'OBJECT', + 'APPLET', + 'VIDEO', + 'AUDIO', + ]; + var unsafeTag = function (info) { + if (['addAttribute', 'modifyAttribute'].indexOf(info.diff.action) !== -1) { + if (/^on/.test(info.diff.name)) { + console.log("Rejecting forbidden element attribute with name", info.diff.element.nodeName); + return true; + } + } + if (['addElement', 'replaceElement'].indexOf(info.diff.action) !== -1) { + var msg = "Rejecting forbidden tag of type (%s)"; + if (info.diff.element && forbiddenTags.indexOf(info.diff.element.nodeName) !== -1) { + console.log(msg, info.diff.element.nodeName); + return true; + } else if (info.diff.newValue && forbiddenTags.indexOf(info.diff.newValue.nodeName) !== -1) { + console.log("Replacing restricted element type (%s) with PRE", info.diff.newValue.nodeName); + info.diff.newValue.nodeName = 'PRE'; + } + } + }; + + var domFromHTML = Slide.domFromHTML = function (html) { + return new DOMParser().parseFromString(html, "text/html"); + }; + + var DD = new DiffDOM({ + preDiffApply: function (info) { + if (unsafeTag(info)) { return true; } + } + }); + + var makeDiff = function (A, B) { + var Err; + var Els = [A, B].map(function (frag) { + if (typeof(frag) === 'object') { + if (!frag && frag.body) { + Err = "No body"; + return; + } + var els = frag.body.querySelectorAll('#content'); + if (els.length) { + return els[0]; + } + } + Err = 'No candidate found'; + }); + if (Err) { return Err; } + var patch = DD.diff(Els[0], Els[1]); + return patch; + }; + var draw = Slide.draw = function (i) { console.log("Trying to draw slide #%s", i); if (typeof(Slide.content[i]) !== 'string') { return; } var c = Slide.content[i]; - console.log(c); - $content.html(Marked(c)); + var Dom = domFromHTML('
' + Marked(c) + '
'); + var patch = makeDiff(domFromHTML($content[0].outerHTML), Dom); + + if (typeof(patch) === 'string') { + $content.html(Marked(c)); + return; + } else { + DD.apply($content[0], patch); + } }; var show = Slide.show = function (bool, content) { @@ -39,9 +106,11 @@ define([ var update = Slide.update = function (content) { if (!Slide.shown) { return; } - console.log(content); + var old = Slide.content[Slide.index]; Slide.content = content.split(/\n\s*\-\-\-\s*\n/).filter(truthy); - draw(Slide.index); + if (old !== Slide.content[Slide.index]) { + draw(Slide.index); + } }; var left = Slide.left = function () {