From 7cc17934a87714fb7a01009f8c8dc059f0577b2d Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Mon, 26 Aug 2019 12:16:50 +0100 Subject: [PATCH] Made MD editor display a sandboxed iframe - Also added escaping of srcdoc elements in escape logic. Related to #1531 --- app/Entities/Repos/EntityRepo.php | 2 +- .../assets/js/components/markdown-editor.js | 32 ++++++++++++++++--- resources/assets/sass/_forms.scss | 14 ++++---- resources/assets/sass/_layout.scss | 1 + .../views/pages/markdown-editor.blade.php | 3 +- tests/Entity/PageContentTest.php | 2 +- 6 files changed, 39 insertions(+), 15 deletions(-) diff --git a/app/Entities/Repos/EntityRepo.php b/app/Entities/Repos/EntityRepo.php index 7ca25b785..996873bcc 100644 --- a/app/Entities/Repos/EntityRepo.php +++ b/app/Entities/Repos/EntityRepo.php @@ -766,7 +766,7 @@ class EntityRepo } // Remove data or JavaScript iFrames - $badIframes = $xPath->query('//*[contains(@src, \'data:\')] | //*[contains(@src, \'javascript:\')]'); + $badIframes = $xPath->query('//*[contains(@src, \'data:\')] | //*[contains(@src, \'javascript:\')] | //*[@srcdoc]'); foreach ($badIframes as $badIframe) { $badIframe->parentNode->removeChild($badIframe); } diff --git a/resources/assets/js/components/markdown-editor.js b/resources/assets/js/components/markdown-editor.js index 7cb56eef8..ac77cb459 100644 --- a/resources/assets/js/components/markdown-editor.js +++ b/resources/assets/js/components/markdown-editor.js @@ -18,6 +18,8 @@ class MarkdownEditor { this.markdown.use(mdTasksLists, {label: true}); this.display = this.elem.querySelector('.markdown-display'); + this.displayDoc = this.display.contentDocument; + this.displayStylesLoaded = false; this.input = this.elem.querySelector('textarea'); this.htmlInput = this.elem.querySelector('input[name=html]'); this.cm = code.markdownEditor(this.input); @@ -38,7 +40,7 @@ class MarkdownEditor { let lastClick = 0; // Prevent markdown display link click redirect - this.display.addEventListener('click', event => { + this.displayDoc.addEventListener('click', event => { let isDblClick = Date.now() - lastClick < 300; let link = event.target.closest('a'); @@ -96,17 +98,37 @@ class MarkdownEditor { // Update the input content and render the display. updateAndRender() { - let content = this.cm.getValue(); + const content = this.cm.getValue(); this.input.value = content; - let html = this.markdown.render(content); + const html = this.markdown.render(content); window.$events.emit('editor-html-change', html); window.$events.emit('editor-markdown-change', content); - this.display.innerHTML = html; + + // Set body content + this.displayDoc.body.className = 'page-content'; + this.displayDoc.body.innerHTML = html; this.htmlInput.value = html; + + // Copy styles from page head and set custom styles for editor + this.loadStylesIntoDisplay(); + } + + loadStylesIntoDisplay() { + if (this.displayStylesLoaded) return; + this.displayDoc.documentElement.className = 'markdown-editor-display'; + + this.displayDoc.head.innerHTML = ''; + const styles = document.head.querySelectorAll('style,link[rel=stylesheet]'); + for (let style of styles) { + const copy = style.cloneNode(true); + this.displayDoc.head.appendChild(copy); + } + + this.displayStylesLoaded = true; } onMarkdownScroll(lineCount) { - const elems = this.display.children; + const elems = this.displayDoc.body.children; if (elems.length <= lineCount) return; const topElem = (lineCount === -1) ? elems[elems.length-1] : elems[lineCount]; diff --git a/resources/assets/sass/_forms.scss b/resources/assets/sass/_forms.scss index 2f34dc092..64308b29e 100644 --- a/resources/assets/sass/_forms.scss +++ b/resources/assets/sass/_forms.scss @@ -93,13 +93,15 @@ } .markdown-display { - padding: 0 $-m 0; margin-left: -1px; - overflow-y: scroll; - &.page-content { - margin: 0 auto; - width: 100%; - max-width: 100%; +} + +.markdown-editor-display { + background-color: #FFFFFF; + body { + background-color: #FFFFFF; + padding-left: 16px; + padding-right: 16px; } [drawio-diagram]:hover { outline: 2px solid var(--color-primary); diff --git a/resources/assets/sass/_layout.scss b/resources/assets/sass/_layout.scss index a280e4ed1..1a7ff2cab 100644 --- a/resources/assets/sass/_layout.scss +++ b/resources/assets/sass/_layout.scss @@ -116,6 +116,7 @@ body.flexbox { min-height: 0; max-width: 100%; position: relative; + overflow-y: hidden; } .flex { diff --git a/resources/views/pages/markdown-editor.blade.php b/resources/views/pages/markdown-editor.blade.php index 87bde33ac..d4f6323b0 100644 --- a/resources/views/pages/markdown-editor.blade.php +++ b/resources/views/pages/markdown-editor.blade.php @@ -28,8 +28,7 @@
{{ trans('entities.pages_md_preview') }}
-
-
+ diff --git a/tests/Entity/PageContentTest.php b/tests/Entity/PageContentTest.php index b447a7c5d..e812d5bfe 100644 --- a/tests/Entity/PageContentTest.php +++ b/tests/Entity/PageContentTest.php @@ -118,7 +118,7 @@ class PageContentTest extends TestCase '', '', '', - + '' ]; $this->asEditor();