dompurify 2.4.9 → 2.5.0

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 2.4.9 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.4.9/LICENSE */
1
+ /*! @license DOMPurify 2.5.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.5.0/LICENSE */
2
2
 
3
3
  (function (global, factory) {
4
4
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -15,21 +15,17 @@
15
15
  return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
16
16
  }, _typeof(obj);
17
17
  }
18
-
19
18
  function _setPrototypeOf(o, p) {
20
19
  _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
21
20
  o.__proto__ = p;
22
21
  return o;
23
22
  };
24
-
25
23
  return _setPrototypeOf(o, p);
26
24
  }
27
-
28
25
  function _isNativeReflectConstruct() {
29
26
  if (typeof Reflect === "undefined" || !Reflect.construct) return false;
30
27
  if (Reflect.construct.sham) return false;
31
28
  if (typeof Proxy === "function") return true;
32
-
33
29
  try {
34
30
  Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
35
31
  return true;
@@ -37,7 +33,6 @@
37
33
  return false;
38
34
  }
39
35
  }
40
-
41
36
  function _construct(Parent, args, Class) {
42
37
  if (_isNativeReflectConstruct()) {
43
38
  _construct = Reflect.construct;
@@ -51,22 +46,17 @@
51
46
  return instance;
52
47
  };
53
48
  }
54
-
55
49
  return _construct.apply(null, arguments);
56
50
  }
57
-
58
51
  function _toConsumableArray(arr) {
59
52
  return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
60
53
  }
61
-
62
54
  function _arrayWithoutHoles(arr) {
63
55
  if (Array.isArray(arr)) return _arrayLikeToArray(arr);
64
56
  }
65
-
66
57
  function _iterableToArray(iter) {
67
58
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
68
59
  }
69
-
70
60
  function _unsupportedIterableToArray(o, minLen) {
71
61
  if (!o) return;
72
62
  if (typeof o === "string") return _arrayLikeToArray(o, minLen);
@@ -75,56 +65,46 @@
75
65
  if (n === "Map" || n === "Set") return Array.from(o);
76
66
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
77
67
  }
78
-
79
68
  function _arrayLikeToArray(arr, len) {
80
69
  if (len == null || len > arr.length) len = arr.length;
81
-
82
70
  for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
83
-
84
71
  return arr2;
85
72
  }
86
-
87
73
  function _nonIterableSpread() {
88
74
  throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
89
75
  }
90
76
 
91
77
  var hasOwnProperty = Object.hasOwnProperty,
92
- setPrototypeOf = Object.setPrototypeOf,
93
- isFrozen = Object.isFrozen,
94
- getPrototypeOf = Object.getPrototypeOf,
95
- getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
78
+ setPrototypeOf = Object.setPrototypeOf,
79
+ isFrozen = Object.isFrozen,
80
+ getPrototypeOf = Object.getPrototypeOf,
81
+ getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
96
82
  var freeze = Object.freeze,
97
- seal = Object.seal,
98
- create = Object.create; // eslint-disable-line import/no-mutable-exports
99
-
83
+ seal = Object.seal,
84
+ create = Object.create; // eslint-disable-line import/no-mutable-exports
100
85
  var _ref = typeof Reflect !== 'undefined' && Reflect,
101
- apply = _ref.apply,
102
- construct = _ref.construct;
103
-
86
+ apply = _ref.apply,
87
+ construct = _ref.construct;
104
88
  if (!apply) {
105
89
  apply = function apply(fun, thisValue, args) {
106
90
  return fun.apply(thisValue, args);
107
91
  };
108
92
  }
109
-
110
93
  if (!freeze) {
111
94
  freeze = function freeze(x) {
112
95
  return x;
113
96
  };
114
97
  }
115
-
116
98
  if (!seal) {
117
99
  seal = function seal(x) {
118
100
  return x;
119
101
  };
120
102
  }
121
-
122
103
  if (!construct) {
123
104
  construct = function construct(Func, args) {
124
105
  return _construct(Func, _toConsumableArray(args));
125
106
  };
126
107
  }
127
-
128
108
  var arrayForEach = unapply(Array.prototype.forEach);
129
109
  var arrayPop = unapply(Array.prototype.pop);
130
110
  var arrayPush = unapply(Array.prototype.push);
@@ -141,7 +121,6 @@
141
121
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
142
122
  args[_key - 1] = arguments[_key];
143
123
  }
144
-
145
124
  return apply(func, thisArg, args);
146
125
  };
147
126
  }
@@ -150,103 +129,89 @@
150
129
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
151
130
  args[_key2] = arguments[_key2];
152
131
  }
153
-
154
132
  return construct(func, args);
155
133
  };
156
134
  }
157
- /* Add properties to a lookup table */
158
135
 
136
+ /* Add properties to a lookup table */
159
137
  function addToSet(set, array, transformCaseFunc) {
160
138
  var _transformCaseFunc;
161
-
162
139
  transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase;
163
-
164
140
  if (setPrototypeOf) {
165
141
  // Make 'in' and truthy checks like Boolean(set.constructor)
166
142
  // independent of any properties defined on Object.prototype.
167
143
  // Prevent prototype setters from intercepting set as a this value.
168
144
  setPrototypeOf(set, null);
169
145
  }
170
-
171
146
  var l = array.length;
172
-
173
147
  while (l--) {
174
148
  var element = array[l];
175
-
176
149
  if (typeof element === 'string') {
177
150
  var lcElement = transformCaseFunc(element);
178
-
179
151
  if (lcElement !== element) {
180
152
  // Config presets (e.g. tags.js, attrs.js) are immutable.
181
153
  if (!isFrozen(array)) {
182
154
  array[l] = lcElement;
183
155
  }
184
-
185
156
  element = lcElement;
186
157
  }
187
158
  }
188
-
189
159
  set[element] = true;
190
160
  }
191
-
192
161
  return set;
193
162
  }
194
- /* Shallow clone an object */
195
163
 
164
+ /* Shallow clone an object */
196
165
  function clone(object) {
197
166
  var newObject = create(null);
198
167
  var property;
199
-
200
168
  for (property in object) {
201
169
  if (apply(hasOwnProperty, object, [property]) === true) {
202
170
  newObject[property] = object[property];
203
171
  }
204
172
  }
205
-
206
173
  return newObject;
207
174
  }
175
+
208
176
  /* IE10 doesn't support __lookupGetter__ so lets'
209
177
  * simulate it. It also automatically checks
210
178
  * if the prop is function or getter and behaves
211
179
  * accordingly. */
212
-
213
180
  function lookupGetter(object, prop) {
214
181
  while (object !== null) {
215
182
  var desc = getOwnPropertyDescriptor(object, prop);
216
-
217
183
  if (desc) {
218
184
  if (desc.get) {
219
185
  return unapply(desc.get);
220
186
  }
221
-
222
187
  if (typeof desc.value === 'function') {
223
188
  return unapply(desc.value);
224
189
  }
225
190
  }
226
-
227
191
  object = getPrototypeOf(object);
228
192
  }
229
-
230
193
  function fallbackValue(element) {
231
194
  console.warn('fallback value for', element);
232
195
  return null;
233
196
  }
234
-
235
197
  return fallbackValue;
236
198
  }
237
199
 
238
- 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
200
+ 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']);
239
201
 
202
+ // SVG
240
203
  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']);
241
- 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.
204
+ 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']);
205
+
206
+ // List of SVG elements that are disallowed by default.
242
207
  // We still need to know them so that we can do namespace
243
208
  // checks properly in case one wants to add them to
244
209
  // allow-list.
245
-
246
210
  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']);
247
- 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,
248
- // even those that we disallow by default.
211
+ 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']);
249
212
 
213
+ // Similarly to SVG, we want to know all MathML elements,
214
+ // even those that we disallow by default.
250
215
  var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
251
216
  var text = freeze(['#text']);
252
217
 
@@ -255,14 +220,12 @@
255
220
  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']);
256
221
  var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
