dompurify 3.0.1 → 3.2.7

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/dist/purify.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @license DOMPurify 3.0.1 | (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.1/LICENSE */
1
+ /*! @license DOMPurify 3.2.7 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.7/LICENSE */
2
2
 
3
3
  (function (global, factory) {
4
4
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -6,411 +6,284 @@
6
6
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.DOMPurify = factory());
7
7
  })(this, (function () { 'use strict';
8
8
 
9
- function _typeof(obj) {
10
- "@babel/helpers - typeof";
11
-
12
- return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
13
- return typeof obj;
14
- } : function (obj) {
15
- return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
16
- }, _typeof(obj);
17
- }
18
-
19
- function _setPrototypeOf(o, p) {
20
- _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
21
- o.__proto__ = p;
22
- return o;
23
- };
24
-
25
- return _setPrototypeOf(o, p);
26
- }
27
-
28
- function _isNativeReflectConstruct() {
29
- if (typeof Reflect === "undefined" || !Reflect.construct) return false;
30
- if (Reflect.construct.sham) return false;
31
- if (typeof Proxy === "function") return true;
32
-
33
- try {
34
- Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
35
- return true;
36
- } catch (e) {
37
- return false;
38
- }
39
- }
40
-
41
- function _construct(Parent, args, Class) {
42
- if (_isNativeReflectConstruct()) {
43
- _construct = Reflect.construct;
44
- } else {
45
- _construct = function _construct(Parent, args, Class) {
46
- var a = [null];
47
- a.push.apply(a, args);
48
- var Constructor = Function.bind.apply(Parent, a);
49
- var instance = new Constructor();
50
- if (Class) _setPrototypeOf(instance, Class.prototype);
51
- return instance;
52
- };
53
- }
54
-
55
- return _construct.apply(null, arguments);
56
- }
57
-
58
- function _slicedToArray(arr, i) {
59
- return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
60
- }
61
-
62
- function _toConsumableArray(arr) {
63
- return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
64
- }
65
-
66
- function _arrayWithoutHoles(arr) {
67
- if (Array.isArray(arr)) return _arrayLikeToArray(arr);
68
- }
69
-
70
- function _arrayWithHoles(arr) {
71
- if (Array.isArray(arr)) return arr;
72
- }
73
-
74
- function _iterableToArray(iter) {
75
- if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
76
- }
77
-
78
- function _iterableToArrayLimit(arr, i) {
79
- var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
80
-
81
- if (_i == null) return;
82
- var _arr = [];
83
- var _n = true;
84
- var _d = false;
85
-
86
- var _s, _e;
87
-
88
- try {
89
- for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
90
- _arr.push(_s.value);
91
-
92
- if (i && _arr.length === i) break;
93
- }
94
- } catch (err) {
95
- _d = true;
96
- _e = err;
97
- } finally {
98
- try {
99
- if (!_n && _i["return"] != null) _i["return"]();
100
- } finally {
101
- if (_d) throw _e;
102
- }
103
- }
104
-
105
- return _arr;
106
- }
107
-
108
- function _unsupportedIterableToArray(o, minLen) {
109
- if (!o) return;
110
- if (typeof o === "string") return _arrayLikeToArray(o, minLen);
111
- var n = Object.prototype.toString.call(o).slice(8, -1);
112
- if (n === "Object" && o.constructor) n = o.constructor.name;
113
- if (n === "Map" || n === "Set") return Array.from(o);
114
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
115
- }
116
-
117
- function _arrayLikeToArray(arr, len) {
118
- if (len == null || len > arr.length) len = arr.length;
119
-
120
- for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
121
-
122
- return arr2;
123
- }
124
-
125
- function _nonIterableSpread() {
126
- throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
127
- }
128
-
129
- function _nonIterableRest() {
130
- throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
131
- }
132
-
133
- function _createForOfIteratorHelper(o, allowArrayLike) {
134
- var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
135
-
136
- if (!it) {
137
- if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
138
- if (it) o = it;
139
- var i = 0;
140
-
141
- var F = function () {};
142
-
143
- return {
144
- s: F,
145
- n: function () {
146
- if (i >= o.length) return {
147
- done: true
148
- };
149
- return {
150
- done: false,
151
- value: o[i++]
152
- };
153
- },
154
- e: function (e) {
155
- throw e;
156
- },
157
- f: F
158
- };
159
- }
160
-
161
- throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
162
- }
163
-
164
- var normalCompletion = true,
165
- didErr = false,
166
- err;
167
- return {
168
- s: function () {
169
- it = it.call(o);
170
- },
171
- n: function () {
172
- var step = it.next();
173
- normalCompletion = step.done;
174
- return step;
175
- },
176
- e: function (e) {
177
- didErr = true;
178
- err = e;
179
- },
180
- f: function () {
181
- try {
182
- if (!normalCompletion && it.return != null) it.return();
183
- } finally {
184
- if (didErr) throw err;
185
- }
186
- }
187
- };
188
- }
189
-
190
- var entries = Object.entries,
191
- setPrototypeOf = Object.setPrototypeOf,
192
- isFrozen = Object.isFrozen,
193
- getPrototypeOf = Object.getPrototypeOf,
194
- getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
195
- var freeze = Object.freeze,
196
- seal = Object.seal,
197
- create = Object.create; // eslint-disable-line import/no-mutable-exports
198
-
199
- var _ref = typeof Reflect !== 'undefined' && Reflect,
200
- apply = _ref.apply,
201
- construct = _ref.construct;
202
-
203
- if (!apply) {
204
- apply = function apply(fun, thisValue, args) {
205
- return fun.apply(thisValue, args);
206
- };
207
- }
208
-
9
+ const {
10
+ entries,
11
+ setPrototypeOf,
12
+ isFrozen,
13
+ getPrototypeOf,
14
+ getOwnPropertyDescriptor
15
+ } = Object;
16
+ let {
17
+ freeze,
18
+ seal,
19
+ create
20
+ } = Object; // eslint-disable-line import/no-mutable-exports
21
+ let {
22
+ apply,
23
+ construct
24
+ } = typeof Reflect !== 'undefined' && Reflect;
209
25
  if (!freeze) {
210
26
  freeze = function freeze(x) {
211
27
  return x;
212
28
  };
213
29
  }
214
-
215
30
  if (!seal) {
216
31
  seal = function seal(x) {
217
32
  return x;
218
33
  };
219
34
  }
220
-
35
+ if (!apply) {
36
+ apply = function apply(func, thisArg) {
37
+ for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
38
+ args[_key - 2] = arguments[_key];
39
+ }
40
+ return func.apply(thisArg, args);
41
+ };
42
+ }
221
43
  if (!construct) {
222
- construct = function construct(Func, args) {
223
- return _construct(Func, _toConsumableArray(args));
44
+ construct = function construct(Func) {
45
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
46
+ args[_key2 - 1] = arguments[_key2];
47
+ }
48
+ return new Func(...args);
224
49
  };
225
50
  }
226
-
227
- var arrayForEach = unapply(Array.prototype.forEach);
228
- var arrayPop = unapply(Array.prototype.pop);
229
- var arrayPush = unapply(Array.prototype.push);
230
- var stringToLowerCase = unapply(String.prototype.toLowerCase);
231
- var stringToString = unapply(String.prototype.toString);
232
- var stringMatch = unapply(String.prototype.match);
233
- var stringReplace = unapply(String.prototype.replace);
234
- var stringIndexOf = unapply(String.prototype.indexOf);
235
- var stringTrim = unapply(String.prototype.trim);
236
- var regExpTest = unapply(RegExp.prototype.test);
237
- var typeErrorCreate = unconstruct(TypeError);
51
+ const arrayForEach = unapply(Array.prototype.forEach);
52
+ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
53
+ const arrayPop = unapply(Array.prototype.pop);
54
+ const arrayPush = unapply(Array.prototype.push);
55
+ const arraySplice = unapply(Array.prototype.splice);
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);
65
+ /**
66
+ * Creates a new function that calls the given function with a specified thisArg and arguments.
67
+ *
68
+ * @param func - The function to be wrapped and called.
69
+ * @returns A new function that calls the given function with a specified thisArg and arguments.
70
+ */
238
71
  function unapply(func) {
239
72
  return function (thisArg) {
240
- for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
241
- args[_key - 1] = arguments[_key];
73
+ if (thisArg instanceof RegExp) {
74
+ thisArg.lastIndex = 0;
75
+ }
76
+ for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
77
+ args[_key3 - 1] = arguments[_key3];
242
78
  }
243
-
244
79
  return apply(func, thisArg, args);
245
80
  };
246
81
  }
247
- function unconstruct(func) {
82
+ /**
83
+ * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
84
+ *
85
+ * @param func - The constructor function to be wrapped and called.
86
+ * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
87
+ */
88
+ function unconstruct(Func) {
248
89
  return function () {
249
- for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
250
- args[_key2] = arguments[_key2];
90
+ for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
91
+ args[_key4] = arguments[_key4];
251
92
  }
252
-
253
- return construct(func, args);
93
+ return construct(Func, args);
254
94
  };
255
95
  }
