Made MD editor display a sandboxed iframe

- Also added escaping of srcdoc elements in escape logic.

Related to #1531
This commit is contained in:
Dan Brown 2019-08-26 12:16:50 +01:00
parent 2dfe6c2d56
commit 7cc17934a8
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
6 changed files with 39 additions and 15 deletions

View File

@ -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);
}

View File

@ -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];

View File

@ -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);

View File

@ -116,6 +116,7 @@ body.flexbox {
min-height: 0;
max-width: 100%;
position: relative;
overflow-y: hidden;
}
.flex {

View File

@ -28,8 +28,7 @@
<div class="editor-toolbar">
<div class="editor-toolbar-label">{{ trans('entities.pages_md_preview') }}</div>
</div>
<div class="markdown-display page-content">
</div>
<iframe class="markdown-display" sandbox="allow-same-origin"></iframe>
</div>
<input type="hidden" name="html"/>

View File

@ -118,7 +118,7 @@ class PageContentTest extends TestCase
'<iframe SRC=" javascript: alert(document.cookie)"></iframe>',
'<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgnaGVsbG8nKTwvc2NyaXB0Pg==" frameborder="0"></iframe>',
'<iframe src=" data:text/html;base64,PHNjcmlwdD5hbGVydCgnaGVsbG8nKTwvc2NyaXB0Pg==" frameborder="0"></iframe>',
'<iframe srcdoc="<script>window.alert(document.cookie)</script>"></iframe>'
];
$this->asEditor();