257
222
 
223
+ // eslint-disable-next-line unicorn/better-regex
258
224
  var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
259
-
260
225
  var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
261
226
  var TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
262
227
  var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
263
-
264
228
  var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
265
-
266
229
  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
267
230
  );
268
231
  var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
@@ -274,6 +237,7 @@
274
237
  var getGlobal = function getGlobal() {
275
238
  return typeof window === 'undefined' ? null : window;
276
239
  };
240
+
277
241
  /**
278
242
  * Creates a no-op policy for internal use only.
279
243
  * Don't export this function outside this module!
@@ -282,25 +246,20 @@
282
246
  * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
283
247
  * are not supported).
284
248
  */
285
-
286
-
287
249
  var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
288
250
  if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
289
251
  return null;
290
- } // Allow the callers to control the unique policy name
252
+ }
253
+
254
+ // Allow the callers to control the unique policy name
291
255
  // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
292
256
  // Policy creation with duplicate names throws in Trusted Types.
293
-
294
-
295
257
  var suffix = null;
296
258
  var ATTR_NAME = 'data-tt-policy-suffix';
297
-
298
259
  if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
299
260
  suffix = document.currentScript.getAttribute(ATTR_NAME);
300
261
  }
301
-
302
262
  var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
303
-
304
263
  try {
305
264
  return trustedTypes.createPolicy(policyName, {
306
265
  createHTML: function createHTML(html) {
@@ -318,115 +277,106 @@
318
277
  return null;
319
278
  }
320
279
  };
321
-
322
280
  function createDOMPurify() {
323
281
  var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
324
-
325
282
  var DOMPurify = function DOMPurify(root) {
326
283
  return createDOMPurify(root);
327
284
  };
285
+
328
286
  /**
329
287
  * Version label, exposed for easier checks
330
288
  * if DOMPurify is up to date or not
331
289
  */
290
+ DOMPurify.version = '2.5.0';
332
291
 
333
-
334
- DOMPurify.version = '2.4.9';
335
292
  /**
336
293
  * Array of elements that DOMPurify removed during sanitation.
337
294
  * Empty if nothing was removed.
338
295
  */
339
-
340
296
  DOMPurify.removed = [];
341
-
342
297
  if (!window || !window.document || window.document.nodeType !== 9) {
343
298
  // Not running in a browser, provide a factory function
344
299
  // so that you can pass your own Window
345
300
  DOMPurify.isSupported = false;
346
301
  return DOMPurify;
347
302
  }
348
-
349
303
  var originalDocument = window.document;
350
304
  var document = window.document;
351
305
  var DocumentFragment = window.DocumentFragment,
352
- HTMLTemplateElement = window.HTMLTemplateElement,
353
- Node = window.Node,
354
- Element = window.Element,
355
- NodeFilter = window.NodeFilter,
356
- _window$NamedNodeMap = window.NamedNodeMap,
357
- NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
358
- HTMLFormElement = window.HTMLFormElement,
359
- DOMParser = window.DOMParser,
360
- trustedTypes = window.trustedTypes;
306
+ HTMLTemplateElement = window.HTMLTemplateElement,
307
+ Node = window.Node,
308
+ Element = window.Element,
309
+ NodeFilter = window.NodeFilter,
310
+ _window$NamedNodeMap = window.NamedNodeMap,
311
+ NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
312
+ HTMLFormElement = window.HTMLFormElement,
313
+ DOMParser = window.DOMParser,
314
+ trustedTypes = window.trustedTypes;
361
315
  var ElementPrototype = Element.prototype;
362
316
  var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
363
317
  var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
364
318
  var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
365
- var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a
319
+ var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
320
+
321
+ // As per issue #47, the web-components registry is inherited by a
366
322
  // new document created via createHTMLDocument. As per the spec
367
323
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
368
324
  // a new empty registry is used when creating a template contents owner
369
325
  // document, so we use that as our parent document to ensure nothing
370
326
  // is inherited.
371
-
372
327
  if (typeof HTMLTemplateElement === 'function') {
373
328
  var template = document.createElement('template');
374
-
375
329
  if (template.content && template.content.ownerDocument) {
376
330
  document = template.content.ownerDocument;
377
331
  }
378
332
  }
379
-
380
333
  var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
381
-
382
334
  var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
383
335
  var _document = document,
384
- implementation = _document.implementation,
385
- createNodeIterator = _document.createNodeIterator,
386
- createDocumentFragment = _document.createDocumentFragment,
387
- getElementsByTagName = _document.getElementsByTagName;
336
+ implementation = _document.implementation,
337
+ createNodeIterator = _document.createNodeIterator,
338
+ createDocumentFragment = _document.createDocumentFragment,
339
+ getElementsByTagName = _document.getElementsByTagName;
388
340
  var importNode = originalDocument.importNode;
389
341
  var documentMode = {};
390
-
391
342
  try {
392
343
  documentMode = clone(document).documentMode ? document.documentMode : {};
393
344
  } catch (_) {}
394
-
395
345
  var hooks = {};
346
+
396
347
  /**
397
348
  * Expose whether this browser supports running the full DOMPurify.
398
349
  */
399
-
400
350
  DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined && documentMode !== 9;
401
351
  var MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
402
- ERB_EXPR$1 = ERB_EXPR,
403
- TMPLIT_EXPR$1 = TMPLIT_EXPR,
404
- DATA_ATTR$1 = DATA_ATTR,
405
- ARIA_ATTR$1 = ARIA_ATTR,
406
- IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
407
- ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
408
- CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
352
+ ERB_EXPR$1 = ERB_EXPR,
353
+ TMPLIT_EXPR$1 = TMPLIT_EXPR,
354
+ DATA_ATTR$1 = DATA_ATTR,
355
+ ARIA_ATTR$1 = ARIA_ATTR,
356
+ IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
357
+ ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
358
+ CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
409
359
  var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
360
+
410
361
  /**
411
362
  * We consider the elements and attributes below to be safe. Ideally
412
363
  * don't add any new ones but feel free to remove unwanted ones.
413
364
  */
414
365
 
415
366
  /* allowed element names */
416
-
417
367
  var ALLOWED_TAGS = null;
418
368
  var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));
419
- /* Allowed attribute names */
420
369
 
370
+ /* Allowed attribute names */
421
371
  var ALLOWED_ATTR = null;
422
372
  var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));
373
+
423
374
  /*
424
375
  * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
425
376
  * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
426
377
  * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
427
378
  * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
428
379
  */
429
-
430
380
  var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
431
381
  tagNameCheck: {
432
382
  writable: true,
@@ -447,59 +397,65 @@
447
397
  value: false
448
398
  }
449
399
  }));
450
- /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
451
400
 
401
+ /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
452
402
  var FORBID_TAGS = null;
453
- /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
454
403
 
404
+ /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
455
405
  var FORBID_ATTR = null;
456
- /* Decide if ARIA attributes are okay */
457
406
 
407
+ /* Decide if ARIA attributes are okay */
458
408
  var ALLOW_ARIA_ATTR = true;
459
- /* Decide if custom data attributes are okay */
460
409
 
410
+ /* Decide if custom data attributes are okay */
461
411
  var ALLOW_DATA_ATTR = true;
462
- /* Decide if unknown protocols are okay */
463
412
 
413
+ /* Decide if unknown protocols are okay */
464
414
  var ALLOW_UNKNOWN_PROTOCOLS = false;
415
+
465
416
  /* Decide if self-closing tags in attributes are allowed.
466
417
  * Usually removed due to a mXSS issue in jQuery 3.0 */
467
-
468
418
  var ALLOW_SELF_CLOSE_IN_ATTR = true;
419
+
469
420
  /* Output should be safe for common template engines.
470
421
  * This means, DOMPurify removes data attributes, mustaches and ERB
471
422
  */
472
-
473
423
  var SAFE_FOR_TEMPLATES = false;
474
- /* Decide if document with <html>... should be returned */
475
424
 