256
- /* Add properties to a lookup table */
257
-
258
- function addToSet(set, array, transformCaseFunc) {
259
- transformCaseFunc = transformCaseFunc ? transformCaseFunc : stringToLowerCase;
260
-
96
+ /**
97
+ * Add properties to a lookup table
98
+ *
99
+ * @param set - The set to which elements will be added.
100
+ * @param array - The array containing elements to be added to the set.
101
+ * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
102
+ * @returns The modified set with added elements.
103
+ */
104
+ function addToSet(set, array) {
105
+ let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
261
106
  if (setPrototypeOf) {
262
107
  // Make 'in' and truthy checks like Boolean(set.constructor)
263
108
  // independent of any properties defined on Object.prototype.
264
109
  // Prevent prototype setters from intercepting set as a this value.
265
110
  setPrototypeOf(set, null);
266
111
  }
267
-
268
- var l = array.length;
269
-
112
+ let l = array.length;
270
113
  while (l--) {
271
- var element = array[l];
272
-
114
+ let element = array[l];
273
115
  if (typeof element === 'string') {
274
- var lcElement = transformCaseFunc(element);
275
-
116
+ const lcElement = transformCaseFunc(element);
276
117
  if (lcElement !== element) {
277
118
  // Config presets (e.g. tags.js, attrs.js) are immutable.
278
119
  if (!isFrozen(array)) {
279
120
  array[l] = lcElement;
280
121
  }
281
-
282
122
  element = lcElement;
283
123
  }
284
124
  }
285
-
286
125
  set[element] = true;
287
126
  }
288
-
289
127
  return set;
290
128
  }
291
- /* Shallow clone an object */
292
-
129
+ /**
130
+ * Clean up an array to harden against CSPP
131
+ *
132
+ * @param array - The array to be cleaned.
133
+ * @returns The cleaned version of the array
134
+ */
135
+ function cleanArray(array) {
136
+ for (let index = 0; index < array.length; index++) {
137
+ const isPropertyExist = objectHasOwnProperty(array, index);
138
+ if (!isPropertyExist) {
139
+ array[index] = null;
140
+ }
141
+ }
142
+ return array;
143
+ }
144
+ /**
145
+ * Shallow clone an object
146
+ *
147
+ * @param object - The object to be cloned.
148
+ * @returns A new object that copies the original.
149
+ */
293
150
  function clone(object) {
294
- var newObject = create(null);
295
-
296
- var _iterator = _createForOfIteratorHelper(entries(object)),
297
- _step;
298
-
299
- try {
300
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
301
- var _step$value = _slicedToArray(_step.value, 2),
302
- property = _step$value[0],
303
- value = _step$value[1];
304
-
305
- newObject[property] = value;
151
+ const newObject = create(null);
152
+ for (const [property, value] of entries(object)) {
153
+ const isPropertyExist = objectHasOwnProperty(object, property);
154
+ if (isPropertyExist) {
155
+ if (Array.isArray(value)) {
156
+ newObject[property] = cleanArray(value);
157
+ } else if (value && typeof value === 'object' && value.constructor === Object) {
158
+ newObject[property] = clone(value);
159
+ } else {
160
+ newObject[property] = value;
161
+ }
306
162
  }
307
- } catch (err) {
308
- _iterator.e(err);
309
- } finally {
310
- _iterator.f();
311
163
  }
312
-
313
164
  return newObject;
314
165
  }
315
- /* This method automatically checks if the prop is function
316
- * or getter and behaves accordingly. */
317
-
166
+ /**
167
+ * This method automatically checks if the prop is function or getter and behaves accordingly.
168
+ *
169
+ * @param object - The object to look up the getter function in its prototype chain.
170
+ * @param prop - The property name for which to find the getter function.
171
+ * @returns The getter function found in the prototype chain or a fallback function.
172
+ */
318
173
  function lookupGetter(object, prop) {
319
174
  while (object !== null) {
320
- var desc = getOwnPropertyDescriptor(object, prop);
321
-
175
+ const desc = getOwnPropertyDescriptor(object, prop);
322
176
  if (desc) {
323
177
  if (desc.get) {
324
178
  return unapply(desc.get);
325
179
  }
326
-
327
180
  if (typeof desc.value === 'function') {
328
181
  return unapply(desc.value);
329
182
  }
330
183
  }
331
-
332
184
  object = getPrototypeOf(object);
333
185
  }
334
-
335
- function fallbackValue(element) {
336
- console.warn('fallback value for', element);
186
+ function fallbackValue() {
337
187
  return null;
338
188
  }
339
-
340
189
  return fallbackValue;
341
190
  }
342
191
 
343
- var html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); // SVG
344
-
345
- var svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
346
- var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']); // List of SVG elements that are disallowed by default.
192
+ const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
193
+ const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'slot', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
194
+ const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
195
+ // List of SVG elements that are disallowed by default.
347
196
  // We still need to know them so that we can do namespace
348
197
  // checks properly in case one wants to add them to
349
198
  // allow-list.
350
-
351
- var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
352
- var mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover']); // Similarly to SVG, we want to know all MathML elements,
199
+ const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
200
+ const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);
201
+ // Similarly to SVG, we want to know all MathML elements,
353
202
  // even those that we disallow by default.
354
-
355
- var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
356
- var text = freeze(['#text']);
357
-
358
- var html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']);
359
- var svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
360
- var mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
361
- var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
362
-
363
- var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
364
-
365
- var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
366
- var TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
367
- var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
368
-
369
- var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
370
-
371
- var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
203
+ const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
204
+ const text = freeze(['#text']);
205
+
206
+ const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
207
+ const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
208
+ const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
209
+ const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
210
+
211
+ // eslint-disable-next-line unicorn/better-regex
212
+ const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
213
+ const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
214
+ const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
215
+ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
216
+ const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
217
+ const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
372
218
  );
373
- var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
374
- var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
219
+ const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
220
+ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
375
221
  );
376
- var DOCTYPE_NAME = seal(/^html$/i);
377
-
378
- var getGlobal = function getGlobal() {
222
+ const DOCTYPE_NAME = seal(/^html$/i);
223
+ const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
224
+
225
+ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
226
+ __proto__: null,
227
+ ARIA_ATTR: ARIA_ATTR,
228
+ ATTR_WHITESPACE: ATTR_WHITESPACE,
229
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT,
230
+ DATA_ATTR: DATA_ATTR,
231
+ DOCTYPE_NAME: DOCTYPE_NAME,
232
+ ERB_EXPR: ERB_EXPR,
233
+ IS_ALLOWED_URI: IS_ALLOWED_URI,
234
+ IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
235
+ MUSTACHE_EXPR: MUSTACHE_EXPR,
236
+ TMPLIT_EXPR: TMPLIT_EXPR
237
+ });
238
+
239
+ /* eslint-disable @typescript-eslint/indent */
240
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
241
+ const NODE_TYPE = {
242
+ element: 1,
243
+ attribute: 2,
244
+ text: 3,
245
+ cdataSection: 4,
246
+ entityReference: 5,
247
+ // Deprecated
248
+ entityNode: 6,
249
+ // Deprecated
250
+ progressingInstruction: 7,
251
+ comment: 8,
252
+ document: 9,
253
+ documentType: 10,
254
+ documentFragment: 11,
255
+ notation: 12 // Deprecated
256
+ };
257
+ const getGlobal = function getGlobal() {
379
258
  return typeof window === 'undefined' ? null : window;
380
259
  };
381
260
  /**
382
261
  * Creates a no-op policy for internal use only.
383
262
  * Don't export this function outside this module!
384
- * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
385
- * @param {Document} document The document object (to determine policy name suffix)
386
- * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
387
- * are not supported).
263
+ * @param trustedTypes The policy factory.
264
+ * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
265
+ * @return The policy created (or null, if Trusted Types
266
+ * are not supported or creating the policy failed).
388
267
  */
389
-
390
-
391
- var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
392
- if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
268
+ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
269
+ if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
393
270
  return null;
394
- } // Allow the callers to control the unique policy name
271
+ }
272
+ // Allow the callers to control the unique policy name
395
273
  // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
396
274
  // Policy creation with duplicate names throws in Trusted Types.
397
-
398
-
399
- var suffix = null;
400
- var ATTR_NAME = 'data-tt-policy-suffix';
401
-
402
- if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
403
- suffix = document.currentScript.getAttribute(ATTR_NAME);
275
+ let suffix = null;
276
+ const ATTR_NAME = 'data-tt-policy-suffix';
277
+ if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
278
+ suffix = purifyHostElement.getAttribute(ATTR_NAME);
404
279
  }
