From 51a2f11c6bb48a00b77c1624ea2ee3bcfa511133 Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht Date: Fri, 18 Jun 2021 14:57:20 +0200 Subject: [PATCH] integrate marked as markdown parser Define our own, rather minimal interface so that we change the parser under the hood if ever needed, I already did so once during evaluating this, as first I checked out Snarkdown[0], which is really nice for the few lines of code it needs, but is a bit to limited for the use case. Currently marked[1] is used, provided by the libjs-marked Debian package. For now statically link the marked parser in on built time to avoid the need to add new directories to serve in our pve/pmg/pbs proxies. This is a bit ugly but can be cleaned up afterwards transparently too. We sanitize the produced HTML ourselves (most MD JS parser/renderer don't do that) by creating a real, but not active, DOM tree and recursively prune bad nodes/attrs from it and let it spit out HTML again at the end. While a tad inefficient it really won't matter for our use case, as the notes/comments we render are only a few KiB of text and it's done on the client side anyway. Signed-off-by: Thomas Lamprecht --- debian/control | 1 + debian/copyright | 9 +++++++++ src/Makefile | 7 ++++++- src/Parser.js | 45 ++++++++++++++++++++++++++++++++++++++++++++ src/css/ext6-pmx.css | 17 +++++++++++++++++ 5 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 src/Parser.js diff --git a/debian/control b/debian/control index ad88929..f0794fa 100644 --- a/debian/control +++ b/debian/control @@ -3,6 +3,7 @@ Section: web Priority: optional Maintainer: Proxmox Support Team Build-Depends: debhelper (>= 12~), + libjs-marked, pve-eslint (>= 7.12.1-1), Standards-Version: 4.5.1 Homepage: https://www.proxmox.com diff --git a/debian/copyright b/debian/copyright index 2f3455d..e7310c2 100644 --- a/debian/copyright +++ b/debian/copyright @@ -15,3 +15,12 @@ License: AGPLv3 . You should have received a copy of the GNU Affero General Public License along with this program. If not, see . + +Marked: + The Marked JavaScript library is shipped through linkage from the Debian + package unmodified alongside proxmox-widget-toolkit. + + Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/) Copyright (c) + 2011-2018, Christopher Jeffrey (https://github.com/chjj/) + + For the license and copyright details see `/usr/share/doc/libjs-marked/copyright` diff --git a/src/Makefile b/src/Makefile index afbe2ec..37da480 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,10 +2,15 @@ include defines.mk SUBDIRS= css images +# bundle it for now from the libjs-marked debian package to avoid touching our proxies file mapper, +# we could also just ship a link to the packages file and load from same path as the widget-toolkit +MARKEDJS=/usr/share/javascript/marked/marked.min.js + JSSRC= \ Utils.js \ Toolkit.js \ Logo.js \ + Parser.js \ mixin/CBind.js \ data/reader/JsonObject.js \ data/ProxmoxProxy.js \ @@ -94,7 +99,7 @@ lint: ${JSSRC} proxmoxlib.js: .lint-incremental ${JSSRC} # add the version as comment in the file echo "// ${DEB_VERSION_UPSTREAM_REVISION}" > $@.tmp - cat ${JSSRC} >> $@.tmp + cat ${JSSRC} ${MARKEDJS} >> $@.tmp mv $@.tmp $@ install: proxmoxlib.js diff --git a/src/Parser.js b/src/Parser.js new file mode 100644 index 0000000..a5e9337 --- /dev/null +++ b/src/Parser.js @@ -0,0 +1,45 @@ +// NOTE: just relays parsing to markedjs parser +Ext.define('Proxmox.Markdown', { + alternateClassName: 'Px.Markdown', // just trying out something, do NOT copy this line + singleton: true, + + // transforms HTML to a DOM tree and recursively descends and prunes every branch with a + // "bad" node.type and drops "bad" attributes from the remaining nodes. + // "bad" means anything which can do XSS or break the layout of the outer page + sanitizeHTML: function(input) { + if (!input) { + return input; + } + let _sanitize; + _sanitize = (node) => { + if (node.nodeType === 3) return; + if (node.nodeType !== 1 || /^(script|iframe|object|embed|svg)$/i.test(node.tagName)) { + node.remove(); + return; + } + for (let i=node.attributes.length; i--;) { + const name = node.attributes[i].name; + // TODO: we may want to also disallow class and id attrs + if (!/^(class|id|name|href|src|alt|align|valign)$/i.test(name)) { + node.attributes.removeNamedItem(name); + } + } + for (let i=node.childNodes.length; i--;) _sanitize(node.childNodes[i]); + }; + + const doc = new DOMParser().parseFromString(`${input}`, 'text/html'); + doc.normalize(); + + _sanitize(doc.body); + + return doc.body.innerHTML; + }, + + parse: function(markdown) { + /*global marked*/ + let unsafeHTML = marked(markdown); + + return `
${this.sanitizeHTML(unsafeHTML)}
`; + }, + +}); diff --git a/src/css/ext6-pmx.css b/src/css/ext6-pmx.css index 0c83d8a..09ec11c 100644 --- a/src/css/ext6-pmx.css +++ b/src/css/ext6-pmx.css @@ -127,3 +127,20 @@ div.right-aligned { .x-legend-inner { padding: 0; } + +/* rules for the markdown content, prefix with the .pmx-md class */ +.pmx-md code { + white-space: pre; + background-color: #f5f5f5; + padding: 1px; +} + +.pmx-md pre code { + display: inline-block; + padding: 5px; + border-left: 3px solid #e0e0e0; +} + +.pmx-md strong { + font-weight: bold; +} -- 2.39.2