@webex/helper-html 2.59.2 → 2.59.3-next.1

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/src/html.shim.js CHANGED
@@ -1,365 +1,365 @@
1
- /*!
2
- * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
- */
4
-
5
- /* eslint-env browser */
6
-
7
- import {curry, forEach, includes, reduce} from 'lodash';
8
-
9
- export {escape, escapeSync} from './html-base';
10
-
11
- /**
12
- * Some browsers don't implement {@link Element#remove()} or
13
- * {@link NodeList#remove()} or {@link HTMLCollection#remove()}. This wrapper
14
- * calls the appropriate `#remove()` method if available, or falls back to a
15
- * non-global-polluting polyfill.
16
- * @param {Element|NodeList|HTMLCollection} node
17
- * @returns {undefined}
18
- */
19
- function removeNode(node) {
20
- if (node.remove) {
21
- node.remove();
22
-
23
- return;
24
- }
25
-
26
- if (node.parentElement) {
27
- node.parentElement.removeChild(node);
28
-
29
- return;
30
- }
31
-
32
- if ('length' in node) {
33
- for (let i = node.length - 1; i >= 0; i -= 1) {
34
- removeNode(node[i]);
35
- }
36
-
37
- return;
38
- }
39
-
40
- throw new Error('Could not find a way to remove node');
41
- }
42
-
43
- /**
44
- * @param {Object} allowedTags
45
- * @param {Array<string>} allowedStyles
46
- * @param {string} html
47
- * @private
48
- * @returns {string}
49
- */
50
- function _filter(...args) {
51
- return new Promise((resolve) => {
52
- resolve(_filterSync(...args));
53
- });
54
- }
55
-
56
- /**
57
- * Curried async HTML filter.
58
- * @param {Object} allowedTags Map of tagName -> array of allowed attributes
59
- * @param {Array<string>} allowedStyles Array of allowed styles
60
- * @param {string} html html to filter
61
- * @returns {string}
62
- */
63
- export const filter = curry(_filter, 4);
64
-
65
- /**
66
- * @param {function} processCallback callback function to do additional
67
- * processing on node. of the form process(node)
68
- * @param {Object} allowedTags
69
- * @param {Array<string>} allowedStyles
70
- * @param {string} html
71
- * @private
72
- * @returns {string}
73
- */
74
- function _filterSync(processCallback, allowedTags, allowedStyles, html) {
75
- if (!html || !allowedStyles || !allowedTags) {
76
- if (html.length === 0) {
77
- return html;
78
- }
79
-
80
- throw new Error('`allowedTags`, `allowedStyles`, and `html` must be provided');
81
- }
82
-
83
- const doc = new DOMParser().parseFromString(html, 'text/html');
84
-
85
- depthFirstForEach(doc.body.childNodes, filterNode);
86
- processCallback(doc.body);
87
-
88
- if (html.indexOf('body') === 1) {
89
- return `<body>${doc.body.innerHTML}</body>`;
90
- }
91
-
92
- return doc.body.innerHTML;
93
-
94
- /**
95
- * @param {Node} node
96
- * @private
97
- * @returns {undefined}
98
- */
99
- function filterNode(node) {
100
- if (!isElement(node)) {
101
- return;
102
- }
103
-
104
- const nodeName = node.nodeName.toLowerCase();
105
- const allowedTagNames = Object.keys(allowedTags);
106
-
107
- depthFirstForEach(node.childNodes, filterNode);
108
-
109
- if (includes(allowedTagNames, nodeName)) {
110
- const allowedAttributes = allowedTags[nodeName];
111
-
112
- forEach(listAttributeNames(node.attributes), (attrName) => {
113
- if (!includes(allowedAttributes, attrName)) {
114
- node.removeAttribute(attrName);
115
- } else if (attrName === 'href' || attrName === 'src') {
116
- const attrValue = node.attributes.getNamedItem(attrName).value.trim().toLowerCase();
117
-
118
- // We're doing at runtime what the no-script-url rule does at compile
119
- // time
120
- // eslint-disable-next-line no-script-url
121
- if (attrValue.indexOf('javascript:') === 0 || attrValue.indexOf('vbscript:') === 0) {
122
- reparent(node);
123
- }
124
- } else if (attrName === 'style') {
125
- const styles = node.attributes
126
- .getNamedItem('style')
127
- .value.split(';')
128
- .map((style) => {
129
- const styleName = trim(style.split(':')[0]);
130
-
131
- if (includes(allowedStyles, styleName)) {
132
- return style;
133
- }
134
-
135
- return null;
136
- })
137
- .filter((style) => Boolean(style))
138
- .join(';');
139
-
140
- node.setAttribute('style', styles);
141
- }
142
- });
143
- } else {
144
- reparent(node);
145
- }
146
- }
147
- }
148
-
149
- /**
150
- * Same as _filter, but escapes rather than removes disallowed values
151
- * @param {Function} processCallback
152
- * @param {Object} allowedTags
153
- * @param {Array<string>} allowedStyles
154
- * @param {string} html
155
- * @returns {Promise<string>}
156
- */
157
- function _filterEscape(...args) {
158
- return new Promise((resolve) => {
159
- resolve(_filterEscapeSync(...args));
160
- });
161
- }
162
-
163
- /**
164
- * Same as _filterSync, but escapes rather than removes disallowed values
165
- * @param {Function} processCallback
166
- * @param {Object} allowedTags
167
- * @param {Array<string>} allowedStyles
168
- * @param {string} html
169
- * @returns {string}
170
- */
171
- function _filterEscapeSync(processCallback, allowedTags, allowedStyles, html) {
172
- if (!html || !allowedStyles || !allowedTags) {
173
- if (html.length === 0) {
174
- return html;
175
- }
176
-
177
- throw new Error('`allowedTags`, `allowedStyles`, and `html` must be provided');
178
- }
179
-
180
- const doc = new DOMParser().parseFromString(html, 'text/html');
181
-
182
- depthFirstForEach(doc.body.childNodes, filterNode);
183
- processCallback(doc.body);
184
-
185
- if (html.indexOf('body') === 1) {
186
- return `<body>${doc.body.innerHTML}</body>`;
187
- }
188
-
189
- return doc.body.innerHTML;
190
-
191
- /**
192
- * @param {Node} node
193
- * @private
194
- * @returns {undefined}
195
- */
196
- function filterNode(node) {
197
- if (!isElement(node)) {
198
- return;
199
- }
200
-
201
- depthFirstForEach(node.childNodes, filterNode);
202
-
203
- const nodeName = node.nodeName.toLowerCase();
204
- const allowedTagNames = Object.keys(allowedTags);
205
-
206
- if (includes(allowedTagNames, nodeName)) {
207
- const allowedAttributes = allowedTags[nodeName];
208
-
209
- forEach(listAttributeNames(node.attributes), (attrName) => {
210
- if (!includes(allowedAttributes, attrName)) {
211
- node.removeAttribute(attrName);
212
- } else if (attrName === 'href' || attrName === 'src') {
213
- const attrValue = node.attributes.getNamedItem(attrName).value.toLowerCase();
214
-
215
- // We're doing at runtime what the no-script-url rule does at compile
216
- // time
217
- // eslint-disable-next-line no-script-url
218
- if (attrValue.indexOf('javascript:') === 0 || attrValue.indexOf('vbscript:') === 0) {
219
- reparent(node);
220
- }
221
- } else if (attrName === 'style') {
222
- const styles = node.attributes
223
- .getNamedItem('style')
224
- .value.split(';')
225
- .map((style) => {
226
- const styleName = trim(style.split(':')[0]);
227
-
228
- if (includes(allowedStyles, styleName)) {
229
- return style;
230
- }
231
-
232
- return null;
233
- })
234
- .filter((style) => Boolean(style))
235
- .join(';');
236
-
237
- node.setAttribute('style', styles);
238
- }
239
- });
240
- } else {
241
- escapeNode(node);
242
- }
243
- }
244
- }
245
-
246
- /**
247
- * Escapes a given html node
248
- * @param {Node} node
249
- * @returns {undefined}
250
- */
251
- function escapeNode(node) {
252
- const before = document.createTextNode(`<${node.nodeName.toLowerCase()}>`);
253
- const after = document.createTextNode(`</${node.nodeName.toLowerCase()}>`);
254
-
255
- node.parentNode.insertBefore(before, node);
256
- while (node.childNodes.length > 0) {
257
- node.parentNode.insertBefore(node.childNodes[0], node);
258
- }
259
- node.parentNode.insertBefore(after, node);
260
-
261
- removeNode(node);
262
- }
263
-
264
- const trimPattern = /^\s|\s$/g;
265
-
266
- /**
267
- * @param {string} str
268
- * @returns {string}
269
- */
270
- function trim(str) {
271
- return str.replace(trimPattern, '');
272
- }
273
-
274
- /**
275
- * @param {Node} node
276
- * @private
277
- * @returns {undefined}
278
- */
279
- function reparent(node) {
280
- while (node.childNodes.length > 0) {
281
- node.parentNode.insertBefore(node.childNodes[0], node);
282
- }
283
- removeNode(node);
284
- }
285
-
286
- /**
287
- * @param {NamedNodeMap} attributes
288
- * @private
289
- * @returns {Array<string>}
290
- */
291
- function listAttributeNames(attributes) {
292
- return reduce(
293
- attributes,
294
- (attrNames, attr) => {
295
- attrNames.push(attr.name);
296
-
297
- return attrNames;
298
- },
299
- []
300
- );
301
- }
302
-
303
- /**
304
- * @param {Array} list
305
- * @param {Function} fn
306
- * @private
307
- * @returns {undefined}
308
- */
309
- function depthFirstForEach(list, fn) {
310
- for (let i = list.length; i >= 0; i -= 1) {
311
- fn(list[i]);
312
- }
313
- }
314
-
315
- /**
316
- * @param {Node} o
317
- * @private
318
- * @returns {Boolean}
319
- */
320
- function isElement(o) {
321
- if (!o) {
322
- return false;
323
- }
324
-
325
- if (o.ownerDocument === undefined) {
326
- return false;
327
- }
328
-
329
- if (o.nodeType !== 1) {
330
- return false;
331
- }
332
-
333
- if (typeof o.nodeName !== 'string') {
334
- return false;
335
- }
336
-
337
- return true;
338
- }
339
-
340
- /**
341
- * Curried HTML filter.
342
- * @param {Object} allowedTags Map of tagName -> array of allowed attributes
343
- * @param {Array<string>} allowedStyles Array of allowed styles
344
- * @param {string} html html to filter
345
- * @returns {string}
346
- */
347
- export const filterSync = curry(_filterSync, 4);
348
-
349
- /**
350
- * Curried HTML filter that escapes rather than removes disallowed tags
351
- * @param {Object} allowedTags Map of tagName -> array of allowed attributes
352
- * @param {Array<string>} allowedStyles Array of allowed styles
353
- * @param {string} html html to filter
354
- * @returns {Promise<string>}
355
- */
356
- export const filterEscape = curry(_filterEscape, 4);
357
-
358
- /**
359
- * Curried HTML filter that escapes rather than removes disallowed tags
360
- * @param {Object} allowedTags Map of tagName -> array of allowed attributes
361
- * @param {Array<string>} allowedStyles Array of allowed styles
362
- * @param {string} html html to filter
363
- * @returns {string}
364
- */
365
- export const filterEscapeSync = curry(_filterEscapeSync, 4);
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ /* eslint-env browser */
6
+
7
+ import {curry, forEach, includes, reduce} from 'lodash';
8
+
9
+ export {escape, escapeSync} from './html-base';
10
+
11
+ /**
12
+ * Some browsers don't implement {@link Element#remove()} or
13
+ * {@link NodeList#remove()} or {@link HTMLCollection#remove()}. This wrapper
14
+ * calls the appropriate `#remove()` method if available, or falls back to a
15
+ * non-global-polluting polyfill.
16
+ * @param {Element|NodeList|HTMLCollection} node
17
+ * @returns {undefined}
18
+ */
19
+ function removeNode(node) {
20
+ if (node.remove) {
21
+ node.remove();
22
+
23
+ return;
24
+ }
25
+
26
+ if (node.parentElement) {
27
+ node.parentElement.removeChild(node);
28
+
29
+ return;
30
+ }
31
+
32
+ if ('length' in node) {
33
+ for (let i = node.length - 1; i >= 0; i -= 1) {
34
+ removeNode(node[i]);
35
+ }
36
+
37
+ return;
38
+ }
39
+
40
+ throw new Error('Could not find a way to remove node');
41
+ }
42
+
43
+ /**
44
+ * @param {Object} allowedTags
45
+ * @param {Array<string>} allowedStyles
46
+ * @param {string} html
47
+ * @private
48
+ * @returns {string}
49
+ */
50
+ function _filter(...args) {
51
+ return new Promise((resolve) => {
52
+ resolve(_filterSync(...args));
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Curried async HTML filter.
58
+ * @param {Object} allowedTags Map of tagName -> array of allowed attributes
59
+ * @param {Array<string>} allowedStyles Array of allowed styles
60
+ * @param {string} html html to filter
61
+ * @returns {string}
62
+ */
63
+ export const filter = curry(_filter, 4);
64
+
65
+ /**
66
+ * @param {function} processCallback callback function to do additional
67
+ * processing on node. of the form process(node)
68
+ * @param {Object} allowedTags
69
+ * @param {Array<string>} allowedStyles
70
+ * @param {string} html
71
+ * @private
72
+ * @returns {string}
73
+ */
74
+ function _filterSync(processCallback, allowedTags, allowedStyles, html) {
75
+ if (!html || !allowedStyles || !allowedTags) {
76
+ if (html.length === 0) {
77
+ return html;
78
+ }
79
+
80
+ throw new Error('`allowedTags`, `allowedStyles`, and `html` must be provided');
81
+ }
82
+
83
+ const doc = new DOMParser().parseFromString(html, 'text/html');
84
+
85
+ depthFirstForEach(doc.body.childNodes, filterNode);
86
+ processCallback(doc.body);
87
+
88
+ if (html.indexOf('body') === 1) {
89
+ return `<body>${doc.body.innerHTML}</body>`;
90
+ }
91
+
92
+ return doc.body.innerHTML;
93
+
94
+ /**
95
+ * @param {Node} node
96
+ * @private
97
+ * @returns {undefined}
98
+ */
99
+ function filterNode(node) {
100
+ if (!isElement(node)) {
101
+ return;
102
+ }
103
+
104
+ const nodeName = node.nodeName.toLowerCase();
105
+ const allowedTagNames = Object.keys(allowedTags);
106
+
107
+ depthFirstForEach(node.childNodes, filterNode);
108
+
109
+ if (includes(allowedTagNames, nodeName)) {
110
+ const allowedAttributes = allowedTags[nodeName];
111
+
112
+ forEach(listAttributeNames(node.attributes), (attrName) => {
113
+ if (!includes(allowedAttributes, attrName)) {
114
+ node.removeAttribute(attrName);
115
+ } else if (attrName === 'href' || attrName === 'src') {
116
+ const attrValue = node.attributes.getNamedItem(attrName).value.trim().toLowerCase();
117
+
118
+ // We're doing at runtime what the no-script-url rule does at compile
119
+ // time
120
+ // eslint-disable-next-line no-script-url
121
+ if (attrValue.indexOf('javascript:') === 0 || attrValue.indexOf('vbscript:') === 0) {
122
+ reparent(node);
123
+ }
124
+ } else if (attrName === 'style') {
125
+ const styles = node.attributes
126
+ .getNamedItem('style')
127
+ .value.split(';')
128
+ .map((style) => {
129
+ const styleName = trim(style.split(':')[0]);
130
+
131
+ if (includes(allowedStyles, styleName)) {
132
+ return style;
133
+ }
134
+
135
+ return null;
136
+ })
137
+ .filter((style) => Boolean(style))
138
+ .join(';');
139
+
140
+ node.setAttribute('style', styles);
141
+ }
142
+ });
143
+ } else {
144
+ reparent(node);
145
+ }
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Same as _filter, but escapes rather than removes disallowed values
151
+ * @param {Function} processCallback
152
+ * @param {Object} allowedTags
153
+ * @param {Array<string>} allowedStyles
154
+ * @param {string} html
155
+ * @returns {Promise<string>}
156
+ */
157
+ function _filterEscape(...args) {
158
+ return new Promise((resolve) => {
159
+ resolve(_filterEscapeSync(...args));
160
+ });
161
+ }
162
+
163
+ /**
164
+ * Same as _filterSync, but escapes rather than removes disallowed values
165
+ * @param {Function} processCallback
166
+ * @param {Object} allowedTags
167
+ * @param {Array<string>} allowedStyles
168
+ * @param {string} html
169
+ * @returns {string}
170
+ */
171
+ function _filterEscapeSync(processCallback, allowedTags, allowedStyles, html) {
172
+ if (!html || !allowedStyles || !allowedTags) {
173
+ if (html.length === 0) {
174
+ return html;
175
+ }
176
+
177
+ throw new Error('`allowedTags`, `allowedStyles`, and `html` must be provided');
178
+ }
179
+
180
+ const doc = new DOMParser().parseFromString(html, 'text/html');
181
+
182
+ depthFirstForEach(doc.body.childNodes, filterNode);
183
+ processCallback(doc.body);
184
+
185
+ if (html.indexOf('body') === 1) {
186
+ return `<body>${doc.body.innerHTML}</body>`;
187
+ }
188
+
189
+ return doc.body.innerHTML;
190
+
191
+ /**
192
+ * @param {Node} node
193
+ * @private
194
+ * @returns {undefined}
195
+ */
196
+ function filterNode(node) {
197
+ if (!isElement(node)) {
198
+ return;
199
+ }
200
+
201
+ depthFirstForEach(node.childNodes, filterNode);
202
+
203
+ const nodeName = node.nodeName.toLowerCase();
204
+ const allowedTagNames = Object.keys(allowedTags);
205
+
206
+ if (includes(allowedTagNames, nodeName)) {
207
+ const allowedAttributes = allowedTags[nodeName];
208
+
209
+ forEach(listAttributeNames(node.attributes), (attrName) => {
210
+ if (!includes(allowedAttributes, attrName)) {
211
+ node.removeAttribute(attrName);
212
+ } else if (attrName === 'href' || attrName === 'src') {
213
+ const attrValue = node.attributes.getNamedItem(attrName).value.toLowerCase();
214
+
215
+ // We're doing at runtime what the no-script-url rule does at compile
216
+ // time
217
+ // eslint-disable-next-line no-script-url
218
+ if (attrValue.indexOf('javascript:') === 0 || attrValue.indexOf('vbscript:') === 0) {
219
+ reparent(node);
220
+ }
221
+ } else if (attrName === 'style') {
222
+ const styles = node.attributes
223
+ .getNamedItem('style')
224
+ .value.split(';')
225
+ .map((style) => {
226
+ const styleName = trim(style.split(':')[0]);
227
+
228
+ if (includes(allowedStyles, styleName)) {
229
+ return style;
230
+ }
231
+
232
+ return null;
233
+ })
234
+ .filter((style) => Boolean(style))
235
+ .join(';');
236
+
237
+ node.setAttribute('style', styles);
238
+ }
239
+ });
240
+ } else {
241
+ escapeNode(node);
242
+ }
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Escapes a given html node
248
+ * @param {Node} node
249
+ * @returns {undefined}
250
+ */
251
+ function escapeNode(node) {
252
+ const before = document.createTextNode(`<${node.nodeName.toLowerCase()}>`);
253
+ const after = document.createTextNode(`</${node.nodeName.toLowerCase()}>`);
254
+
255
+ node.parentNode.insertBefore(before, node);
256
+ while (node.childNodes.length > 0) {
257
+ node.parentNode.insertBefore(node.childNodes[0], node);
258
+ }
259
+ node.parentNode.insertBefore(after, node);
260
+
261
+ removeNode(node);
262
+ }
263
+
264
+ const trimPattern = /^\s|\s$/g;
265
+
266
+ /**
267
+ * @param {string} str
268
+ * @returns {string}
269
+ */
270
+ function trim(str) {
271
+ return str.replace(trimPattern, '');
272
+ }
273
+
274
+ /**
275
+ * @param {Node} node
276
+ * @private
277
+ * @returns {undefined}
278
+ */
279
+ function reparent(node) {
280
+ while (node.childNodes.length > 0) {
281
+ node.parentNode.insertBefore(node.childNodes[0], node);
282
+ }
283
+ removeNode(node);
284
+ }
285
+
286
+ /**
287
+ * @param {NamedNodeMap} attributes
288
+ * @private
289
+ * @returns {Array<string>}
290
+ */
291
+ function listAttributeNames(attributes) {
292
+ return reduce(
293
+ attributes,
294
+ (attrNames, attr) => {
295
+ attrNames.push(attr.name);
296
+
297
+ return attrNames;
298
+ },
299
+ []
300
+ );
301
+ }
302
+
303
+ /**
304
+ * @param {Array} list
305
+ * @param {Function} fn
306
+ * @private
307
+ * @returns {undefined}
308
+ */
309
+ function depthFirstForEach(list, fn) {
310
+ for (let i = list.length; i >= 0; i -= 1) {
311
+ fn(list[i]);
312
+ }
313
+ }
314
+
315
+ /**
316
+ * @param {Node} o
317
+ * @private
318
+ * @returns {Boolean}
319
+ */
320
+ function isElement(o) {
321
+ if (!o) {
322
+ return false;
323
+ }
324
+
325
+ if (o.ownerDocument === undefined) {
326
+ return false;
327
+ }
328
+
329
+ if (o.nodeType !== 1) {
330
+ return false;
331
+ }
332
+
333
+ if (typeof o.nodeName !== 'string') {
334
+ return false;
335
+ }
336
+
337
+ return true;
338
+ }
339
+
340
+ /**
341
+ * Curried HTML filter.
342
+ * @param {Object} allowedTags Map of tagName -> array of allowed attributes
343
+ * @param {Array<string>} allowedStyles Array of allowed styles
344
+ * @param {string} html html to filter
345
+ * @returns {string}
346
+ */
347
+ export const filterSync = curry(_filterSync, 4);
348
+
349
+ /**
350
+ * Curried HTML filter that escapes rather than removes disallowed tags
351
+ * @param {Object} allowedTags Map of tagName -> array of allowed attributes
352
+ * @param {Array<string>} allowedStyles Array of allowed styles
353
+ * @param {string} html html to filter
354
+ * @returns {Promise<string>}
355
+ */
356
+ export const filterEscape = curry(_filterEscape, 4);
357
+
358
+ /**
359
+ * Curried HTML filter that escapes rather than removes disallowed tags
360
+ * @param {Object} allowedTags Map of tagName -> array of allowed attributes
361
+ * @param {Array<string>} allowedStyles Array of allowed styles
362
+ * @param {string} html html to filter
363
+ * @returns {string}
364
+ */
365
+ export const filterEscapeSync = curry(_filterEscapeSync, 4);