405
-
406
- var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
407
-
280
+ const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
408
281
  try {
409
282
  return trustedTypes.createPolicy(policyName, {
410
- createHTML: function createHTML(html) {
283
+ createHTML(html) {
411
284
  return html;
412
285
  },
413
- createScriptURL: function createScriptURL(scriptUrl) {
286
+ createScriptURL(scriptUrl) {
414
287
  return scriptUrl;
415
288
  }
416
289
  });
@@ -422,109 +295,110 @@
422
295
  return null;
423
296
  }
424
297
  };
425
-
426
- function createDOMPurify() {
427
- var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
428
-
429
- var DOMPurify = function DOMPurify(root) {
430
- return createDOMPurify(root);
298
+ const _createHooksMap = function _createHooksMap() {
299
+ return {
300
+ afterSanitizeAttributes: [],
301
+ afterSanitizeElements: [],
302
+ afterSanitizeShadowDOM: [],
303
+ beforeSanitizeAttributes: [],
304
+ beforeSanitizeElements: [],
305
+ beforeSanitizeShadowDOM: [],
306
+ uponSanitizeAttribute: [],
307
+ uponSanitizeElement: [],
308
+ uponSanitizeShadowNode: []
431
309
  };
432
- /**
433
- * Version label, exposed for easier checks
434
- * if DOMPurify is up to date or not
435
- */
436
-
437
-
438
- DOMPurify.version = '3.0.1';
439
- /**
440
- * Array of elements that DOMPurify removed during sanitation.
441
- * Empty if nothing was removed.
442
- */
443
-
310
+ };
311
+ function createDOMPurify() {
312
+ let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
313
+ const DOMPurify = root => createDOMPurify(root);
314
+ DOMPurify.version = '3.2.7';
444
315
  DOMPurify.removed = [];
445
-
446
- if (!window || !window.document || window.document.nodeType !== 9) {
316
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
447
317
  // Not running in a browser, provide a factory function
448
318
  // so that you can pass your own Window
449
319
  DOMPurify.isSupported = false;
450
320
  return DOMPurify;
451
321
  }
452
-
453
- var originalDocument = window.document;
454
- var document = window.document;
455
- var DocumentFragment = window.DocumentFragment,
456
- HTMLTemplateElement = window.HTMLTemplateElement,
457
- Node = window.Node,
458
- Element = window.Element,
459
- NodeFilter = window.NodeFilter,
460
- _window$NamedNodeMap = window.NamedNodeMap,
461
- NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
462
- HTMLFormElement = window.HTMLFormElement,
463
- DOMParser = window.DOMParser,
464
- trustedTypes = window.trustedTypes;
465
- var ElementPrototype = Element.prototype;
466
- var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
467
- var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
468
- var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
469
- var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a
322
+ let {
323
+ document
324
+ } = window;
325
+ const originalDocument = document;
326
+ const currentScript = originalDocument.currentScript;
327
+ const {
328
+ DocumentFragment,
329
+ HTMLTemplateElement,
330
+ Node,
331
+ Element,
332
+ NodeFilter,
333
+ NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
334
+ HTMLFormElement,
335
+ DOMParser,
336
+ trustedTypes
337
+ } = window;
338
+ const ElementPrototype = Element.prototype;
339
+ const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
340
+ const remove = lookupGetter(ElementPrototype, 'remove');
341
+ const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
342
+ const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
343
+ const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
344
+ // As per issue #47, the web-components registry is inherited by a
470
345
  // new document created via createHTMLDocument. As per the spec
471
346
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
472
347
  // a new empty registry is used when creating a template contents owner
473
348
  // document, so we use that as our parent document to ensure nothing
474
349
  // is inherited.
475
-
476
350
  if (typeof HTMLTemplateElement === 'function') {
477
- var template = document.createElement('template');
478
-
351
+ const template = document.createElement('template');
479
352
  if (template.content && template.content.ownerDocument) {
480
353
  document = template.content.ownerDocument;
481
354
  }
482
355
  }
483
-
484
- var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
485
-
486
- var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
487
- var _document = document,
488
- implementation = _document.implementation,
489
- createNodeIterator = _document.createNodeIterator,
490
- createDocumentFragment = _document.createDocumentFragment,
491
- getElementsByTagName = _document.getElementsByTagName;
492
- var importNode = originalDocument.importNode;
493
- var hooks = {};
356
+ let trustedTypesPolicy;
357
+ let emptyHTML = '';
358
+ const {
359
+ implementation,
360
+ createNodeIterator,
361
+ createDocumentFragment,
362
+ getElementsByTagName
363
+ } = document;
364
+ const {
365
+ importNode
366
+ } = originalDocument;
367
+ let hooks = _createHooksMap();
494
368
  /**
495
369
  * Expose whether this browser supports running the full DOMPurify.
496
370
  */
497
-
498
- DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined';
499
- var MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
500
- ERB_EXPR$1 = ERB_EXPR,
501
- TMPLIT_EXPR$1 = TMPLIT_EXPR,
502
- DATA_ATTR$1 = DATA_ATTR,
503
- ARIA_ATTR$1 = ARIA_ATTR,
504
- IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
505
- ATTR_WHITESPACE$1 = ATTR_WHITESPACE;
506
- var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
371
+ DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
372
+ const {
373
+ MUSTACHE_EXPR,
374
+ ERB_EXPR,
375
+ TMPLIT_EXPR,
376
+ DATA_ATTR,
377
+ ARIA_ATTR,
378
+ IS_SCRIPT_OR_DATA,
379
+ ATTR_WHITESPACE,
380
+ CUSTOM_ELEMENT
381
+ } = EXPRESSIONS;
382
+ let {
383
+ IS_ALLOWED_URI: IS_ALLOWED_URI$1
384
+ } = EXPRESSIONS;
507
385
  /**
508
386
  * We consider the elements and attributes below to be safe. Ideally
509
387
  * don't add any new ones but feel free to remove unwanted ones.
510
388
  */
511
-
512
389
  /* allowed element names */
513
-
514
- var ALLOWED_TAGS = null;
515
- var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));
390
+ let ALLOWED_TAGS = null;
391
+ const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
516
392
  /* Allowed attribute names */
517
-
518
- var ALLOWED_ATTR = null;
519
- var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));
393
+ let ALLOWED_ATTR = null;
394
+ const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
520
395
  /*
521
- * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
396
+ * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
522
397
  * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
523
398
  * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
524
399
  * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
525
400
  */
526
-
527
- var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
401
+ let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
528
402
  tagNameCheck: {
529
403
  writable: true,
530
404
  configurable: false,
@@ -545,58 +419,48 @@
545
419
  }
546
420
  }));
547
421
  /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
548
-
549
- var FORBID_TAGS = null;
422
+ let FORBID_TAGS = null;
550
423
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
551
-
552
- var FORBID_ATTR = null;
424
+ let FORBID_ATTR = null;
553
425
  /* Decide if ARIA attributes are okay */
554
-
555
- var ALLOW_ARIA_ATTR = true;
426
+ let ALLOW_ARIA_ATTR = true;
556
427
  /* Decide if custom data attributes are okay */
557
-
558
- var ALLOW_DATA_ATTR = true;
428
+ let ALLOW_DATA_ATTR = true;
559
429
  /* Decide if unknown protocols are okay */
560
-
561
- var ALLOW_UNKNOWN_PROTOCOLS = false;
430
+ let ALLOW_UNKNOWN_PROTOCOLS = false;
562
431
  /* Decide if self-closing tags in attributes are allowed.
563
432
  * Usually removed due to a mXSS issue in jQuery 3.0 */
564
-
565
- var ALLOW_SELF_CLOSE_IN_ATTR = true;
433
+ let ALLOW_SELF_CLOSE_IN_ATTR = true;
566
434
  /* Output should be safe for common template engines.
567
435
  * This means, DOMPurify removes data attributes, mustaches and ERB
568
436
  */
569
-
570
- var SAFE_FOR_TEMPLATES = false;
437
+ let SAFE_FOR_TEMPLATES = false;
438
+ /* Output should be safe even for XML used within HTML and alike.
439
+ * This means, DOMPurify removes comments when containing risky content.
440
+ */
441
+ let SAFE_FOR_XML = true;
571
442
  /* Decide if document with <html>... should be returned */
572
-
573
- var WHOLE_DOCUMENT = false;
443
+ let WHOLE_DOCUMENT = false;
574
444
  /* Track whether config is already set on this instance of DOMPurify. */
575
-
576
- var SET_CONFIG = false;
445
+ let SET_CONFIG = false;
577
446
  /* Decide if all elements (e.g. style, script) must be children of
578
447
  * document.body. By default, browsers might move them to document.head */
579
-
580
- var FORCE_BODY = false;
448
+ let FORCE_BODY = false;
581
449
  /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
582
450
  * string (or a TrustedHTML object if Trusted Types are supported).
583
451
  * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
584
452
  */
585
-
586
- var RETURN_DOM = false;
453
+ let RETURN_DOM = false;
587
454
  /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
588
455
  * string (or a TrustedHTML object if Trusted Types are supported) */
589
-
590
- var RETURN_DOM_FRAGMENT = false;
456
+ let RETURN_DOM_FRAGMENT = false;
591
457
  /* Try to return a Trusted Type object instead of a string, return a string in
592
458
  * case Trusted Types are not supported */
593
-
594
- var RETURN_TRUSTED_TYPE = false;
459
+ let RETURN_TRUSTED_TYPE = false;
595
460
  /* Output should be free from DOM clobbering attacks?
596
461
  * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
597
462
  */
598
-
599
- var SANITIZE_DOM = true;
463
+ let SANITIZE_DOM = true;
600
464
  /* Achieve full DOM Clobbering protection by isolating the namespace of named
601
465
  * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
602
466
  *
@@ -610,180 +474,138 @@
610
474
  * Namespace isolation is implemented by prefixing `id` and `name` attributes
611
475
  * with a constant string, i.e., `user-content-`
612
476
  */
613
-
614
- var SANITIZE_NAMED_PROPS = false;
615
- var SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
477
+ let SANITIZE_NAMED_PROPS = false;
478
+ const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
616
479
  /* Keep element content when removing element? */
617
-
618
- var KEEP_CONTENT = true;
480
+ let KEEP_CONTENT = true;
619
481
  /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
620
482
  * of importing it into a new Document and returning a sanitized copy */
621
-
622
- var IN_PLACE = false;
483
+ let IN_PLACE = false;
623
484
  /* Allow usage of profiles like html, svg and mathMl */
624
-
625
- var USE_PROFILES = {};
485
+ let USE_PROFILES = {};
626
486
  /* Tags to ignore content of when KEEP_CONTENT is true */
627
-
628
- var FORBID_CONTENTS = null;
629
- var DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
487
+ let FORBID_CONTENTS = null;
488
+ const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
630
489
  /* Tags that are safe for data: URIs */
631
-
632
- var DATA_URI_TAGS = null;
633
- var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
490
+ let DATA_URI_TAGS = null;
491
+ const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
634
492
  /* Attributes safe for values like "javascript:" */