425
+ /* Output should be safe even for XML used within HTML and alike.
426
+ * This means, DOMPurify removes comments when containing risky content.
427
+ */
428
+ var SAFE_FOR_XML = true;
429
+
430
+ /* Decide if document with <html>... should be returned */
476
431
  var WHOLE_DOCUMENT = false;
477
- /* Track whether config is already set on this instance of DOMPurify. */
478
432
 
433
+ /* Track whether config is already set on this instance of DOMPurify. */
479
434
  var SET_CONFIG = false;
435
+
480
436
  /* Decide if all elements (e.g. style, script) must be children of
481
437
  * document.body. By default, browsers might move them to document.head */
482
-
483
438
  var FORCE_BODY = false;
439
+
484
440
  /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
485
441
  * string (or a TrustedHTML object if Trusted Types are supported).
486
442
  * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
487
443
  */
488
-
489
444
  var RETURN_DOM = false;
445
+
490
446
  /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
491
447
  * string (or a TrustedHTML object if Trusted Types are supported) */
492
-
493
448
  var RETURN_DOM_FRAGMENT = false;
449
+
494
450
  /* Try to return a Trusted Type object instead of a string, return a string in
495
451
  * case Trusted Types are not supported */
496
-
497
452
  var RETURN_TRUSTED_TYPE = false;
453
+
498
454
  /* Output should be free from DOM clobbering attacks?
499
455
  * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
500
456
  */
501
-
502
457
  var SANITIZE_DOM = true;
458
+
503
459
  /* Achieve full DOM Clobbering protection by isolating the namespace of named
504
460
  * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
505
461
  *
@@ -513,98 +469,98 @@
513
469
  * Namespace isolation is implemented by prefixing `id` and `name` attributes
514
470
  * with a constant string, i.e., `user-content-`
515
471
  */
516
-
517
472
  var SANITIZE_NAMED_PROPS = false;
518
473
  var SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
519
- /* Keep element content when removing element? */
520
474
 
475
+ /* Keep element content when removing element? */
521
476
  var KEEP_CONTENT = true;
477
+
522
478
  /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
523
479
  * of importing it into a new Document and returning a sanitized copy */
524
-
525
480
  var IN_PLACE = false;
526
- /* Allow usage of profiles like html, svg and mathMl */
527
481
 
482
+ /* Allow usage of profiles like html, svg and mathMl */
528
483
  var USE_PROFILES = {};
529
- /* Tags to ignore content of when KEEP_CONTENT is true */
530
484
 
485
+ /* Tags to ignore content of when KEEP_CONTENT is true */
531
486
  var FORBID_CONTENTS = null;
532
487
  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']);
533
- /* Tags that are safe for data: URIs */
534
488
 
489
+ /* Tags that are safe for data: URIs */
535
490
  var DATA_URI_TAGS = null;
536
491
  var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
537
- /* Attributes safe for values like "javascript:" */
538
492
 
493
+ /* Attributes safe for values like "javascript:" */
539
494
  var URI_SAFE_ATTRIBUTES = null;
540
495
  var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
541
496
  var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
542
497
  var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
543
498
  var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
544
499
  /* Document namespace */
545
-
546
500
  var NAMESPACE = HTML_NAMESPACE;
547
501
  var IS_EMPTY_INPUT = false;
548
- /* Allowed XHTML+XML namespaces */
549
502
 
503
+ /* Allowed XHTML+XML namespaces */
550
504
  var ALLOWED_NAMESPACES = null;
551
505
  var DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
552
- /* Parsing of strict XHTML documents */
553
506
 
507
+ /* Parsing of strict XHTML documents */
554
508
  var PARSER_MEDIA_TYPE;
555
509
  var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
556
510
  var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
557
511
  var transformCaseFunc;
558
- /* Keep a reference to config to pass to hooks */
559
512
 
513
+ /* Keep a reference to config to pass to hooks */
560
514
  var CONFIG = null;
561
- /* Ideally, do not touch anything below this line */
562
515
 
516
+ /* Ideally, do not touch anything below this line */
563
517
  /* ______________________________________________ */
564
518
 
565
519
  var formElement = document.createElement('form');
566
-
567
520
  var isRegexOrFunction = function isRegexOrFunction(testValue) {
568
521
  return testValue instanceof RegExp || testValue instanceof Function;
569
522
  };
523
+
570
524
  /**
571
525
  * _parseConfig
572
526
  *
573
527
  * @param {Object} cfg optional config literal
574
528
  */
575
529
  // eslint-disable-next-line complexity
576
-
577
-
578
530
  var _parseConfig = function _parseConfig(cfg) {
579
531
  if (CONFIG && CONFIG === cfg) {
580
532
  return;
581
533
  }
582
- /* Shield configuration object from tampering */
583
-
584
534
 
535
+ /* Shield configuration object from tampering */
585
536
  if (!cfg || _typeof(cfg) !== 'object') {
586
537
  cfg = {};
587
538
  }
588
- /* Shield configuration object from prototype pollution */
589
-
590
539
 
540
+ /* Shield configuration object from prototype pollution */
591
541
  cfg = clone(cfg);
592
- PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
593
- 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.
542
+ PARSER_MEDIA_TYPE =
543
+ // eslint-disable-next-line unicorn/prefer-includes
544
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
594
545
 
546
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
595
547
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
596
- /* Set configuration parameters */
597
548
 
549
+ /* Set configuration parameters */
598
550
  ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
599
551
  ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
600
552
  ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
601
- URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent
602
- cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent
553
+ URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
554
+ // eslint-disable-line indent
555
+ cfg.ADD_URI_SAFE_ATTR,
556
+ // eslint-disable-line indent
603
557
  transformCaseFunc // eslint-disable-line indent
604
558
  ) // eslint-disable-line indent
605
559
  : DEFAULT_URI_SAFE_ATTRIBUTES;
606
- DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent
607
- cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent
560
+ DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
561
+ // eslint-disable-line indent
562
+ cfg.ADD_DATA_URI_TAGS,
563
+ // eslint-disable-line indent
608
564
  transformCaseFunc // eslint-disable-line indent
609
565
  ) // eslint-disable-line indent
610
566
  : DEFAULT_DATA_URI_TAGS;
@@ -613,161 +569,128 @@
613
569
  FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
614
570
  USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
615
571
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
616
-
617
572
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
618
-
619
573
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
620
-
621
574
  ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
622
-
623
575
  SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
624
-
576
+ SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
625
577
  WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
626
-
627
578
  RETURN_DOM = cfg.RETURN_DOM || false; // Default false
628
-
629
579
  RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
630
-
631
580
  RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
632
-
633
581
  FORCE_BODY = cfg.FORCE_BODY || false; // Default false
634
-
635
582
  SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
636
-
637
583
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
638
-
639
584
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
640
-
641
585
  IN_PLACE = cfg.IN_PLACE || false; // Default false
642
-
643
586
  IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
644
587
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
645
588
  CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
646
-
647
589
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
648
590
  CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
649
591
  }
650
-
651
592
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
652
593
  CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
653
594
  }
654
-
655
595
  if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
656
596
  CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
657
597
  }
658
-
659
598
  if (SAFE_FOR_TEMPLATES) {
660
599
  ALLOW_DATA_ATTR = false;
661
600
  }
662
-
663
601
  if (RETURN_DOM_FRAGMENT) {
664
602
  RETURN_DOM = true;
665
603
  }
666
- /* Parse profile info */
667
-
668
604
 
605
+ /* Parse profile info */
669
606
  if (USE_PROFILES) {
670
607
  ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));
671
608
  ALLOWED_ATTR = [];
672
-
673
609
  if (USE_PROFILES.html === true) {
674
610
  addToSet(ALLOWED_TAGS, html$1);
675
611
  addToSet(ALLOWED_ATTR, html);
676
612
  }
