]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/Parser.js
markdown: extend blocked tags in sanitizer
[proxmox-widget-toolkit.git] / src / Parser.js
1 // NOTE: just relays parsing to markedjs parser
2 Ext.define('Proxmox.Markdown', {
3 alternateClassName: 'Px.Markdown', // just trying out something, do NOT copy this line
4 singleton: true,
5
6 // transforms HTML to a DOM tree and recursively descends and HTML-encodes every branch with a
7 // "bad" node.type and drops "bad" attributes from the remaining nodes.
8 // "bad" means anything which can do XSS or break the layout of the outer page
9 sanitizeHTML: function(input) {
10 if (!input) {
11 return input;
12 }
13 let _isHTTPLike = value => value.match(/^\s*https?:/i); // URL's protocol ends with :
14 let _sanitize;
15 _sanitize = (node) => {
16 if (node.nodeType === 3) return;
17 if (node.nodeType !== 1 ||
18 /^(script|style|form|select|option|optgroup|map|area|canvas|textarea|applet|font|iframe|audio|video|object|embed|svg)$/i.test(node.tagName)
19 ) {
20 // could do node.remove() instead, but it's nicer UX if we keep the (encoded!) html
21 node.outerHTML = Ext.String.htmlEncode(node.outerHTML);
22 return;
23 }
24 for (let i=node.attributes.length; i--;) {
25 const name = node.attributes[i].name;
26 const value = node.attributes[i].value;
27 // TODO: we may want to also disallow class and id attrs
28 if (
29 !/^(class|id|name|href|src|alt|align|valign|disabled|checked|start|type)$/i.test(name)
30 ) {
31 node.attributes.removeNamedItem(name);
32 } else if ((name === 'href' || name === 'src') && !_isHTTPLike(value)) {
33 try {
34 let url = new URL(value, window.location.origin);
35 if (_isHTTPLike(url.protocol)) {
36 node.attributes[i].value = url.href;
37 } else {
38 node.attributes.removeNamedItem(name);
39 }
40 } catch (e) {
41 node.attributes[i].removeNamedItem(name);
42 }
43 }
44 }
45 for (let i=node.childNodes.length; i--;) _sanitize(node.childNodes[i]);
46 };
47
48 const doc = new DOMParser().parseFromString(`<!DOCTYPE html><html><body>${input}`, 'text/html');
49 doc.normalize();
50
51 _sanitize(doc.body);
52
53 return doc.body.innerHTML;
54 },
55
56 parse: function(markdown) {
57 /*global marked*/
58 let unsafeHTML = marked(markdown);
59
60 return `<div class="pmx-md">${this.sanitizeHTML(unsafeHTML)}</div>`;
61 },
62
63 });