From ddc8adbbd3f843161a4a0faebd9e8cbee1575c50 Mon Sep 17 00:00:00 2001 From: Isiah Meadows Date: Wed, 3 Jul 2019 14:41:37 -0400 Subject: [PATCH] Fix #2424 (#2450) * Fix #2424 * Add PR ref --- docs/change-log.md | 1 + render/render.js | 30 +++++++++--------- render/tests/test-attributes.js | 56 ++++++++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 16 deletions(-) diff --git a/docs/change-log.md b/docs/change-log.md index 4582b2d9..29f3a8c5 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -100,6 +100,7 @@ - render: fix internal error when `onbeforeupdate` returns false and then true with new child tree ([#2447](https://github.com/MithrilJS/mithril.js/pull/2447) [@isiahmeadows](https://github.com/isiahmeadows)) - route: arbitrary prefixes are properly supported now, including odd prefixes like `?#` and invalid prefixes like `#foo#bar` ([#2448](https://github.com/MithrilJS/mithril.js/pull/2448) [@isiahmeadows](https://github.com/isiahmeadows)) - request: correct IE workaround for response type non-support ([#2449](https://github.com/MithrilJS/mithril.js/pull/2449) [@isiahmeadows](https://github.com/isiahmeadows)) +- render: correct `contenteditable` check to also check for `contentEditable` property name ([#2450](https://github.com/MithrilJS/mithril.js/pull/2450) [@isiahmeadows](https://github.com/isiahmeadows)) --- diff --git a/render/render.js b/render/render.js index 43d0ab31..c818193a 100644 --- a/render/render.js +++ b/render/render.js @@ -123,10 +123,7 @@ module.exports = function($window) { insertNode(parent, element, nextSibling) - if (attrs != null && attrs.contenteditable != null) { - setContentEditable(vnode) - } - else { + if (!maybeSetContentEditable(vnode)) { if (vnode.text != null) { if (vnode.text !== "") element.textContent = vnode.text else vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)] @@ -496,16 +493,15 @@ module.exports = function($window) { } } updateAttrs(vnode, old.attrs, vnode.attrs, ns) - if (vnode.attrs != null && vnode.attrs.contenteditable != null) { - setContentEditable(vnode) - } - else if (old.text != null && vnode.text != null && vnode.text !== "") { - if (old.text.toString() !== vnode.text.toString()) old.dom.firstChild.nodeValue = vnode.text - } - else { - if (old.text != null) old.children = [Vnode("#", undefined, undefined, old.text, undefined, old.dom.firstChild)] - if (vnode.text != null) vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)] - updateNodes(element, old.children, vnode.children, hooks, null, ns) + if (!maybeSetContentEditable(vnode)) { + if (old.text != null && vnode.text != null && vnode.text !== "") { + if (old.text.toString() !== vnode.text.toString()) old.dom.firstChild.nodeValue = vnode.text + } + else { + if (old.text != null) old.children = [Vnode("#", undefined, undefined, old.text, undefined, old.dom.firstChild)] + if (vnode.text != null) vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)] + updateNodes(element, old.children, vnode.children, hooks, null, ns) + } } } function updateComponent(parent, old, vnode, hooks, nextSibling, ns) { @@ -613,7 +609,11 @@ module.exports = function($window) { else parent.appendChild(dom) } - function setContentEditable(vnode) { + function maybeSetContentEditable(vnode) { + if (vnode.attrs == null || ( + vnode.attrs.contenteditable == null && // attribute + vnode.attrs.contentEditable == null // property + )) return var children = vnode.children if (children != null && children.length === 1 && children[0].tag === "<") { var content = children[0].children diff --git a/render/tests/test-attributes.js b/render/tests/test-attributes.js index 9b6f2ba3..00eb80dc 100644 --- a/render/tests/test-attributes.js +++ b/render/tests/test-attributes.js @@ -648,7 +648,7 @@ o.spec("attributes", function() { o(d.dom.value).equals("2") }) }) - o.spec("contenteditable throws on untrusted children", function() { + o.spec("contenteditable attr throws on untrusted children", function() { o("including text nodes", function() { var div = {tag: "div", attrs: {contenteditable: true}, text: ""} var succeeded = false @@ -699,6 +699,60 @@ o.spec("attributes", function() { } catch(e){/* ignore */} + o(succeeded).equals(true) + }) + }) + o.spec("contentEditable prop throws on untrusted children", function() { + o("including text nodes", function() { + var div = {tag: "div", attrs: {contentEditable: true}, text: ""} + var succeeded = false + + try { + render(root, div) + + succeeded = true + } + catch(e){/* ignore */} + + o(succeeded).equals(false) + }) + o("including elements", function() { + var div = {tag: "div", attrs: {contentEditable: true}, children: [{tag: "script", attrs: {src: "http://evil.com"}}]} + var succeeded = false + + try { + render(root, div) + + succeeded = true + } + catch(e){/* ignore */} + + o(succeeded).equals(false) + }) + o("tolerating empty children", function() { + var div = {tag: "div", attrs: {contentEditable: true}, children: []} + var succeeded = false + + try { + render(root, div) + + succeeded = true + } + catch(e){/* ignore */} + + o(succeeded).equals(true) + }) + o("tolerating trusted content", function() { + var div = {tag: "div", attrs: {contentEditable: true}, children: [{tag: "<", children: ""}]} + var succeeded = false + + try { + render(root, div) + + succeeded = true + } + catch(e){/* ignore */} + o(succeeded).equals(true) }) })