677
-
678
613
  if (USE_PROFILES.svg === true) {
679
614
  addToSet(ALLOWED_TAGS, svg$1);
680
615
  addToSet(ALLOWED_ATTR, svg);
681
616
  addToSet(ALLOWED_ATTR, xml);
682
617
  }
683
-
684
618
  if (USE_PROFILES.svgFilters === true) {
685
619
  addToSet(ALLOWED_TAGS, svgFilters);
686
620
  addToSet(ALLOWED_ATTR, svg);
687
621
  addToSet(ALLOWED_ATTR, xml);
688
622
  }
689
-
690
623
  if (USE_PROFILES.mathMl === true) {
691
624
  addToSet(ALLOWED_TAGS, mathMl$1);
692
625
  addToSet(ALLOWED_ATTR, mathMl);
693
626
  addToSet(ALLOWED_ATTR, xml);
694
627
  }
695
628
  }
696
- /* Merge configuration parameters */
697
-
698
629
 
630
+ /* Merge configuration parameters */
699
631
  if (cfg.ADD_TAGS) {
700
632
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
701
633
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
702
634
  }
703
-
704
635
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
705
636
  }
706
-
707
637
  if (cfg.ADD_ATTR) {
708
638
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
709
639
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
710
640
  }
711
-
712
641
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
713
642
  }
714
-
715
643
  if (cfg.ADD_URI_SAFE_ATTR) {
716
644
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
717
645
  }
718
-
719
646
  if (cfg.FORBID_CONTENTS) {
720
647
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
721
648
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
722
649
  }
723
-
724
650
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
725
651
  }
726
- /* Add #text in case KEEP_CONTENT is set to true */
727
-
728
652
 
653
+ /* Add #text in case KEEP_CONTENT is set to true */
729
654
  if (KEEP_CONTENT) {
730
655
  ALLOWED_TAGS['#text'] = true;
731
656
  }
732
- /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
733
-
734
657
 
658
+ /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
735
659
  if (WHOLE_DOCUMENT) {
736
660
  addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
737
661
  }
738
- /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
739
-
740
662
 
663
+ /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
741
664
  if (ALLOWED_TAGS.table) {
742
665
  addToSet(ALLOWED_TAGS, ['tbody']);
743
666
  delete FORBID_TAGS.tbody;
744
- } // Prevent further manipulation of configuration.
745
- // Not available in IE8, Safari 5, etc.
746
-
667
+ }
747
668
 
669
+ // Prevent further manipulation of configuration.
670
+ // Not available in IE8, Safari 5, etc.
748
671
  if (freeze) {
749
672
  freeze(cfg);
750
673
  }
751
-
752
674
  CONFIG = cfg;
753
675
  };
754
-
755
676
  var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
756
- var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML
677
+ var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
678
+
679
+ // Certain elements are allowed in both SVG and HTML
757
680
  // namespace. We need to specify them explicitly
758
681
  // so that they don't get erroneously deleted from
759
682
  // HTML namespace.
760
-
761
683
  var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
684
+
762
685
  /* Keep track of all possible SVG and MathML tags
763
686
  * so that we can perform the namespace checks
764
687
  * correctly. */
765
-
766
688
  var ALL_SVG_TAGS = addToSet({}, svg$1);
767
689
  addToSet(ALL_SVG_TAGS, svgFilters);
768
690
  addToSet(ALL_SVG_TAGS, svgDisallowed);
769
691
  var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
770
692
  addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
693
+
771
694
  /**
772
695
  *
773
696
  *
@@ -776,64 +699,59 @@
776
699
  * namespace that a spec-compliant parser would never
777
700
  * return. Return true otherwise.
778
701
  */
779
-
780
702
  var _checkValidNamespace = function _checkValidNamespace(element) {
781
- var parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode
782
- // can be null. We just simulate parent in this case.
703
+ var parent = getParentNode(element);
783
704
 
705
+ // In JSDOM, if we're inside shadow DOM, then parentNode
706
+ // can be null. We just simulate parent in this case.
784
707
  if (!parent || !parent.tagName) {
785
708
  parent = {
786
709
  namespaceURI: NAMESPACE,
787
710
  tagName: 'template'
788
711
  };
789
712
  }
790
-
791
713
  var tagName = stringToLowerCase(element.tagName);
792
714
  var parentTagName = stringToLowerCase(parent.tagName);
793
-
794
715
  if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
795
716
  return false;
796
717
  }
797
-
798
718
  if (element.namespaceURI === SVG_NAMESPACE) {
799
719
  // The only way to switch from HTML namespace to SVG
800
720
  // is via <svg>. If it happens via any other tag, then
801
721
  // it should be killed.
802
722
  if (parent.namespaceURI === HTML_NAMESPACE) {
803
723
  return tagName === 'svg';
804
- } // The only way to switch from MathML to SVG is via`
724
+ }
725
+
726
+ // The only way to switch from MathML to SVG is via`
805
727
  // svg if parent is either <annotation-xml> or MathML
806
728
  // text integration points.
807
-
808
-
809
729
  if (parent.namespaceURI === MATHML_NAMESPACE) {
810
730
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
811
- } // We only allow elements that are defined in SVG
812
- // spec. All others are disallowed in SVG namespace.
813
-
731
+ }
814
732
 
733
+ // We only allow elements that are defined in SVG
734
+ // spec. All others are disallowed in SVG namespace.
815
735
  return Boolean(ALL_SVG_TAGS[tagName]);
816
736
  }
817
-
818
737
  if (element.namespaceURI === MATHML_NAMESPACE) {
819
738
  // The only way to switch from HTML namespace to MathML
820
739
  // is via <math>. If it happens via any other tag, then
821
740
  // it should be killed.
822
741
  if (parent.namespaceURI === HTML_NAMESPACE) {
823
742
  return tagName === 'math';
824
- } // The only way to switch from SVG to MathML is via
825
- // <math> and HTML integration points
826
-
743
+ }
827
744
 
745
+ // The only way to switch from SVG to MathML is via
746
+ // <math> and HTML integration points
828
747
  if (parent.namespaceURI === SVG_NAMESPACE) {
829
748
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
830
- } // We only allow elements that are defined in MathML
831
- // spec. All others are disallowed in MathML namespace.
832
-
749
+ }
833
750
 
751
+ // We only allow elements that are defined in MathML
752
+ // spec. All others are disallowed in MathML namespace.
834
753
  return Boolean(ALL_MATHML_TAGS[tagName]);
835
754
  }
836
-
837
755
  if (element.namespaceURI === HTML_NAMESPACE) {
838
756
  // The only way to switch from SVG to HTML is via
839
757
  // HTML integration points, and from MathML to HTML
@@ -841,39 +759,36 @@
841
759
  if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
842
760
  return false;
843
761
  }
844
-
845
762
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
846
763
  return false;
847
- } // We disallow tags that are specific for MathML
848
- // or SVG and should never appear in HTML namespace
849
-
764
+ }
850
765
 
766
+ // We disallow tags that are specific for MathML
767
+ // or SVG and should never appear in HTML namespace
851
768
  return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
852
- } // For XHTML and XML documents that support custom namespaces
853
-
769
+ }
854
770
 
771
+ // For XHTML and XML documents that support custom namespaces
855
772
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
856
773
  return true;
857
- } // The code should never reach this place (this means
774
+ }
775
+
776
+ // The code should never reach this place (this means
858
777
  // that the element somehow got namespace that is not
859
778
  // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
860
779
  // Return false just in case.
861
-
862
-
863
780
  return false;
864
781
  };
782
+
865
783
  /**
866
784
  * _forceRemove
867
785
  *
868
786
  * @param {Node} node a DOM node
869
787
  */
870
-
871
-
872
788
  var _forceRemove = function _forceRemove(node) {
873
789
  arrayPush(DOMPurify.removed, {
874
790
  element: node
875
791
  });
876
-
877
792
  try {
878
793
  // eslint-disable-next-line unicorn/prefer-dom-node-remove
879
794
  node.parentNode.removeChild(node);
@@ -885,14 +800,13 @@
885
800
  }
886
801
  }