635
-
636
- var URI_SAFE_ATTRIBUTES = null;
637
- var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
638
- var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
639
- var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
640
- var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
493
+ let URI_SAFE_ATTRIBUTES = null;
494
+ const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
495
+ const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
496
+ const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
497
+ const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
641
498
  /* Document namespace */
642
-
643
- var NAMESPACE = HTML_NAMESPACE;
644
- var IS_EMPTY_INPUT = false;
499
+ let NAMESPACE = HTML_NAMESPACE;
500
+ let IS_EMPTY_INPUT = false;
645
501
  /* Allowed XHTML+XML namespaces */
646
-
647
- var ALLOWED_NAMESPACES = null;
648
- var DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
502
+ let ALLOWED_NAMESPACES = null;
503
+ const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
504
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
505
+ let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
506
+ // Certain elements are allowed in both SVG and HTML
507
+ // namespace. We need to specify them explicitly
508
+ // so that they don't get erroneously deleted from
509
+ // HTML namespace.
510
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
649
511
  /* Parsing of strict XHTML documents */
650
-
651
- var PARSER_MEDIA_TYPE;
652
- var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
653
- var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
654
- var transformCaseFunc;
512
+ let PARSER_MEDIA_TYPE = null;
513
+ const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
514
+ const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
515
+ let transformCaseFunc = null;
655
516
  /* Keep a reference to config to pass to hooks */
656
-
657
- var CONFIG = null;
517
+ let CONFIG = null;
658
518
  /* Ideally, do not touch anything below this line */
659
-
660
519
  /* ______________________________________________ */
661
-
662
- var formElement = document.createElement('form');
663
-
664
- var isRegexOrFunction = function isRegexOrFunction(testValue) {
520
+ const formElement = document.createElement('form');
521
+ const isRegexOrFunction = function isRegexOrFunction(testValue) {
665
522
  return testValue instanceof RegExp || testValue instanceof Function;
666
523
  };
667
524
  /**
668
525
  * _parseConfig
669
526
  *
670
- * @param {Object} cfg optional config literal
527
+ * @param cfg optional config literal
671
528
  */
672
529
  // eslint-disable-next-line complexity
673
-
674
-
675
- var _parseConfig = function _parseConfig(cfg) {
530
+ const _parseConfig = function _parseConfig() {
531
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
676
532
  if (CONFIG && CONFIG === cfg) {
677
533
  return;
678
534
  }
679
535
  /* Shield configuration object from tampering */
680
-
681
-
682
- if (!cfg || _typeof(cfg) !== 'object') {
536
+ if (!cfg || typeof cfg !== 'object') {
683
537
  cfg = {};
684
538
  }
685
539
  /* Shield configuration object from prototype pollution */
686
-
687
-
688
540
  cfg = clone(cfg);
689
- PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
690
- SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE; // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
691
-
541
+ PARSER_MEDIA_TYPE =
542
+ // eslint-disable-next-line unicorn/prefer-includes
543
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
544
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
692
545
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
693
546
  /* Set configuration parameters */
694
-
695
- ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
696
- ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
697
- ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
698
- URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent
699
- cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent
700
- transformCaseFunc // eslint-disable-line indent
701
- ) // eslint-disable-line indent
702
- : DEFAULT_URI_SAFE_ATTRIBUTES;
703
- DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent
704
- cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent
705
- transformCaseFunc // eslint-disable-line indent
706
- ) // eslint-disable-line indent
707
- : DEFAULT_DATA_URI_TAGS;
708
- FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
709
- FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
710
- FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
711
- USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
547
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
548
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
549
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
550
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
551
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
552
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
553
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
554
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
555
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
712
556
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
713
-
714
557
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
715
-
716
558
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
717
-
718
559
  ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
719
-
720
560
  SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
721
-
561
+ SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
722
562
  WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
723
-
724
563
  RETURN_DOM = cfg.RETURN_DOM || false; // Default false
725
-
726
564
  RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
727
-
728
565
  RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
729
-
730
566
  FORCE_BODY = cfg.FORCE_BODY || false; // Default false
731
-
732
567
  SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
733
-
734
568
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
735
-
736
569
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
737
-
738
570
  IN_PLACE = cfg.IN_PLACE || false; // Default false
739
-
740
- IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
571
+ IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
741
572
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
573
+ MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
574
+ HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
742
575
  CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
743
-
744
576
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
745
577
  CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
746
578
  }
747
-
748
579
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
749
580
  CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
750
581
  }
751
-
752
582
  if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
753
583
  CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
754
584
  }
755
-
756
585
  if (SAFE_FOR_TEMPLATES) {
757
586
  ALLOW_DATA_ATTR = false;
758
587
  }
759
-
760
588
  if (RETURN_DOM_FRAGMENT) {
761
589
  RETURN_DOM = true;
762
590
  }
763
591
  /* Parse profile info */
764
-
765
-
766
592
  if (USE_PROFILES) {
767
- ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));
593
+ ALLOWED_TAGS = addToSet({}, text);
768
594
  ALLOWED_ATTR = [];
769
-
770
595
  if (USE_PROFILES.html === true) {
771
596
  addToSet(ALLOWED_TAGS, html$1);
772
597
  addToSet(ALLOWED_ATTR, html);
773
598
  }
774
-
775
599
  if (USE_PROFILES.svg === true) {
776
600
  addToSet(ALLOWED_TAGS, svg$1);
777
601
  addToSet(ALLOWED_ATTR, svg);
778
602
  addToSet(ALLOWED_ATTR, xml);
779
603
  }
780
-
781
604
  if (USE_PROFILES.svgFilters === true) {
782
605
  addToSet(ALLOWED_TAGS, svgFilters);
783
606
  addToSet(ALLOWED_ATTR, svg);
784
607
  addToSet(ALLOWED_ATTR, xml);
785
608
  }
786
-
787
609
  if (USE_PROFILES.mathMl === true) {
788
610
  addToSet(ALLOWED_TAGS, mathMl$1);
789
611
  addToSet(ALLOWED_ATTR, mathMl);
@@ -791,146 +613,127 @@
791
613
  }
792
614
  }
793
615
  /* Merge configuration parameters */
794
-
795
-
796
616
  if (cfg.ADD_TAGS) {
797
617
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
798
618
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
799
619
  }
800
-
801
620
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
802
621
  }
803
-
804
622
  if (cfg.ADD_ATTR) {
805
623
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
806
624
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
807
625
  }
808
-
809
626
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
810
627
  }
811
-
812
628
  if (cfg.ADD_URI_SAFE_ATTR) {
813
629
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
814
630
  }
815
-
816
631
  if (cfg.FORBID_CONTENTS) {
817
632
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
818
633
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
819
634
  }
820
-
821
635
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
822
636
  }
823
637
  /* Add #text in case KEEP_CONTENT is set to true */
824
-
825
-
826
638
  if (KEEP_CONTENT) {
827
639
  ALLOWED_TAGS['#text'] = true;
828
640
  }
829
641
  /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
830
-
831
-
832
642
  if (WHOLE_DOCUMENT) {
833
643
  addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
834
644
  }
835
645
  /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
836
-
837
-
838
646
  if (ALLOWED_TAGS.table) {
839
647
  addToSet(ALLOWED_TAGS, ['tbody']);
840
648
  delete FORBID_TAGS.tbody;
841
- } // Prevent further manipulation of configuration.
649
+ }
650
+ if (cfg.TRUSTED_TYPES_POLICY) {
651
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
652
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
653
+ }
654
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
655
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
656
+ }
657
+ // Overwrite existing TrustedTypes policy.
658
+ trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
659
+ // Sign local variables required by `sanitize`.
660
+ emptyHTML = trustedTypesPolicy.createHTML('');
661
+ } else {
662
+ // Uninitialized policy, attempt to initialize the internal dompurify policy.
663
+ if (trustedTypesPolicy === undefined) {
664
+ trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
665
+ }
666
+ // If creating the internal policy succeeded sign internal variables.
667
+ if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
668
+ emptyHTML = trustedTypesPolicy.createHTML('');
669
+ }
670
+ }
671
+ // Prevent further manipulation of configuration.
842
672
  // Not available in IE8, Safari 5, etc.
843
-
844
-
845
673
  if (freeze) {
846
674
  freeze(cfg);
847
675
  }
848
-
849
676
  CONFIG = cfg;
850
677
  };
851
-
852
- var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
853
- var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML
854
- // namespace. We need to specify them explicitly
855
- // so that they don't get erroneously deleted from
856
- // HTML namespace.
857
-
858
- var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
859
678
  /* Keep track of all possible SVG and MathML tags
860
679
  * so that we can perform the namespace checks
861
680
  * correctly. */
862
-
863
- var ALL_SVG_TAGS = addToSet({}, svg$1);
864
- addToSet(ALL_SVG_TAGS, svgFilters);
865
- addToSet(ALL_SVG_TAGS, svgDisallowed);
866
- var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
867
- addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
681
+ const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
682
+ const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
868
683
  /**
869
- *
870
- *
871
- * @param {Element} element a DOM element whose namespace is being checked
872
- * @returns {boolean} Return false if the element has a
684
+ * @param element a DOM element whose namespace is being checked
685
+ * @returns Return false if the element has a
873
686
  * namespace that a spec-compliant parser would never
874
687
  * return. Return true otherwise.
875
688
  */
