lightning-base-components 1.21.8-alpha → 1.22.1-alpha
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/metadata/raptor.json +3 -0
- package/package.json +3 -5
- package/scopedImports/@salesforce-internal-core.appVersion.js +1 -1
- package/src/lightning/accordionSection/button.slds.css +16 -13
- package/src/lightning/baseComboboxFormattedText/baseComboboxFormattedText.js-meta.xml +1 -1
- package/src/lightning/button/button.slds.css +16 -13
- package/src/lightning/buttonIcon/button-icon.slds.css +16 -13
- package/src/lightning/buttonIconStateful/button-icon.slds.css +16 -13
- package/src/lightning/buttonIconStateful/button.slds.css +16 -13
- package/src/lightning/buttonMenu/button-icon.slds.css +16 -13
- package/src/lightning/buttonMenu/button.slds.css +16 -13
- package/src/lightning/buttonMenu/buttonMenu.js +5 -3
- package/src/lightning/buttonStateful/button-stateful.slds.css +23 -0
- package/src/lightning/buttonStateful/button.slds.css +16 -13
- package/src/lightning/buttonStateful/buttonStateful.js-meta.xml +3 -0
- package/src/lightning/calendar/keyboard.js +11 -3
- package/src/lightning/combobox/combobox.js +5 -13
- package/src/lightning/datatable/__examples__/withVirtualization/withVirtualization.js +0 -1
- package/src/lightning/datatable/datatable.js +35 -39
- package/src/lightning/datatable/keyboard.js +11 -10
- package/src/lightning/datatable/renderManager.js +1 -3
- package/src/lightning/datatable/rows.js +16 -9
- package/src/lightning/datatable/state.js +0 -1
- package/src/lightning/datatable/templates/div/div.html +8 -8
- package/src/lightning/datatable/templates/div/div.lbc.synthetic.css +7 -0
- package/src/lightning/datatable/virtualization.js +11 -14
- package/src/lightning/formattedAddress/formattedAddress.js +3 -2
- package/src/lightning/formattedDateTime/formattedDateTime.js +3 -2
- package/src/lightning/formattedDateTime/formattedDateTime.js-meta.xml +1 -1
- package/src/lightning/formattedLocation/formattedLocation.js +3 -2
- package/src/lightning/formattedPhone/formattedPhone.js +3 -2
- package/src/lightning/formattedTime/formattedTime.js +3 -2
- package/src/lightning/formattedUrl/formattedUrl.js +3 -2
- package/src/lightning/helptext/button-icon.slds.css +16 -13
- package/src/lightning/helptext/helptext.js-meta.xml +3 -0
- package/src/lightning/inputAddress/__docs__/inputAddress.md +44 -3
- package/src/lightning/inputAddress/inputAddress.js +8 -6
- package/src/lightning/inputName/inputName.js-meta.xml +3 -0
- package/src/lightning/interactiveDialogBase/button.slds.css +507 -0
- package/src/lightning/interactiveDialogBase/interactive-dialog-base.slds.css +45 -17
- package/src/lightning/interactiveDialogBase/interactiveDialogBase.lbc.native.css +6 -1
- package/src/lightning/interactiveDialogBase/modal-base.slds.css +242 -0
- package/src/lightning/interactiveDialogBase/modal-body.slds.css +60 -0
- package/src/lightning/interactiveDialogBase/modal-footer.slds.css +79 -0
- package/src/lightning/interactiveDialogBase/modal-header.slds.css +76 -0
- package/src/lightning/lookupAddress/lookupAddress.js-meta.xml +3 -0
- package/src/lightning/menuDivider/menuDivider.js-meta.xml +3 -0
- package/src/lightning/menuItem/menuItem.js +19 -3
- package/src/lightning/menuSubheader/menuSubheader.js-meta.xml +3 -0
- package/src/lightning/modal/modal.js-meta.xml +3 -0
- package/src/lightning/modalBase/modalBase.js +7 -5
- package/src/lightning/modalBody/modalBody.js-meta.xml +3 -0
- package/src/lightning/modalFooter/modalFooter.js-meta.xml +3 -0
- package/src/lightning/modalHeader/modalHeader.js-meta.xml +3 -0
- package/src/lightning/picklist/picklist.js +3 -2
- package/src/lightning/pill/pill.js +1 -1
- package/src/lightning/pillContainer/button.slds.css +16 -13
- package/src/lightning/primitiveDatatableTooltip/primitiveDatatableTooltip.js +11 -7
- package/src/lightning/primitiveDatatableTooltipBubble/primitiveDatatableTooltipBubble.html +1 -1
- package/src/lightning/primitiveFileDroppableZone/primitiveFileDroppableZone.js +3 -2
- package/src/lightning/primitiveInputCheckbox/input-checkbox.slds.css +1 -1
- package/src/lightning/primitiveInputFile/button.slds.css +16 -13
- package/src/lightning/primitiveInputFile/input-file.slds.css +3 -2
- package/src/lightning/primitiveInputToggle/input-toggle.slds.css +13 -7
- package/src/lightning/progressBar/progressBar.js-meta.xml +3 -0
- package/src/lightning/progressRing/progressRing.js-meta.xml +3 -0
- package/src/lightning/prompt/form-element.slds.css +319 -0
- package/src/lightning/prompt/input-text.slds.css +506 -0
- package/src/lightning/prompt/prompt.lbc.native.css +2 -0
- package/src/lightning/purifyLib/purify.js +838 -586
- package/src/lightning/radioGroup/radioGroup.js-meta.xml +3 -0
- package/src/lightning/textarea/textarea.js-meta.xml +3 -0
- package/src/lightning/toast/button-icon.slds.css +16 -13
- package/src/lightning/toast/toast.js-meta.xml +3 -0
- package/src/lightning/toastContainer/toastContainer.js-meta.xml +3 -0
- package/src/lightning/verticalNavigationOverflow/button.slds.css +16 -13
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/*! @license DOMPurify 3.0.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.11/LICENSE */
|
|
1
2
|
/**
|
|
2
3
|
* This library is copied from https://github.com/cure53/DOMPurify
|
|
3
4
|
*
|
|
@@ -7,7 +8,7 @@
|
|
|
7
8
|
*
|
|
8
9
|
* How?
|
|
9
10
|
* In order to update this library, follow the steps:
|
|
10
|
-
* Current version:
|
|
11
|
+
* Current version: 3.0.11
|
|
11
12
|
*
|
|
12
13
|
* 1. Check if there is a new version published at https://github.com/cure53/DOMPurify/releases or find the specific release you need.
|
|
13
14
|
* 2. Download the Source Code zip of that release.
|
|
@@ -18,169 +19,197 @@
|
|
|
18
19
|
* 5.2 Default export the variable `purify`
|
|
19
20
|
*/
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
var hasOwnProperty = Object.hasOwnProperty,
|
|
33
|
-
setPrototypeOf = Object.setPrototypeOf,
|
|
34
|
-
isFrozen = Object.isFrozen,
|
|
35
|
-
getPrototypeOf = Object.getPrototypeOf,
|
|
36
|
-
getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
37
|
-
var freeze = Object.freeze,
|
|
38
|
-
seal = Object.seal,
|
|
39
|
-
create = Object.create; // eslint-disable-line import/no-mutable-exports
|
|
40
|
-
|
|
41
|
-
var _ref = typeof Reflect !== 'undefined' && Reflect,
|
|
42
|
-
apply = _ref.apply,
|
|
43
|
-
construct = _ref.construct;
|
|
44
|
-
|
|
45
|
-
if (!apply) {
|
|
46
|
-
apply = function apply(fun, thisValue, args) {
|
|
47
|
-
return fun.apply(thisValue, args);
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
22
|
+
'use strict';
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
entries,
|
|
26
|
+
setPrototypeOf,
|
|
27
|
+
isFrozen,
|
|
28
|
+
getPrototypeOf,
|
|
29
|
+
getOwnPropertyDescriptor,
|
|
30
|
+
} = Object;
|
|
31
|
+
let { freeze, seal, create } = Object; // eslint-disable-line import/no-mutable-exports
|
|
32
|
+
let { apply, construct } = typeof Reflect !== 'undefined' && Reflect;
|
|
51
33
|
if (!freeze) {
|
|
52
34
|
freeze = function freeze(x) {
|
|
53
35
|
return x;
|
|
54
36
|
};
|
|
55
37
|
}
|
|
56
|
-
|
|
57
38
|
if (!seal) {
|
|
58
39
|
seal = function seal(x) {
|
|
59
40
|
return x;
|
|
60
41
|
};
|
|
61
42
|
}
|
|
62
|
-
|
|
43
|
+
if (!apply) {
|
|
44
|
+
apply = function apply(fun, thisValue, args) {
|
|
45
|
+
return fun.apply(thisValue, args);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
63
48
|
if (!construct) {
|
|
64
49
|
construct = function construct(Func, args) {
|
|
65
|
-
return new (
|
|
66
|
-
Func,
|
|
67
|
-
[null].concat(_toConsumableArray(args))
|
|
68
|
-
))();
|
|
50
|
+
return new Func(...args);
|
|
69
51
|
};
|
|
70
52
|
}
|
|
53
|
+
const arrayForEach = unapply(Array.prototype.forEach);
|
|
54
|
+
const arrayPop = unapply(Array.prototype.pop);
|
|
55
|
+
const arrayPush = unapply(Array.prototype.push);
|
|
56
|
+
const stringToLowerCase = unapply(String.prototype.toLowerCase);
|
|
57
|
+
const stringToString = unapply(String.prototype.toString);
|
|
58
|
+
const stringMatch = unapply(String.prototype.match);
|
|
59
|
+
const stringReplace = unapply(String.prototype.replace);
|
|
60
|
+
const stringIndexOf = unapply(String.prototype.indexOf);
|
|
61
|
+
const stringTrim = unapply(String.prototype.trim);
|
|
62
|
+
const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
|
|
63
|
+
const regExpTest = unapply(RegExp.prototype.test);
|
|
64
|
+
const typeErrorCreate = unconstruct(TypeError);
|
|
71
65
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
var stringReplace = unapply(String.prototype.replace);
|
|
79
|
-
var stringIndexOf = unapply(String.prototype.indexOf);
|
|
80
|
-
var stringTrim = unapply(String.prototype.trim);
|
|
81
|
-
|
|
82
|
-
var regExpTest = unapply(RegExp.prototype.test);
|
|
83
|
-
|
|
84
|
-
var typeErrorCreate = unconstruct(TypeError);
|
|
85
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Creates a new function that calls the given function with a specified thisArg and arguments.
|
|
68
|
+
*
|
|
69
|
+
* @param {Function} func - The function to be wrapped and called.
|
|
70
|
+
* @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
|
|
71
|
+
*/
|
|
86
72
|
function unapply(func) {
|
|
87
73
|
return function (thisArg) {
|
|
88
74
|
for (
|
|
89
75
|
var _len = arguments.length,
|
|
90
|
-
args = Array(_len > 1 ? _len - 1 : 0),
|
|
76
|
+
args = new Array(_len > 1 ? _len - 1 : 0),
|
|
91
77
|
_key = 1;
|
|
92
78
|
_key < _len;
|
|
93
79
|
_key++
|
|
94
80
|
) {
|
|
95
81
|
args[_key - 1] = arguments[_key];
|
|
96
82
|
}
|
|
97
|
-
|
|
98
83
|
return apply(func, thisArg, args);
|
|
99
84
|
};
|
|
100
85
|
}
|
|
101
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Creates a new function that constructs an instance of the given constructor function with the provided arguments.
|
|
89
|
+
*
|
|
90
|
+
* @param {Function} func - The constructor function to be wrapped and called.
|
|
91
|
+
* @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
|
|
92
|
+
*/
|
|
102
93
|
function unconstruct(func) {
|
|
103
94
|
return function () {
|
|
104
95
|
for (
|
|
105
|
-
var _len2 = arguments.length, args = Array(_len2), _key2 = 0;
|
|
96
|
+
var _len2 = arguments.length, args = new Array(_len2), _key2 = 0;
|
|
106
97
|
_key2 < _len2;
|
|
107
98
|
_key2++
|
|
108
99
|
) {
|
|
109
100
|
args[_key2] = arguments[_key2];
|
|
110
101
|
}
|
|
111
|
-
|
|
112
102
|
return construct(func, args);
|
|
113
103
|
};
|
|
114
104
|
}
|
|
115
105
|
|
|
116
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Add properties to a lookup table
|
|
108
|
+
*
|
|
109
|
+
* @param {Object} set - The set to which elements will be added.
|
|
110
|
+
* @param {Array} array - The array containing elements to be added to the set.
|
|
111
|
+
* @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
|
|
112
|
+
* @returns {Object} The modified set with added elements.
|
|
113
|
+
*/
|
|
117
114
|
function addToSet(set, array) {
|
|
115
|
+
let transformCaseFunc =
|
|
116
|
+
arguments.length > 2 && arguments[2] !== undefined
|
|
117
|
+
? arguments[2]
|
|
118
|
+
: stringToLowerCase;
|
|
118
119
|
if (setPrototypeOf) {
|
|
119
120
|
// Make 'in' and truthy checks like Boolean(set.constructor)
|
|
120
121
|
// independent of any properties defined on Object.prototype.
|
|
121
122
|
// Prevent prototype setters from intercepting set as a this value.
|
|
122
123
|
setPrototypeOf(set, null);
|
|
123
124
|
}
|
|
124
|
-
|
|
125
|
-
var l = array.length;
|
|
125
|
+
let l = array.length;
|
|
126
126
|
while (l--) {
|
|
127
|
-
|
|
127
|
+
let element = array[l];
|
|
128
128
|
if (typeof element === 'string') {
|
|
129
|
-
|
|
129
|
+
const lcElement = transformCaseFunc(element);
|
|
130
130
|
if (lcElement !== element) {
|
|
131
131
|
// Config presets (e.g. tags.js, attrs.js) are immutable.
|
|
132
132
|
if (!isFrozen(array)) {
|
|
133
133
|
array[l] = lcElement;
|
|
134
134
|
}
|
|
135
|
-
|
|
136
135
|
element = lcElement;
|
|
137
136
|
}
|
|
138
137
|
}
|
|
139
|
-
|
|
140
138
|
set[element] = true;
|
|
141
139
|
}
|
|
142
|
-
|
|
143
140
|
return set;
|
|
144
141
|
}
|
|
145
142
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Clean up an array to harden against CSPP
|
|
145
|
+
*
|
|
146
|
+
* @param {Array} array - The array to be cleaned.
|
|
147
|
+
* @returns {Array} The cleaned version of the array
|
|
148
|
+
*/
|
|
149
|
+
function cleanArray(array) {
|
|
150
|
+
for (let index = 0; index < array.length; index++) {
|
|
151
|
+
const isPropertyExist = objectHasOwnProperty(array, index);
|
|
152
|
+
if (!isPropertyExist) {
|
|
153
|
+
array[index] = null;
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
+
return array;
|
|
157
|
+
}
|
|
156
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Shallow clone an object
|
|
161
|
+
*
|
|
162
|
+
* @param {Object} object - The object to be cloned.
|
|
163
|
+
* @returns {Object} A new object that copies the original.
|
|
164
|
+
*/
|
|
165
|
+
function clone(object) {
|
|
166
|
+
const newObject = create(null);
|
|
167
|
+
for (const [property, value] of entries(object)) {
|
|
168
|
+
const isPropertyExist = objectHasOwnProperty(object, property);
|
|
169
|
+
if (isPropertyExist) {
|
|
170
|
+
if (Array.isArray(value)) {
|
|
171
|
+
newObject[property] = cleanArray(value);
|
|
172
|
+
} else if (
|
|
173
|
+
value &&
|
|
174
|
+
typeof value === 'object' &&
|
|
175
|
+
value.constructor === Object
|
|
176
|
+
) {
|
|
177
|
+
newObject[property] = clone(value);
|
|
178
|
+
} else {
|
|
179
|
+
newObject[property] = value;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
157
183
|
return newObject;
|
|
158
184
|
}
|
|
159
185
|
|
|
160
|
-
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
186
|
+
/**
|
|
187
|
+
* This method automatically checks if the prop is function or getter and behaves accordingly.
|
|
188
|
+
*
|
|
189
|
+
* @param {Object} object - The object to look up the getter function in its prototype chain.
|
|
190
|
+
* @param {String} prop - The property name for which to find the getter function.
|
|
191
|
+
* @returns {Function} The getter function found in the prototype chain or a fallback function.
|
|
192
|
+
*/
|
|
164
193
|
function lookupGetter(object, prop) {
|
|
165
194
|
while (object !== null) {
|
|
166
|
-
|
|
195
|
+
const desc = getOwnPropertyDescriptor(object, prop);
|
|
167
196
|
if (desc) {
|
|
168
197
|
if (desc.get) {
|
|
169
198
|
return unapply(desc.get);
|
|
170
199
|
}
|
|
171
|
-
|
|
172
200
|
if (typeof desc.value === 'function') {
|
|
173
201
|
return unapply(desc.value);
|
|
174
202
|
}
|
|
175
203
|
}
|
|
176
|
-
|
|
177
204
|
object = getPrototypeOf(object);
|
|
178
205
|
}
|
|
179
|
-
|
|
180
|
-
|
|
206
|
+
function fallbackValue() {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
return fallbackValue;
|
|
181
210
|
}
|
|
182
211
|
|
|
183
|
-
|
|
212
|
+
const html$1 = freeze([
|
|
184
213
|
'a',
|
|
185
214
|
'abbr',
|
|
186
215
|
'acronym',
|
|
@@ -301,7 +330,7 @@ var html = freeze([
|
|
|
301
330
|
]);
|
|
302
331
|
|
|
303
332
|
// SVG
|
|
304
|
-
|
|
333
|
+
const svg$1 = freeze([
|
|
305
334
|
'svg',
|
|
306
335
|
'a',
|
|
307
336
|
'altglyph',
|
|
@@ -346,8 +375,7 @@ var svg = freeze([
|
|
|
346
375
|
'view',
|
|
347
376
|
'vkern',
|
|
348
377
|
]);
|
|
349
|
-
|
|
350
|
-
var svgFilters = freeze([
|
|
378
|
+
const svgFilters = freeze([
|
|
351
379
|
'feBlend',
|
|
352
380
|
'feColorMatrix',
|
|
353
381
|
'feComponentTransfer',
|
|
@@ -356,12 +384,14 @@ var svgFilters = freeze([
|
|
|
356
384
|
'feDiffuseLighting',
|
|
357
385
|
'feDisplacementMap',
|
|
358
386
|
'feDistantLight',
|
|
387
|
+
'feDropShadow',
|
|
359
388
|
'feFlood',
|
|
360
389
|
'feFuncA',
|
|
361
390
|
'feFuncB',
|
|
362
391
|
'feFuncG',
|
|
363
392
|
'feFuncR',
|
|
364
393
|
'feGaussianBlur',
|
|
394
|
+
'feImage',
|
|
365
395
|
'feMerge',
|
|
366
396
|
'feMergeNode',
|
|
367
397
|
'feMorphology',
|
|
@@ -377,13 +407,11 @@ var svgFilters = freeze([
|
|
|
377
407
|
// We still need to know them so that we can do namespace
|
|
378
408
|
// checks properly in case one wants to add them to
|
|
379
409
|
// allow-list.
|
|
380
|
-
|
|
410
|
+
const svgDisallowed = freeze([
|
|
381
411
|
'animate',
|
|
382
412
|
'color-profile',
|
|
383
413
|
'cursor',
|
|
384
414
|
'discard',
|
|
385
|
-
'fedropshadow',
|
|
386
|
-
'feimage',
|
|
387
415
|
'font-face',
|
|
388
416
|
'font-face-format',
|
|
389
417
|
'font-face-name',
|
|
@@ -403,8 +431,7 @@ var svgDisallowed = freeze([
|
|
|
403
431
|
'unknown',
|
|
404
432
|
'use',
|
|
405
433
|
]);
|
|
406
|
-
|
|
407
|
-
var mathMl = freeze([
|
|
434
|
+
const mathMl$1 = freeze([
|
|
408
435
|
'math',
|
|
409
436
|
'menclose',
|
|
410
437
|
'merror',
|
|
@@ -434,11 +461,12 @@ var mathMl = freeze([
|
|
|
434
461
|
'mtr',
|
|
435
462
|
'munder',
|
|
436
463
|
'munderover',
|
|
464
|
+
'mprescripts',
|
|
437
465
|
]);
|
|
438
466
|
|
|
439
467
|
// Similarly to SVG, we want to know all MathML elements,
|
|
440
468
|
// even those that we disallow by default.
|
|
441
|
-
|
|
469
|
+
const mathMlDisallowed = freeze([
|
|
442
470
|
'maction',
|
|
443
471
|
'maligngroup',
|
|
444
472
|
'malignmark',
|
|
@@ -455,10 +483,9 @@ var mathMlDisallowed = freeze([
|
|
|
455
483
|
'mprescripts',
|
|
456
484
|
'none',
|
|
457
485
|
]);
|
|
486
|
+
const text = freeze(['#text']);
|
|
458
487
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
var html$1 = freeze([
|
|
488
|
+
const html = freeze([
|
|
462
489
|
'accept',
|
|
463
490
|
'action',
|
|
464
491
|
'align',
|
|
@@ -523,6 +550,7 @@ var html$1 = freeze([
|
|
|
523
550
|
'multiple',
|
|
524
551
|
'muted',
|
|
525
552
|
'name',
|
|
553
|
+
'nonce',
|
|
526
554
|
'noshade',
|
|
527
555
|
'novalidate',
|
|
528
556
|
'nowrap',
|
|
@@ -566,9 +594,9 @@ var html$1 = freeze([
|
|
|
566
594
|
'value',
|
|
567
595
|
'width',
|
|
568
596
|
'xmlns',
|
|
597
|
+
'slot',
|
|
569
598
|
]);
|
|
570
|
-
|
|
571
|
-
var svg$1 = freeze([
|
|
599
|
+
const svg = freeze([
|
|
572
600
|
'accent-height',
|
|
573
601
|
'accumulate',
|
|
574
602
|
'additive',
|
|
@@ -720,6 +748,7 @@ var svg$1 = freeze([
|
|
|
720
748
|
'targetx',
|
|
721
749
|
'targety',
|
|
722
750
|
'transform',
|
|
751
|
+
'transform-origin',
|
|
723
752
|
'text-anchor',
|
|
724
753
|
'text-decoration',
|
|
725
754
|
'text-rendering',
|
|
@@ -751,8 +780,7 @@ var svg$1 = freeze([
|
|
|
751
780
|
'z',
|
|
752
781
|
'zoomandpan',
|
|
753
782
|
]);
|
|
754
|
-
|
|
755
|
-
var mathMl$1 = freeze([
|
|
783
|
+
const mathMl = freeze([
|
|
756
784
|
'accent',
|
|
757
785
|
'accentunder',
|
|
758
786
|
'align',
|
|
@@ -807,8 +835,7 @@ var mathMl$1 = freeze([
|
|
|
807
835
|
'width',
|
|
808
836
|
'xmlns',
|
|
809
837
|
]);
|
|
810
|
-
|
|
811
|
-
var xml = freeze([
|
|
838
|
+
const xml = freeze([
|
|
812
839
|
'xlink:href',
|
|
813
840
|
'xml:id',
|
|
814
841
|
'xlink:title',
|
|
@@ -817,63 +844,55 @@ var xml = freeze([
|
|
|
817
844
|
]);
|
|
818
845
|
|
|
819
846
|
// eslint-disable-next-line unicorn/better-regex
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
847
|
+
const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
|
|
848
|
+
const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
|
|
849
|
+
const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
|
|
850
|
+
const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
|
|
851
|
+
const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
852
|
+
const IS_ALLOWED_URI = seal(
|
|
853
|
+
/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
|
826
854
|
);
|
|
827
|
-
|
|
828
|
-
|
|
855
|
+
|
|
856
|
+
const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
|
857
|
+
const ATTR_WHITESPACE = seal(
|
|
829
858
|
/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
|
|
830
859
|
);
|
|
831
860
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
}
|
|
851
|
-
return arr2;
|
|
852
|
-
} else {
|
|
853
|
-
return Array.from(arr);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
var getGlobal = function getGlobal() {
|
|
861
|
+
const DOCTYPE_NAME = seal(/^html$/i);
|
|
862
|
+
const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
|
|
863
|
+
|
|
864
|
+
var EXPRESSIONS = /*#__PURE__*/ Object.freeze({
|
|
865
|
+
__proto__: null,
|
|
866
|
+
MUSTACHE_EXPR: MUSTACHE_EXPR,
|
|
867
|
+
ERB_EXPR: ERB_EXPR,
|
|
868
|
+
TMPLIT_EXPR: TMPLIT_EXPR,
|
|
869
|
+
DATA_ATTR: DATA_ATTR,
|
|
870
|
+
ARIA_ATTR: ARIA_ATTR,
|
|
871
|
+
IS_ALLOWED_URI: IS_ALLOWED_URI,
|
|
872
|
+
IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
|
|
873
|
+
ATTR_WHITESPACE: ATTR_WHITESPACE,
|
|
874
|
+
DOCTYPE_NAME: DOCTYPE_NAME,
|
|
875
|
+
CUSTOM_ELEMENT: CUSTOM_ELEMENT,
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
const getGlobal = function getGlobal() {
|
|
858
879
|
return typeof window === 'undefined' ? null : window;
|
|
859
880
|
};
|
|
860
881
|
|
|
861
882
|
/**
|
|
862
883
|
* Creates a no-op policy for internal use only.
|
|
863
884
|
* Don't export this function outside this module!
|
|
864
|
-
* @param {
|
|
865
|
-
* @param {
|
|
866
|
-
* @return {
|
|
867
|
-
* are not supported).
|
|
885
|
+
* @param {TrustedTypePolicyFactory} trustedTypes The policy factory.
|
|
886
|
+
* @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
|
|
887
|
+
* @return {TrustedTypePolicy} The policy created (or null, if Trusted Types
|
|
888
|
+
* are not supported or creating the policy failed).
|
|
868
889
|
*/
|
|
869
|
-
|
|
890
|
+
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(
|
|
870
891
|
trustedTypes,
|
|
871
|
-
|
|
892
|
+
purifyHostElement
|
|
872
893
|
) {
|
|
873
894
|
if (
|
|
874
|
-
|
|
875
|
-
? 'undefined'
|
|
876
|
-
: _typeof(trustedTypes)) !== 'object' ||
|
|
895
|
+
typeof trustedTypes !== 'object' ||
|
|
877
896
|
typeof trustedTypes.createPolicy !== 'function'
|
|
878
897
|
) {
|
|
879
898
|
return null;
|
|
@@ -882,21 +901,19 @@ var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(
|
|
|
882
901
|
// Allow the callers to control the unique policy name
|
|
883
902
|
// by adding a data-tt-policy-suffix to the script element with the DOMPurify.
|
|
884
903
|
// Policy creation with duplicate names throws in Trusted Types.
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
if (
|
|
888
|
-
|
|
889
|
-
document.currentScript.hasAttribute(ATTR_NAME)
|
|
890
|
-
) {
|
|
891
|
-
suffix = document.currentScript.getAttribute(ATTR_NAME);
|
|
904
|
+
let suffix = null;
|
|
905
|
+
const ATTR_NAME = 'data-tt-policy-suffix';
|
|
906
|
+
if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
|
|
907
|
+
suffix = purifyHostElement.getAttribute(ATTR_NAME);
|
|
892
908
|
}
|
|
893
|
-
|
|
894
|
-
var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
|
|
895
|
-
|
|
909
|
+
const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
|
|
896
910
|
try {
|
|
897
911
|
return trustedTypes.createPolicy(policyName, {
|
|
898
|
-
createHTML
|
|
899
|
-
return html
|
|
912
|
+
createHTML(html) {
|
|
913
|
+
return html;
|
|
914
|
+
},
|
|
915
|
+
createScriptURL(scriptUrl) {
|
|
916
|
+
return scriptUrl;
|
|
900
917
|
},
|
|
901
918
|
});
|
|
902
919
|
} catch (_) {
|
|
@@ -909,61 +926,49 @@ var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(
|
|
|
909
926
|
return null;
|
|
910
927
|
}
|
|
911
928
|
};
|
|
912
|
-
|
|
913
929
|
function createDOMPurify() {
|
|
914
|
-
|
|
930
|
+
let window =
|
|
915
931
|
arguments.length > 0 && arguments[0] !== undefined
|
|
916
932
|
? arguments[0]
|
|
917
933
|
: getGlobal();
|
|
918
|
-
|
|
919
|
-
var DOMPurify = function DOMPurify(root) {
|
|
920
|
-
return createDOMPurify(root);
|
|
921
|
-
};
|
|
934
|
+
const DOMPurify = (root) => createDOMPurify(root);
|
|
922
935
|
|
|
923
936
|
/**
|
|
924
937
|
* Version label, exposed for easier checks
|
|
925
938
|
* if DOMPurify is up to date or not
|
|
926
939
|
*/
|
|
927
|
-
DOMPurify.version = '
|
|
940
|
+
DOMPurify.version = '3.0.11';
|
|
928
941
|
|
|
929
942
|
/**
|
|
930
943
|
* Array of elements that DOMPurify removed during sanitation.
|
|
931
944
|
* Empty if nothing was removed.
|
|
932
945
|
*/
|
|
933
946
|
DOMPurify.removed = [];
|
|
934
|
-
|
|
935
947
|
if (!window || !window.document || window.document.nodeType !== 9) {
|
|
936
948
|
// Not running in a browser, provide a factory function
|
|
937
949
|
// so that you can pass your own Window
|
|
938
950
|
DOMPurify.isSupported = false;
|
|
939
|
-
|
|
940
951
|
return DOMPurify;
|
|
941
952
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
HTMLTemplateElement
|
|
948
|
-
Node
|
|
949
|
-
Element
|
|
950
|
-
NodeFilter
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
var ElementPrototype = Element.prototype;
|
|
962
|
-
|
|
963
|
-
var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
964
|
-
var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
965
|
-
var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
966
|
-
var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
953
|
+
let { document } = window;
|
|
954
|
+
const originalDocument = document;
|
|
955
|
+
const currentScript = originalDocument.currentScript;
|
|
956
|
+
const {
|
|
957
|
+
DocumentFragment,
|
|
958
|
+
HTMLTemplateElement,
|
|
959
|
+
Node,
|
|
960
|
+
Element,
|
|
961
|
+
NodeFilter,
|
|
962
|
+
NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
|
|
963
|
+
HTMLFormElement,
|
|
964
|
+
DOMParser,
|
|
965
|
+
trustedTypes,
|
|
966
|
+
} = window;
|
|
967
|
+
const ElementPrototype = Element.prototype;
|
|
968
|
+
const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
969
|
+
const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
970
|
+
const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
971
|
+
const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
967
972
|
|
|
968
973
|
// As per issue #47, the web-components registry is inherited by a
|
|
969
974
|
// new document created via createHTMLDocument. As per the spec
|
|
@@ -972,52 +977,41 @@ function createDOMPurify() {
|
|
|
972
977
|
// document, so we use that as our parent document to ensure nothing
|
|
973
978
|
// is inherited.
|
|
974
979
|
if (typeof HTMLTemplateElement === 'function') {
|
|
975
|
-
|
|
980
|
+
const template = document.createElement('template');
|
|
976
981
|
if (template.content && template.content.ownerDocument) {
|
|
977
982
|
document = template.content.ownerDocument;
|
|
978
983
|
}
|
|
979
984
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
var _document = document,
|
|
991
|
-
implementation = _document.implementation,
|
|
992
|
-
createNodeIterator = _document.createNodeIterator,
|
|
993
|
-
getElementsByTagName = _document.getElementsByTagName,
|
|
994
|
-
createDocumentFragment = _document.createDocumentFragment;
|
|
995
|
-
var importNode = originalDocument.importNode;
|
|
996
|
-
|
|
997
|
-
var documentMode = {};
|
|
998
|
-
try {
|
|
999
|
-
documentMode = clone(document).documentMode
|
|
1000
|
-
? document.documentMode
|
|
1001
|
-
: {};
|
|
1002
|
-
} catch (_) {}
|
|
1003
|
-
|
|
1004
|
-
var hooks = {};
|
|
985
|
+
let trustedTypesPolicy;
|
|
986
|
+
let emptyHTML = '';
|
|
987
|
+
const {
|
|
988
|
+
implementation,
|
|
989
|
+
createNodeIterator,
|
|
990
|
+
createDocumentFragment,
|
|
991
|
+
getElementsByTagName,
|
|
992
|
+
} = document;
|
|
993
|
+
const { importNode } = originalDocument;
|
|
994
|
+
let hooks = {};
|
|
1005
995
|
|
|
1006
996
|
/**
|
|
1007
997
|
* Expose whether this browser supports running the full DOMPurify.
|
|
1008
998
|
*/
|
|
1009
999
|
DOMPurify.isSupported =
|
|
1000
|
+
typeof entries === 'function' &&
|
|
1001
|
+
typeof getParentNode === 'function' &&
|
|
1010
1002
|
implementation &&
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
DATA_ATTR
|
|
1017
|
-
ARIA_ATTR
|
|
1018
|
-
IS_SCRIPT_OR_DATA
|
|
1019
|
-
ATTR_WHITESPACE
|
|
1020
|
-
|
|
1003
|
+
implementation.createHTMLDocument !== undefined;
|
|
1004
|
+
const {
|
|
1005
|
+
MUSTACHE_EXPR,
|
|
1006
|
+
ERB_EXPR,
|
|
1007
|
+
TMPLIT_EXPR,
|
|
1008
|
+
DATA_ATTR,
|
|
1009
|
+
ARIA_ATTR,
|
|
1010
|
+
IS_SCRIPT_OR_DATA,
|
|
1011
|
+
ATTR_WHITESPACE,
|
|
1012
|
+
CUSTOM_ELEMENT,
|
|
1013
|
+
} = EXPRESSIONS;
|
|
1014
|
+
let { IS_ALLOWED_URI: IS_ALLOWED_URI$1 } = EXPRESSIONS;
|
|
1021
1015
|
|
|
1022
1016
|
/**
|
|
1023
1017
|
* We consider the elements and attributes below to be safe. Ideally
|
|
@@ -1025,101 +1019,135 @@ function createDOMPurify() {
|
|
|
1025
1019
|
*/
|
|
1026
1020
|
|
|
1027
1021
|
/* allowed element names */
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
_toConsumableArray$1(mathMl),
|
|
1037
|
-
_toConsumableArray$1(text)
|
|
1038
|
-
)
|
|
1039
|
-
);
|
|
1022
|
+
let ALLOWED_TAGS = null;
|
|
1023
|
+
const DEFAULT_ALLOWED_TAGS = addToSet({}, [
|
|
1024
|
+
...html$1,
|
|
1025
|
+
...svg$1,
|
|
1026
|
+
...svgFilters,
|
|
1027
|
+
...mathMl$1,
|
|
1028
|
+
...text,
|
|
1029
|
+
]);
|
|
1040
1030
|
|
|
1041
1031
|
/* Allowed attribute names */
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1032
|
+
let ALLOWED_ATTR = null;
|
|
1033
|
+
const DEFAULT_ALLOWED_ATTR = addToSet({}, [
|
|
1034
|
+
...html,
|
|
1035
|
+
...svg,
|
|
1036
|
+
...mathMl,
|
|
1037
|
+
...xml,
|
|
1038
|
+
]);
|
|
1039
|
+
|
|
1040
|
+
/*
|
|
1041
|
+
* Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
|
|
1042
|
+
* @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
|
|
1043
|
+
* @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
|
|
1044
|
+
* @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
|
|
1045
|
+
*/
|
|
1046
|
+
let CUSTOM_ELEMENT_HANDLING = Object.seal(
|
|
1047
|
+
create(null, {
|
|
1048
|
+
tagNameCheck: {
|
|
1049
|
+
writable: true,
|
|
1050
|
+
configurable: false,
|
|
1051
|
+
enumerable: true,
|
|
1052
|
+
value: null,
|
|
1053
|
+
},
|
|
1054
|
+
attributeNameCheck: {
|
|
1055
|
+
writable: true,
|
|
1056
|
+
configurable: false,
|
|
1057
|
+
enumerable: true,
|
|
1058
|
+
value: null,
|
|
1059
|
+
},
|
|
1060
|
+
allowCustomizedBuiltInElements: {
|
|
1061
|
+
writable: true,
|
|
1062
|
+
configurable: false,
|
|
1063
|
+
enumerable: true,
|
|
1064
|
+
value: false,
|
|
1065
|
+
},
|
|
1066
|
+
})
|
|
1051
1067
|
);
|
|
1052
1068
|
|
|
1053
1069
|
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
|
|
1054
|
-
|
|
1070
|
+
let FORBID_TAGS = null;
|
|
1055
1071
|
|
|
1056
1072
|
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
|
|
1057
|
-
|
|
1073
|
+
let FORBID_ATTR = null;
|
|
1058
1074
|
|
|
1059
1075
|
/* Decide if ARIA attributes are okay */
|
|
1060
|
-
|
|
1076
|
+
let ALLOW_ARIA_ATTR = true;
|
|
1061
1077
|
|
|
1062
1078
|
/* Decide if custom data attributes are okay */
|
|
1063
|
-
|
|
1079
|
+
let ALLOW_DATA_ATTR = true;
|
|
1064
1080
|
|
|
1065
1081
|
/* Decide if unknown protocols are okay */
|
|
1066
|
-
|
|
1082
|
+
let ALLOW_UNKNOWN_PROTOCOLS = false;
|
|
1083
|
+
|
|
1084
|
+
/* Decide if self-closing tags in attributes are allowed.
|
|
1085
|
+
* Usually removed due to a mXSS issue in jQuery 3.0 */
|
|
1086
|
+
let ALLOW_SELF_CLOSE_IN_ATTR = true;
|
|
1067
1087
|
|
|
1068
1088
|
/* Output should be safe for common template engines.
|
|
1069
1089
|
* This means, DOMPurify removes data attributes, mustaches and ERB
|
|
1070
1090
|
*/
|
|
1071
|
-
|
|
1091
|
+
let SAFE_FOR_TEMPLATES = false;
|
|
1072
1092
|
|
|
1073
1093
|
/* Decide if document with <html>... should be returned */
|
|
1074
|
-
|
|
1094
|
+
let WHOLE_DOCUMENT = false;
|
|
1075
1095
|
|
|
1076
1096
|
/* Track whether config is already set on this instance of DOMPurify. */
|
|
1077
|
-
|
|
1097
|
+
let SET_CONFIG = false;
|
|
1078
1098
|
|
|
1079
1099
|
/* Decide if all elements (e.g. style, script) must be children of
|
|
1080
1100
|
* document.body. By default, browsers might move them to document.head */
|
|
1081
|
-
|
|
1101
|
+
let FORCE_BODY = false;
|
|
1082
1102
|
|
|
1083
1103
|
/* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
|
|
1084
1104
|
* string (or a TrustedHTML object if Trusted Types are supported).
|
|
1085
1105
|
* If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
|
|
1086
1106
|
*/
|
|
1087
|
-
|
|
1107
|
+
let RETURN_DOM = false;
|
|
1088
1108
|
|
|
1089
1109
|
/* Decide if a DOM `DocumentFragment` should be returned, instead of a html
|
|
1090
1110
|
* string (or a TrustedHTML object if Trusted Types are supported) */
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
/* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM
|
|
1094
|
-
* `Node` is imported into the current `Document`. If this flag is not enabled the
|
|
1095
|
-
* `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by
|
|
1096
|
-
* DOMPurify.
|
|
1097
|
-
*
|
|
1098
|
-
* This defaults to `true` starting DOMPurify 2.2.0. Note that setting it to `false`
|
|
1099
|
-
* might cause XSS from attacks hidden in closed shadowroots in case the browser
|
|
1100
|
-
* supports Declarative Shadow: DOM https://web.dev/declarative-shadow-dom/
|
|
1101
|
-
*/
|
|
1102
|
-
var RETURN_DOM_IMPORT = true;
|
|
1111
|
+
let RETURN_DOM_FRAGMENT = false;
|
|
1103
1112
|
|
|
1104
1113
|
/* Try to return a Trusted Type object instead of a string, return a string in
|
|
1105
1114
|
* case Trusted Types are not supported */
|
|
1106
|
-
|
|
1115
|
+
let RETURN_TRUSTED_TYPE = false;
|
|
1107
1116
|
|
|
1108
|
-
/* Output should be free from DOM clobbering attacks?
|
|
1109
|
-
|
|
1117
|
+
/* Output should be free from DOM clobbering attacks?
|
|
1118
|
+
* This sanitizes markups named with colliding, clobberable built-in DOM APIs.
|
|
1119
|
+
*/
|
|
1120
|
+
let SANITIZE_DOM = true;
|
|
1121
|
+
|
|
1122
|
+
/* Achieve full DOM Clobbering protection by isolating the namespace of named
|
|
1123
|
+
* properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
|
|
1124
|
+
*
|
|
1125
|
+
* HTML/DOM spec rules that enable DOM Clobbering:
|
|
1126
|
+
* - Named Access on Window (§7.3.3)
|
|
1127
|
+
* - DOM Tree Accessors (§3.1.5)
|
|
1128
|
+
* - Form Element Parent-Child Relations (§4.10.3)
|
|
1129
|
+
* - Iframe srcdoc / Nested WindowProxies (§4.8.5)
|
|
1130
|
+
* - HTMLCollection (§4.2.10.2)
|
|
1131
|
+
*
|
|
1132
|
+
* Namespace isolation is implemented by prefixing `id` and `name` attributes
|
|
1133
|
+
* with a constant string, i.e., `user-content-`
|
|
1134
|
+
*/
|
|
1135
|
+
let SANITIZE_NAMED_PROPS = false;
|
|
1136
|
+
const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
|
|
1110
1137
|
|
|
1111
1138
|
/* Keep element content when removing element? */
|
|
1112
|
-
|
|
1139
|
+
let KEEP_CONTENT = true;
|
|
1113
1140
|
|
|
1114
1141
|
/* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
|
|
1115
1142
|
* of importing it into a new Document and returning a sanitized copy */
|
|
1116
|
-
|
|
1143
|
+
let IN_PLACE = false;
|
|
1117
1144
|
|
|
1118
1145
|
/* Allow usage of profiles like html, svg and mathMl */
|
|
1119
|
-
|
|
1146
|
+
let USE_PROFILES = {};
|
|
1120
1147
|
|
|
1121
1148
|
/* Tags to ignore content of when KEEP_CONTENT is true */
|
|
1122
|
-
|
|
1149
|
+
let FORBID_CONTENTS = null;
|
|
1150
|
+
const DEFAULT_FORBID_CONTENTS = addToSet({}, [
|
|
1123
1151
|
'annotation-xml',
|
|
1124
1152
|
'audio',
|
|
1125
1153
|
'colgroup',
|
|
@@ -1148,8 +1176,8 @@ function createDOMPurify() {
|
|
|
1148
1176
|
]);
|
|
1149
1177
|
|
|
1150
1178
|
/* Tags that are safe for data: URIs */
|
|
1151
|
-
|
|
1152
|
-
|
|
1179
|
+
let DATA_URI_TAGS = null;
|
|
1180
|
+
const DEFAULT_DATA_URI_TAGS = addToSet({}, [
|
|
1153
1181
|
'audio',
|
|
1154
1182
|
'video',
|
|
1155
1183
|
'img',
|
|
@@ -1159,8 +1187,8 @@ function createDOMPurify() {
|
|
|
1159
1187
|
]);
|
|
1160
1188
|
|
|
1161
1189
|
/* Attributes safe for values like "javascript:" */
|
|
1162
|
-
|
|
1163
|
-
|
|
1190
|
+
let URI_SAFE_ATTRIBUTES = null;
|
|
1191
|
+
const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [
|
|
1164
1192
|
'alt',
|
|
1165
1193
|
'class',
|
|
1166
1194
|
'for',
|
|
@@ -1169,20 +1197,44 @@ function createDOMPurify() {
|
|
|
1169
1197
|
'name',
|
|
1170
1198
|
'pattern',
|
|
1171
1199
|
'placeholder',
|
|
1200
|
+
'role',
|
|
1172
1201
|
'summary',
|
|
1173
1202
|
'title',
|
|
1174
1203
|
'value',
|
|
1175
1204
|
'style',
|
|
1176
1205
|
'xmlns',
|
|
1177
1206
|
]);
|
|
1207
|
+
const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
|
|
1208
|
+
const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
1209
|
+
const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
1210
|
+
/* Document namespace */
|
|
1211
|
+
let NAMESPACE = HTML_NAMESPACE;
|
|
1212
|
+
let IS_EMPTY_INPUT = false;
|
|
1213
|
+
|
|
1214
|
+
/* Allowed XHTML+XML namespaces */
|
|
1215
|
+
let ALLOWED_NAMESPACES = null;
|
|
1216
|
+
const DEFAULT_ALLOWED_NAMESPACES = addToSet(
|
|
1217
|
+
{},
|
|
1218
|
+
[MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE],
|
|
1219
|
+
stringToString
|
|
1220
|
+
);
|
|
1221
|
+
|
|
1222
|
+
/* Parsing of strict XHTML documents */
|
|
1223
|
+
let PARSER_MEDIA_TYPE = null;
|
|
1224
|
+
const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
|
|
1225
|
+
const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
|
|
1226
|
+
let transformCaseFunc = null;
|
|
1178
1227
|
|
|
1179
1228
|
/* Keep a reference to config to pass to hooks */
|
|
1180
|
-
|
|
1229
|
+
let CONFIG = null;
|
|
1181
1230
|
|
|
1182
1231
|
/* Ideally, do not touch anything below this line */
|
|
1183
1232
|
/* ______________________________________________ */
|
|
1184
1233
|
|
|
1185
|
-
|
|
1234
|
+
const formElement = document.createElement('form');
|
|
1235
|
+
const isRegexOrFunction = function isRegexOrFunction(testValue) {
|
|
1236
|
+
return testValue instanceof RegExp || testValue instanceof Function;
|
|
1237
|
+
};
|
|
1186
1238
|
|
|
1187
1239
|
/**
|
|
1188
1240
|
* _parseConfig
|
|
@@ -1190,92 +1242,141 @@ function createDOMPurify() {
|
|
|
1190
1242
|
* @param {Object} cfg optional config literal
|
|
1191
1243
|
*/
|
|
1192
1244
|
// eslint-disable-next-line complexity
|
|
1193
|
-
|
|
1245
|
+
const _parseConfig = function _parseConfig() {
|
|
1246
|
+
let cfg =
|
|
1247
|
+
arguments.length > 0 && arguments[0] !== undefined
|
|
1248
|
+
? arguments[0]
|
|
1249
|
+
: {};
|
|
1194
1250
|
if (CONFIG && CONFIG === cfg) {
|
|
1195
1251
|
return;
|
|
1196
1252
|
}
|
|
1197
1253
|
|
|
1198
1254
|
/* Shield configuration object from tampering */
|
|
1199
|
-
if (
|
|
1200
|
-
!cfg ||
|
|
1201
|
-
(typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !==
|
|
1202
|
-
'object'
|
|
1203
|
-
) {
|
|
1255
|
+
if (!cfg || typeof cfg !== 'object') {
|
|
1204
1256
|
cfg = {};
|
|
1205
1257
|
}
|
|
1206
1258
|
|
|
1207
1259
|
/* Shield configuration object from prototype pollution */
|
|
1208
1260
|
cfg = clone(cfg);
|
|
1261
|
+
PARSER_MEDIA_TYPE =
|
|
1262
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
1263
|
+
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1
|
|
1264
|
+
? DEFAULT_PARSER_MEDIA_TYPE
|
|
1265
|
+
: cfg.PARSER_MEDIA_TYPE;
|
|
1266
|
+
|
|
1267
|
+
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
1268
|
+
transformCaseFunc =
|
|
1269
|
+
PARSER_MEDIA_TYPE === 'application/xhtml+xml'
|
|
1270
|
+
? stringToString
|
|
1271
|
+
: stringToLowerCase;
|
|
1209
1272
|
|
|
1210
1273
|
/* Set configuration parameters */
|
|
1211
|
-
ALLOWED_TAGS =
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1274
|
+
ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS')
|
|
1275
|
+
? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc)
|
|
1276
|
+
: DEFAULT_ALLOWED_TAGS;
|
|
1277
|
+
ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR')
|
|
1278
|
+
? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc)
|
|
1279
|
+
: DEFAULT_ALLOWED_ATTR;
|
|
1280
|
+
ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES')
|
|
1281
|
+
? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString)
|
|
1282
|
+
: DEFAULT_ALLOWED_NAMESPACES;
|
|
1283
|
+
URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR')
|
|
1284
|
+
? addToSet(
|
|
1285
|
+
clone(DEFAULT_URI_SAFE_ATTRIBUTES),
|
|
1286
|
+
// eslint-disable-line indent
|
|
1287
|
+
cfg.ADD_URI_SAFE_ATTR,
|
|
1288
|
+
// eslint-disable-line indent
|
|
1289
|
+
transformCaseFunc // eslint-disable-line indent
|
|
1290
|
+
) // eslint-disable-line indent
|
|
1291
|
+
: DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
1292
|
+
DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS')
|
|
1293
|
+
? addToSet(
|
|
1294
|
+
clone(DEFAULT_DATA_URI_TAGS),
|
|
1295
|
+
// eslint-disable-line indent
|
|
1296
|
+
cfg.ADD_DATA_URI_TAGS,
|
|
1297
|
+
// eslint-disable-line indent
|
|
1298
|
+
transformCaseFunc // eslint-disable-line indent
|
|
1299
|
+
) // eslint-disable-line indent
|
|
1300
|
+
: DEFAULT_DATA_URI_TAGS;
|
|
1301
|
+
FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS')
|
|
1302
|
+
? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc)
|
|
1303
|
+
: DEFAULT_FORBID_CONTENTS;
|
|
1304
|
+
FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS')
|
|
1305
|
+
? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc)
|
|
1306
|
+
: {};
|
|
1307
|
+
FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR')
|
|
1308
|
+
? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc)
|
|
1309
|
+
: {};
|
|
1310
|
+
USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES')
|
|
1311
|
+
? cfg.USE_PROFILES
|
|
1312
|
+
: false;
|
|
1233
1313
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
1234
1314
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
1235
1315
|
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
|
|
1316
|
+
ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
|
|
1236
1317
|
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
|
|
1237
1318
|
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
|
|
1238
1319
|
RETURN_DOM = cfg.RETURN_DOM || false; // Default false
|
|
1239
1320
|
RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
|
|
1240
|
-
RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true
|
|
1241
1321
|
RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
|
|
1242
1322
|
FORCE_BODY = cfg.FORCE_BODY || false; // Default false
|
|
1243
1323
|
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
|
|
1324
|
+
SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
|
|
1244
1325
|
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
|
|
1245
1326
|
IN_PLACE = cfg.IN_PLACE || false; // Default false
|
|
1246
|
-
IS_ALLOWED_URI
|
|
1327
|
+
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
|
|
1328
|
+
NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
|
|
1329
|
+
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
|
|
1330
|
+
if (
|
|
1331
|
+
cfg.CUSTOM_ELEMENT_HANDLING &&
|
|
1332
|
+
isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)
|
|
1333
|
+
) {
|
|
1334
|
+
CUSTOM_ELEMENT_HANDLING.tagNameCheck =
|
|
1335
|
+
cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
|
|
1336
|
+
}
|
|
1337
|
+
if (
|
|
1338
|
+
cfg.CUSTOM_ELEMENT_HANDLING &&
|
|
1339
|
+
isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)
|
|
1340
|
+
) {
|
|
1341
|
+
CUSTOM_ELEMENT_HANDLING.attributeNameCheck =
|
|
1342
|
+
cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
|
|
1343
|
+
}
|
|
1344
|
+
if (
|
|
1345
|
+
cfg.CUSTOM_ELEMENT_HANDLING &&
|
|
1346
|
+
typeof cfg.CUSTOM_ELEMENT_HANDLING
|
|
1347
|
+
.allowCustomizedBuiltInElements === 'boolean'
|
|
1348
|
+
) {
|
|
1349
|
+
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements =
|
|
1350
|
+
cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
|
|
1351
|
+
}
|
|
1247
1352
|
if (SAFE_FOR_TEMPLATES) {
|
|
1248
1353
|
ALLOW_DATA_ATTR = false;
|
|
1249
1354
|
}
|
|
1250
|
-
|
|
1251
1355
|
if (RETURN_DOM_FRAGMENT) {
|
|
1252
1356
|
RETURN_DOM = true;
|
|
1253
1357
|
}
|
|
1254
1358
|
|
|
1255
1359
|
/* Parse profile info */
|
|
1256
1360
|
if (USE_PROFILES) {
|
|
1257
|
-
ALLOWED_TAGS = addToSet({},
|
|
1361
|
+
ALLOWED_TAGS = addToSet({}, text);
|
|
1258
1362
|
ALLOWED_ATTR = [];
|
|
1259
1363
|
if (USE_PROFILES.html === true) {
|
|
1260
|
-
addToSet(ALLOWED_TAGS, html);
|
|
1261
|
-
addToSet(ALLOWED_ATTR, html
|
|
1364
|
+
addToSet(ALLOWED_TAGS, html$1);
|
|
1365
|
+
addToSet(ALLOWED_ATTR, html);
|
|
1262
1366
|
}
|
|
1263
|
-
|
|
1264
1367
|
if (USE_PROFILES.svg === true) {
|
|
1265
|
-
addToSet(ALLOWED_TAGS, svg);
|
|
1266
|
-
addToSet(ALLOWED_ATTR, svg
|
|
1368
|
+
addToSet(ALLOWED_TAGS, svg$1);
|
|
1369
|
+
addToSet(ALLOWED_ATTR, svg);
|
|
1267
1370
|
addToSet(ALLOWED_ATTR, xml);
|
|
1268
1371
|
}
|
|
1269
|
-
|
|
1270
1372
|
if (USE_PROFILES.svgFilters === true) {
|
|
1271
1373
|
addToSet(ALLOWED_TAGS, svgFilters);
|
|
1272
|
-
addToSet(ALLOWED_ATTR, svg
|
|
1374
|
+
addToSet(ALLOWED_ATTR, svg);
|
|
1273
1375
|
addToSet(ALLOWED_ATTR, xml);
|
|
1274
1376
|
}
|
|
1275
|
-
|
|
1276
1377
|
if (USE_PROFILES.mathMl === true) {
|
|
1277
|
-
addToSet(ALLOWED_TAGS, mathMl);
|
|
1278
|
-
addToSet(ALLOWED_ATTR, mathMl
|
|
1378
|
+
addToSet(ALLOWED_TAGS, mathMl$1);
|
|
1379
|
+
addToSet(ALLOWED_ATTR, mathMl);
|
|
1279
1380
|
addToSet(ALLOWED_ATTR, xml);
|
|
1280
1381
|
}
|
|
1281
1382
|
}
|
|
@@ -1285,20 +1386,26 @@ function createDOMPurify() {
|
|
|
1285
1386
|
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
1286
1387
|
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
1287
1388
|
}
|
|
1288
|
-
|
|
1289
|
-
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
|
|
1389
|
+
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
1290
1390
|
}
|
|
1291
|
-
|
|
1292
1391
|
if (cfg.ADD_ATTR) {
|
|
1293
1392
|
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
1294
1393
|
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
1295
1394
|
}
|
|
1296
|
-
|
|
1297
|
-
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
|
|
1395
|
+
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
1298
1396
|
}
|
|
1299
|
-
|
|
1300
1397
|
if (cfg.ADD_URI_SAFE_ATTR) {
|
|
1301
|
-
addToSet(
|
|
1398
|
+
addToSet(
|
|
1399
|
+
URI_SAFE_ATTRIBUTES,
|
|
1400
|
+
cfg.ADD_URI_SAFE_ATTR,
|
|
1401
|
+
transformCaseFunc
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1404
|
+
if (cfg.FORBID_CONTENTS) {
|
|
1405
|
+
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
|
|
1406
|
+
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
1407
|
+
}
|
|
1408
|
+
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
1302
1409
|
}
|
|
1303
1410
|
|
|
1304
1411
|
/* Add #text in case KEEP_CONTENT is set to true */
|
|
@@ -1316,68 +1423,105 @@ function createDOMPurify() {
|
|
|
1316
1423
|
addToSet(ALLOWED_TAGS, ['tbody']);
|
|
1317
1424
|
delete FORBID_TAGS.tbody;
|
|
1318
1425
|
}
|
|
1426
|
+
if (cfg.TRUSTED_TYPES_POLICY) {
|
|
1427
|
+
if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
|
|
1428
|
+
throw typeErrorCreate(
|
|
1429
|
+
'TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.'
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1432
|
+
if (
|
|
1433
|
+
typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function'
|
|
1434
|
+
) {
|
|
1435
|
+
throw typeErrorCreate(
|
|
1436
|
+
'TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.'
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
// Overwrite existing TrustedTypes policy.
|
|
1441
|
+
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
|
|
1442
|
+
|
|
1443
|
+
// Sign local variables required by `sanitize`.
|
|
1444
|
+
emptyHTML = trustedTypesPolicy.createHTML('');
|
|
1445
|
+
} else {
|
|
1446
|
+
// Uninitialized policy, attempt to initialize the internal dompurify policy.
|
|
1447
|
+
if (trustedTypesPolicy === undefined) {
|
|
1448
|
+
trustedTypesPolicy = _createTrustedTypesPolicy(
|
|
1449
|
+
trustedTypes,
|
|
1450
|
+
currentScript
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// If creating the internal policy succeeded sign internal variables.
|
|
1455
|
+
if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
|
|
1456
|
+
emptyHTML = trustedTypesPolicy.createHTML('');
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1319
1459
|
|
|
1320
1460
|
// Prevent further manipulation of configuration.
|
|
1321
1461
|
// Not available in IE8, Safari 5, etc.
|
|
1322
1462
|
if (freeze) {
|
|
1323
1463
|
freeze(cfg);
|
|
1324
1464
|
}
|
|
1325
|
-
|
|
1326
1465
|
CONFIG = cfg;
|
|
1327
1466
|
};
|
|
1328
|
-
|
|
1329
|
-
var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [
|
|
1467
|
+
const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [
|
|
1330
1468
|
'mi',
|
|
1331
1469
|
'mo',
|
|
1332
1470
|
'mn',
|
|
1333
1471
|
'ms',
|
|
1334
1472
|
'mtext',
|
|
1335
1473
|
]);
|
|
1336
|
-
|
|
1337
|
-
var HTML_INTEGRATION_POINTS = addToSet({}, [
|
|
1474
|
+
const HTML_INTEGRATION_POINTS = addToSet({}, [
|
|
1338
1475
|
'foreignobject',
|
|
1339
1476
|
'desc',
|
|
1340
1477
|
'title',
|
|
1341
1478
|
'annotation-xml',
|
|
1342
1479
|
]);
|
|
1343
1480
|
|
|
1481
|
+
// Certain elements are allowed in both SVG and HTML
|
|
1482
|
+
// namespace. We need to specify them explicitly
|
|
1483
|
+
// so that they don't get erroneously deleted from
|
|
1484
|
+
// HTML namespace.
|
|
1485
|
+
const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [
|
|
1486
|
+
'title',
|
|
1487
|
+
'style',
|
|
1488
|
+
'font',
|
|
1489
|
+
'a',
|
|
1490
|
+
'script',
|
|
1491
|
+
]);
|
|
1492
|
+
|
|
1344
1493
|
/* Keep track of all possible SVG and MathML tags
|
|
1345
1494
|
* so that we can perform the namespace checks
|
|
1346
1495
|
* correctly. */
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
addToSet(
|
|
1353
|
-
|
|
1354
|
-
var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
|
|
1355
|
-
var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
1356
|
-
var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
1496
|
+
const ALL_SVG_TAGS = addToSet({}, [
|
|
1497
|
+
...svg$1,
|
|
1498
|
+
...svgFilters,
|
|
1499
|
+
...svgDisallowed,
|
|
1500
|
+
]);
|
|
1501
|
+
const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
|
|
1357
1502
|
|
|
1358
1503
|
/**
|
|
1359
|
-
*
|
|
1360
|
-
*
|
|
1361
1504
|
* @param {Element} element a DOM element whose namespace is being checked
|
|
1362
1505
|
* @returns {boolean} Return false if the element has a
|
|
1363
1506
|
* namespace that a spec-compliant parser would never
|
|
1364
1507
|
* return. Return true otherwise.
|
|
1365
1508
|
*/
|
|
1366
|
-
|
|
1367
|
-
|
|
1509
|
+
const _checkValidNamespace = function _checkValidNamespace(element) {
|
|
1510
|
+
let parent = getParentNode(element);
|
|
1368
1511
|
|
|
1369
1512
|
// In JSDOM, if we're inside shadow DOM, then parentNode
|
|
1370
1513
|
// can be null. We just simulate parent in this case.
|
|
1371
1514
|
if (!parent || !parent.tagName) {
|
|
1372
1515
|
parent = {
|
|
1373
|
-
namespaceURI:
|
|
1516
|
+
namespaceURI: NAMESPACE,
|
|
1374
1517
|
tagName: 'template',
|
|
1375
1518
|
};
|
|
1376
1519
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1520
|
+
const tagName = stringToLowerCase(element.tagName);
|
|
1521
|
+
const parentTagName = stringToLowerCase(parent.tagName);
|
|
1522
|
+
if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
1523
|
+
return false;
|
|
1524
|
+
}
|
|
1381
1525
|
if (element.namespaceURI === SVG_NAMESPACE) {
|
|
1382
1526
|
// The only way to switch from HTML namespace to SVG
|
|
1383
1527
|
// is via <svg>. If it happens via any other tag, then
|
|
@@ -1386,7 +1530,7 @@ function createDOMPurify() {
|
|
|
1386
1530
|
return tagName === 'svg';
|
|
1387
1531
|
}
|
|
1388
1532
|
|
|
1389
|
-
// The only way to switch from MathML to SVG is via
|
|
1533
|
+
// The only way to switch from MathML to SVG is via`
|
|
1390
1534
|
// svg if parent is either <annotation-xml> or MathML
|
|
1391
1535
|
// text integration points.
|
|
1392
1536
|
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
@@ -1401,7 +1545,6 @@ function createDOMPurify() {
|
|
|
1401
1545
|
// spec. All others are disallowed in SVG namespace.
|
|
1402
1546
|
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
1403
1547
|
}
|
|
1404
|
-
|
|
1405
1548
|
if (element.namespaceURI === MATHML_NAMESPACE) {
|
|
1406
1549
|
// The only way to switch from HTML namespace to MathML
|
|
1407
1550
|
// is via <math>. If it happens via any other tag, then
|
|
@@ -1422,7 +1565,6 @@ function createDOMPurify() {
|
|
|
1422
1565
|
// spec. All others are disallowed in MathML namespace.
|
|
1423
1566
|
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
1424
1567
|
}
|
|
1425
|
-
|
|
1426
1568
|
if (element.namespaceURI === HTML_NAMESPACE) {
|
|
1427
1569
|
// The only way to switch from SVG to HTML is via
|
|
1428
1570
|
// HTML integration points, and from MathML to HTML
|
|
@@ -1433,7 +1575,6 @@ function createDOMPurify() {
|
|
|
1433
1575
|
) {
|
|
1434
1576
|
return false;
|
|
1435
1577
|
}
|
|
1436
|
-
|
|
1437
1578
|
if (
|
|
1438
1579
|
parent.namespaceURI === MATHML_NAMESPACE &&
|
|
1439
1580
|
!MATHML_TEXT_INTEGRATION_POINTS[parentTagName]
|
|
@@ -1441,29 +1582,27 @@ function createDOMPurify() {
|
|
|
1441
1582
|
return false;
|
|
1442
1583
|
}
|
|
1443
1584
|
|
|
1444
|
-
// Certain elements are allowed in both SVG and HTML
|
|
1445
|
-
// namespace. We need to specify them explicitly
|
|
1446
|
-
// so that they don't get erronously deleted from
|
|
1447
|
-
// HTML namespace.
|
|
1448
|
-
var commonSvgAndHTMLElements = addToSet({}, [
|
|
1449
|
-
'title',
|
|
1450
|
-
'style',
|
|
1451
|
-
'font',
|
|
1452
|
-
'a',
|
|
1453
|
-
'script',
|
|
1454
|
-
]);
|
|
1455
|
-
|
|
1456
1585
|
// We disallow tags that are specific for MathML
|
|
1457
1586
|
// or SVG and should never appear in HTML namespace
|
|
1458
1587
|
return (
|
|
1459
1588
|
!ALL_MATHML_TAGS[tagName] &&
|
|
1460
|
-
(
|
|
1589
|
+
(COMMON_SVG_AND_HTML_ELEMENTS[tagName] ||
|
|
1590
|
+
!ALL_SVG_TAGS[tagName])
|
|
1461
1591
|
);
|
|
1462
1592
|
}
|
|
1463
1593
|
|
|
1594
|
+
// For XHTML and XML documents that support custom namespaces
|
|
1595
|
+
if (
|
|
1596
|
+
PARSER_MEDIA_TYPE === 'application/xhtml+xml' &&
|
|
1597
|
+
ALLOWED_NAMESPACES[element.namespaceURI]
|
|
1598
|
+
) {
|
|
1599
|
+
return true;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1464
1602
|
// The code should never reach this place (this means
|
|
1465
1603
|
// that the element somehow got namespace that is not
|
|
1466
|
-
// HTML, SVG
|
|
1604
|
+
// HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
|
|
1605
|
+
// Return false just in case.
|
|
1467
1606
|
return false;
|
|
1468
1607
|
};
|
|
1469
1608
|
|
|
@@ -1472,16 +1611,15 @@ function createDOMPurify() {
|
|
|
1472
1611
|
*
|
|
1473
1612
|
* @param {Node} node a DOM node
|
|
1474
1613
|
*/
|
|
1475
|
-
|
|
1476
|
-
arrayPush(DOMPurify.removed, {
|
|
1614
|
+
const _forceRemove = function _forceRemove(node) {
|
|
1615
|
+
arrayPush(DOMPurify.removed, {
|
|
1616
|
+
element: node,
|
|
1617
|
+
});
|
|
1477
1618
|
try {
|
|
1619
|
+
// eslint-disable-next-line unicorn/prefer-dom-node-remove
|
|
1478
1620
|
node.parentNode.removeChild(node);
|
|
1479
1621
|
} catch (_) {
|
|
1480
|
-
|
|
1481
|
-
node.outerHTML = emptyHTML;
|
|
1482
|
-
} catch (_) {
|
|
1483
|
-
node.remove();
|
|
1484
|
-
}
|
|
1622
|
+
node.remove();
|
|
1485
1623
|
}
|
|
1486
1624
|
};
|
|
1487
1625
|
|
|
@@ -1491,7 +1629,7 @@ function createDOMPurify() {
|
|
|
1491
1629
|
* @param {String} name an Attribute name
|
|
1492
1630
|
* @param {Node} node a DOM node
|
|
1493
1631
|
*/
|
|
1494
|
-
|
|
1632
|
+
const _removeAttribute = function _removeAttribute(name, node) {
|
|
1495
1633
|
try {
|
|
1496
1634
|
arrayPush(DOMPurify.removed, {
|
|
1497
1635
|
attribute: node.getAttributeNode(name),
|
|
@@ -1503,8 +1641,20 @@ function createDOMPurify() {
|
|
|
1503
1641
|
from: node,
|
|
1504
1642
|
});
|
|
1505
1643
|
}
|
|
1506
|
-
|
|
1507
1644
|
node.removeAttribute(name);
|
|
1645
|
+
|
|
1646
|
+
// We void attribute values for unremovable "is"" attributes
|
|
1647
|
+
if (name === 'is' && !ALLOWED_ATTR[name]) {
|
|
1648
|
+
if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
|
|
1649
|
+
try {
|
|
1650
|
+
_forceRemove(node);
|
|
1651
|
+
} catch (_) {}
|
|
1652
|
+
} else {
|
|
1653
|
+
try {
|
|
1654
|
+
node.setAttribute(name, '');
|
|
1655
|
+
} catch (_) {}
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1508
1658
|
};
|
|
1509
1659
|
|
|
1510
1660
|
/**
|
|
@@ -1513,68 +1663,89 @@ function createDOMPurify() {
|
|
|
1513
1663
|
* @param {String} dirty a string of dirty markup
|
|
1514
1664
|
* @return {Document} a DOM, filled with the dirty markup
|
|
1515
1665
|
*/
|
|
1516
|
-
|
|
1666
|
+
const _initDocument = function _initDocument(dirty) {
|
|
1517
1667
|
/* Create a HTML document */
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1668
|
+
let doc = null;
|
|
1669
|
+
let leadingWhitespace = null;
|
|
1521
1670
|
if (FORCE_BODY) {
|
|
1522
1671
|
dirty = '<remove></remove>' + dirty;
|
|
1523
1672
|
} else {
|
|
1524
1673
|
/* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
|
|
1525
|
-
|
|
1674
|
+
const matches = stringMatch(dirty, /^[\r\n\t ]+/);
|
|
1526
1675
|
leadingWhitespace = matches && matches[0];
|
|
1527
1676
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1677
|
+
if (
|
|
1678
|
+
PARSER_MEDIA_TYPE === 'application/xhtml+xml' &&
|
|
1679
|
+
NAMESPACE === HTML_NAMESPACE
|
|
1680
|
+
) {
|
|
1681
|
+
// Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
|
|
1682
|
+
dirty =
|
|
1683
|
+
'<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' +
|
|
1684
|
+
dirty +
|
|
1685
|
+
'</body></html>';
|
|
1686
|
+
}
|
|
1687
|
+
const dirtyPayload = trustedTypesPolicy
|
|
1530
1688
|
? trustedTypesPolicy.createHTML(dirty)
|
|
1531
1689
|
: dirty;
|
|
1532
|
-
/*
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1690
|
+
/*
|
|
1691
|
+
* Use the DOMParser API by default, fallback later if needs be
|
|
1692
|
+
* DOMParser not work for svg when has multiple root element.
|
|
1693
|
+
*/
|
|
1694
|
+
if (NAMESPACE === HTML_NAMESPACE) {
|
|
1695
|
+
try {
|
|
1696
|
+
doc = new DOMParser().parseFromString(
|
|
1697
|
+
dirtyPayload,
|
|
1698
|
+
PARSER_MEDIA_TYPE
|
|
1699
|
+
);
|
|
1700
|
+
} catch (_) {}
|
|
1701
|
+
}
|
|
1536
1702
|
|
|
1537
1703
|
/* Use createHTMLDocument in case DOMParser is not available */
|
|
1538
1704
|
if (!doc || !doc.documentElement) {
|
|
1539
|
-
doc = implementation.
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1705
|
+
doc = implementation.createDocument(NAMESPACE, 'template', null);
|
|
1706
|
+
try {
|
|
1707
|
+
doc.documentElement.innerHTML = IS_EMPTY_INPUT
|
|
1708
|
+
? emptyHTML
|
|
1709
|
+
: dirtyPayload;
|
|
1710
|
+
} catch (_) {
|
|
1711
|
+
// Syntax error if dirtyPayload is invalid xml
|
|
1712
|
+
}
|
|
1545
1713
|
}
|
|
1546
|
-
|
|
1714
|
+
const body = doc.body || doc.documentElement;
|
|
1547
1715
|
if (dirty && leadingWhitespace) {
|
|
1548
|
-
|
|
1716
|
+
body.insertBefore(
|
|
1549
1717
|
document.createTextNode(leadingWhitespace),
|
|
1550
|
-
|
|
1718
|
+
body.childNodes[0] || null
|
|
1551
1719
|
);
|
|
1552
1720
|
}
|
|
1553
1721
|
|
|
1554
1722
|
/* Work on whole document or just its body */
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1723
|
+
if (NAMESPACE === HTML_NAMESPACE) {
|
|
1724
|
+
return getElementsByTagName.call(
|
|
1725
|
+
doc,
|
|
1726
|
+
WHOLE_DOCUMENT ? 'html' : 'body'
|
|
1727
|
+
)[0];
|
|
1728
|
+
}
|
|
1729
|
+
return WHOLE_DOCUMENT ? doc.documentElement : body;
|
|
1559
1730
|
};
|
|
1560
1731
|
|
|
1561
1732
|
/**
|
|
1562
|
-
*
|
|
1733
|
+
* Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
|
|
1563
1734
|
*
|
|
1564
|
-
* @param {
|
|
1565
|
-
* @return {
|
|
1735
|
+
* @param {Node} root The root element or node to start traversing on.
|
|
1736
|
+
* @return {NodeIterator} The created NodeIterator
|
|
1566
1737
|
*/
|
|
1567
|
-
|
|
1738
|
+
const _createNodeIterator = function _createNodeIterator(root) {
|
|
1568
1739
|
return createNodeIterator.call(
|
|
1569
1740
|
root.ownerDocument || root,
|
|
1570
1741
|
root,
|
|
1742
|
+
// eslint-disable-next-line no-bitwise
|
|
1571
1743
|
NodeFilter.SHOW_ELEMENT |
|
|
1572
1744
|
NodeFilter.SHOW_COMMENT |
|
|
1573
|
-
NodeFilter.SHOW_TEXT
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
false
|
|
1745
|
+
NodeFilter.SHOW_TEXT |
|
|
1746
|
+
NodeFilter.SHOW_PROCESSING_INSTRUCTION |
|
|
1747
|
+
NodeFilter.SHOW_CDATA_SECTION,
|
|
1748
|
+
null
|
|
1578
1749
|
);
|
|
1579
1750
|
};
|
|
1580
1751
|
|
|
@@ -1584,43 +1755,29 @@ function createDOMPurify() {
|
|
|
1584
1755
|
* @param {Node} elm element to check for clobbering attacks
|
|
1585
1756
|
* @return {Boolean} true if clobbered, false if safe
|
|
1586
1757
|
*/
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
typeof elm.insertBefore !== 'function'
|
|
1601
|
-
) {
|
|
1602
|
-
return true;
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
return false;
|
|
1758
|
+
const _isClobbered = function _isClobbered(elm) {
|
|
1759
|
+
return (
|
|
1760
|
+
elm instanceof HTMLFormElement &&
|
|
1761
|
+
(typeof elm.nodeName !== 'string' ||
|
|
1762
|
+
typeof elm.textContent !== 'string' ||
|
|
1763
|
+
typeof elm.removeChild !== 'function' ||
|
|
1764
|
+
!(elm.attributes instanceof NamedNodeMap) ||
|
|
1765
|
+
typeof elm.removeAttribute !== 'function' ||
|
|
1766
|
+
typeof elm.setAttribute !== 'function' ||
|
|
1767
|
+
typeof elm.namespaceURI !== 'string' ||
|
|
1768
|
+
typeof elm.insertBefore !== 'function' ||
|
|
1769
|
+
typeof elm.hasChildNodes !== 'function')
|
|
1770
|
+
);
|
|
1606
1771
|
};
|
|
1607
1772
|
|
|
1608
1773
|
/**
|
|
1609
|
-
*
|
|
1774
|
+
* Checks whether the given object is a DOM node.
|
|
1610
1775
|
*
|
|
1611
|
-
* @param {Node}
|
|
1776
|
+
* @param {Node} object object to check whether it's a DOM node
|
|
1612
1777
|
* @return {Boolean} true is object is a DOM node
|
|
1613
1778
|
*/
|
|
1614
|
-
|
|
1615
|
-
return
|
|
1616
|
-
'object'
|
|
1617
|
-
? object instanceof Node
|
|
1618
|
-
: object &&
|
|
1619
|
-
(typeof object === 'undefined'
|
|
1620
|
-
? 'undefined'
|
|
1621
|
-
: _typeof(object)) === 'object' &&
|
|
1622
|
-
typeof object.nodeType === 'number' &&
|
|
1623
|
-
typeof object.nodeName === 'string';
|
|
1779
|
+
const _isNode = function _isNode(object) {
|
|
1780
|
+
return typeof Node === 'function' && object instanceof Node;
|
|
1624
1781
|
};
|
|
1625
1782
|
|
|
1626
1783
|
/**
|
|
@@ -1631,12 +1788,11 @@ function createDOMPurify() {
|
|
|
1631
1788
|
* @param {Node} currentNode node to work on with the hook
|
|
1632
1789
|
* @param {Object} data additional hook parameters
|
|
1633
1790
|
*/
|
|
1634
|
-
|
|
1791
|
+
const _executeHook = function _executeHook(entryPoint, currentNode, data) {
|
|
1635
1792
|
if (!hooks[entryPoint]) {
|
|
1636
1793
|
return;
|
|
1637
1794
|
}
|
|
1638
|
-
|
|
1639
|
-
arrayForEach(hooks[entryPoint], function (hook) {
|
|
1795
|
+
arrayForEach(hooks[entryPoint], (hook) => {
|
|
1640
1796
|
hook.call(DOMPurify, currentNode, data, CONFIG);
|
|
1641
1797
|
});
|
|
1642
1798
|
};
|
|
@@ -1651,8 +1807,8 @@ function createDOMPurify() {
|
|
|
1651
1807
|
* @param {Node} currentNode to check for permission to exist
|
|
1652
1808
|
* @return {Boolean} true if node was killed, false if left alive
|
|
1653
1809
|
*/
|
|
1654
|
-
|
|
1655
|
-
|
|
1810
|
+
const _sanitizeElements = function _sanitizeElements(currentNode) {
|
|
1811
|
+
let content = null;
|
|
1656
1812
|
|
|
1657
1813
|
/* Execute a hook if present */
|
|
1658
1814
|
_executeHook('beforeSanitizeElements', currentNode, null);
|
|
@@ -1663,26 +1819,19 @@ function createDOMPurify() {
|
|
|
1663
1819
|
return true;
|
|
1664
1820
|
}
|
|
1665
1821
|
|
|
1666
|
-
/* Check if tagname contains Unicode */
|
|
1667
|
-
if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) {
|
|
1668
|
-
_forceRemove(currentNode);
|
|
1669
|
-
return true;
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
1822
|
/* Now let's check the element's type and name */
|
|
1673
|
-
|
|
1823
|
+
const tagName = transformCaseFunc(currentNode.nodeName);
|
|
1674
1824
|
|
|
1675
1825
|
/* Execute a hook if present */
|
|
1676
1826
|
_executeHook('uponSanitizeElement', currentNode, {
|
|
1677
|
-
tagName
|
|
1827
|
+
tagName,
|
|
1678
1828
|
allowedTags: ALLOWED_TAGS,
|
|
1679
1829
|
});
|
|
1680
1830
|
|
|
1681
1831
|
/* Detect mXSS attempts abusing namespace confusion */
|
|
1682
1832
|
if (
|
|
1833
|
+
currentNode.hasChildNodes() &&
|
|
1683
1834
|
!_isNode(currentNode.firstElementChild) &&
|
|
1684
|
-
(!_isNode(currentNode.content) ||
|
|
1685
|
-
!_isNode(currentNode.content.firstElementChild)) &&
|
|
1686
1835
|
regExpTest(/<[/\w]/g, currentNode.innerHTML) &&
|
|
1687
1836
|
regExpTest(/<[/\w]/g, currentNode.textContent)
|
|
1688
1837
|
) {
|
|
@@ -1690,21 +1839,46 @@ function createDOMPurify() {
|
|
|
1690
1839
|
return true;
|
|
1691
1840
|
}
|
|
1692
1841
|
|
|
1842
|
+
/* Remove any ocurrence of processing instructions */
|
|
1843
|
+
if (currentNode.nodeType === 7) {
|
|
1844
|
+
_forceRemove(currentNode);
|
|
1845
|
+
return true;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1693
1848
|
/* Remove element if anything forbids its presence */
|
|
1694
1849
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1850
|
+
/* Check if we have a custom element to handle */
|
|
1851
|
+
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
|
|
1852
|
+
if (
|
|
1853
|
+
CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp &&
|
|
1854
|
+
regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)
|
|
1855
|
+
) {
|
|
1856
|
+
return false;
|
|
1857
|
+
}
|
|
1858
|
+
if (
|
|
1859
|
+
CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function &&
|
|
1860
|
+
CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)
|
|
1861
|
+
) {
|
|
1862
|
+
return false;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1695
1866
|
/* Keep content except for bad-listed elements */
|
|
1696
1867
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1868
|
+
const parentNode =
|
|
1869
|
+
getParentNode(currentNode) || currentNode.parentNode;
|
|
1870
|
+
const childNodes =
|
|
1871
|
+
getChildNodes(currentNode) || currentNode.childNodes;
|
|
1872
|
+
if (childNodes && parentNode) {
|
|
1873
|
+
const childCount = childNodes.length;
|
|
1874
|
+
for (let i = childCount - 1; i >= 0; --i) {
|
|
1875
|
+
parentNode.insertBefore(
|
|
1876
|
+
cloneNode(childNodes[i], true),
|
|
1877
|
+
getNextSibling(currentNode)
|
|
1878
|
+
);
|
|
1879
|
+
}
|
|
1705
1880
|
}
|
|
1706
1881
|
}
|
|
1707
|
-
|
|
1708
1882
|
_forceRemove(currentNode);
|
|
1709
1883
|
return true;
|
|
1710
1884
|
}
|
|
@@ -1718,9 +1892,12 @@ function createDOMPurify() {
|
|
|
1718
1892
|
return true;
|
|
1719
1893
|
}
|
|
1720
1894
|
|
|
1895
|
+
/* Make sure that older browsers don't get fallback-tag mXSS */
|
|
1721
1896
|
if (
|
|
1722
|
-
(tagName === 'noscript' ||
|
|
1723
|
-
|
|
1897
|
+
(tagName === 'noscript' ||
|
|
1898
|
+
tagName === 'noembed' ||
|
|
1899
|
+
tagName === 'noframes') &&
|
|
1900
|
+
regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)
|
|
1724
1901
|
) {
|
|
1725
1902
|
_forceRemove(currentNode);
|
|
1726
1903
|
return true;
|
|
@@ -1730,8 +1907,9 @@ function createDOMPurify() {
|
|
|
1730
1907
|
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
|
|
1731
1908
|
/* Get the element's text content */
|
|
1732
1909
|
content = currentNode.textContent;
|
|
1733
|
-
|
|
1734
|
-
|
|
1910
|
+
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], (expr) => {
|
|
1911
|
+
content = stringReplace(content, expr, ' ');
|
|
1912
|
+
});
|
|
1735
1913
|
if (currentNode.textContent !== content) {
|
|
1736
1914
|
arrayPush(DOMPurify.removed, {
|
|
1737
1915
|
element: currentNode.cloneNode(),
|
|
@@ -1742,7 +1920,6 @@ function createDOMPurify() {
|
|
|
1742
1920
|
|
|
1743
1921
|
/* Execute a hook if present */
|
|
1744
1922
|
_executeHook('afterSanitizeElements', currentNode, null);
|
|
1745
|
-
|
|
1746
1923
|
return false;
|
|
1747
1924
|
};
|
|
1748
1925
|
|
|
@@ -1755,7 +1932,7 @@ function createDOMPurify() {
|
|
|
1755
1932
|
* @return {Boolean} Returns true if `value` is valid, otherwise false.
|
|
1756
1933
|
*/
|
|
1757
1934
|
// eslint-disable-next-line complexity
|
|
1758
|
-
|
|
1935
|
+
const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
1759
1936
|
/* Make sure attribute cannot clobber */
|
|
1760
1937
|
if (
|
|
1761
1938
|
SANITIZE_DOM &&
|
|
@@ -1769,17 +1946,59 @@ function createDOMPurify() {
|
|
|
1769
1946
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
1770
1947
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
1771
1948
|
We don't need to check the value; it's always URI safe. */
|
|
1772
|
-
if (
|
|
1773
|
-
|
|
1949
|
+
if (
|
|
1950
|
+
ALLOW_DATA_ATTR &&
|
|
1951
|
+
!FORBID_ATTR[lcName] &&
|
|
1952
|
+
regExpTest(DATA_ATTR, lcName)
|
|
1953
|
+
);
|
|
1954
|
+
else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName));
|
|
1774
1955
|
else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
|
|
1775
|
-
|
|
1776
|
-
|
|
1956
|
+
if (
|
|
1957
|
+
// First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
1958
|
+
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1959
|
+
// and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
|
|
1960
|
+
(_isBasicCustomElement(lcTag) &&
|
|
1961
|
+
((CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp &&
|
|
1962
|
+
regExpTest(
|
|
1963
|
+
CUSTOM_ELEMENT_HANDLING.tagNameCheck,
|
|
1964
|
+
lcTag
|
|
1965
|
+
)) ||
|
|
1966
|
+
(CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof
|
|
1967
|
+
Function &&
|
|
1968
|
+
CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag))) &&
|
|
1969
|
+
((CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof
|
|
1970
|
+
RegExp &&
|
|
1971
|
+
regExpTest(
|
|
1972
|
+
CUSTOM_ELEMENT_HANDLING.attributeNameCheck,
|
|
1973
|
+
lcName
|
|
1974
|
+
)) ||
|
|
1975
|
+
(CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof
|
|
1976
|
+
Function &&
|
|
1977
|
+
CUSTOM_ELEMENT_HANDLING.attributeNameCheck(
|
|
1978
|
+
lcName
|
|
1979
|
+
)))) ||
|
|
1980
|
+
// Alternative, second condition checks if it's an `is`-attribute, AND
|
|
1981
|
+
// the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1982
|
+
(lcName === 'is' &&
|
|
1983
|
+
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements &&
|
|
1984
|
+
((CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp &&
|
|
1985
|
+
regExpTest(
|
|
1986
|
+
CUSTOM_ELEMENT_HANDLING.tagNameCheck,
|
|
1987
|
+
value
|
|
1988
|
+
)) ||
|
|
1989
|
+
(CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof
|
|
1990
|
+
Function &&
|
|
1991
|
+
CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))))
|
|
1992
|
+
);
|
|
1993
|
+
else {
|
|
1994
|
+
return false;
|
|
1995
|
+
}
|
|
1777
1996
|
/* Check value is safe. First, is attr inert? If so, is safe */
|
|
1778
1997
|
} else if (URI_SAFE_ATTRIBUTES[lcName]);
|
|
1779
1998
|
else if (
|
|
1780
1999
|
regExpTest(
|
|
1781
|
-
IS_ALLOWED_URI
|
|
1782
|
-
stringReplace(value, ATTR_WHITESPACE
|
|
2000
|
+
IS_ALLOWED_URI$1,
|
|
2001
|
+
stringReplace(value, ATTR_WHITESPACE, '')
|
|
1783
2002
|
)
|
|
1784
2003
|
);
|
|
1785
2004
|
else if (
|
|
@@ -1793,18 +2012,30 @@ function createDOMPurify() {
|
|
|
1793
2012
|
else if (
|
|
1794
2013
|
ALLOW_UNKNOWN_PROTOCOLS &&
|
|
1795
2014
|
!regExpTest(
|
|
1796
|
-
IS_SCRIPT_OR_DATA
|
|
1797
|
-
stringReplace(value, ATTR_WHITESPACE
|
|
2015
|
+
IS_SCRIPT_OR_DATA,
|
|
2016
|
+
stringReplace(value, ATTR_WHITESPACE, '')
|
|
1798
2017
|
)
|
|
1799
2018
|
);
|
|
1800
|
-
else if (
|
|
1801
|
-
else {
|
|
2019
|
+
else if (value) {
|
|
1802
2020
|
return false;
|
|
1803
|
-
}
|
|
1804
|
-
|
|
2021
|
+
} else;
|
|
1805
2022
|
return true;
|
|
1806
2023
|
};
|
|
1807
2024
|
|
|
2025
|
+
/**
|
|
2026
|
+
* _isBasicCustomElement
|
|
2027
|
+
* checks if at least one dash is included in tagName, and it's not the first char
|
|
2028
|
+
* for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
|
|
2029
|
+
*
|
|
2030
|
+
* @param {string} tagName name of the tag of the node to sanitize
|
|
2031
|
+
* @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
|
|
2032
|
+
*/
|
|
2033
|
+
const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
|
|
2034
|
+
return (
|
|
2035
|
+
tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT)
|
|
2036
|
+
);
|
|
2037
|
+
};
|
|
2038
|
+
|
|
1808
2039
|
/**
|
|
1809
2040
|
* _sanitizeAttributes
|
|
1810
2041
|
*
|
|
@@ -1815,39 +2046,29 @@ function createDOMPurify() {
|
|
|
1815
2046
|
*
|
|
1816
2047
|
* @param {Node} currentNode to sanitize
|
|
1817
2048
|
*/
|
|
1818
|
-
|
|
1819
|
-
var attr = void 0;
|
|
1820
|
-
var value = void 0;
|
|
1821
|
-
var lcName = void 0;
|
|
1822
|
-
var l = void 0;
|
|
2049
|
+
const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
1823
2050
|
/* Execute a hook if present */
|
|
1824
2051
|
_executeHook('beforeSanitizeAttributes', currentNode, null);
|
|
1825
|
-
|
|
1826
|
-
var attributes = currentNode.attributes;
|
|
2052
|
+
const { attributes } = currentNode;
|
|
1827
2053
|
|
|
1828
2054
|
/* Check if we have attributes; if not we might have a text node */
|
|
1829
|
-
|
|
1830
2055
|
if (!attributes) {
|
|
1831
2056
|
return;
|
|
1832
2057
|
}
|
|
1833
|
-
|
|
1834
|
-
var hookEvent = {
|
|
2058
|
+
const hookEvent = {
|
|
1835
2059
|
attrName: '',
|
|
1836
2060
|
attrValue: '',
|
|
1837
2061
|
keepAttr: true,
|
|
1838
2062
|
allowedAttributes: ALLOWED_ATTR,
|
|
1839
2063
|
};
|
|
1840
|
-
l = attributes.length;
|
|
2064
|
+
let l = attributes.length;
|
|
1841
2065
|
|
|
1842
2066
|
/* Go backwards over all attributes; safely remove bad ones */
|
|
1843
2067
|
while (l--) {
|
|
1844
|
-
attr = attributes[l];
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
value = stringTrim(attr.value);
|
|
1850
|
-
lcName = stringToLowerCase(name);
|
|
2068
|
+
const attr = attributes[l];
|
|
2069
|
+
const { name, namespaceURI, value: attrValue } = attr;
|
|
2070
|
+
const lcName = transformCaseFunc(name);
|
|
2071
|
+
let value = name === 'value' ? attrValue : stringTrim(attrValue);
|
|
1851
2072
|
|
|
1852
2073
|
/* Execute a hook if present */
|
|
1853
2074
|
hookEvent.attrName = lcName;
|
|
@@ -1870,23 +2091,59 @@ function createDOMPurify() {
|
|
|
1870
2091
|
}
|
|
1871
2092
|
|
|
1872
2093
|
/* Work around a security issue in jQuery 3.0 */
|
|
1873
|
-
if (regExpTest(/\/>/i, value)) {
|
|
2094
|
+
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
|
|
1874
2095
|
_removeAttribute(name, currentNode);
|
|
1875
2096
|
continue;
|
|
1876
2097
|
}
|
|
1877
2098
|
|
|
1878
2099
|
/* Sanitize attribute content to be template-safe */
|
|
1879
2100
|
if (SAFE_FOR_TEMPLATES) {
|
|
1880
|
-
|
|
1881
|
-
|
|
2101
|
+
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], (expr) => {
|
|
2102
|
+
value = stringReplace(value, expr, ' ');
|
|
2103
|
+
});
|
|
1882
2104
|
}
|
|
1883
2105
|
|
|
1884
2106
|
/* Is `value` valid for this attribute? */
|
|
1885
|
-
|
|
2107
|
+
const lcTag = transformCaseFunc(currentNode.nodeName);
|
|
1886
2108
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
1887
2109
|
continue;
|
|
1888
2110
|
}
|
|
1889
2111
|
|
|
2112
|
+
/* Full DOM Clobbering protection via namespace isolation,
|
|
2113
|
+
* Prefix id and name attributes with `user-content-`
|
|
2114
|
+
*/
|
|
2115
|
+
if (
|
|
2116
|
+
SANITIZE_NAMED_PROPS &&
|
|
2117
|
+
(lcName === 'id' || lcName === 'name')
|
|
2118
|
+
) {
|
|
2119
|
+
// Remove the attribute with this value
|
|
2120
|
+
_removeAttribute(name, currentNode);
|
|
2121
|
+
|
|
2122
|
+
// Prefix the value and later re-create the attribute with the sanitized value
|
|
2123
|
+
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
/* Handle attributes that require Trusted Types */
|
|
2127
|
+
if (
|
|
2128
|
+
trustedTypesPolicy &&
|
|
2129
|
+
typeof trustedTypes === 'object' &&
|
|
2130
|
+
typeof trustedTypes.getAttributeType === 'function'
|
|
2131
|
+
) {
|
|
2132
|
+
if (namespaceURI);
|
|
2133
|
+
else {
|
|
2134
|
+
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
|
|
2135
|
+
case 'TrustedHTML': {
|
|
2136
|
+
value = trustedTypesPolicy.createHTML(value);
|
|
2137
|
+
break;
|
|
2138
|
+
}
|
|
2139
|
+
case 'TrustedScriptURL': {
|
|
2140
|
+
value = trustedTypesPolicy.createScriptURL(value);
|
|
2141
|
+
break;
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
|
|
1890
2147
|
/* Handle invalid data-* attribute set by try-catching it */
|
|
1891
2148
|
try {
|
|
1892
2149
|
if (namespaceURI) {
|
|
@@ -1895,7 +2152,6 @@ function createDOMPurify() {
|
|
|
1895
2152
|
/* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
|
|
1896
2153
|
currentNode.setAttribute(name, value);
|
|
1897
2154
|
}
|
|
1898
|
-
|
|
1899
2155
|
arrayPop(DOMPurify.removed);
|
|
1900
2156
|
} catch (_) {}
|
|
1901
2157
|
}
|
|
@@ -1909,13 +2165,12 @@ function createDOMPurify() {
|
|
|
1909
2165
|
*
|
|
1910
2166
|
* @param {DocumentFragment} fragment to iterate over recursively
|
|
1911
2167
|
*/
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
2168
|
+
const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
|
|
2169
|
+
let shadowNode = null;
|
|
2170
|
+
const shadowIterator = _createNodeIterator(fragment);
|
|
1915
2171
|
|
|
1916
2172
|
/* Execute a hook if present */
|
|
1917
2173
|
_executeHook('beforeSanitizeShadowDOM', fragment, null);
|
|
1918
|
-
|
|
1919
2174
|
while ((shadowNode = shadowIterator.nextNode())) {
|
|
1920
2175
|
/* Execute a hook if present */
|
|
1921
2176
|
_executeHook('uponSanitizeShadowNode', shadowNode, null);
|
|
@@ -1943,50 +2198,40 @@ function createDOMPurify() {
|
|
|
1943
2198
|
* Public method providing core sanitation functionality
|
|
1944
2199
|
*
|
|
1945
2200
|
* @param {String|Node} dirty string or DOM node
|
|
1946
|
-
* @param {Object}
|
|
2201
|
+
* @param {Object} cfg object
|
|
1947
2202
|
*/
|
|
1948
2203
|
// eslint-disable-next-line complexity
|
|
1949
|
-
DOMPurify.sanitize = function (dirty
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
2204
|
+
DOMPurify.sanitize = function (dirty) {
|
|
2205
|
+
let cfg =
|
|
2206
|
+
arguments.length > 1 && arguments[1] !== undefined
|
|
2207
|
+
? arguments[1]
|
|
2208
|
+
: {};
|
|
2209
|
+
let body = null;
|
|
2210
|
+
let importedNode = null;
|
|
2211
|
+
let currentNode = null;
|
|
2212
|
+
let returnNode = null;
|
|
1955
2213
|
/* Make sure we have a string to sanitize.
|
|
1956
2214
|
DO NOT return early, as this will return the wrong type if
|
|
1957
2215
|
the user has requested a DOM object rather than a string */
|
|
1958
|
-
|
|
2216
|
+
IS_EMPTY_INPUT = !dirty;
|
|
2217
|
+
if (IS_EMPTY_INPUT) {
|
|
1959
2218
|
dirty = '<!-->';
|
|
1960
2219
|
}
|
|
1961
2220
|
|
|
1962
2221
|
/* Stringify, in case dirty is an object */
|
|
1963
2222
|
if (typeof dirty !== 'string' && !_isNode(dirty)) {
|
|
1964
|
-
|
|
1965
|
-
if (typeof dirty.toString !== 'function') {
|
|
1966
|
-
throw typeErrorCreate('toString is not a function');
|
|
1967
|
-
} else {
|
|
2223
|
+
if (typeof dirty.toString === 'function') {
|
|
1968
2224
|
dirty = dirty.toString();
|
|
1969
2225
|
if (typeof dirty !== 'string') {
|
|
1970
2226
|
throw typeErrorCreate('dirty is not a string, aborting');
|
|
1971
2227
|
}
|
|
2228
|
+
} else {
|
|
2229
|
+
throw typeErrorCreate('toString is not a function');
|
|
1972
2230
|
}
|
|
1973
2231
|
}
|
|
1974
2232
|
|
|
1975
|
-
/*
|
|
2233
|
+
/* Return dirty HTML if DOMPurify cannot run */
|
|
1976
2234
|
if (!DOMPurify.isSupported) {
|
|
1977
|
-
if (
|
|
1978
|
-
_typeof(window.toStaticHTML) === 'object' ||
|
|
1979
|
-
typeof window.toStaticHTML === 'function'
|
|
1980
|
-
) {
|
|
1981
|
-
if (typeof dirty === 'string') {
|
|
1982
|
-
return window.toStaticHTML(dirty);
|
|
1983
|
-
}
|
|
1984
|
-
|
|
1985
|
-
if (_isNode(dirty)) {
|
|
1986
|
-
return window.toStaticHTML(dirty.outerHTML);
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
|
|
1990
2235
|
return dirty;
|
|
1991
2236
|
}
|
|
1992
2237
|
|
|
@@ -2002,11 +2247,19 @@ function createDOMPurify() {
|
|
|
2002
2247
|
if (typeof dirty === 'string') {
|
|
2003
2248
|
IN_PLACE = false;
|
|
2004
2249
|
}
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2250
|
+
if (IN_PLACE) {
|
|
2251
|
+
/* Do some early pre-sanitization to avoid unsafe root nodes */
|
|
2252
|
+
if (dirty.nodeName) {
|
|
2253
|
+
const tagName = transformCaseFunc(dirty.nodeName);
|
|
2254
|
+
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
2255
|
+
throw typeErrorCreate(
|
|
2256
|
+
'root node is forbidden and cannot be sanitized in-place'
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
} else if (dirty instanceof Node) {
|
|
2008
2261
|
/* If dirty is a DOM element, append to an empty document to avoid
|
|
2009
|
-
|
|
2262
|
+
elements being stripped by the parser */
|
|
2010
2263
|
body = _initDocument('<!---->');
|
|
2011
2264
|
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
2012
2265
|
if (
|
|
@@ -2018,7 +2271,7 @@ function createDOMPurify() {
|
|
|
2018
2271
|
} else if (importedNode.nodeName === 'HTML') {
|
|
2019
2272
|
body = importedNode;
|
|
2020
2273
|
} else {
|
|
2021
|
-
// eslint-disable-next-line unicorn/prefer-node-append
|
|
2274
|
+
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
2022
2275
|
body.appendChild(importedNode);
|
|
2023
2276
|
}
|
|
2024
2277
|
} else {
|
|
@@ -2040,7 +2293,7 @@ function createDOMPurify() {
|
|
|
2040
2293
|
|
|
2041
2294
|
/* Check we have a DOM node from the data */
|
|
2042
2295
|
if (!body) {
|
|
2043
|
-
return RETURN_DOM ? null : emptyHTML;
|
|
2296
|
+
return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
|
|
2044
2297
|
}
|
|
2045
2298
|
}
|
|
2046
2299
|
|
|
@@ -2050,15 +2303,10 @@ function createDOMPurify() {
|
|
|
2050
2303
|
}
|
|
2051
2304
|
|
|
2052
2305
|
/* Get node iterator */
|
|
2053
|
-
|
|
2306
|
+
const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
|
|
2054
2307
|
|
|
2055
2308
|
/* Now start iterating over the created document */
|
|
2056
2309
|
while ((currentNode = nodeIterator.nextNode())) {
|
|
2057
|
-
/* Fix IE's strange behavior with manipulated textNodes #89 */
|
|
2058
|
-
if (currentNode.nodeType === 3 && currentNode === oldNode) {
|
|
2059
|
-
continue;
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2062
2310
|
/* Sanitize tags and elements */
|
|
2063
2311
|
if (_sanitizeElements(currentNode)) {
|
|
2064
2312
|
continue;
|
|
@@ -2071,12 +2319,8 @@ function createDOMPurify() {
|
|
|
2071
2319
|
|
|
2072
2320
|
/* Check attributes, sanitize if necessary */
|
|
2073
2321
|
_sanitizeAttributes(currentNode);
|
|
2074
|
-
|
|
2075
|
-
oldNode = currentNode;
|
|
2076
2322
|
}
|
|
2077
2323
|
|
|
2078
|
-
oldNode = null;
|
|
2079
|
-
|
|
2080
2324
|
/* If we sanitized `dirty` in-place, return it. */
|
|
2081
2325
|
if (IN_PLACE) {
|
|
2082
2326
|
return dirty;
|
|
@@ -2086,45 +2330,53 @@ function createDOMPurify() {
|
|
|
2086
2330
|
if (RETURN_DOM) {
|
|
2087
2331
|
if (RETURN_DOM_FRAGMENT) {
|
|
2088
2332
|
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
2089
|
-
|
|
2090
2333
|
while (body.firstChild) {
|
|
2091
|
-
// eslint-disable-next-line unicorn/prefer-node-append
|
|
2334
|
+
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
2092
2335
|
returnNode.appendChild(body.firstChild);
|
|
2093
2336
|
}
|
|
2094
2337
|
} else {
|
|
2095
2338
|
returnNode = body;
|
|
2096
2339
|
}
|
|
2097
|
-
|
|
2098
|
-
if (RETURN_DOM_IMPORT) {
|
|
2340
|
+
if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
|
|
2099
2341
|
/*
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2342
|
+
AdoptNode() is not used because internal state is not reset
|
|
2343
|
+
(e.g. the past names map of a HTMLFormElement), this is safe
|
|
2344
|
+
in theory but we would rather not risk another attack vector.
|
|
2345
|
+
The state that is cloned by importNode() is explicitly defined
|
|
2346
|
+
by the specs.
|
|
2347
|
+
*/
|
|
2106
2348
|
returnNode = importNode.call(
|
|
2107
2349
|
originalDocument,
|
|
2108
2350
|
returnNode,
|
|
2109
2351
|
true
|
|
2110
2352
|
);
|
|
2111
2353
|
}
|
|
2112
|
-
|
|
2113
2354
|
return returnNode;
|
|
2114
2355
|
}
|
|
2356
|
+
let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
|
|
2115
2357
|
|
|
2116
|
-
|
|
2358
|
+
/* Serialize doctype if allowed */
|
|
2359
|
+
if (
|
|
2360
|
+
WHOLE_DOCUMENT &&
|
|
2361
|
+
ALLOWED_TAGS['!doctype'] &&
|
|
2362
|
+
body.ownerDocument &&
|
|
2363
|
+
body.ownerDocument.doctype &&
|
|
2364
|
+
body.ownerDocument.doctype.name &&
|
|
2365
|
+
regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)
|
|
2366
|
+
) {
|
|
2367
|
+
serializedHTML =
|
|
2368
|
+
'<!DOCTYPE ' +
|
|
2369
|
+
body.ownerDocument.doctype.name +
|
|
2370
|
+
'>\n' +
|
|
2371
|
+
serializedHTML;
|
|
2372
|
+
}
|
|
2117
2373
|
|
|
2118
2374
|
/* Sanitize final string template-safe */
|
|
2119
2375
|
if (SAFE_FOR_TEMPLATES) {
|
|
2120
|
-
|
|
2121
|
-
serializedHTML,
|
|
2122
|
-
|
|
2123
|
-
' '
|
|
2124
|
-
);
|
|
2125
|
-
serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');
|
|
2376
|
+
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], (expr) => {
|
|
2377
|
+
serializedHTML = stringReplace(serializedHTML, expr, ' ');
|
|
2378
|
+
});
|
|
2126
2379
|
}
|
|
2127
|
-
|
|
2128
2380
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE
|
|
2129
2381
|
? trustedTypesPolicy.createHTML(serializedHTML)
|
|
2130
2382
|
: serializedHTML;
|
|
@@ -2136,7 +2388,11 @@ function createDOMPurify() {
|
|
|
2136
2388
|
*
|
|
2137
2389
|
* @param {Object} cfg configuration object
|
|
2138
2390
|
*/
|
|
2139
|
-
DOMPurify.setConfig = function (
|
|
2391
|
+
DOMPurify.setConfig = function () {
|
|
2392
|
+
let cfg =
|
|
2393
|
+
arguments.length > 0 && arguments[0] !== undefined
|
|
2394
|
+
? arguments[0]
|
|
2395
|
+
: {};
|
|
2140
2396
|
_parseConfig(cfg);
|
|
2141
2397
|
SET_CONFIG = true;
|
|
2142
2398
|
};
|
|
@@ -2156,9 +2412,9 @@ function createDOMPurify() {
|
|
|
2156
2412
|
* Uses last set config, if any. Otherwise, uses config defaults.
|
|
2157
2413
|
* isValidAttribute
|
|
2158
2414
|
*
|
|
2159
|
-
* @param {
|
|
2160
|
-
* @param {
|
|
2161
|
-
* @param {
|
|
2415
|
+
* @param {String} tag Tag name of containing element.
|
|
2416
|
+
* @param {String} attr Attribute name.
|
|
2417
|
+
* @param {String} value Attribute value.
|
|
2162
2418
|
* @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
|
|
2163
2419
|
*/
|
|
2164
2420
|
DOMPurify.isValidAttribute = function (tag, attr, value) {
|
|
@@ -2166,9 +2422,8 @@ function createDOMPurify() {
|
|
|
2166
2422
|
if (!CONFIG) {
|
|
2167
2423
|
_parseConfig({});
|
|
2168
2424
|
}
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
var lcName = stringToLowerCase(attr);
|
|
2425
|
+
const lcTag = transformCaseFunc(tag);
|
|
2426
|
+
const lcName = transformCaseFunc(attr);
|
|
2172
2427
|
return _isValidAttribute(lcTag, lcName, value);
|
|
2173
2428
|
};
|
|
2174
2429
|
|
|
@@ -2183,7 +2438,6 @@ function createDOMPurify() {
|
|
|
2183
2438
|
if (typeof hookFunction !== 'function') {
|
|
2184
2439
|
return;
|
|
2185
2440
|
}
|
|
2186
|
-
|
|
2187
2441
|
hooks[entryPoint] = hooks[entryPoint] || [];
|
|
2188
2442
|
arrayPush(hooks[entryPoint], hookFunction);
|
|
2189
2443
|
};
|
|
@@ -2194,10 +2448,11 @@ function createDOMPurify() {
|
|
|
2194
2448
|
* (pops it from the stack of hooks if more are present)
|
|
2195
2449
|
*
|
|
2196
2450
|
* @param {String} entryPoint entry point for the hook to remove
|
|
2451
|
+
* @return {Function} removed(popped) hook
|
|
2197
2452
|
*/
|
|
2198
2453
|
DOMPurify.removeHook = function (entryPoint) {
|
|
2199
2454
|
if (hooks[entryPoint]) {
|
|
2200
|
-
arrayPop(hooks[entryPoint]);
|
|
2455
|
+
return arrayPop(hooks[entryPoint]);
|
|
2201
2456
|
}
|
|
2202
2457
|
};
|
|
2203
2458
|
|
|
@@ -2216,15 +2471,12 @@ function createDOMPurify() {
|
|
|
2216
2471
|
/**
|
|
2217
2472
|
* RemoveAllHooks
|
|
2218
2473
|
* Public method to remove all DOMPurify hooks
|
|
2219
|
-
*
|
|
2220
2474
|
*/
|
|
2221
2475
|
DOMPurify.removeAllHooks = function () {
|
|
2222
2476
|
hooks = {};
|
|
2223
2477
|
};
|
|
2224
|
-
|
|
2225
2478
|
return DOMPurify;
|
|
2226
2479
|
}
|
|
2227
|
-
|
|
2228
2480
|
var purify = createDOMPurify();
|
|
2229
2481
|
|
|
2230
2482
|
export default purify;
|