887
802
  };
803
+
888
804
  /**
889
805
  * _removeAttribute
890
806
  *
891
807
  * @param {String} name an Attribute name
892
808
  * @param {Node} node a DOM node
893
809
  */
894
-
895
-
896
810
  var _removeAttribute = function _removeAttribute(name, node) {
897
811
  try {
898
812
  arrayPush(DOMPurify.removed, {
@@ -905,9 +819,9 @@
905
819
  from: node
906
820
  });
907
821
  }
822
+ node.removeAttribute(name);
908
823
 
909
- node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes
910
-
824
+ // We void attribute values for unremovable "is"" attributes
911
825
  if (name === 'is' && !ALLOWED_ATTR[name]) {
912
826
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
913
827
  try {
@@ -920,19 +834,17 @@
920
834
  }
921
835
  }
922
836
  };
837
+
923
838
  /**
924
839
  * _initDocument
925
840
  *
926
841
  * @param {String} dirty a string of dirty markup
927
842
  * @return {Document} a DOM, filled with the dirty markup
928
843
  */
929
-
930
-
931
844
  var _initDocument = function _initDocument(dirty) {
932
845
  /* Create a HTML document */
933
846
  var doc;
934
847
  var leadingWhitespace;
935
-
936
848
  if (FORCE_BODY) {
937
849
  dirty = '<remove></remove>' + dirty;
938
850
  } else {
@@ -940,83 +852,74 @@
940
852
  var matches = stringMatch(dirty, /^[\r\n\t ]+/);
941
853
  leadingWhitespace = matches && matches[0];
942
854
  }
943
-
944
855
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
945
856
  // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
946
857
  dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
947
858
  }
948
-
949
859
  var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
950
860
  /*
951
861
  * Use the DOMParser API by default, fallback later if needs be
952
862
  * DOMParser not work for svg when has multiple root element.
953
863
  */
954
-
955
864
  if (NAMESPACE === HTML_NAMESPACE) {
956
865
  try {
957
866
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
958
867
  } catch (_) {}
959
868
  }
960
- /* Use createHTMLDocument in case DOMParser is not available */
961
-
962
869
 
870
+ /* Use createHTMLDocument in case DOMParser is not available */
963
871
  if (!doc || !doc.documentElement) {
964
872
  doc = implementation.createDocument(NAMESPACE, 'template', null);
965
-
966
873
  try {
967
874
  doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
968
- } catch (_) {// Syntax error if dirtyPayload is invalid xml
875
+ } catch (_) {
876
+ // Syntax error if dirtyPayload is invalid xml
969
877
  }
970
878
  }
971
-
972
879
  var body = doc.body || doc.documentElement;
973
-
974
880
  if (dirty && leadingWhitespace) {
975
881
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
976
882
  }
977
- /* Work on whole document or just its body */
978
-
979
883
 
884
+ /* Work on whole document or just its body */
980
885
  if (NAMESPACE === HTML_NAMESPACE) {
981
886
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
982
887
  }
983
-
984
888
  return WHOLE_DOCUMENT ? doc.documentElement : body;
985
889
  };
890
+
986
891
  /**
987
892
  * _createIterator
988
893
  *
989
894
  * @param {Document} root document/fragment to create iterator for
990
895
  * @return {Iterator} iterator instance
991
896
  */
992
-
993
-
994
897
  var _createIterator = function _createIterator(root) {
995
- return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
898
+ return createNodeIterator.call(root.ownerDocument || root, root,
899
+ // eslint-disable-next-line no-bitwise
996
900
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null, false);
997
901
  };
902
+
998
903
  /**
999
904
  * _isClobbered
1000
905
  *
1001
906
  * @param {Node} elm element to check for clobbering attacks
1002
907
  * @return {Boolean} true if clobbered, false if safe
1003
908
  */
1004
-
1005
-
1006
909
  var _isClobbered = function _isClobbered(elm) {
1007
910
  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');
1008
911
  };
912
+
1009
913
  /**
1010
914
  * _isNode
1011
915
  *
1012
916
  * @param {Node} obj object to check whether it's a DOM node
1013
917
  * @return {Boolean} true is object is a DOM node
1014
918
  */
1015
-
1016
-
1017
919
  var _isNode = function _isNode(object) {
1018
920
  return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
1019
921
  };
922
+
1020
923
  /**
1021
924
  * _executeHook
1022
925
  * Execute user configurable hooks
@@ -1025,17 +928,15 @@
1025
928
  * @param {Node} currentNode node to work on with the hook
1026
929
  * @param {Object} data additional hook parameters
1027
930
  */
1028
-
1029
-
1030
931
  var _executeHook = function _executeHook(entryPoint, currentNode, data) {
1031
932
  if (!hooks[entryPoint]) {
1032
933
  return;
1033
934
  }
1034
-
1035
935
  arrayForEach(hooks[entryPoint], function (hook) {
1036
936
  hook.call(DOMPurify, currentNode, data, CONFIG);
1037
937
  });
1038
938
  };
939
+
1039
940
  /**
1040
941
  * _sanitizeElements
1041
942
  *
@@ -1046,118 +947,99 @@
1046
947
  * @param {Node} currentNode to check for permission to exist
1047
948
  * @return {Boolean} true if node was killed, false if left alive
1048
949
  */
1049
-
1050
-
1051
950
  var _sanitizeElements = function _sanitizeElements(currentNode) {
1052
951
  var content;
1053
- /* Execute a hook if present */
1054
952
 
953
+ /* Execute a hook if present */
1055
954
  _executeHook('beforeSanitizeElements', currentNode, null);
1056
- /* Check if element is clobbered or can clobber */
1057
-
1058
955
 
956
+ /* Check if element is clobbered or can clobber */
1059
957
  if (_isClobbered(currentNode)) {
1060
958
  _forceRemove(currentNode);
1061
-
1062
959
  return true;
1063
960
  }
1064
- /* Check if tagname contains Unicode */
1065
-
1066
961
 
962
+ /* Check if tagname contains Unicode */
1067
963
  if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) {
1068
964
  _forceRemove(currentNode);
1069
-
1070
965
  return true;
1071
966
  }
1072
- /* Now let's check the element's type and name */
1073
-
1074
967
 
968
+ /* Now let's check the element's type and name */
1075
969
  var tagName = transformCaseFunc(currentNode.nodeName);
1076
- /* Execute a hook if present */
1077
970
 
971
+ /* Execute a hook if present */
1078
972
  _executeHook('uponSanitizeElement', currentNode, {
1079
973
  tagName: tagName,
1080
974
  allowedTags: ALLOWED_TAGS
1081
975
  });
1082
- /* Detect mXSS attempts abusing namespace confusion */
1083
-
1084
976
 
977
+ /* Detect mXSS attempts abusing namespace confusion */
1085
978
  if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
1086
979
  _forceRemove(currentNode);
1087
-
1088
980
  return true;
1089
981
  }
1090
- /* Mitigate a problem with templates inside select */
1091
-
1092
982
 
983
+ /* Mitigate a problem with templates inside select */
1093
984
  if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
1094
985
  _forceRemove(currentNode);
1095
-
1096
986
  return true;
1097
987
  }
1098
- /* Remove any ocurrence of processing instructions */
1099
-
1100
988
 
989
+ /* Remove any ocurrence of processing instructions */
1101
990
  if (currentNode.nodeType === 7) {
1102
991
  _forceRemove(currentNode);
1103
-
1104
992
  return true;
1105
993
  }
1106
- /* Remove element if anything forbids its presence */
1107
994
 
995
+ /* Remove any kind of possibly harmful comments */
996
+ if (SAFE_FOR_XML && currentNode.nodeType === 8 && regExpTest(/<[/\w]/g, currentNode.data)) {
997
+ _forceRemove(currentNode);
998
+ return true;
999
+ }
1108
1000
 
1001
+ /* Remove element if anything forbids its presence */
1109
1002
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1110
1003
  /* Check if we have a custom element to handle */