876
-
877
- var _checkValidNamespace = function _checkValidNamespace(element) {
878
- var parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode
689
+ const _checkValidNamespace = function _checkValidNamespace(element) {
690
+ let parent = getParentNode(element);
691
+ // In JSDOM, if we're inside shadow DOM, then parentNode
879
692
  // can be null. We just simulate parent in this case.
880
-
881
693
  if (!parent || !parent.tagName) {
882
694
  parent = {
883
695
  namespaceURI: NAMESPACE,
884
696
  tagName: 'template'
885
697
  };
886
698
  }
887
-
888
- var tagName = stringToLowerCase(element.tagName);
889
- var parentTagName = stringToLowerCase(parent.tagName);
890
-
699
+ const tagName = stringToLowerCase(element.tagName);
700
+ const parentTagName = stringToLowerCase(parent.tagName);
891
701
  if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
892
702
  return false;
893
703
  }
894
-
895
704
  if (element.namespaceURI === SVG_NAMESPACE) {
896
705
  // The only way to switch from HTML namespace to SVG
897
706
  // is via <svg>. If it happens via any other tag, then
898
707
  // it should be killed.
899
708
  if (parent.namespaceURI === HTML_NAMESPACE) {
900
709
  return tagName === 'svg';
901
- } // The only way to switch from MathML to SVG is via`
710
+ }
711
+ // The only way to switch from MathML to SVG is via`
902
712
  // svg if parent is either <annotation-xml> or MathML
903
713
  // text integration points.
904
-
905
-
906
714
  if (parent.namespaceURI === MATHML_NAMESPACE) {
907
715
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
908
- } // We only allow elements that are defined in SVG
716
+ }
717
+ // We only allow elements that are defined in SVG
909
718
  // spec. All others are disallowed in SVG namespace.
910
-
911
-
912
719
  return Boolean(ALL_SVG_TAGS[tagName]);
913
720
  }
914
-
915
721
  if (element.namespaceURI === MATHML_NAMESPACE) {
916
722
  // The only way to switch from HTML namespace to MathML
917
723
  // is via <math>. If it happens via any other tag, then
918
724
  // it should be killed.
919
725
  if (parent.namespaceURI === HTML_NAMESPACE) {
920
726
  return tagName === 'math';
921
- } // The only way to switch from SVG to MathML is via
727
+ }
728
+ // The only way to switch from SVG to MathML is via
922
729
  // <math> and HTML integration points
923
-
924
-
925
730
  if (parent.namespaceURI === SVG_NAMESPACE) {
926
731
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
927
- } // We only allow elements that are defined in MathML
732
+ }
733
+ // We only allow elements that are defined in MathML
928
734
  // spec. All others are disallowed in MathML namespace.
929
-
930
-
931
735
  return Boolean(ALL_MATHML_TAGS[tagName]);
932
736
  }
933
-
934
737
  if (element.namespaceURI === HTML_NAMESPACE) {
935
738
  // The only way to switch from SVG to HTML is via
936
739
  // HTML integration points, and from MathML to HTML
@@ -938,77 +741,67 @@
938
741
  if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
939
742
  return false;
940
743
  }
941
-
942
744
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
943
745
  return false;
944
- } // We disallow tags that are specific for MathML
746
+ }
747
+ // We disallow tags that are specific for MathML
945
748
  // or SVG and should never appear in HTML namespace
946
-
947
-
948
749
  return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
949
- } // For XHTML and XML documents that support custom namespaces
950
-
951
-
750
+ }
751
+ // For XHTML and XML documents that support custom namespaces
952
752
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
953
753
  return true;
954
- } // The code should never reach this place (this means
754
+ }
755
+ // The code should never reach this place (this means
955
756
  // that the element somehow got namespace that is not
956
757
  // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
957
758
  // Return false just in case.
958
-
959
-
960
759
  return false;
961
760
  };
962
761
  /**
963
762
  * _forceRemove
964
763
  *
965
- * @param {Node} node a DOM node
764
+ * @param node a DOM node
966
765
  */
