@webex/helper-html 3.0.0-beta.13 → 3.0.0-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browsers.js +2 -2
- package/dist/html.shim.js.map +1 -1
- package/package.json +4 -4
- package/src/html.shim.js +21 -27
- package/test/unit/spec/html.js +71 -57
package/browsers.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
-
module.exports = function(browsers) {
|
|
9
|
+
module.exports = function (browsers) {
|
|
10
10
|
// For reasons as-yet unexplained, the html filter test suite hangs when run
|
|
11
11
|
// on Safari/Sauce Labs
|
|
12
|
-
Object.keys(browsers).forEach(function(key) {
|
|
12
|
+
Object.keys(browsers).forEach(function (key) {
|
|
13
13
|
if (key.indexOf('safari') !== -1) {
|
|
14
14
|
delete browsers[key];
|
|
15
15
|
}
|
package/dist/html.shim.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["removeNode","node","remove","parentElement","removeChild","i","length","Error","_filter","args","resolve","_filterSync","filter","processCallback","allowedTags","allowedStyles","html","doc","DOMParser","parseFromString","depthFirstForEach","body","childNodes","filterNode","indexOf","innerHTML","isElement","nodeName","toLowerCase","allowedTagNames","allowedAttributes","listAttributeNames","attributes","attrName","removeAttribute","attrValue","getNamedItem","value","trim","reparent","styles","split","map","style","styleName","Boolean","join","setAttribute","_filterEscape","_filterEscapeSync","escapeNode","before","document","createTextNode","after","parentNode","insertBefore","trimPattern","str","replace","attrNames","attr","push","name","list","fn","o","ownerDocument","undefined","nodeType","filterSync","filterEscape","filterEscapeSync"],"sources":["html.shim.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\n/* eslint-env browser */\n\nimport {curry, forEach, includes, reduce} from 'lodash';\n\nexport {escape, escapeSync} from './html-base';\n\n/**\n * Some browsers don't implement {@link Element#remove()} or\n * {@link NodeList#remove()} or {@link HTMLCollection#remove()}. This wrapper\n * calls the appropriate `#remove()` method if available, or falls back to a\n * non-global-polluting polyfill.\n * @param {Element|NodeList|HTMLCollection} node\n * @returns {undefined}\n */\nfunction removeNode(node) {\n if (node.remove) {\n node.remove();\n\n return;\n }\n\n if (node.parentElement) {\n node.parentElement.removeChild(node);\n\n return;\n }\n\n if ('length' in node) {\n for (let i = node.length - 1; i >= 0; i -= 1) {\n removeNode(node[i]);\n }\n\n return;\n }\n\n throw new Error('Could not find a way to remove node');\n}\n\n/**\n * @param {Object} allowedTags\n * @param {Array<string>} allowedStyles\n * @param {string} html\n * @private\n * @returns {string}\n */\nfunction _filter(...args) {\n return new Promise((resolve) => {\n resolve(_filterSync(...args));\n });\n}\n\n/**\n * Curried async HTML filter.\n * @param {Object} allowedTags Map of tagName -> array of allowed attributes\n * @param {Array<string>} allowedStyles Array of allowed styles\n * @param {string} html html to filter\n * @returns {string}\n */\nexport const filter = curry(_filter, 4);\n\n/**\n * @param {function} processCallback callback function to do additional\n * processing on node. of the form process(node)\n * @param {Object} allowedTags\n * @param {Array<string>} allowedStyles\n * @param {string} html\n * @private\n * @returns {string}\n */\nfunction _filterSync(processCallback, allowedTags, allowedStyles, html) {\n if (!html || !allowedStyles || !allowedTags) {\n if (html.length === 0) {\n return html;\n }\n\n throw new Error('`allowedTags`, `allowedStyles`, and `html` must be provided');\n }\n\n const doc = (new DOMParser()).parseFromString(html, 'text/html');\n\n depthFirstForEach(doc.body.childNodes, filterNode);\n processCallback(doc.body);\n\n if (html.indexOf('body') === 1) {\n return `<body>${doc.body.innerHTML}</body>`;\n }\n\n return doc.body.innerHTML;\n\n /**\n * @param {Node} node\n * @private\n * @returns {undefined}\n */\n function filterNode(node) {\n if (!isElement(node)) {\n return;\n }\n\n const nodeName = node.nodeName.toLowerCase();\n const allowedTagNames = Object.keys(allowedTags);\n\n depthFirstForEach(node.childNodes, filterNode);\n\n if (includes(allowedTagNames, nodeName)) {\n const allowedAttributes = allowedTags[nodeName];\n\n forEach(listAttributeNames(node.attributes), (attrName) => {\n if (!includes(allowedAttributes, attrName)) {\n node.removeAttribute(attrName);\n }\n else if (attrName === 'href' || attrName === 'src') {\n const attrValue = node.attributes.getNamedItem(attrName).value.trim().toLowerCase();\n\n // We're doing at runtime what the no-script-url rule does at compile\n // time\n // eslint-disable-next-line no-script-url\n if (attrValue.indexOf('javascript:') === 0 || attrValue.indexOf('vbscript:') === 0) {\n reparent(node);\n }\n }\n else if (attrName === 'style') {\n const styles = node\n .attributes\n .getNamedItem('style')\n .value\n .split(';')\n .map((style) => {\n const styleName = trim(style.split(':')[0]);\n\n if (includes(allowedStyles, styleName)) {\n return style;\n }\n\n return null;\n })\n .filter((style) => Boolean(style))\n .join(';');\n\n node.setAttribute('style', styles);\n }\n });\n }\n else {\n reparent(node);\n }\n }\n}\n\n/**\n * Same as _filter, but escapes rather than removes disallowed values\n * @param {Function} processCallback\n * @param {Object} allowedTags\n * @param {Array<string>} allowedStyles\n * @param {string} html\n * @returns {Promise<string>}\n */\nfunction _filterEscape(...args) {\n return new Promise((resolve) => {\n resolve(_filterEscapeSync(...args));\n });\n}\n\n/**\n * Same as _filterSync, but escapes rather than removes disallowed values\n * @param {Function} processCallback\n * @param {Object} allowedTags\n * @param {Array<string>} allowedStyles\n * @param {string} html\n * @returns {string}\n */\nfunction _filterEscapeSync(processCallback, allowedTags, allowedStyles, html) {\n if (!html || !allowedStyles || !allowedTags) {\n if (html.length === 0) {\n return html;\n }\n\n throw new Error('`allowedTags`, `allowedStyles`, and `html` must be provided');\n }\n\n const doc = (new DOMParser()).parseFromString(html, 'text/html');\n\n depthFirstForEach(doc.body.childNodes, filterNode);\n processCallback(doc.body);\n\n if (html.indexOf('body') === 1) {\n return `<body>${doc.body.innerHTML}</body>`;\n }\n\n return doc.body.innerHTML;\n\n /**\n * @param {Node} node\n * @private\n * @returns {undefined}\n */\n function filterNode(node) {\n if (!isElement(node)) {\n return;\n }\n\n depthFirstForEach(node.childNodes, filterNode);\n\n const nodeName = node.nodeName.toLowerCase();\n const allowedTagNames = Object.keys(allowedTags);\n\n if (includes(allowedTagNames, nodeName)) {\n const allowedAttributes = allowedTags[nodeName];\n\n forEach(listAttributeNames(node.attributes), (attrName) => {\n if (!includes(allowedAttributes, attrName)) {\n node.removeAttribute(attrName);\n }\n else if (attrName === 'href' || attrName === 'src') {\n const attrValue = node.attributes.getNamedItem(attrName).value.toLowerCase();\n\n // We're doing at runtime what the no-script-url rule does at compile\n // time\n // eslint-disable-next-line no-script-url\n if (attrValue.indexOf('javascript:') === 0 || attrValue.indexOf('vbscript:') === 0) {\n reparent(node);\n }\n }\n else if (attrName === 'style') {\n const styles = node\n .attributes\n .getNamedItem('style')\n .value\n .split(';')\n .map((style) => {\n const styleName = trim(style.split(':')[0]);\n\n if (includes(allowedStyles, styleName)) {\n return style;\n }\n\n return null;\n })\n .filter((style) => Boolean(style))\n .join(';');\n\n node.setAttribute('style', styles);\n }\n });\n }\n else {\n escapeNode(node);\n }\n }\n}\n\n/**\n * Escapes a given html node\n * @param {Node} node\n * @returns {undefined}\n */\nfunction escapeNode(node) {\n const before = document.createTextNode(`<${node.nodeName.toLowerCase()}>`);\n const after = document.createTextNode(`</${node.nodeName.toLowerCase()}>`);\n\n node.parentNode.insertBefore(before, node);\n while (node.childNodes.length > 0) {\n node.parentNode.insertBefore(node.childNodes[0], node);\n }\n node.parentNode.insertBefore(after, node);\n\n removeNode(node);\n}\n\nconst trimPattern = /^\\s|\\s$/g;\n\n/**\n * @param {string} str\n * @returns {string}\n */\nfunction trim(str) {\n return str.replace(trimPattern, '');\n}\n\n/**\n * @param {Node} node\n * @private\n * @returns {undefined}\n */\nfunction reparent(node) {\n while (node.childNodes.length > 0) {\n node.parentNode.insertBefore(node.childNodes[0], node);\n }\n removeNode(node);\n}\n\n/**\n * @param {NamedNodeMap} attributes\n * @private\n * @returns {Array<string>}\n */\nfunction listAttributeNames(attributes) {\n return reduce(attributes, (attrNames, attr) => {\n attrNames.push(attr.name);\n\n return attrNames;\n }, []);\n}\n\n/**\n * @param {Array} list\n * @param {Function} fn\n * @private\n * @returns {undefined}\n */\nfunction depthFirstForEach(list, fn) {\n for (let i = list.length; i >= 0; i -= 1) {\n fn(list[i]);\n }\n}\n\n/**\n * @param {Node} o\n * @private\n * @returns {Boolean}\n */\nfunction isElement(o) {\n if (!o) {\n return false;\n }\n\n if (o.ownerDocument === undefined) {\n return false;\n }\n\n if (o.nodeType !== 1) {\n return false;\n }\n\n if (typeof o.nodeName !== 'string') {\n return false;\n }\n\n return true;\n}\n\n/**\n * Curried HTML filter.\n * @param {Object} allowedTags Map of tagName -> array of allowed attributes\n * @param {Array<string>} allowedStyles Array of allowed styles\n * @param {string} html html to filter\n * @returns {string}\n */\nexport const filterSync = curry(_filterSync, 4);\n\n/**\n * Curried HTML filter that escapes rather than removes disallowed tags\n * @param {Object} allowedTags Map of tagName -> array of allowed attributes\n * @param {Array<string>} allowedStyles Array of allowed styles\n * @param {string} html html to filter\n * @returns {Promise<string>}\n */\nexport const filterEscape = curry(_filterEscape, 4);\n\n/**\n * Curried HTML filter that escapes rather than removes disallowed tags\n * @param {Object} allowedTags Map of tagName -> array of allowed attributes\n * @param {Array<string>} allowedStyles Array of allowed styles\n * @param {string} html html to filter\n * @returns {string}\n */\nexport const filterEscapeSync = curry(_filterEscapeSync, 4);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"names":["removeNode","node","remove","parentElement","removeChild","i","length","Error","_filter","args","resolve","_filterSync","filter","processCallback","allowedTags","allowedStyles","html","doc","DOMParser","parseFromString","depthFirstForEach","body","childNodes","filterNode","indexOf","innerHTML","isElement","nodeName","toLowerCase","allowedTagNames","allowedAttributes","listAttributeNames","attributes","attrName","removeAttribute","attrValue","getNamedItem","value","trim","reparent","styles","split","map","style","styleName","Boolean","join","setAttribute","_filterEscape","_filterEscapeSync","escapeNode","before","document","createTextNode","after","parentNode","insertBefore","trimPattern","str","replace","attrNames","attr","push","name","list","fn","o","ownerDocument","undefined","nodeType","filterSync","filterEscape","filterEscapeSync"],"sources":["html.shim.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\n/* eslint-env browser */\n\nimport {curry, forEach, includes, reduce} from 'lodash';\n\nexport {escape, escapeSync} from './html-base';\n\n/**\n * Some browsers don't implement {@link Element#remove()} or\n * {@link NodeList#remove()} or {@link HTMLCollection#remove()}. This wrapper\n * calls the appropriate `#remove()` method if available, or falls back to a\n * non-global-polluting polyfill.\n * @param {Element|NodeList|HTMLCollection} node\n * @returns {undefined}\n */\nfunction removeNode(node) {\n if (node.remove) {\n node.remove();\n\n return;\n }\n\n if (node.parentElement) {\n node.parentElement.removeChild(node);\n\n return;\n }\n\n if ('length' in node) {\n for (let i = node.length - 1; i >= 0; i -= 1) {\n removeNode(node[i]);\n }\n\n return;\n }\n\n throw new Error('Could not find a way to remove node');\n}\n\n/**\n * @param {Object} allowedTags\n * @param {Array<string>} allowedStyles\n * @param {string} html\n * @private\n * @returns {string}\n */\nfunction _filter(...args) {\n return new Promise((resolve) => {\n resolve(_filterSync(...args));\n });\n}\n\n/**\n * Curried async HTML filter.\n * @param {Object} allowedTags Map of tagName -> array of allowed attributes\n * @param {Array<string>} allowedStyles Array of allowed styles\n * @param {string} html html to filter\n * @returns {string}\n */\nexport const filter = curry(_filter, 4);\n\n/**\n * @param {function} processCallback callback function to do additional\n * processing on node. of the form process(node)\n * @param {Object} allowedTags\n * @param {Array<string>} allowedStyles\n * @param {string} html\n * @private\n * @returns {string}\n */\nfunction _filterSync(processCallback, allowedTags, allowedStyles, html) {\n if (!html || !allowedStyles || !allowedTags) {\n if (html.length === 0) {\n return html;\n }\n\n throw new Error('`allowedTags`, `allowedStyles`, and `html` must be provided');\n }\n\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n depthFirstForEach(doc.body.childNodes, filterNode);\n processCallback(doc.body);\n\n if (html.indexOf('body') === 1) {\n return `<body>${doc.body.innerHTML}</body>`;\n }\n\n return doc.body.innerHTML;\n\n /**\n * @param {Node} node\n * @private\n * @returns {undefined}\n */\n function filterNode(node) {\n if (!isElement(node)) {\n return;\n }\n\n const nodeName = node.nodeName.toLowerCase();\n const allowedTagNames = Object.keys(allowedTags);\n\n depthFirstForEach(node.childNodes, filterNode);\n\n if (includes(allowedTagNames, nodeName)) {\n const allowedAttributes = allowedTags[nodeName];\n\n forEach(listAttributeNames(node.attributes), (attrName) => {\n if (!includes(allowedAttributes, attrName)) {\n node.removeAttribute(attrName);\n } else if (attrName === 'href' || attrName === 'src') {\n const attrValue = node.attributes.getNamedItem(attrName).value.trim().toLowerCase();\n\n // We're doing at runtime what the no-script-url rule does at compile\n // time\n // eslint-disable-next-line no-script-url\n if (attrValue.indexOf('javascript:') === 0 || attrValue.indexOf('vbscript:') === 0) {\n reparent(node);\n }\n } else if (attrName === 'style') {\n const styles = node.attributes\n .getNamedItem('style')\n .value.split(';')\n .map((style) => {\n const styleName = trim(style.split(':')[0]);\n\n if (includes(allowedStyles, styleName)) {\n return style;\n }\n\n return null;\n })\n .filter((style) => Boolean(style))\n .join(';');\n\n node.setAttribute('style', styles);\n }\n });\n } else {\n reparent(node);\n }\n }\n}\n\n/**\n * Same as _filter, but escapes rather than removes disallowed values\n * @param {Function} processCallback\n * @param {Object} allowedTags\n * @param {Array<string>} allowedStyles\n * @param {string} html\n * @returns {Promise<string>}\n */\nfunction _filterEscape(...args) {\n return new Promise((resolve) => {\n resolve(_filterEscapeSync(...args));\n });\n}\n\n/**\n * Same as _filterSync, but escapes rather than removes disallowed values\n * @param {Function} processCallback\n * @param {Object} allowedTags\n * @param {Array<string>} allowedStyles\n * @param {string} html\n * @returns {string}\n */\nfunction _filterEscapeSync(processCallback, allowedTags, allowedStyles, html) {\n if (!html || !allowedStyles || !allowedTags) {\n if (html.length === 0) {\n return html;\n }\n\n throw new Error('`allowedTags`, `allowedStyles`, and `html` must be provided');\n }\n\n const doc = new DOMParser().parseFromString(html, 'text/html');\n\n depthFirstForEach(doc.body.childNodes, filterNode);\n processCallback(doc.body);\n\n if (html.indexOf('body') === 1) {\n return `<body>${doc.body.innerHTML}</body>`;\n }\n\n return doc.body.innerHTML;\n\n /**\n * @param {Node} node\n * @private\n * @returns {undefined}\n */\n function filterNode(node) {\n if (!isElement(node)) {\n return;\n }\n\n depthFirstForEach(node.childNodes, filterNode);\n\n const nodeName = node.nodeName.toLowerCase();\n const allowedTagNames = Object.keys(allowedTags);\n\n if (includes(allowedTagNames, nodeName)) {\n const allowedAttributes = allowedTags[nodeName];\n\n forEach(listAttributeNames(node.attributes), (attrName) => {\n if (!includes(allowedAttributes, attrName)) {\n node.removeAttribute(attrName);\n } else if (attrName === 'href' || attrName === 'src') {\n const attrValue = node.attributes.getNamedItem(attrName).value.toLowerCase();\n\n // We're doing at runtime what the no-script-url rule does at compile\n // time\n // eslint-disable-next-line no-script-url\n if (attrValue.indexOf('javascript:') === 0 || attrValue.indexOf('vbscript:') === 0) {\n reparent(node);\n }\n } else if (attrName === 'style') {\n const styles = node.attributes\n .getNamedItem('style')\n .value.split(';')\n .map((style) => {\n const styleName = trim(style.split(':')[0]);\n\n if (includes(allowedStyles, styleName)) {\n return style;\n }\n\n return null;\n })\n .filter((style) => Boolean(style))\n .join(';');\n\n node.setAttribute('style', styles);\n }\n });\n } else {\n escapeNode(node);\n }\n }\n}\n\n/**\n * Escapes a given html node\n * @param {Node} node\n * @returns {undefined}\n */\nfunction escapeNode(node) {\n const before = document.createTextNode(`<${node.nodeName.toLowerCase()}>`);\n const after = document.createTextNode(`</${node.nodeName.toLowerCase()}>`);\n\n node.parentNode.insertBefore(before, node);\n while (node.childNodes.length > 0) {\n node.parentNode.insertBefore(node.childNodes[0], node);\n }\n node.parentNode.insertBefore(after, node);\n\n removeNode(node);\n}\n\nconst trimPattern = /^\\s|\\s$/g;\n\n/**\n * @param {string} str\n * @returns {string}\n */\nfunction trim(str) {\n return str.replace(trimPattern, '');\n}\n\n/**\n * @param {Node} node\n * @private\n * @returns {undefined}\n */\nfunction reparent(node) {\n while (node.childNodes.length > 0) {\n node.parentNode.insertBefore(node.childNodes[0], node);\n }\n removeNode(node);\n}\n\n/**\n * @param {NamedNodeMap} attributes\n * @private\n * @returns {Array<string>}\n */\nfunction listAttributeNames(attributes) {\n return reduce(\n attributes,\n (attrNames, attr) => {\n attrNames.push(attr.name);\n\n return attrNames;\n },\n []\n );\n}\n\n/**\n * @param {Array} list\n * @param {Function} fn\n * @private\n * @returns {undefined}\n */\nfunction depthFirstForEach(list, fn) {\n for (let i = list.length; i >= 0; i -= 1) {\n fn(list[i]);\n }\n}\n\n/**\n * @param {Node} o\n * @private\n * @returns {Boolean}\n */\nfunction isElement(o) {\n if (!o) {\n return false;\n }\n\n if (o.ownerDocument === undefined) {\n return false;\n }\n\n if (o.nodeType !== 1) {\n return false;\n }\n\n if (typeof o.nodeName !== 'string') {\n return false;\n }\n\n return true;\n}\n\n/**\n * Curried HTML filter.\n * @param {Object} allowedTags Map of tagName -> array of allowed attributes\n * @param {Array<string>} allowedStyles Array of allowed styles\n * @param {string} html html to filter\n * @returns {string}\n */\nexport const filterSync = curry(_filterSync, 4);\n\n/**\n * Curried HTML filter that escapes rather than removes disallowed tags\n * @param {Object} allowedTags Map of tagName -> array of allowed attributes\n * @param {Array<string>} allowedStyles Array of allowed styles\n * @param {string} html html to filter\n * @returns {Promise<string>}\n */\nexport const filterEscape = curry(_filterEscape, 4);\n\n/**\n * Curried HTML filter that escapes rather than removes disallowed tags\n * @param {Object} allowedTags Map of tagName -> array of allowed attributes\n * @param {Array<string>} allowedStyles Array of allowed styles\n * @param {string} html html to filter\n * @returns {string}\n */\nexport const filterEscapeSync = curry(_filterEscapeSync, 4);\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/helper-html",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.15",
|
|
4
4
|
"description": "HTML Utiltities",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@webex/test-helper-chai": "3.0.0-beta.
|
|
28
|
-
"@webex/test-helper-mocha": "3.0.0-beta.
|
|
27
|
+
"@webex/test-helper-chai": "3.0.0-beta.15",
|
|
28
|
+
"@webex/test-helper-mocha": "3.0.0-beta.15"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@webex/helper-html": "3.0.0-beta.
|
|
31
|
+
"@webex/helper-html": "3.0.0-beta.15",
|
|
32
32
|
"lodash": "^4.17.21"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/src/html.shim.js
CHANGED
|
@@ -80,7 +80,7 @@ function _filterSync(processCallback, allowedTags, allowedStyles, html) {
|
|
|
80
80
|
throw new Error('`allowedTags`, `allowedStyles`, and `html` must be provided');
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const doc =
|
|
83
|
+
const doc = new DOMParser().parseFromString(html, 'text/html');
|
|
84
84
|
|
|
85
85
|
depthFirstForEach(doc.body.childNodes, filterNode);
|
|
86
86
|
processCallback(doc.body);
|
|
@@ -112,8 +112,7 @@ function _filterSync(processCallback, allowedTags, allowedStyles, html) {
|
|
|
112
112
|
forEach(listAttributeNames(node.attributes), (attrName) => {
|
|
113
113
|
if (!includes(allowedAttributes, attrName)) {
|
|
114
114
|
node.removeAttribute(attrName);
|
|
115
|
-
}
|
|
116
|
-
else if (attrName === 'href' || attrName === 'src') {
|
|
115
|
+
} else if (attrName === 'href' || attrName === 'src') {
|
|
117
116
|
const attrValue = node.attributes.getNamedItem(attrName).value.trim().toLowerCase();
|
|
118
117
|
|
|
119
118
|
// We're doing at runtime what the no-script-url rule does at compile
|
|
@@ -122,13 +121,10 @@ function _filterSync(processCallback, allowedTags, allowedStyles, html) {
|
|
|
122
121
|
if (attrValue.indexOf('javascript:') === 0 || attrValue.indexOf('vbscript:') === 0) {
|
|
123
122
|
reparent(node);
|
|
124
123
|
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const styles = node
|
|
128
|
-
.attributes
|
|
124
|
+
} else if (attrName === 'style') {
|
|
125
|
+
const styles = node.attributes
|
|
129
126
|
.getNamedItem('style')
|
|
130
|
-
.value
|
|
131
|
-
.split(';')
|
|
127
|
+
.value.split(';')
|
|
132
128
|
.map((style) => {
|
|
133
129
|
const styleName = trim(style.split(':')[0]);
|
|
134
130
|
|
|
@@ -144,8 +140,7 @@ function _filterSync(processCallback, allowedTags, allowedStyles, html) {
|
|
|
144
140
|
node.setAttribute('style', styles);
|
|
145
141
|
}
|
|
146
142
|
});
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
143
|
+
} else {
|
|
149
144
|
reparent(node);
|
|
150
145
|
}
|
|
151
146
|
}
|
|
@@ -182,7 +177,7 @@ function _filterEscapeSync(processCallback, allowedTags, allowedStyles, html) {
|
|
|
182
177
|
throw new Error('`allowedTags`, `allowedStyles`, and `html` must be provided');
|
|
183
178
|
}
|
|
184
179
|
|
|
185
|
-
const doc =
|
|
180
|
+
const doc = new DOMParser().parseFromString(html, 'text/html');
|
|
186
181
|
|
|
187
182
|
depthFirstForEach(doc.body.childNodes, filterNode);
|
|
188
183
|
processCallback(doc.body);
|
|
@@ -214,8 +209,7 @@ function _filterEscapeSync(processCallback, allowedTags, allowedStyles, html) {
|
|
|
214
209
|
forEach(listAttributeNames(node.attributes), (attrName) => {
|
|
215
210
|
if (!includes(allowedAttributes, attrName)) {
|
|
216
211
|
node.removeAttribute(attrName);
|
|
217
|
-
}
|
|
218
|
-
else if (attrName === 'href' || attrName === 'src') {
|
|
212
|
+
} else if (attrName === 'href' || attrName === 'src') {
|
|
219
213
|
const attrValue = node.attributes.getNamedItem(attrName).value.toLowerCase();
|
|
220
214
|
|
|
221
215
|
// We're doing at runtime what the no-script-url rule does at compile
|
|
@@ -224,13 +218,10 @@ function _filterEscapeSync(processCallback, allowedTags, allowedStyles, html) {
|
|
|
224
218
|
if (attrValue.indexOf('javascript:') === 0 || attrValue.indexOf('vbscript:') === 0) {
|
|
225
219
|
reparent(node);
|
|
226
220
|
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const styles = node
|
|
230
|
-
.attributes
|
|
221
|
+
} else if (attrName === 'style') {
|
|
222
|
+
const styles = node.attributes
|
|
231
223
|
.getNamedItem('style')
|
|
232
|
-
.value
|
|
233
|
-
.split(';')
|
|
224
|
+
.value.split(';')
|
|
234
225
|
.map((style) => {
|
|
235
226
|
const styleName = trim(style.split(':')[0]);
|
|
236
227
|
|
|
@@ -246,8 +237,7 @@ function _filterEscapeSync(processCallback, allowedTags, allowedStyles, html) {
|
|
|
246
237
|
node.setAttribute('style', styles);
|
|
247
238
|
}
|
|
248
239
|
});
|
|
249
|
-
}
|
|
250
|
-
else {
|
|
240
|
+
} else {
|
|
251
241
|
escapeNode(node);
|
|
252
242
|
}
|
|
253
243
|
}
|
|
@@ -299,11 +289,15 @@ function reparent(node) {
|
|
|
299
289
|
* @returns {Array<string>}
|
|
300
290
|
*/
|
|
301
291
|
function listAttributeNames(attributes) {
|
|
302
|
-
return reduce(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
292
|
+
return reduce(
|
|
293
|
+
attributes,
|
|
294
|
+
(attrNames, attr) => {
|
|
295
|
+
attrNames.push(attr.name);
|
|
296
|
+
|
|
297
|
+
return attrNames;
|
|
298
|
+
},
|
|
299
|
+
[]
|
|
300
|
+
);
|
|
307
301
|
}
|
|
308
302
|
|
|
309
303
|
/**
|
package/test/unit/spec/html.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
filter,
|
|
10
10
|
filterSync,
|
|
11
11
|
filterEscape,
|
|
12
|
-
filterEscapeSync
|
|
12
|
+
filterEscapeSync,
|
|
13
13
|
} from '@webex/helper-html';
|
|
14
14
|
import {skipInNode} from '@webex/test-helper-mocha';
|
|
15
15
|
|
|
@@ -29,7 +29,7 @@ skipInNode(describe)('html', () => {
|
|
|
29
29
|
span: ['style'],
|
|
30
30
|
ul: ['style'],
|
|
31
31
|
body: ['style', 'xmlns', 'xml:lang'],
|
|
32
|
-
'webex-mention': ['data-object-id', 'data-object-type']
|
|
32
|
+
'webex-mention': ['data-object-id', 'data-object-type'],
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
const allowedStyles = [
|
|
@@ -42,7 +42,7 @@ skipInNode(describe)('html', () => {
|
|
|
42
42
|
'margin-left',
|
|
43
43
|
'margin-right',
|
|
44
44
|
'text-align',
|
|
45
|
-
'text-decoration'
|
|
45
|
+
'text-decoration',
|
|
46
46
|
];
|
|
47
47
|
|
|
48
48
|
/**
|
|
@@ -58,12 +58,15 @@ skipInNode(describe)('html', () => {
|
|
|
58
58
|
const cfilterEscapeSync = filterEscapeSync(noop, allowedTags, allowedStyles);
|
|
59
59
|
|
|
60
60
|
describe('#filter()', () => {
|
|
61
|
-
it('sanitizes trivial html', () =>
|
|
62
|
-
|
|
61
|
+
it('sanitizes trivial html', () =>
|
|
62
|
+
cfilter('<p data-test="5"><em>foo</em></p>').then((result) =>
|
|
63
|
+
assert.deepEqual(result, '<p><em>foo</em></p>')
|
|
64
|
+
));
|
|
63
65
|
});
|
|
64
66
|
|
|
65
67
|
describe('#filterSync()', () => {
|
|
66
|
-
it('sanitizes trivial html', () =>
|
|
68
|
+
it('sanitizes trivial html', () =>
|
|
69
|
+
assert.deepEqual(cfilterSync('<p data-test="5"><em>foo</em></p>'), '<p><em>foo</em></p>'));
|
|
67
70
|
});
|
|
68
71
|
|
|
69
72
|
[
|
|
@@ -72,147 +75,154 @@ skipInNode(describe)('html', () => {
|
|
|
72
75
|
// emptry string
|
|
73
76
|
it: 'accepts blank strings',
|
|
74
77
|
input: '',
|
|
75
|
-
output: ''
|
|
78
|
+
output: '',
|
|
76
79
|
},
|
|
77
80
|
{
|
|
78
81
|
it: 'allows custom tags',
|
|
79
|
-
input:
|
|
80
|
-
|
|
82
|
+
input:
|
|
83
|
+
'<webex-mention data-object-id="88888888-4444-4444-4444-AAAAAAAAAAAA">John Doe</webex-mention>',
|
|
84
|
+
output:
|
|
85
|
+
'<webex-mention data-object-id="88888888-4444-4444-4444-AAAAAAAAAAAA">John Doe</webex-mention>',
|
|
81
86
|
},
|
|
82
87
|
{
|
|
83
88
|
it: 'filters tags',
|
|
84
|
-
input:
|
|
85
|
-
|
|
89
|
+
input:
|
|
90
|
+
'<p><remove-me><bar>text1<em>text2</em>text3</bar>text4</remove-me><strong>text5</strong>text6</p>',
|
|
91
|
+
output: '<p>text1<em>text2</em>text3text4<strong>text5</strong>text6</p>',
|
|
86
92
|
},
|
|
87
93
|
{
|
|
88
94
|
it: 'filters attributes',
|
|
89
95
|
input: '<p remove="me" style="font-size:large"><em>foo</em></p>',
|
|
90
|
-
output: /<p style="font-size:\s?large;?"><em>foo<\/em><\/p
|
|
96
|
+
output: /<p style="font-size:\s?large;?"><em>foo<\/em><\/p>/,
|
|
91
97
|
},
|
|
92
98
|
{
|
|
93
99
|
it: 'filters styles',
|
|
94
100
|
input: '<p style="color:red;remove:me;font-size:large"><em>foo</em></p>',
|
|
95
|
-
output: /<p style="color:\s?red;\s?font-size:\s?large;?"><em>foo<\/em><\/p
|
|
101
|
+
output: /<p style="color:\s?red;\s?font-size:\s?large;?"><em>foo<\/em><\/p>/,
|
|
96
102
|
},
|
|
97
103
|
{
|
|
98
104
|
it: 'filters child attributes',
|
|
99
105
|
input: '<body><span bcd="abc" style="font-size:large"><p><em>foo</em></p></span></body>',
|
|
100
|
-
output: /<body><span style="font-size:\s?large;?"><p><em>foo<\/em><\/p><\/span><\/body
|
|
106
|
+
output: /<body><span style="font-size:\s?large;?"><p><em>foo<\/em><\/p><\/span><\/body>/,
|
|
101
107
|
},
|
|
102
108
|
{
|
|
103
109
|
it: 'filters disallowed attributes from allowed tags',
|
|
104
110
|
input: '<strong style="font-size:large"><span>text</span></strong>',
|
|
105
|
-
output: '<strong><span>text</span></strong>'
|
|
111
|
+
output: '<strong><span>text</span></strong>',
|
|
106
112
|
},
|
|
107
113
|
{
|
|
108
114
|
it: 'filters javascript: from a href',
|
|
109
115
|
input: '<p><a href="javascript:window.close(); return false">click here</a></p>',
|
|
110
|
-
output: '<p>click here</p>'
|
|
116
|
+
output: '<p>click here</p>',
|
|
111
117
|
},
|
|
112
118
|
{
|
|
113
119
|
it: 'filters javascript: from a href (mixed case)',
|
|
114
120
|
input: '<p><a href="JavaScript:window.close(); return false">click here</a></p>',
|
|
115
|
-
output: '<p>click here</p>'
|
|
121
|
+
output: '<p>click here</p>',
|
|
116
122
|
},
|
|
117
123
|
{
|
|
118
124
|
it: 'filters vbscript: from a href',
|
|
119
125
|
input: '<p><a href="vbscript:window.close(); return false">click here</a></p>',
|
|
120
|
-
output: '<p>click here</p>'
|
|
126
|
+
output: '<p>click here</p>',
|
|
121
127
|
},
|
|
122
128
|
{
|
|
123
129
|
it: 'filters vbscript: from a href (mixed case)',
|
|
124
130
|
input: '<p><a href="VBScript:window.close(); return false">click here</a></p>',
|
|
125
|
-
output: '<p>click here</p>'
|
|
131
|
+
output: '<p>click here</p>',
|
|
126
132
|
},
|
|
127
133
|
{
|
|
128
134
|
it: 'does not filter arbitrary strings from a href',
|
|
129
135
|
input: '<p><a href="window.close(); return false">click here</a></p>',
|
|
130
|
-
output: '<p><a href="window.close(); return false">click here</a></p>'
|
|
136
|
+
output: '<p><a href="window.close(); return false">click here</a></p>',
|
|
131
137
|
},
|
|
132
138
|
{
|
|
133
139
|
it: 'filters javascript: from img src',
|
|
134
140
|
input: '<p><img src="javascript:window.close(); return false">foo</img>bar</p>',
|
|
135
|
-
output: '<p>foobar</p>'
|
|
141
|
+
output: '<p>foobar</p>',
|
|
136
142
|
},
|
|
137
143
|
{
|
|
138
144
|
it: 'filters javascript: from img src (mixed case)',
|
|
139
145
|
input: '<p><img src="javaScript:window.close(); return false">foo</img>bar</p>',
|
|
140
|
-
output: '<p>foobar</p>'
|
|
146
|
+
output: '<p>foobar</p>',
|
|
141
147
|
},
|
|
142
148
|
{
|
|
143
149
|
it: 'filters vbscript: from img src',
|
|
144
150
|
input: '<p><img src="vbscript:window.close(); return false">foo</img>bar</p>',
|
|
145
|
-
output: '<p>foobar</p>'
|
|
151
|
+
output: '<p>foobar</p>',
|
|
146
152
|
},
|
|
147
153
|
{
|
|
148
154
|
it: 'filters vbscript: from img src (mixed case)',
|
|
149
155
|
input: '<p><img src="VBScript:window.close(); return false">foo</img>bar</p>',
|
|
150
|
-
output: '<p>foobar</p>'
|
|
156
|
+
output: '<p>foobar</p>',
|
|
151
157
|
},
|
|
152
158
|
{
|
|
153
159
|
it: 'does not filter arbitrary strings from img src',
|
|
154
160
|
input: '<p><img src="window.close(); return false">foo</img></p>',
|
|
155
|
-
output: '<p><img src="window.close(); return false">foo</p>'
|
|
161
|
+
output: '<p><img src="window.close(); return false">foo</p>',
|
|
156
162
|
},
|
|
157
163
|
{
|
|
158
164
|
it: 'correctly cleans nested a/img tags with javscript: href/src',
|
|
159
|
-
input:
|
|
160
|
-
|
|
165
|
+
input:
|
|
166
|
+
'<p><a href="javascript:window.close()">Click here<img src="http://example.com/img">bar</img></a> for something with <a href="http://www.cisco.com/">MORE<img src="javascript:window.location=`http://www.cisco.com`">of my</img>MOJO</a></p>',
|
|
167
|
+
output:
|
|
168
|
+
'<p>Click here<img src="http://example.com/img">bar for something with <a href="http://www.cisco.com/">MOREof myMOJO</a></p>',
|
|
161
169
|
},
|
|
162
170
|
{
|
|
163
171
|
it: 'correctly cleans nested a/img tags with javscript: href/src, even cleverly obfuscated with whitespace',
|
|
164
|
-
input:
|
|
165
|
-
|
|
172
|
+
input:
|
|
173
|
+
'<p><a href=" javascript:window.close()">Click here<img src="http://example.com/img">bar</img></a> for something with <a href="http://www.cisco.com/">MORE<img src=" javascript:window.location=`http://www.cisco.com`">of my</img>MOJO</a></p>',
|
|
174
|
+
output:
|
|
175
|
+
'<p>Click here<img src="http://example.com/img">bar for something with <a href="http://www.cisco.com/">MOREof myMOJO</a></p>',
|
|
166
176
|
},
|
|
167
177
|
{
|
|
168
178
|
it: 'handles weirder nesting',
|
|
169
|
-
input:
|
|
170
|
-
|
|
179
|
+
input:
|
|
180
|
+
'<p>text</p><div><p>text0</p><div style="font-size: large;"><span>text1</span><span>text2</span><script></script></div></div>',
|
|
181
|
+
output: '<p>text</p><p>text0</p><span>text1</span><span>text2</span>',
|
|
171
182
|
},
|
|
172
183
|
{
|
|
173
184
|
it: 'filters bad html from unwrapped strings',
|
|
174
185
|
input: 'Hi <script></script><em style="font-size:large;">Steve</em>',
|
|
175
|
-
output: 'Hi <em>Steve</em>'
|
|
186
|
+
output: 'Hi <em>Steve</em>',
|
|
176
187
|
},
|
|
177
188
|
{
|
|
178
189
|
it: 'filters disallowed attributes from a href',
|
|
179
190
|
input: '<a remove="me" href="http://www.jabber.org/"><p><em>foo</em></p></a>',
|
|
180
|
-
output: '<a href="http://www.jabber.org/"><p><em>foo</em></p></a>'
|
|
191
|
+
output: '<a href="http://www.jabber.org/"><p><em>foo</em></p></a>',
|
|
181
192
|
},
|
|
182
193
|
{
|
|
183
194
|
it: 'filters disallowed attributes from a style',
|
|
184
195
|
input: '<a remove="me" style="font-size:large"><p><em>foo</em></p></a>',
|
|
185
|
-
output: /<a style="font-size:\s?large;?"><p><em>foo<\/em><\/p><\/a
|
|
196
|
+
output: /<a style="font-size:\s?large;?"><p><em>foo<\/em><\/p><\/a>/,
|
|
186
197
|
},
|
|
187
198
|
{
|
|
188
199
|
it: 'filters disallowed attributes from a type',
|
|
189
200
|
input: '<a remove="me" type="stuff"><p><em>foo</em></p></a>',
|
|
190
|
-
output: '<a type="stuff"><p><em>foo</em></p></a>'
|
|
201
|
+
output: '<a type="stuff"><p><em>foo</em></p></a>',
|
|
191
202
|
},
|
|
192
203
|
{
|
|
193
204
|
it: 'filters disallowed attributes from img src',
|
|
194
205
|
input: '<img remove="me" src="http://www.xmpp.org/images/psa-license.jpg">bar</img>',
|
|
195
|
-
output: '<img src="http://www.xmpp.org/images/psa-license.jpg">bar'
|
|
206
|
+
output: '<img src="http://www.xmpp.org/images/psa-license.jpg">bar',
|
|
196
207
|
},
|
|
197
208
|
{
|
|
198
209
|
it: 'filters disallowed attributes from img alt',
|
|
199
210
|
input: '<img remove="me" alt="A License to Jabber">bar</img>',
|
|
200
|
-
output: '<img alt="A License to Jabber">bar'
|
|
211
|
+
output: '<img alt="A License to Jabber">bar',
|
|
201
212
|
},
|
|
202
213
|
{
|
|
203
214
|
it: 'filters disallowed attributes from img height',
|
|
204
215
|
input: '<img remove="me" height="261">bar</img>',
|
|
205
|
-
output: '<img height="261">bar'
|
|
216
|
+
output: '<img height="261">bar',
|
|
206
217
|
},
|
|
207
218
|
{
|
|
208
219
|
it: 'filters disallowed attributes from img width',
|
|
209
220
|
input: '<img remove="me" width="537">bar</img>',
|
|
210
|
-
output: '<img width="537">bar'
|
|
211
|
-
}
|
|
221
|
+
output: '<img width="537">bar',
|
|
222
|
+
},
|
|
212
223
|
].forEach((def) => {
|
|
213
224
|
describe('#filter()', () => {
|
|
214
|
-
it(def.it, () => cfilter(def.input)
|
|
215
|
-
.then((out) => assert.match(out, def.output)));
|
|
225
|
+
it(def.it, () => cfilter(def.input).then((out) => assert.match(out, def.output)));
|
|
216
226
|
});
|
|
217
227
|
|
|
218
228
|
describe('#filterSync()', () => {
|
|
@@ -222,14 +232,15 @@ skipInNode(describe)('html', () => {
|
|
|
222
232
|
});
|
|
223
233
|
});
|
|
224
234
|
|
|
225
|
-
[
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
235
|
+
[
|
|
236
|
+
{
|
|
237
|
+
it: 'escapes html',
|
|
238
|
+
input: 'This is an <b>invalid</b> tag',
|
|
239
|
+
output: 'This is an <b>invalid</b> tag',
|
|
240
|
+
},
|
|
241
|
+
].forEach((def) => {
|
|
230
242
|
describe('#escape()', () => {
|
|
231
|
-
it(def.it, () => escape(def.input)
|
|
232
|
-
.then((result) => assert.deepEqual(result, def.output)));
|
|
243
|
+
it(def.it, () => escape(def.input).then((result) => assert.deepEqual(result, def.output)));
|
|
233
244
|
});
|
|
234
245
|
|
|
235
246
|
describe('#escapeSync()', () => {
|
|
@@ -243,24 +254,27 @@ skipInNode(describe)('html', () => {
|
|
|
243
254
|
{
|
|
244
255
|
it: 'escapes invalid tags',
|
|
245
256
|
input: '<bar>text1<em>text2</em>text3</bar>',
|
|
246
|
-
output: '<bar>text1<em>text2</em>text3</bar>'
|
|
257
|
+
output: '<bar>text1<em>text2</em>text3</bar>',
|
|
247
258
|
},
|
|
248
259
|
{
|
|
249
260
|
it: 'escapes deeply nested invalid tags',
|
|
250
|
-
input:
|
|
251
|
-
|
|
261
|
+
input:
|
|
262
|
+
'<p><remove-me><bar>text1<em>text2</em>text3</bar>text4</remove-me><strong>text5</strong>text6</p>',
|
|
263
|
+
output:
|
|
264
|
+
'<p><remove-me><bar>text1<em>text2</em>text3</bar>text4</remove-me><strong>text5</strong>text6</p>',
|
|
252
265
|
},
|
|
253
266
|
{
|
|
254
267
|
it: 'esapes special characters',
|
|
255
268
|
input: '<b>&<</b>',
|
|
256
|
-
output: '<b>&<</b>'
|
|
257
|
-
}
|
|
269
|
+
output: '<b>&<</b>',
|
|
270
|
+
},
|
|
258
271
|
].forEach((def) => {
|
|
259
272
|
describe('#filterEscape()', () => {
|
|
260
|
-
it(def.it, () =>
|
|
261
|
-
.then((out) => {
|
|
273
|
+
it(def.it, () =>
|
|
274
|
+
cfilterEscape(def.input).then((out) => {
|
|
262
275
|
assert.match(out, def.output);
|
|
263
|
-
})
|
|
276
|
+
})
|
|
277
|
+
);
|
|
264
278
|
});
|
|
265
279
|
|
|
266
280
|
describe('#filterEscapeSync()', () => {
|