1111
1004
  if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
1112
1005
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
1113
1006
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
1114
1007
  }
1115
- /* Keep content except for bad-listed elements */
1116
-
1117
1008
 
1009
+ /* Keep content except for bad-listed elements */
1118
1010
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1119
1011
  var parentNode = getParentNode(currentNode) || currentNode.parentNode;
1120
1012
  var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
1121
-
1122
1013
  if (childNodes && parentNode) {
1123
1014
  var childCount = childNodes.length;
1124
-
1125
1015
  for (var i = childCount - 1; i >= 0; --i) {
1126
1016
  parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
1127
1017
  }
1128
1018
  }
1129
1019
  }
1130
-
1131
1020
  _forceRemove(currentNode);
1132
-
1133
1021
  return true;
1134
1022
  }
1135
- /* Check whether element has a valid namespace */
1136
-
1137
1023
 
1024
+ /* Check whether element has a valid namespace */
1138
1025
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
1139
1026
  _forceRemove(currentNode);
1140
-
1141
1027
  return true;
1142
1028
  }
1143
- /* Make sure that older browsers don't get fallback-tag mXSS */
1144
-
1145
1029
 
1030
+ /* Make sure that older browsers don't get fallback-tag mXSS */
1146
1031
  if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
1147
1032
  _forceRemove(currentNode);
1148
-
1149
1033
  return true;
1150
1034
  }
1151
- /* Sanitize element content to be template-safe */
1152
-
1153
1035
 
1036
+ /* Sanitize element content to be template-safe */
1154
1037
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
1155
1038
  /* Get the element's text content */
1156
1039
  content = currentNode.textContent;
1157
1040
  content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
1158
1041
  content = stringReplace(content, ERB_EXPR$1, ' ');
1159
1042
  content = stringReplace(content, TMPLIT_EXPR$1, ' ');
1160
-
1161
1043
  if (currentNode.textContent !== content) {
1162
1044
  arrayPush(DOMPurify.removed, {
1163
1045
  element: currentNode.cloneNode()
@@ -1165,13 +1047,12 @@
1165
1047
  currentNode.textContent = content;
1166
1048
  }
1167
1049
  }
1168
- /* Execute a hook if present */
1169
-
1170
1050
 
1051
+ /* Execute a hook if present */
1171
1052
  _executeHook('afterSanitizeElements', currentNode, null);
1172
-
1173
1053
  return false;
1174
1054
  };
1055
+
1175
1056
  /**
1176
1057
  * _isValidAttribute
1177
1058
  *
@@ -1181,47 +1062,44 @@
1181
1062
  * @return {Boolean} Returns true if `value` is valid, otherwise false.
1182
1063
  */
1183
1064
  // eslint-disable-next-line complexity
1184
-
1185
-
1186
1065
  var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1187
1066
  /* Make sure attribute cannot clobber */
1188
1067
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1189
1068
  return false;
1190
1069
  }
1070
+
1191
1071
  /* Allow valid data-* attributes: At least one character after "-"
1192
1072
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1193
1073
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1194
1074
  We don't need to check the value; it's always URI safe. */
1195
-
1196
-
1197
1075
  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]) {
1198
- if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1076
+ if (
1077
+ // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1199
1078
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1200
1079
  // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1201
- _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
1080
+ _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)) ||
1081
+ // Alternative, second condition checks if it's an `is`-attribute, AND
1202
1082
  // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1203
1083
  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 {
1204
1084
  return false;
1205
1085
  }
1206
1086
  /* Check value is safe. First, is attr inert? If so, is safe */
1207
-
1208
1087
  } 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) {
1209
1088
  return false;
1210
1089
  } else ;
1211
-
1212
1090
  return true;
1213
1091
  };
1092
+
1214
1093
  /**
1215
1094
  * _basicCustomElementCheck
1216
1095
  * checks if at least one dash is included in tagName, and it's not the first char
1217
1096
  * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1218
1097
  * @param {string} tagName name of the tag of the node to sanitize
1219
1098
  */
1220
-
1221
-
1222
1099
  var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
1223
1100
  return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT$1);
1224
1101
  };
1102
+
1225
1103
  /**
1226
1104
  * _sanitizeAttributes
1227
1105
  *
@@ -1232,24 +1110,19 @@
1232
1110
  *
1233
1111
  * @param {Node} currentNode to sanitize
1234
1112
  */
1235
-
1236
-
1237
1113
  var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1238
1114
  var attr;
1239
1115
  var value;
1240
1116
  var lcName;
1241
1117
  var l;
1242
1118
  /* Execute a hook if present */
1243
-
1244
1119
  _executeHook('beforeSanitizeAttributes', currentNode, null);
1245
-
1246
1120
  var attributes = currentNode.attributes;
1247
- /* Check if we have attributes; if not we might have a text node */
1248
1121
 
1122
+ /* Check if we have attributes; if not we might have a text node */
1249
1123
  if (!attributes) {
1250
1124
  return;
1251
1125
  }
1252
-
1253
1126
  var hookEvent = {
1254
1127
  attrName: '',
1255
1128
  attrValue: '',
@@ -1257,79 +1130,67 @@
1257
1130
  allowedAttributes: ALLOWED_ATTR
1258
1131
  };
1259
1132
  l = attributes.length;
1260
- /* Go backwards over all attributes; safely remove bad ones */
1261
1133
 
1134
+ /* Go backwards over all attributes; safely remove bad ones */
1262
1135
  while (l--) {
1263
1136
  attr = attributes[l];
1264
1137
  var _attr = attr,
1265
- name = _attr.name,
1266
- namespaceURI = _attr.namespaceURI;
1138
+ name = _attr.name,
1139
+ namespaceURI = _attr.namespaceURI;
1267
1140
  value = name === 'value' ? attr.value : stringTrim(attr.value);
1268
1141
  lcName = transformCaseFunc(name);
1269
- /* Execute a hook if present */
1270
1142
 
1143
+ /* Execute a hook if present */
1271
1144
  hookEvent.attrName = lcName;
1272
1145
  hookEvent.attrValue = value;
1273
1146
  hookEvent.keepAttr = true;
1274
1147
  hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
1275
-
1276
1148
  _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
1277
-
1278
1149
  value = hookEvent.attrValue;
1279
1150
  /* Did the hooks approve of the attribute? */
1280
-
1281
1151
  if (hookEvent.forceKeepAttr) {
1282
1152
  continue;
1283
1153
  }
1284
- /* Remove attribute */
1285
-
1286
1154
 
1155
+ /* Remove attribute */
1287
1156
  _removeAttribute(name, currentNode);
1288
- /* Did the hooks approve of the attribute? */
1289
-
1290
1157
 
1158
+ /* Did the hooks approve of the attribute? */
1291
1159
  if (!hookEvent.keepAttr) {
1292
1160
  continue;
1293
1161
  }
1294
- /* Work around a security issue in jQuery 3.0 */
1295
-
1296
1162
 
1163
+ /* Work around a security issue in jQuery 3.0 */
1297
1164
  if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1298
1165
  _removeAttribute(name, currentNode);
1299
-
1300
1166
  continue;
1301
1167
  }
1302
- /* Sanitize attribute content to be template-safe */
1303
-
1304
1168
 
1169
+ /* Sanitize attribute content to be template-safe */
1305
1170
  if (SAFE_FOR_TEMPLATES) {
1306
1171
  value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
1307
1172
  value = stringReplace(value, ERB_EXPR$1, ' ');
1308
1173
  value = stringReplace(value, TMPLIT_EXPR$1, ' ');
1309
1174
  }
1310
- /* Is `value` valid for this attribute? */
1311
-
1312
1175
 
1176
+ /* Is `value` valid for this attribute? */
1313
1177
  var lcTag = transformCaseFunc(currentNode.nodeName);
1314
-
1315
1178
  if (!_isValidAttribute(lcTag, lcName, value)) {
1316
1179
  continue;
1317
1180
  }