967
-
968
-
969
- var _forceRemove = function _forceRemove(node) {
766
+ const _forceRemove = function _forceRemove(node) {
970
767
  arrayPush(DOMPurify.removed, {
971
768
  element: node
972
769
  });
973
-
974
770
  try {
975
771
  // eslint-disable-next-line unicorn/prefer-dom-node-remove
976
- node.parentNode.removeChild(node);
772
+ getParentNode(node).removeChild(node);
977
773
  } catch (_) {
978
- node.remove();
774
+ remove(node);
979
775
  }
980
776
  };
981
777
  /**
982
778
  * _removeAttribute
983
779
  *
984
- * @param {String} name an Attribute name
985
- * @param {Node} node a DOM node
780
+ * @param name an Attribute name
781
+ * @param element a DOM node
986
782
  */
987
-
988
-
989
- var _removeAttribute = function _removeAttribute(name, node) {
783
+ const _removeAttribute = function _removeAttribute(name, element) {
990
784
  try {
991
785
  arrayPush(DOMPurify.removed, {
992
- attribute: node.getAttributeNode(name),
993
- from: node
786
+ attribute: element.getAttributeNode(name),
787
+ from: element
994
788
  });
995
789
  } catch (_) {
996
790
  arrayPush(DOMPurify.removed, {
997
791
  attribute: null,
998
- from: node
792
+ from: element
999
793
  });
1000
794
  }
1001
-
1002
- node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes
1003
-
1004
- if (name === 'is' && !ALLOWED_ATTR[name]) {
795
+ element.removeAttribute(name);
796
+ // We void attribute values for unremovable "is" attributes
797
+ if (name === 'is') {
1005
798
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
1006
799
  try {
1007
- _forceRemove(node);
800
+ _forceRemove(element);
1008
801
  } catch (_) {}
1009
802
  } else {
1010
803
  try {
1011
- node.setAttribute(name, '');
804
+ element.setAttribute(name, '');
1012
805
  } catch (_) {}
1013
806
  }
1014
807
  }
@@ -1016,217 +809,171 @@
1016
809
  /**
1017
810
  * _initDocument
1018
811
  *
1019
- * @param {String} dirty a string of dirty markup
1020
- * @return {Document} a DOM, filled with the dirty markup
812
+ * @param dirty - a string of dirty markup
813
+ * @return a DOM, filled with the dirty markup
1021
814
  */
1022
-
1023
-
1024
- var _initDocument = function _initDocument(dirty) {
815
+ const _initDocument = function _initDocument(dirty) {
1025
816
  /* Create a HTML document */
1026
- var doc;
1027
- var leadingWhitespace;
1028
-
817
+ let doc = null;
818
+ let leadingWhitespace = null;
1029
819
  if (FORCE_BODY) {
1030
820
  dirty = '<remove></remove>' + dirty;
1031
821
  } else {
1032
822
  /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
1033
- var matches = stringMatch(dirty, /^[\r\n\t ]+/);
823
+ const matches = stringMatch(dirty, /^[\r\n\t ]+/);
1034
824
  leadingWhitespace = matches && matches[0];
1035
825
  }
1036
-
1037
826
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
1038
827
  // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
1039
828
  dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
1040
829
  }
1041
-
1042
- var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
830
+ const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
1043
831
  /*
1044
832
  * Use the DOMParser API by default, fallback later if needs be
1045
833
  * DOMParser not work for svg when has multiple root element.
1046
834
  */
1047
-
1048
835
  if (NAMESPACE === HTML_NAMESPACE) {
1049
836
  try {
1050
837
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
1051
838
  } catch (_) {}
1052
839
  }
1053
840
  /* Use createHTMLDocument in case DOMParser is not available */
1054
-
1055
-
1056
841
  if (!doc || !doc.documentElement) {
1057
842
  doc = implementation.createDocument(NAMESPACE, 'template', null);
1058
-
1059
843
  try {
1060
844
  doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
1061
- } catch (_) {// Syntax error if dirtyPayload is invalid xml
845
+ } catch (_) {
846
+ // Syntax error if dirtyPayload is invalid xml
1062
847
  }
1063
848
  }
1064
-
1065
- var body = doc.body || doc.documentElement;
1066
-
849
+ const body = doc.body || doc.documentElement;
1067
850
  if (dirty && leadingWhitespace) {
1068
851
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
1069
852
  }
1070
853
  /* Work on whole document or just its body */
1071
-
1072
-
1073
854
  if (NAMESPACE === HTML_NAMESPACE) {
1074
855
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
1075
856
  }
1076
-
1077
857
  return WHOLE_DOCUMENT ? doc.documentElement : body;
1078
858
  };
1079
859
  /**
1080
- * _createIterator
860
+ * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
1081
861
  *
1082
- * @param {Document} root document/fragment to create iterator for
1083
- * @return {Iterator} iterator instance
862
+ * @param root The root element or node to start traversing on.
863
+ * @return The created NodeIterator
1084
864
  */
1085
-
1086
-
1087
- var _createIterator = function _createIterator(root) {
1088
- return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
1089
- NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
865
+ const _createNodeIterator = function _createNodeIterator(root) {
866
+ return createNodeIterator.call(root.ownerDocument || root, root,
867
+ // eslint-disable-next-line no-bitwise
868
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
1090
869
  };
1091
870
  /**
1092
871
  * _isClobbered
1093
872
  *
1094
- * @param {Node} elm element to check for clobbering attacks
1095
- * @return {Boolean} true if clobbered, false if safe
873
+ * @param element element to check for clobbering attacks
874
+ * @return true if clobbered, false if safe
1096
875
  */
1097
-
1098
-
1099
- var _isClobbered = function _isClobbered(elm) {
1100
- return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function');
876
+ const _isClobbered = function _isClobbered(element) {
877
+ return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');
1101
878
  };
1102
879
  /**
1103
- * _isNode
880
+ * Checks whether the given object is a DOM node.
1104
881
  *
1105
- * @param {Node} obj object to check whether it's a DOM node
1106
- * @return {Boolean} true is object is a DOM node
882
+ * @param value object to check whether it's a DOM node
883
+ * @return true is object is a DOM node
1107
884
  */
1108
-
1109
-
1110
- var _isNode = function _isNode(object) {
1111
- return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
885
+ const _isNode = function _isNode(value) {
886
+ return typeof Node === 'function' && value instanceof Node;
1112
887
  };
1113
- /**
1114
- * _executeHook
1115
- * Execute user configurable hooks
1116
- *
1117
- * @param {String} entryPoint Name of the hook's entry point
1118
- * @param {Node} currentNode node to work on with the hook
1119
- * @param {Object} data additional hook parameters
1120
- */
1121
-
1122
-
1123
- var _executeHook = function _executeHook(entryPoint, currentNode, data) {
1124
- if (!hooks[entryPoint]) {
1125
- return;
1126
- }
1127
-
1128
- arrayForEach(hooks[entryPoint], function (hook) {
888
+ function _executeHooks(hooks, currentNode, data) {
889
+ arrayForEach(hooks, hook => {
1129
890
  hook.call(DOMPurify, currentNode, data, CONFIG);
1130
891
  });
1131
- };
892
+ }
1132
893
  /**
1133
894
  * _sanitizeElements
1134
895
  *
1135
896
  * @protect nodeName
1136
897
  * @protect textContent
1137
898
  * @protect removeChild
1138
- *
1139
- * @param {Node} currentNode to check for permission to exist
1140
- * @return {Boolean} true if node was killed, false if left alive
899
+ * @param currentNode to check for permission to exist
900
+ * @return true if node was killed, false if left alive
1141
901
  */
1142
-
1143
-
1144
- var _sanitizeElements = function _sanitizeElements(currentNode) {
1145
- var content;
902
+ const _sanitizeElements = function _sanitizeElements(currentNode) {
903
+ let content = null;
1146
904
  /* Execute a hook if present */
1147
-
1148
- _executeHook('beforeSanitizeElements', currentNode, null);
905
+ _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
1149
906
  /* Check if element is clobbered or can clobber */
1150
-
1151
-
1152
907
  if (_isClobbered(currentNode)) {
1153
908
  _forceRemove(currentNode);
1154
-
1155
909
  return true;
1156
910
  }
1157
911
  /* Now let's check the element's type and name */
1158
-
1159
-
1160
- var tagName = transformCaseFunc(currentNode.nodeName);
912
+ const tagName = transformCaseFunc(currentNode.nodeName);
1161
913
  /* Execute a hook if present */
1162
-
1163
- _executeHook('uponSanitizeElement', currentNode, {
1164
- tagName: tagName,
914
+ _executeHooks(hooks.uponSanitizeElement, currentNode, {
915
+ tagName,
1165
916
  allowedTags: ALLOWED_TAGS
1166
917
  });
1167
918
  /* Detect mXSS attempts abusing namespace confusion */
1168
-
1169
-
1170
- if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
919
+ if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
920
+ _forceRemove(currentNode);
921
+ return true;
922
+ }
923
+ /* Remove any occurrence of processing instructions */
924
+ if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
925
+ _forceRemove(currentNode);
926
+ return true;
927
+ }
928
+ /* Remove any kind of possibly harmful comments */
929
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
1171
930
  _forceRemove(currentNode);
1172
-
1173
931
  return true;
1174
932
  }
1175
933
  /* Remove element if anything forbids its presence */
1176
-
1177
-
1178
934
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1179
935
  /* Check if we have a custom element to handle */
1180
- if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
1181
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
1182
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
936
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
937
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
938
+ return false;
939
+ }
940
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
941
+ return false;
942
+ }
1183
943
  }
1184
944
  /* Keep content except for bad-listed elements */
1185
-
1186
-
1187
945
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1188
- var parentNode = getParentNode(currentNode) || currentNode.parentNode;
1189
- var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
1190
-
946
+ const parentNode = getParentNode(currentNode) || currentNode.parentNode;
947
+ const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
1191
948
  if (childNodes && parentNode) {
1192
- var childCount = childNodes.length;
1193
-
1194
- for (var i = childCount - 1; i >= 0; --i) {
1195
- parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
949
+ const childCount = childNodes.length;
950
+ for (let i = childCount - 1; i >= 0; --i) {
951
+ const childClone = cloneNode(childNodes[i], true);
952
+ childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
953
+ parentNode.insertBefore(childClone, getNextSibling(currentNode));
1196
954
  }
1197
955
  }
1198
956
  }
1199
-
1200
957
  _forceRemove(currentNode);
1201
-
1202
958
  return true;
1203
959
  }
1204
960
  /* Check whether element has a valid namespace */
1205
-
1206
-
1207
961
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
1208
962
  _forceRemove(currentNode);
1209
-
1210
963
  return true;
1211
964
  }
1212
- /* Make sure that older browsers don't get noscript mXSS */
1213
-
1214
-
1215
- if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
965
+ /* Make sure that older browsers don't get fallback-tag mXSS */
966
+ if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
1216
967
  _forceRemove(currentNode);
1217
-
1218
968
  return true;
1219
969
  }
1220
970
  /* Sanitize element content to be template-safe */
1221
-
1222
-
1223
- if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
971
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
1224
972
  /* Get the element's text content */
1225
973
  content = currentNode.textContent;
1226
- content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
1227
- content = stringReplace(content, ERB_EXPR$1, ' ');
1228
- content = stringReplace(content, TMPLIT_EXPR$1, ' ');
1229
-
974
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
975
+ content = stringReplace(content, expr, ' ');
976
+ });
1230
977
  if (currentNode.textContent !== content) {
1231
978
  arrayPush(DOMPurify.removed, {
1232
979
  element: currentNode.cloneNode()
@@ -1235,24 +982,19 @@
1235
982
  }
1236
983
  }
1237
984
  /* Execute a hook if present */
1238
-
1239
-
1240
- _executeHook('afterSanitizeElements', currentNode, null);
1241
-
985
+ _executeHooks(hooks.afterSanitizeElements, currentNode, null);
1242
986
  return false;
1243
987
  };
1244
988
  /**
1245
989
  * _isValidAttribute
1246
990
  *
1247
- * @param {string} lcTag Lowercase tag name of containing element.
1248
- * @param {string} lcName Lowercase attribute name.
1249
- * @param {string} value Attribute value.
1250
- * @return {Boolean} Returns true if `value` is valid, otherwise false.
991
+ * @param lcTag Lowercase tag name of containing element.
992
+ * @param lcName Lowercase attribute name.
993
+ * @param value Attribute value.
994
+ * @return Returns true if `value` is valid, otherwise false.
1251
995
  */
1252
996
  // eslint-disable-next-line complexity
1253
-
1254
-
1255
- var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
997
+ const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1256
998
  /* Make sure attribute cannot clobber */
1257
999
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1258
1000
  return false;
@@ -1261,35 +1003,33 @@
1261
1003
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1262
1004
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1263
1005
  We don't need to check the value; it's always URI safe. */
1264
-
1265
-
1266
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
1267
- if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1006
+ if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
1007
+ if (
1008
+ // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1268
1009
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1269
1010
  // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1270
- _basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || // Alternative, second condition checks if it's an `is`-attribute, AND
1011
+ _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName, lcTag)) ||
1012
+ // Alternative, second condition checks if it's an `is`-attribute, AND
1271
1013
  // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1272
1014
  lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {
1273
1015
  return false;
1274
1016
  }
1275
1017
  /* Check value is safe. First, is attr inert? If so, is safe */
1276
-
1277
- } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (!value) ; else {
1018
+ } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
1278
1019
  return false;
1279
- }
1280
-
1020
+ } else ;
1281
1021
  return true;
1282
1022
  };
1283
1023
  /**
1284
- * _basicCustomElementCheck
1024
+ * _isBasicCustomElement
1285
1025
  * checks if at least one dash is included in tagName, and it's not the first char
1286
1026
  * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1287
- * @param {string} tagName name of the tag of the node to sanitize
1027
+ *
1028
+ * @param tagName name of the tag of the node to sanitize
1029
+ * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1288
1030
  */
1289
-
1290
-
1291
- var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
1292
- return tagName.indexOf('-') > 0;
1031
+ const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
1032
+ return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
1293
1033
  };
1294
1034
  /**
1295
1035
  * _sanitizeAttributes
@@ -1299,246 +1039,196 @@
1299
1039
  * @protect removeAttribute
1300
1040
  * @protect setAttribute
1301
1041
  *
1302
- * @param {Node} currentNode to sanitize
1042
+ * @param currentNode to sanitize
1303
1043
  */
1304
-
1305
-
1306
- var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1307
- var attr;
1308
- var value;
1309
- var lcName;
1310
- var l;
1044
+ const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1311
1045
  /* Execute a hook if present */
1312
-
1313
- _executeHook('beforeSanitizeAttributes', currentNode, null);
1314
-
1315
- var attributes = currentNode.attributes;
1046
+ _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
1047
+ const {
1048
+ attributes
1049
+ } = currentNode;
1316
1050
  /* Check if we have attributes; if not we might have a text node */