1181
+
1318
1182
  /* Full DOM Clobbering protection via namespace isolation,
1319
1183
  * Prefix id and name attributes with `user-content-`
1320
1184
  */
1321
-
1322
-
1323
1185
  if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1324
1186
  // Remove the attribute with this value
1325
- _removeAttribute(name, currentNode); // Prefix the value and later re-create the attribute with the sanitized value
1326
-
1187
+ _removeAttribute(name, currentNode);
1327
1188
 
1189
+ // Prefix the value and later re-create the attribute with the sanitized value
1328
1190
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
1329
1191
  }
1330
- /* Handle attributes that require Trusted Types */
1331
-
1332
1192
 
1193
+ /* Handle attributes that require Trusted Types */
1333
1194
  if (trustedTypesPolicy && _typeof(trustedTypes) === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1334
1195
  if (namespaceURI) ; else {
1335
1196
  switch (trustedTypes.getAttributeType(lcTag, lcName)) {
@@ -1338,7 +1199,6 @@
1338
1199
  value = trustedTypesPolicy.createHTML(value);
1339
1200
  break;
1340
1201
  }
1341
-
1342
1202
  case 'TrustedScriptURL':
1343
1203
  {
1344
1204
  value = trustedTypesPolicy.createScriptURL(value);
@@ -1347,9 +1207,8 @@
1347
1207
  }
1348
1208
  }
1349
1209
  }
1350
- /* Handle invalid data-* attribute set by try-catching it */
1351
-
1352
1210
 
1211
+ /* Handle invalid data-* attribute set by try-catching it */
1353
1212
  try {
1354
1213
  if (namespaceURI) {
1355
1214
  currentNode.setAttributeNS(namespaceURI, name, value);
@@ -1357,56 +1216,47 @@
1357
1216
  /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1358
1217
  currentNode.setAttribute(name, value);
1359
1218
  }
1360
-
1361
1219
  arrayPop(DOMPurify.removed);
1362
1220
  } catch (_) {}
1363
1221
  }
1364
- /* Execute a hook if present */
1365
-
1366
1222
 
1223
+ /* Execute a hook if present */
1367
1224
  _executeHook('afterSanitizeAttributes', currentNode, null);
1368
1225
  };
1226
+
1369
1227
  /**
1370
1228
  * _sanitizeShadowDOM
1371
1229
  *
1372
1230
  * @param {DocumentFragment} fragment to iterate over recursively
1373
1231
  */
1374
-
1375
-
1376
1232
  var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1377
1233
  var shadowNode;
1378
-
1379
1234
  var shadowIterator = _createIterator(fragment);
1380
- /* Execute a hook if present */
1381
-
1382
1235
 
1236
+ /* Execute a hook if present */
1383
1237
  _executeHook('beforeSanitizeShadowDOM', fragment, null);
1384
-
1385
1238
  while (shadowNode = shadowIterator.nextNode()) {
1386
1239
  /* Execute a hook if present */
1387
1240
  _executeHook('uponSanitizeShadowNode', shadowNode, null);
1388
- /* Sanitize tags and elements */
1389
-
1390
1241
 
1242
+ /* Sanitize tags and elements */
1391
1243
  if (_sanitizeElements(shadowNode)) {
1392
1244
  continue;
1393
1245
  }
1394
- /* Deep shadow DOM detected */
1395
-
1396
1246
 
1247
+ /* Deep shadow DOM detected */
1397
1248
  if (shadowNode.content instanceof DocumentFragment) {
1398
1249
  _sanitizeShadowDOM(shadowNode.content);
1399
1250
  }
1400
- /* Check attributes, sanitize if necessary */
1401
-
1402
1251
 
1252
+ /* Check attributes, sanitize if necessary */
1403
1253
  _sanitizeAttributes(shadowNode);
1404
1254
  }
1405
- /* Execute a hook if present */
1406
-
1407
1255
 
1256
+ /* Execute a hook if present */
1408
1257
  _executeHook('afterSanitizeShadowDOM', fragment, null);
1409
1258
  };
1259
+
1410
1260
  /**
1411
1261
  * Sanitize
1412
1262
  * Public method providing core sanitation functionality
@@ -1415,8 +1265,6 @@
1415
1265
  * @param {Object} configuration object
1416
1266
  */
1417
1267
  // eslint-disable-next-line complexity
1418
-
1419
-
1420
1268
  DOMPurify.sanitize = function (dirty) {
1421
1269
  var cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1422
1270
  var body;
@@ -1427,19 +1275,15 @@
1427
1275
  /* Make sure we have a string to sanitize.
1428
1276
  DO NOT return early, as this will return the wrong type if
1429
1277
  the user has requested a DOM object rather than a string */
1430
-
1431
1278
  IS_EMPTY_INPUT = !dirty;
1432
-
1433
1279
  if (IS_EMPTY_INPUT) {
1434
1280
  dirty = '<!-->';
1435
1281
  }
1436
- /* Stringify, in case dirty is an object */
1437
-
1438
1282
 
1283
+ /* Stringify, in case dirty is an object */
1439
1284
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
1440
1285
  if (typeof dirty.toString === 'function') {
1441
1286
  dirty = dirty.toString();
1442
-
1443
1287
  if (typeof dirty !== 'string') {
1444
1288
  throw typeErrorCreate('dirty is not a string, aborting');
1445
1289
  }
@@ -1447,43 +1291,36 @@
1447
1291
  throw typeErrorCreate('toString is not a function');
1448
1292
  }
1449
1293
  }
1450
- /* Check we can run. Otherwise fall back or ignore */
1451
-
1452
1294
 
1295
+ /* Check we can run. Otherwise fall back or ignore */
1453
1296
  if (!DOMPurify.isSupported) {
1454
1297
  if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
1455
1298
  if (typeof dirty === 'string') {
1456
1299
  return window.toStaticHTML(dirty);
1457
1300
  }
1458
-
1459
1301
  if (_isNode(dirty)) {
1460
1302
  return window.toStaticHTML(dirty.outerHTML);
1461
1303
  }
1462
1304
  }
1463
-
1464
1305
  return dirty;
1465
1306
  }
1466
- /* Assign config vars */
1467
-
1468
1307
 
1308
+ /* Assign config vars */
1469
1309
  if (!SET_CONFIG) {
1470
1310
  _parseConfig(cfg);
1471
1311
  }
1472
- /* Clean up removed elements */
1473
-
1474
1312
 
1313
+ /* Clean up removed elements */
1475
1314
  DOMPurify.removed = [];
1476
- /* Check if dirty is correctly typed for IN_PLACE */
1477
1315
 
1316
+ /* Check if dirty is correctly typed for IN_PLACE */
1478
1317
  if (typeof dirty === 'string') {
1479
1318
  IN_PLACE = false;
1480
1319
  }
1481
-
1482
1320
  if (IN_PLACE) {
1483
1321
  /* Do some early pre-sanitization to avoid unsafe root nodes */
1484
1322
  if (dirty.nodeName) {
1485
1323
  var tagName = transformCaseFunc(dirty.nodeName);
1486
-
1487
1324
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1488
1325
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1489
1326
  }
@@ -1493,7 +1330,6 @@
1493
1330
  elements being stripped by the parser */
1494
1331
  body = _initDocument('<!---->');
1495
1332
  importedNode = body.ownerDocument.importNode(dirty, true);
1496
-
1497
1333
  if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1498
1334
  /* Node is already a body, use as is */
1499
1335
  body = importedNode;
@@ -1505,71 +1341,61 @@
1505
1341
  }
1506
1342
  } else {
1507
1343
  /* Exit directly if we have nothing to do */
1508
- if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
1344
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
1345
+ // eslint-disable-next-line unicorn/prefer-includes
1509
1346
  dirty.indexOf('<') === -1) {
1510
1347
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1511
1348
  }
1512
- /* Initialize the document to work on */
1513
-
1514
1349
 
1350
+ /* Initialize the document to work on */
1515
1351
  body = _initDocument(dirty);
1516
- /* Check we have a DOM node from the data */
1517
1352
 
1353
+ /* Check we have a DOM node from the data */
1518
1354
  if (!body) {
1519
1355
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
1520
1356
  }
1521
1357
  }
1522
- /* Remove first element node (ours) if FORCE_BODY is set */
1523
-
1524
1358
 
1359
+ /* Remove first element node (ours) if FORCE_BODY is set */
1525
1360
  if (body && FORCE_BODY) {
1526
1361
  _forceRemove(body.firstChild);
1527
1362
  }
1528
- /* Get node iterator */
1529
-
1530
1363
 
1364
+ /* Get node iterator */
1531
1365
  var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1532
- /* Now start iterating over the created document */
1533
-
1534
1366
 
1367
+ /* Now start iterating over the created document */
1535
1368
  while (currentNode = nodeIterator.nextNode()) {
1536
1369
  /* Fix IE's strange behavior with manipulated textNodes #89 */
1537
1370
  if (currentNode.nodeType === 3 && currentNode === oldNode) {
1538
1371
  continue;
1539
1372
  }
1540
- /* Sanitize tags and elements */
1541
-
1542
1373
 
1374
+ /* Sanitize tags and elements */
1543
1375
  if (_sanitizeElements(currentNode)) {
1544
1376
  continue;
1545
1377
  }
1546
- /* Shadow DOM detected, sanitize it */
1547
-
1548
1378
 
1379
+ /* Shadow DOM detected, sanitize it */
1549
1380
  if (currentNode.content instanceof DocumentFragment) {
1550
1381
  _sanitizeShadowDOM(currentNode.content);
1551
1382
  }
1552
- /* Check attributes, sanitize if necessary */
1553
-
1554
1383
 
1384
+ /* Check attributes, sanitize if necessary */
1555
1385
  _sanitizeAttributes(currentNode);
1556
-
1557
1386
  oldNode = currentNode;
1558
1387
  }
1559
-
1560
1388
  oldNode = null;
1561
- /* If we sanitized `dirty` in-place, return it. */
1562
1389
 
1390
+ /* If we sanitized `dirty` in-place, return it. */
1563
1391
  if (IN_PLACE) {
1564
1392
  return dirty;
1565
1393
  }
1566
- /* Return sanitized string or DOM */
1567
-
1568
1394
 
1395
+ /* Return sanitized string or DOM */
1569
1396
  if (RETURN_DOM) {
1570
1397
  if (RETURN_DOM_FRAGMENT) {
1571
1398
  returnNode = createDocumentFragment.call(body.ownerDocument);
1572
-
1573
1399
  while (body.firstChild) {
1574
1400
  // eslint-disable-next-line unicorn/prefer-dom-node-append
1575
1401
  returnNode.appendChild(body.firstChild);
@@ -1577,7 +1403,6 @@
1577
1403
  } else {
1578
1404
  returnNode = body;
1579
1405
  }
1580
-
1581
1406
  if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
1582
1407
  /*
1583
1408
  AdoptNode() is not used because internal state is not reset
@@ -1588,51 +1413,45 @@
1588
1413
  */
1589
1414
  returnNode = importNode.call(originalDocument, returnNode, true);
1590
1415
  }
1591
-
1592
1416
  return returnNode;
1593
1417
  }
1594
-
1595
1418
  var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1596
- /* Serialize doctype if allowed */
1597
1419
 
1420
+ /* Serialize doctype if allowed */
1598
1421
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
1599
1422
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
1600
1423
  }
1601
- /* Sanitize final string template-safe */
1602
-
1603
1424
 
1425
+ /* Sanitize final string template-safe */
1604
1426
  if (SAFE_FOR_TEMPLATES) {
1605
1427
  serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
1606
1428
  serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
1607
1429
  serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR$1, ' ');
1608
1430
  }
1609
-
1610
1431
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1611
1432
  };
1433
+
1612
1434
  /**
1613
1435
  * Public method to set the configuration once
1614
1436
  * setConfig
1615
1437
  *
1616
1438
  * @param {Object} cfg configuration object
1617
1439
  */
1618
-
1619
-
1620
1440
  DOMPurify.setConfig = function (cfg) {
1621
1441
  _parseConfig(cfg);
1622
-
1623
1442
  SET_CONFIG = true;
1624
1443
  };
1444
+
1625
1445
  /**
1626
1446
  * Public method to remove the configuration
1627
1447
  * clearConfig
1628
1448
  *
1629
1449
  */
1630
-
1631
-
1632
1450
  DOMPurify.clearConfig = function () {
1633
1451
  CONFIG = null;
1634
1452
  SET_CONFIG = false;
1635
1453
  };
1454
+
1636
1455
  /**
1637
1456
  * Public method to check if an attribute value is valid.
1638
1457
  * Uses last set config, if any. Otherwise, uses config defaults.
@@ -1643,18 +1462,16 @@
1643
1462
  * @param {string} value Attribute value.
1644
1463
  * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1645
1464
  */
1646
-
1647
-
1648
1465
  DOMPurify.isValidAttribute = function (tag, attr, value) {
1649
1466
  /* Initialize shared config vars if necessary. */
1650
1467
  if (!CONFIG) {
1651
1468
  _parseConfig({});
1652
1469
  }
1653
-
1654
1470
  var lcTag = transformCaseFunc(tag);
1655
1471
  var lcName = transformCaseFunc(attr);
1656
1472
  return _isValidAttribute(lcTag, lcName, value);
1657
1473
  };
1474
+
1658
1475
  /**
1659
1476
  * AddHook
1660
1477
  * Public method to add DOMPurify hooks
@@ -1662,16 +1479,14 @@
1662
1479
  * @param {String} entryPoint entry point for the hook to add
1663
1480
  * @param {Function} hookFunction function to execute
1664
1481
  */
1665
-
1666
-
1667
1482
  DOMPurify.addHook = function (entryPoint, hookFunction) {
1668
1483
  if (typeof hookFunction !== 'function') {
1669
1484
  return;
1670
1485
  }
1671
-
1672
1486
  hooks[entryPoint] = hooks[entryPoint] || [];
1673
1487
  arrayPush(hooks[entryPoint], hookFunction);
1674
1488
  };
1489
+
1675
1490
  /**
1676
1491
  * RemoveHook
1677
1492
  * Public method to remove a DOMPurify hook at a given entryPoint
@@ -1680,40 +1495,34 @@
1680
1495
  * @param {String} entryPoint entry point for the hook to remove
1681
1496
  * @return {Function} removed(popped) hook
1682
1497
  */
1683
-
1684
-
1685
1498
  DOMPurify.removeHook = function (entryPoint) {
1686
1499
  if (hooks[entryPoint]) {
1687
1500
  return arrayPop(hooks[entryPoint]);
1688
1501
  }
1689
1502
  };
1503
+
1690
1504
  /**
1691
1505
  * RemoveHooks
1692
1506
  * Public method to remove all DOMPurify hooks at a given entryPoint
1693
1507
  *
1694
1508
  * @param {String} entryPoint entry point for the hooks to remove
1695
1509
  */
1696
-
1697
-
1698
1510
  DOMPurify.removeHooks = function (entryPoint) {
1699
1511
  if (hooks[entryPoint]) {
1700
1512
  hooks[entryPoint] = [];
1701
1513
  }
1702
1514
  };
1515
+
1703
1516
  /**
1704
1517
  * RemoveAllHooks
1705
1518
  * Public method to remove all DOMPurify hooks
1706
1519
  *
1707
1520
  */
1708
-
1709
-
1710
1521
  DOMPurify.removeAllHooks = function () {
1711
1522
  hooks = {};
1712
1523
  };
1713
-
1714
1524
  return DOMPurify;
1715
1525
  }
1716
-
1717
1526
  var purify = createDOMPurify();
1718
1527
 
1719
1528
  return purify;