1317
-
1318
- if (!attributes) {
1051
+ if (!attributes || _isClobbered(currentNode)) {
1319
1052
  return;
1320
1053
  }
1321
-
1322
- var hookEvent = {
1054
+ const hookEvent = {
1323
1055
  attrName: '',
1324
1056
  attrValue: '',
1325
1057
  keepAttr: true,
1326
- allowedAttributes: ALLOWED_ATTR
1058
+ allowedAttributes: ALLOWED_ATTR,
1059
+ forceKeepAttr: undefined
1327
1060
  };
1328
- l = attributes.length;
1061
+ let l = attributes.length;
1329
1062
  /* Go backwards over all attributes; safely remove bad ones */
1330
-
1331
1063
  while (l--) {
1332
- attr = attributes[l];
1333
- var _attr = attr,
1334
- name = _attr.name,
1335
- namespaceURI = _attr.namespaceURI;
1336
- value = name === 'value' ? attr.value : stringTrim(attr.value);
1337
- lcName = transformCaseFunc(name);
1064
+ const attr = attributes[l];
1065
+ const {
1066
+ name,
1067
+ namespaceURI,
1068
+ value: attrValue
1069
+ } = attr;
1070
+ const lcName = transformCaseFunc(name);
1071
+ const initValue = attrValue;
1072
+ let value = name === 'value' ? initValue : stringTrim(initValue);
1338
1073
  /* Execute a hook if present */
1339
-
1340
1074
  hookEvent.attrName = lcName;
1341
1075
  hookEvent.attrValue = value;
1342
1076
  hookEvent.keepAttr = true;
1343
1077
  hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
1344
-
1345
- _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
1346
-
1078
+ _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
1347
1079
  value = hookEvent.attrValue;
1080
+ /* Full DOM Clobbering protection via namespace isolation,
1081
+ * Prefix id and name attributes with `user-content-`
1082
+ */
1083
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1084
+ // Remove the attribute with this value
1085
+ _removeAttribute(name, currentNode);
1086
+ // Prefix the value and later re-create the attribute with the sanitized value
1087
+ value = SANITIZE_NAMED_PROPS_PREFIX + value;
1088
+ }
1089
+ /* Work around a security issue with comments inside attributes */
1090
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title|textarea)/i, value)) {
1091
+ _removeAttribute(name, currentNode);
1092
+ continue;
1093
+ }
1094
+ /* Make sure we cannot easily use animated hrefs, even if animations are allowed */
1095
+ if (lcName === 'attributename' && stringMatch(value, 'href')) {
1096
+ _removeAttribute(name, currentNode);
1097
+ continue;
1098
+ }
1348
1099
  /* Did the hooks approve of the attribute? */
1349
-
1350
1100
  if (hookEvent.forceKeepAttr) {
1351
1101
  continue;
1352
1102
  }
1353
- /* Remove attribute */
1354
-
1355
-
1356
- _removeAttribute(name, currentNode);
1357
1103
  /* Did the hooks approve of the attribute? */
1358
-
1359
-
1360
1104
  if (!hookEvent.keepAttr) {
1105
+ _removeAttribute(name, currentNode);
1361
1106
  continue;
1362
1107
  }
1363
1108
  /* Work around a security issue in jQuery 3.0 */
1364
-
1365
-
1366
1109
  if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1367
1110
  _removeAttribute(name, currentNode);
1368
-
1369
1111
  continue;
1370
1112
  }
1371
1113
  /* Sanitize attribute content to be template-safe */
1372
-
1373
-
1374
1114
  if (SAFE_FOR_TEMPLATES) {
1375
- value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
1376
- value = stringReplace(value, ERB_EXPR$1, ' ');
1377
- value = stringReplace(value, TMPLIT_EXPR$1, ' ');
1115
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1116
+ value = stringReplace(value, expr, ' ');
1117
+ });
1378
1118
  }
1379
1119
  /* Is `value` valid for this attribute? */
1380
-
1381
-
1382
- var lcTag = transformCaseFunc(currentNode.nodeName);
1383
-
1120
+ const lcTag = transformCaseFunc(currentNode.nodeName);
1384
1121
  if (!_isValidAttribute(lcTag, lcName, value)) {
1122
+ _removeAttribute(name, currentNode);
1385
1123
  continue;
1386
1124
  }
1387
- /* Full DOM Clobbering protection via namespace isolation,
1388
- * Prefix id and name attributes with `user-content-`
1389
- */
1390
-
1391
-
1392
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1393
- // Remove the attribute with this value
1394
- _removeAttribute(name, currentNode); // Prefix the value and later re-create the attribute with the sanitized value
1395
-
1396
-
1397
- value = SANITIZE_NAMED_PROPS_PREFIX + value;
1398
- }
1399
1125
  /* Handle attributes that require Trusted Types */
1400
-
1401
-
1402
- if (trustedTypesPolicy && _typeof(trustedTypes) === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1126
+ if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1403
1127
  if (namespaceURI) ; else {
1404
1128
  switch (trustedTypes.getAttributeType(lcTag, lcName)) {
1405
1129
  case 'TrustedHTML':
1406
- value = trustedTypesPolicy.createHTML(value);
1407
- break;
1408
-
1130
+ {
1131
+ value = trustedTypesPolicy.createHTML(value);
1132
+ break;
1133
+ }
1409
1134
  case 'TrustedScriptURL':
1410
- value = trustedTypesPolicy.createScriptURL(value);
1411
- break;
1135
+ {
1136
+ value = trustedTypesPolicy.createScriptURL(value);
1137
+ break;
1138
+ }
1412
1139
  }
1413
1140
  }
1414
1141
  }
1415
1142
  /* Handle invalid data-* attribute set by try-catching it */
1416
-
1417
-
1418
- try {
1419
- if (namespaceURI) {
1420
- currentNode.setAttributeNS(namespaceURI, name, value);
1421
- } else {
1422
- /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1423
- currentNode.setAttribute(name, value);
1143
+ if (value !== initValue) {
1144
+ try {
1145
+ if (namespaceURI) {
1146
+ currentNode.setAttributeNS(namespaceURI, name, value);
1147
+ } else {
1148
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1149
+ currentNode.setAttribute(name, value);
1150
+ }
1151
+ if (_isClobbered(currentNode)) {
1152
+ _forceRemove(currentNode);
1153
+ } else {
1154
+ arrayPop(DOMPurify.removed);
1155
+ }
1156
+ } catch (_) {
1157
+ _removeAttribute(name, currentNode);
1424
1158
  }
1425
-
1426
- arrayPop(DOMPurify.removed);
1427
- } catch (_) {}
1159
+ }
1428
1160
  }
1429
1161
  /* Execute a hook if present */
1430
-
1431
-
1432
- _executeHook('afterSanitizeAttributes', currentNode, null);
1162
+ _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
1433
1163
  };
1434
1164
  /**
1435
1165
  * _sanitizeShadowDOM
1436
1166
  *
1437
- * @param {DocumentFragment} fragment to iterate over recursively
1167
+ * @param fragment to iterate over recursively
1438
1168
  */
1439
-
1440
-
1441
- var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1442
- var shadowNode;
1443
-
1444
- var shadowIterator = _createIterator(fragment);
1169
+ const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1170
+ let shadowNode = null;
1171
+ const shadowIterator = _createNodeIterator(fragment);
1445
1172
  /* Execute a hook if present */
1446
-
1447
-
1448
- _executeHook('beforeSanitizeShadowDOM', fragment, null);
1449
-
1173
+ _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
1450
1174
  while (shadowNode = shadowIterator.nextNode()) {
1451
1175
  /* Execute a hook if present */
1452
- _executeHook('uponSanitizeShadowNode', shadowNode, null);
1176
+ _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
1453
1177
  /* Sanitize tags and elements */
1454
-
1455
-
1456
- if (_sanitizeElements(shadowNode)) {
1457
- continue;
1458
- }
1178
+ _sanitizeElements(shadowNode);
1179
+ /* Check attributes next */
1180
+ _sanitizeAttributes(shadowNode);
1459
1181
  /* Deep shadow DOM detected */
1460
-
1461
-
1462
1182
  if (shadowNode.content instanceof DocumentFragment) {
1463
1183
  _sanitizeShadowDOM(shadowNode.content);
1464
1184
  }
1465
- /* Check attributes, sanitize if necessary */
1466
-
1467
-
1468
- _sanitizeAttributes(shadowNode);
1469
1185
  }
1470
1186
  /* Execute a hook if present */
1471
-
1472
-
1473
- _executeHook('afterSanitizeShadowDOM', fragment, null);
1187
+ _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
1474
1188
  };
1475
- /**
1476
- * Sanitize
1477
- * Public method providing core sanitation functionality
1478
- *
1479
- * @param {String|Node} dirty string or DOM node
1480
- * @param {Object} configuration object
1481
- */
1482
1189
  // eslint-disable-next-line complexity
1483
-
1484
-
1485
1190
  DOMPurify.sanitize = function (dirty) {
1486
- var cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1487
- var body;
1488
- var importedNode;
1489
- var currentNode;
1490
- var returnNode;
1191
+ let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1192
+ let body = null;
1193
+ let importedNode = null;
1194
+ let currentNode = null;
1195
+ let returnNode = null;
1491
1196
  /* Make sure we have a string to sanitize.
1492
1197
  DO NOT return early, as this will return the wrong type if
1493
1198
  the user has requested a DOM object rather than a string */
1494
-
1495
1199
  IS_EMPTY_INPUT = !dirty;
1496
-
1497
1200
  if (IS_EMPTY_INPUT) {
1498
1201
  dirty = '<!-->';
1499
1202
  }
1500
1203
  /* Stringify, in case dirty is an object */
1501
-
1502
-
1503
1204
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
1504
- // eslint-disable-next-line no-negated-condition
1505
- if (typeof dirty.toString !== 'function') {
1506
- throw typeErrorCreate('toString is not a function');
1507
- } else {
1205
+ if (typeof dirty.toString === 'function') {
1508
1206
  dirty = dirty.toString();
1509
-
1510
1207
  if (typeof dirty !== 'string') {
1511
1208
  throw typeErrorCreate('dirty is not a string, aborting');
1512
1209
  }
1210
+ } else {
1211
+ throw typeErrorCreate('toString is not a function');
1513
1212
  }
1514
1213
  }
1515
1214
  /* Return dirty HTML if DOMPurify cannot run */
1516
-
1517
-
1518
1215
  if (!DOMPurify.isSupported) {
1519
1216
  return dirty;
1520
1217
  }
1521
1218
  /* Assign config vars */
1522
-
1523
-
1524
1219
  if (!SET_CONFIG) {
1525
1220
  _parseConfig(cfg);
1526
1221
  }
1527
1222
  /* Clean up removed elements */
1528
-
1529
-
1530
1223
  DOMPurify.removed = [];
1531
1224
  /* Check if dirty is correctly typed for IN_PLACE */
1532
-
1533
1225
  if (typeof dirty === 'string') {
1534
1226
  IN_PLACE = false;
1535
1227
  }
1536
-
1537
1228
  if (IN_PLACE) {
1538
1229
  /* Do some early pre-sanitization to avoid unsafe root nodes */
1539
1230
  if (dirty.nodeName) {
1540
- var tagName = transformCaseFunc(dirty.nodeName);
1541
-
1231
+ const tagName = transformCaseFunc(dirty.nodeName);
1542
1232
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1543
1233
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1544
1234
  }
@@ -1548,8 +1238,7 @@
1548
1238
  elements being stripped by the parser */
1549
1239
  body = _initDocument('<!---->');
1550
1240
  importedNode = body.ownerDocument.importNode(dirty, true);
1551
-
1552
- if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1241
+ if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
1553
1242
  /* Node is already a body, use as is */
1554
1243
  body = importedNode;
1555
1244
  } else if (importedNode.nodeName === 'HTML') {
@@ -1560,62 +1249,43 @@
1560
1249
  }
1561
1250
  } else {
1562
1251
  /* Exit directly if we have nothing to do */
1563
- if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
1252
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
1253
+ // eslint-disable-next-line unicorn/prefer-includes
1564
1254
  dirty.indexOf('<') === -1) {
1565
1255
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1566
1256
  }
1567
1257
  /* Initialize the document to work on */
1568
-
1569
-
1570
1258
  body = _initDocument(dirty);
1571
1259
  /* Check we have a DOM node from the data */
1572
-
1573
1260
  if (!body) {
1574
1261
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
1575
1262
  }
1576
1263
  }
1577
1264
  /* Remove first element node (ours) if FORCE_BODY is set */
1578
-
1579
-
1580
1265
  if (body && FORCE_BODY) {
1581
1266
  _forceRemove(body.firstChild);
1582
1267
  }
1583
1268
  /* Get node iterator */
1584
-
1585
-
1586
- var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1269
+ const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
1587
1270
  /* Now start iterating over the created document */
1588
-
1589
-
1590
1271
  while (currentNode = nodeIterator.nextNode()) {
1591
1272
  /* Sanitize tags and elements */
1592
- if (_sanitizeElements(currentNode)) {
1593
- continue;
1594
- }
1273
+ _sanitizeElements(currentNode);
1274
+ /* Check attributes next */
1275
+ _sanitizeAttributes(currentNode);
1595
1276
  /* Shadow DOM detected, sanitize it */
1596
-
1597
-
1598
1277
  if (currentNode.content instanceof DocumentFragment) {
1599
1278
  _sanitizeShadowDOM(currentNode.content);
1600
1279
  }
1601
- /* Check attributes, sanitize if necessary */
1602
-
1603
-
1604
- _sanitizeAttributes(currentNode);
1605
1280
  }
1606
1281
  /* If we sanitized `dirty` in-place, return it. */
1607
-
1608
-
1609
1282
  if (IN_PLACE) {
1610
1283
  return dirty;
1611
1284
  }
1612
1285
  /* Return sanitized string or DOM */
1613
-
1614
-
1615
1286
  if (RETURN_DOM) {
1616
1287
  if (RETURN_DOM_FRAGMENT) {
1617
1288
  returnNode = createDocumentFragment.call(body.ownerDocument);
1618
-
1619
1289
  while (body.firstChild) {
1620
1290
  // eslint-disable-next-line unicorn/prefer-dom-node-append
1621
1291
  returnNode.appendChild(body.firstChild);
@@ -1623,8 +1293,7 @@
1623
1293
  } else {
1624
1294
  returnNode = body;
1625
1295
  }
1626
-
1627
- if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
1296
+ if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
1628
1297
  /*
1629
1298
  AdoptNode() is not used because internal state is not reset
1630
1299
  (e.g. the past names map of a HTMLFormElement), this is safe
@@ -1634,132 +1303,60 @@
1634
1303
  */
1635
1304
  returnNode = importNode.call(originalDocument, returnNode, true);
1636
1305
  }
1637
-
1638
1306
  return returnNode;
1639
1307
  }
1640
-
1641
- var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1308
+ let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1642
1309
  /* Serialize doctype if allowed */
1643
-
1644
1310
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
1645
1311
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
1646
1312
  }
1647
1313
  /* Sanitize final string template-safe */
1648
-
1649
-
1650
1314
  if (SAFE_FOR_TEMPLATES) {
1651
- serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
1652
- serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
1653
- serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR$1, ' ');
1315
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1316
+ serializedHTML = stringReplace(serializedHTML, expr, ' ');
1317
+ });
1654
1318
  }
1655
-
1656
1319
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1657
1320
  };
1658
- /**
1659
- * Public method to set the configuration once
1660
- * setConfig
1661
- *
1662
- * @param {Object} cfg configuration object
1663
- */
1664
-
1665
-
1666
- DOMPurify.setConfig = function (cfg) {
1321
+ DOMPurify.setConfig = function () {
1322
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1667
1323
  _parseConfig(cfg);
1668
-
1669
1324
  SET_CONFIG = true;
1670
1325
  };
1671
- /**
1672
- * Public method to remove the configuration
1673
- * clearConfig
1674
- *
1675
- */
1676
-
1677
-
1678
1326
  DOMPurify.clearConfig = function () {
1679
1327
  CONFIG = null;
1680
1328
  SET_CONFIG = false;
1681
1329
  };
1682
- /**
1683
- * Public method to check if an attribute value is valid.
1684
- * Uses last set config, if any. Otherwise, uses config defaults.
1685
- * isValidAttribute
1686
- *
1687
- * @param {string} tag Tag name of containing element.
1688
- * @param {string} attr Attribute name.
1689
- * @param {string} value Attribute value.
1690
- * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1691
- */
1692
-
1693
-
1694
1330
  DOMPurify.isValidAttribute = function (tag, attr, value) {
1695
1331
  /* Initialize shared config vars if necessary. */
1696
1332
  if (!CONFIG) {
1697
1333
  _parseConfig({});
1698
1334
  }
1699
-
1700
- var lcTag = transformCaseFunc(tag);
1701
- var lcName = transformCaseFunc(attr);
1335
+ const lcTag = transformCaseFunc(tag);
1336
+ const lcName = transformCaseFunc(attr);
1702
1337
  return _isValidAttribute(lcTag, lcName, value);
1703
1338
  };
1704
- /**
1705
- * AddHook
1706
- * Public method to add DOMPurify hooks
1707
- *
1708
- * @param {String} entryPoint entry point for the hook to add
1709
- * @param {Function} hookFunction function to execute
1710
- */
1711
-
1712
-
1713
1339
  DOMPurify.addHook = function (entryPoint, hookFunction) {
1714
1340
  if (typeof hookFunction !== 'function') {
1715
1341
  return;
1716
1342
  }
1717
-
1718
- hooks[entryPoint] = hooks[entryPoint] || [];
1719
1343
  arrayPush(hooks[entryPoint], hookFunction);
1720
1344
  };
1721
- /**
1722
- * RemoveHook
1723
- * Public method to remove a DOMPurify hook at a given entryPoint
1724
- * (pops it from the stack of hooks if more are present)
1725
- *
1726
- * @param {String} entryPoint entry point for the hook to remove
1727
- * @return {Function} removed(popped) hook
1728
- */
1729
-
1730
-
1731
- DOMPurify.removeHook = function (entryPoint) {
1732
- if (hooks[entryPoint]) {
1733
- return arrayPop(hooks[entryPoint]);
1345
+ DOMPurify.removeHook = function (entryPoint, hookFunction) {
1346
+ if (hookFunction !== undefined) {
1347
+ const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
1348
+ return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];
1734
1349
  }
1350
+ return arrayPop(hooks[entryPoint]);
1735
1351
  };
1736
- /**
1737
- * RemoveHooks
1738
- * Public method to remove all DOMPurify hooks at a given entryPoint
1739
- *
1740
- * @param {String} entryPoint entry point for the hooks to remove
1741
- */
1742
-
1743
-
1744
1352
  DOMPurify.removeHooks = function (entryPoint) {
1745
- if (hooks[entryPoint]) {
1746
- hooks[entryPoint] = [];
1747
- }
1353
+ hooks[entryPoint] = [];
1748
1354
  };
1749
- /**
1750
- * RemoveAllHooks
1751
- * Public method to remove all DOMPurify hooks
1752
- *
1753
- */
1754
-
1755
-
1756
1355
  DOMPurify.removeAllHooks = function () {
1757
- hooks = {};
1356
+ hooks = _createHooksMap();
1758
1357
  };
1759
-
1760
1358
  return DOMPurify;
1761
1359
  }
1762
-
1763
1360
  var purify = createDOMPurify();
1764
1361
 
1765
1362
  return purify;