dompurify 3.1.7 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- /*! @license DOMPurify 3.1.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.1.7/LICENSE */
1
+ /*! @license DOMPurify 3.2.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.2.1/LICENSE */
2
2
 
3
3
  'use strict';
4
4
 
@@ -50,12 +50,11 @@ const stringTrim = unapply(String.prototype.trim);
50
50
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
51
51
  const regExpTest = unapply(RegExp.prototype.test);
52
52
  const typeErrorCreate = unconstruct(TypeError);
53
-
54
53
  /**
55
54
  * Creates a new function that calls the given function with a specified thisArg and arguments.
56
55
  *
57
- * @param {Function} func - The function to be wrapped and called.
58
- * @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
56
+ * @param func - The function to be wrapped and called.
57
+ * @returns A new function that calls the given function with a specified thisArg and arguments.
59
58
  */
60
59
  function unapply(func) {
61
60
  return function (thisArg) {
@@ -65,12 +64,11 @@ function unapply(func) {
65
64
  return apply(func, thisArg, args);
66
65
  };
67
66
  }
68
-
69
67
  /**
70
68
  * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
71
69
  *
72
- * @param {Function} func - The constructor function to be wrapped and called.
73
- * @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
70
+ * @param func - The constructor function to be wrapped and called.
71
+ * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
74
72
  */
75
73
  function unconstruct(func) {
76
74
  return function () {
@@ -80,14 +78,13 @@ function unconstruct(func) {
80
78
  return construct(func, args);
81
79
  };
82
80
  }
83
-
84
81
  /**
85
82
  * Add properties to a lookup table
86
83
  *
87
- * @param {Object} set - The set to which elements will be added.
88
- * @param {Array} array - The array containing elements to be added to the set.
89
- * @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
90
- * @returns {Object} The modified set with added elements.
84
+ * @param set - The set to which elements will be added.
85
+ * @param array - The array containing elements to be added to the set.
86
+ * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
87
+ * @returns The modified set with added elements.
91
88
  */
92
89
  function addToSet(set, array) {
93
90
  let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
@@ -114,12 +111,11 @@ function addToSet(set, array) {
114
111
  }
115
112
  return set;
116
113
  }
117
-
118
114
  /**
119
115
  * Clean up an array to harden against CSPP
120
116
  *
121
- * @param {Array} array - The array to be cleaned.
122
- * @returns {Array} The cleaned version of the array
117
+ * @param array - The array to be cleaned.
118
+ * @returns The cleaned version of the array
123
119
  */
124
120
  function cleanArray(array) {
125
121
  for (let index = 0; index < array.length; index++) {
@@ -130,12 +126,11 @@ function cleanArray(array) {
130
126
  }
131
127
  return array;
132
128
  }
133
-
134
129
  /**
135
130
  * Shallow clone an object
136
131
  *
137
- * @param {Object} object - The object to be cloned.
138
- * @returns {Object} A new object that copies the original.
132
+ * @param object - The object to be cloned.
133
+ * @returns A new object that copies the original.
139
134
  */
140
135
  function clone(object) {
141
136
  const newObject = create(null);
@@ -153,13 +148,12 @@ function clone(object) {
153
148
  }
154
149
  return newObject;
155
150
  }
156
-
157
151
  /**
158
152
  * This method automatically checks if the prop is function or getter and behaves accordingly.
159
153
  *
160
- * @param {Object} object - The object to look up the getter function in its prototype chain.
161
- * @param {String} prop - The property name for which to find the getter function.
162
- * @returns {Function} The getter function found in the prototype chain or a fallback function.
154
+ * @param object - The object to look up the getter function in its prototype chain.
155
+ * @param prop - The property name for which to find the getter function.
156
+ * @returns The getter function found in the prototype chain or a fallback function.
163
157
  */
164
158
  function lookupGetter(object, prop) {
165
159
  while (object !== null) {
@@ -181,18 +175,15 @@ function lookupGetter(object, prop) {
181
175
  }
182
176
 
183
177
  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', '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']);
184
-
185
178
  // SVG
186
179
  const 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']);
187
180
  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']);
188
-
189
181
  // List of SVG elements that are disallowed by default.
190
182
  // We still need to know them so that we can do namespace
191
183
  // checks properly in case one wants to add them to
192
184
  // allow-list.
193
185
  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']);
194
186
  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']);
195
-
196
187
  // Similarly to SVG, we want to know all MathML elements,
197
188
  // even those that we disallow by default.
198
189
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
@@ -219,18 +210,19 @@ const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
219
210
 
220
211
  var EXPRESSIONS = /*#__PURE__*/Object.freeze({
221
212
  __proto__: null,
222
- MUSTACHE_EXPR: MUSTACHE_EXPR,
223
- ERB_EXPR: ERB_EXPR,
224
- TMPLIT_EXPR: TMPLIT_EXPR,
225
- DATA_ATTR: DATA_ATTR,
226
213
  ARIA_ATTR: ARIA_ATTR,
227
- IS_ALLOWED_URI: IS_ALLOWED_URI,
228
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
229
214
  ATTR_WHITESPACE: ATTR_WHITESPACE,
215
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT,
216
+ DATA_ATTR: DATA_ATTR,
230
217
  DOCTYPE_NAME: DOCTYPE_NAME,
231
- CUSTOM_ELEMENT: CUSTOM_ELEMENT
218
+ ERB_EXPR: ERB_EXPR,
219
+ IS_ALLOWED_URI: IS_ALLOWED_URI,
220
+ IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
221
+ MUSTACHE_EXPR: MUSTACHE_EXPR,
222
+ TMPLIT_EXPR: TMPLIT_EXPR
232
223
  });
233
224
 
225
+ /* eslint-disable @typescript-eslint/indent */
234
226
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
235
227
  const NODE_TYPE = {
236
228
  element: 1,
@@ -251,20 +243,18 @@ const NODE_TYPE = {
251
243
  const getGlobal = function getGlobal() {
252
244
  return typeof window === 'undefined' ? null : window;
253
245
  };
254
-
255
246
  /**
256
247
  * Creates a no-op policy for internal use only.
257
248
  * Don't export this function outside this module!
258
- * @param {TrustedTypePolicyFactory} trustedTypes The policy factory.
259
- * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
260
- * @return {TrustedTypePolicy} The policy created (or null, if Trusted Types
249
+ * @param trustedTypes The policy factory.
250
+ * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
251
+ * @return The policy created (or null, if Trusted Types
261
252
  * are not supported or creating the policy failed).
262
253
  */
263
254
  const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
264
255
  if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
265
256
  return null;
266
257
  }
267
-
268
258
  // Allow the callers to control the unique policy name
269
259
  // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
270
260
  // Policy creation with duplicate names throws in Trusted Types.
@@ -294,17 +284,7 @@ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedType
294
284
  function createDOMPurify() {
295
285
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
296
286
  const DOMPurify = root => createDOMPurify(root);
297
-
298
- /**
299
- * Version label, exposed for easier checks
300
- * if DOMPurify is up to date or not
301
- */
302
- DOMPurify.version = '3.1.7';
303
-
304
- /**
305
- * Array of elements that DOMPurify removed during sanitation.
306
- * Empty if nothing was removed.
307
- */
287
+ DOMPurify.version = '3.2.1';
308
288
  DOMPurify.removed = [];
309
289
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
310
290
  // Not running in a browser, provide a factory function
@@ -334,7 +314,6 @@ function createDOMPurify() {
334
314
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
335
315
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
336
316
  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
337
-
338
317
  // As per issue #47, the web-components registry is inherited by a
339
318
  // new document created via createHTMLDocument. As per the spec
340
319
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
@@ -359,7 +338,6 @@ function createDOMPurify() {
359
338
  importNode
360
339
  } = originalDocument;
361
340
  let hooks = {};
362
-
363
341
  /**
364
342
  * Expose whether this browser supports running the full DOMPurify.
365
343
  */
@@ -377,22 +355,18 @@ function createDOMPurify() {
377
355
  let {
378
356
  IS_ALLOWED_URI: IS_ALLOWED_URI$1
379
357
  } = EXPRESSIONS;
380
-
381
358
  /**
382
359
  * We consider the elements and attributes below to be safe. Ideally
383
360
  * don't add any new ones but feel free to remove unwanted ones.
384
361
  */
385
-
386
362
  /* allowed element names */
387
363
  let ALLOWED_TAGS = null;
388
364
  const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
389
-
390
365
  /* Allowed attribute names */
391
366
  let ALLOWED_ATTR = null;
392
367
  const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
393
-
394
368
  /*
395
- * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
369
+ * Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
396
370
  * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
397
371
  * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
398
372
  * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
@@ -417,65 +391,49 @@ function createDOMPurify() {
417
391
  value: false
418
392
  }
419
393
  }));
420
-
421
394
  /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
422
395
  let FORBID_TAGS = null;
423
-
424
396
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
425
397
  let FORBID_ATTR = null;
426
-
427
398
  /* Decide if ARIA attributes are okay */
428
399
  let ALLOW_ARIA_ATTR = true;
429
-
430
400
  /* Decide if custom data attributes are okay */
431
401
  let ALLOW_DATA_ATTR = true;
432
-
433
402
  /* Decide if unknown protocols are okay */
434
403
  let ALLOW_UNKNOWN_PROTOCOLS = false;
435
-
436
404
  /* Decide if self-closing tags in attributes are allowed.
437
405
  * Usually removed due to a mXSS issue in jQuery 3.0 */
438
406
  let ALLOW_SELF_CLOSE_IN_ATTR = true;
439
-
440
407
  /* Output should be safe for common template engines.
441
408
  * This means, DOMPurify removes data attributes, mustaches and ERB
442
409
  */
443
410
  let SAFE_FOR_TEMPLATES = false;
444
-
445
411
  /* Output should be safe even for XML used within HTML and alike.
446
412
  * This means, DOMPurify removes comments when containing risky content.
447
413
  */
448
414
  let SAFE_FOR_XML = true;
449
-
450
415
  /* Decide if document with <html>... should be returned */
451
416
  let WHOLE_DOCUMENT = false;
452
-
453
417
  /* Track whether config is already set on this instance of DOMPurify. */
454
418
  let SET_CONFIG = false;
455
-
456
419
  /* Decide if all elements (e.g. style, script) must be children of
457
420
  * document.body. By default, browsers might move them to document.head */
458
421
  let FORCE_BODY = false;
459
-
460
422
  /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
461
423
  * string (or a TrustedHTML object if Trusted Types are supported).
462
424
  * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
463
425
  */
464
426
  let RETURN_DOM = false;
465
-
466
427
  /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
467
428
  * string (or a TrustedHTML object if Trusted Types are supported) */
468
429
  let RETURN_DOM_FRAGMENT = false;
469
-
470
430
  /* Try to return a Trusted Type object instead of a string, return a string in
471
431
  * case Trusted Types are not supported */
472
432
  let RETURN_TRUSTED_TYPE = false;
473
-
474
433
  /* Output should be free from DOM clobbering attacks?
475
434
  * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
476
435
  */
477
436
  let SANITIZE_DOM = true;
478
-
479
437
  /* Achieve full DOM Clobbering protection by isolating the namespace of named
480
438
  * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
481
439
  *
@@ -491,25 +449,19 @@ function createDOMPurify() {
491
449
  */
492
450
  let SANITIZE_NAMED_PROPS = false;
493
451
  const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
494
-
495
452
  /* Keep element content when removing element? */
496
453
  let KEEP_CONTENT = true;
497
-
498
454
  /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
499
455
  * of importing it into a new Document and returning a sanitized copy */
500
456
  let IN_PLACE = false;
501
-
502
457
  /* Allow usage of profiles like html, svg and mathMl */
503
458
  let USE_PROFILES = {};
504
-
505
459
  /* Tags to ignore content of when KEEP_CONTENT is true */
506
460
  let FORBID_CONTENTS = null;
507
461
  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']);
508
-
509
462
  /* Tags that are safe for data: URIs */
510
463
  let DATA_URI_TAGS = null;
511
464
  const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
512
-
513
465
  /* Attributes safe for values like "javascript:" */
514
466
  let URI_SAFE_ATTRIBUTES = null;
515
467
  const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
@@ -519,32 +471,33 @@ function createDOMPurify() {
519
471
  /* Document namespace */
520
472
  let NAMESPACE = HTML_NAMESPACE;
521
473
  let IS_EMPTY_INPUT = false;
522
-
523
474
  /* Allowed XHTML+XML namespaces */
524
475
  let ALLOWED_NAMESPACES = null;
525
476
  const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
526
-
477
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
478
+ let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
479
+ // Certain elements are allowed in both SVG and HTML
480
+ // namespace. We need to specify them explicitly
481
+ // so that they don't get erroneously deleted from
482
+ // HTML namespace.
483
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
527
484
  /* Parsing of strict XHTML documents */
528
485
  let PARSER_MEDIA_TYPE = null;
529
486
  const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
530
487
  const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
531
488
  let transformCaseFunc = null;
532
-
533
489
  /* Keep a reference to config to pass to hooks */
534
490
  let CONFIG = null;
535
-
536
491
  /* Ideally, do not touch anything below this line */
537
492
  /* ______________________________________________ */
538
-
539
493
  const formElement = document.createElement('form');
540
494
  const isRegexOrFunction = function isRegexOrFunction(testValue) {
541
495
  return testValue instanceof RegExp || testValue instanceof Function;
542
496
  };
543
-
544
497
  /**
545
498
  * _parseConfig
546
499
  *
547
- * @param {Object} cfg optional config literal
500
+ * @param cfg optional config literal
548
501
  */
549
502
  // eslint-disable-next-line complexity
550
503
  const _parseConfig = function _parseConfig() {
@@ -552,39 +505,23 @@ function createDOMPurify() {
552
505
  if (CONFIG && CONFIG === cfg) {
553
506
  return;
554
507
  }
555
-
556
508
  /* Shield configuration object from tampering */
557
509
  if (!cfg || typeof cfg !== 'object') {
558
510
  cfg = {};
559
511
  }
560
-
561
512
  /* Shield configuration object from prototype pollution */
562
513
  cfg = clone(cfg);
563
514
  PARSER_MEDIA_TYPE =
564
515
  // eslint-disable-next-line unicorn/prefer-includes
565
516
  SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
566
-
567
517
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
568
518
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
569
-
570
519
  /* Set configuration parameters */
571
520
  ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
572
521
  ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
573
522
  ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
574
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
575
- // eslint-disable-line indent
576
- cfg.ADD_URI_SAFE_ATTR,
577
- // eslint-disable-line indent
578
- transformCaseFunc // eslint-disable-line indent
579
- ) // eslint-disable-line indent
580
- : DEFAULT_URI_SAFE_ATTRIBUTES;
581
- DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
582
- // eslint-disable-line indent
583
- cfg.ADD_DATA_URI_TAGS,
584
- // eslint-disable-line indent
585
- transformCaseFunc // eslint-disable-line indent
586
- ) // eslint-disable-line indent
587
- : DEFAULT_DATA_URI_TAGS;
523
+ 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;
524
+ 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;
588
525
  FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
589
526
  FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
590
527
  FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
@@ -606,6 +543,8 @@ function createDOMPurify() {
606
543
  IN_PLACE = cfg.IN_PLACE || false; // Default false
607
544
  IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
608
545
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
546
+ MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
547
+ HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
609
548
  CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
610
549
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
611
550
  CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
@@ -622,7 +561,6 @@ function createDOMPurify() {
622
561
  if (RETURN_DOM_FRAGMENT) {
623
562
  RETURN_DOM = true;
624
563
  }
625
-
626
564
  /* Parse profile info */
627
565
  if (USE_PROFILES) {
628
566
  ALLOWED_TAGS = addToSet({}, text);
@@ -647,7 +585,6 @@ function createDOMPurify() {
647
585
  addToSet(ALLOWED_ATTR, xml);
648
586
  }
649
587
  }
650
-
651
588
  /* Merge configuration parameters */
652
589
  if (cfg.ADD_TAGS) {
653
590
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
@@ -670,17 +607,14 @@ function createDOMPurify() {
670
607
  }
671
608
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
672
609
  }
673
-
674
610
  /* Add #text in case KEEP_CONTENT is set to true */
675
611
  if (KEEP_CONTENT) {
676
612
  ALLOWED_TAGS['#text'] = true;
677
613
  }
678
-
679
614
  /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
680
615
  if (WHOLE_DOCUMENT) {
681
616
  addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
682
617
  }
683
-
684
618
  /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
685
619
  if (ALLOWED_TAGS.table) {
686
620
  addToSet(ALLOWED_TAGS, ['tbody']);
@@ -693,10 +627,8 @@ function createDOMPurify() {
693
627
  if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
694
628
  throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
695
629
  }
696
-
697
630
  // Overwrite existing TrustedTypes policy.
698
631
  trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
699
-
700
632
  // Sign local variables required by `sanitize`.
701
633
  emptyHTML = trustedTypesPolicy.createHTML('');
702
634
  } else {
@@ -704,13 +636,11 @@ function createDOMPurify() {
704
636
  if (trustedTypesPolicy === undefined) {
705
637
  trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
706
638
  }
707
-
708
639
  // If creating the internal policy succeeded sign internal variables.
709
640
  if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
710
641
  emptyHTML = trustedTypesPolicy.createHTML('');
711
642
  }
712
643
  }
713
-
714
644
  // Prevent further manipulation of configuration.
715
645
  // Not available in IE8, Safari 5, etc.
716
646
  if (freeze) {
@@ -718,30 +648,19 @@ function createDOMPurify() {
718
648
  }
719
649
  CONFIG = cfg;
720
650
  };
721
- const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
722
- const HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
723
-
724
- // Certain elements are allowed in both SVG and HTML
725
- // namespace. We need to specify them explicitly
726
- // so that they don't get erroneously deleted from
727
- // HTML namespace.
728
- const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
729
-
730
651
  /* Keep track of all possible SVG and MathML tags
731
652
  * so that we can perform the namespace checks
732
653
  * correctly. */
733
654
  const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
734
655
  const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
735
-
736
656
  /**
737
- * @param {Element} element a DOM element whose namespace is being checked
738
- * @returns {boolean} Return false if the element has a
657
+ * @param element a DOM element whose namespace is being checked
658
+ * @returns Return false if the element has a
739
659
  * namespace that a spec-compliant parser would never
740
660
  * return. Return true otherwise.
741
661
  */
742
662
  const _checkValidNamespace = function _checkValidNamespace(element) {
743
663
  let parent = getParentNode(element);
744
-
745
664
  // In JSDOM, if we're inside shadow DOM, then parentNode
746
665
  // can be null. We just simulate parent in this case.
747
666
  if (!parent || !parent.tagName) {
@@ -762,14 +681,12 @@ function createDOMPurify() {
762
681
  if (parent.namespaceURI === HTML_NAMESPACE) {
763
682
  return tagName === 'svg';
764
683
  }
765
-
766
684
  // The only way to switch from MathML to SVG is via`
767
685
  // svg if parent is either <annotation-xml> or MathML
768
686
  // text integration points.
769
687
  if (parent.namespaceURI === MATHML_NAMESPACE) {
770
688
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
771
689
  }
772
-
773
690
  // We only allow elements that are defined in SVG
774
691
  // spec. All others are disallowed in SVG namespace.
775
692
  return Boolean(ALL_SVG_TAGS[tagName]);
@@ -781,13 +698,11 @@ function createDOMPurify() {
781
698
  if (parent.namespaceURI === HTML_NAMESPACE) {
782
699
  return tagName === 'math';
783
700
  }
784
-
785
701
  // The only way to switch from SVG to MathML is via
786
702
  // <math> and HTML integration points
787
703
  if (parent.namespaceURI === SVG_NAMESPACE) {
788
704
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
789
705
  }
790
-
791
706
  // We only allow elements that are defined in MathML
792
707
  // spec. All others are disallowed in MathML namespace.
793
708
  return Boolean(ALL_MATHML_TAGS[tagName]);
@@ -802,28 +717,24 @@ function createDOMPurify() {
802
717
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
803
718
  return false;
804
719
  }
805
-
806
720
  // We disallow tags that are specific for MathML
807
721
  // or SVG and should never appear in HTML namespace
808
722
  return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
809
723
  }
810
-
811
724
  // For XHTML and XML documents that support custom namespaces
812
725
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
813
726
  return true;
814
727
  }
815
-
816
728
  // The code should never reach this place (this means
817
729
  // that the element somehow got namespace that is not
818
730
  // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
819
731
  // Return false just in case.
820
732
  return false;
821
733
  };
822
-
823
734
  /**
824
735
  * _forceRemove
825
736
  *
826
- * @param {Node} node a DOM node
737
+ * @param node a DOM node
827
738
  */
828
739
  const _forceRemove = function _forceRemove(node) {
829
740
  arrayPush(DOMPurify.removed, {
@@ -836,46 +747,43 @@ function createDOMPurify() {
836
747
  remove(node);
837
748
  }
838
749
  };
839
-
840
750
  /**
841
751
  * _removeAttribute
842
752
  *
843
- * @param {String} name an Attribute name
844
- * @param {Node} node a DOM node
753
+ * @param name an Attribute name
754
+ * @param element a DOM node
845
755
  */
846
- const _removeAttribute = function _removeAttribute(name, node) {
756
+ const _removeAttribute = function _removeAttribute(name, element) {
847
757
  try {
848
758
  arrayPush(DOMPurify.removed, {
849
- attribute: node.getAttributeNode(name),
850
- from: node
759
+ attribute: element.getAttributeNode(name),
760
+ from: element
851
761
  });
852
762
  } catch (_) {
853
763
  arrayPush(DOMPurify.removed, {
854
764
  attribute: null,
855
- from: node
765
+ from: element
856
766
  });
857
767
  }
858
- node.removeAttribute(name);
859
-
768
+ element.removeAttribute(name);
860
769
  // We void attribute values for unremovable "is"" attributes
861
770
  if (name === 'is' && !ALLOWED_ATTR[name]) {
862
771
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
863
772
  try {
864
- _forceRemove(node);
773
+ _forceRemove(element);
865
774
  } catch (_) {}
866
775
  } else {
867
776
  try {
868
- node.setAttribute(name, '');
777
+ element.setAttribute(name, '');
869
778
  } catch (_) {}
870
779
  }
871
780
  }
872
781
  };
873
-
874
782
  /**
875
783
  * _initDocument
876
784
  *
877
- * @param {String} dirty a string of dirty markup
878
- * @return {Document} a DOM, filled with the dirty markup
785
+ * @param dirty - a string of dirty markup
786
+ * @return a DOM, filled with the dirty markup
879
787
  */
880
788
  const _initDocument = function _initDocument(dirty) {
881
789
  /* Create a HTML document */
@@ -902,7 +810,6 @@ function createDOMPurify() {
902
810
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
903
811
  } catch (_) {}
904
812
  }
905
-
906
813
  /* Use createHTMLDocument in case DOMParser is not available */
907
814
  if (!doc || !doc.documentElement) {
908
815
  doc = implementation.createDocument(NAMESPACE, 'template', null);
@@ -916,112 +823,89 @@ function createDOMPurify() {
916
823
  if (dirty && leadingWhitespace) {
917
824
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
918
825
  }
919
-
920
826
  /* Work on whole document or just its body */
921
827
  if (NAMESPACE === HTML_NAMESPACE) {
922
828
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
923
829
  }
924
830
  return WHOLE_DOCUMENT ? doc.documentElement : body;
925
831
  };
926
-
927
832
  /**
928
833
  * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
929
834
  *
930
- * @param {Node} root The root element or node to start traversing on.
931
- * @return {NodeIterator} The created NodeIterator
835
+ * @param root The root element or node to start traversing on.
836
+ * @return The created NodeIterator
932
837
  */
933
838
  const _createNodeIterator = function _createNodeIterator(root) {
934
839
  return createNodeIterator.call(root.ownerDocument || root, root,
935
840
  // eslint-disable-next-line no-bitwise
936
841
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
937
842
  };
938
-
939
843
  /**
940
844
  * _isClobbered
941
845
  *
942
- * @param {Node} elm element to check for clobbering attacks
943
- * @return {Boolean} true if clobbered, false if safe
846
+ * @param element element to check for clobbering attacks
847
+ * @return true if clobbered, false if safe
944
848
  */
945
- const _isClobbered = function _isClobbered(elm) {
946
- 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');
849
+ const _isClobbered = function _isClobbered(element) {
850
+ 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');
947
851
  };
948
-
949
852
  /**
950
853
  * Checks whether the given object is a DOM node.
951
854
  *
952
- * @param {Node} object object to check whether it's a DOM node
953
- * @return {Boolean} true is object is a DOM node
855
+ * @param value object to check whether it's a DOM node
856
+ * @return true is object is a DOM node
954
857
  */
955
- const _isNode = function _isNode(object) {
956
- return typeof Node === 'function' && object instanceof Node;
858
+ const _isNode = function _isNode(value) {
859
+ return typeof Node === 'function' && value instanceof Node;
957
860
  };
958
-
959
- /**
960
- * _executeHook
961
- * Execute user configurable hooks
962
- *
963
- * @param {String} entryPoint Name of the hook's entry point
964
- * @param {Node} currentNode node to work on with the hook
965
- * @param {Object} data additional hook parameters
966
- */
967
- const _executeHook = function _executeHook(entryPoint, currentNode, data) {
861
+ function _executeHook(entryPoint, currentNode, data) {
968
862
  if (!hooks[entryPoint]) {
969
863
  return;
970
864
  }
971
865
  arrayForEach(hooks[entryPoint], hook => {
972
866
  hook.call(DOMPurify, currentNode, data, CONFIG);
973
867
  });
974
- };
975
-
868
+ }
976
869
  /**
977
870
  * _sanitizeElements
978
871
  *
979
872
  * @protect nodeName
980
873
  * @protect textContent
981
874
  * @protect removeChild
982
- *
983
- * @param {Node} currentNode to check for permission to exist
984
- * @return {Boolean} true if node was killed, false if left alive
875
+ * @param currentNode to check for permission to exist
876
+ * @return true if node was killed, false if left alive
985
877
  */
986
878
  const _sanitizeElements = function _sanitizeElements(currentNode) {
987
879
  let content = null;
988
-
989
880
  /* Execute a hook if present */
990
881
  _executeHook('beforeSanitizeElements', currentNode, null);
991
-
992
882
  /* Check if element is clobbered or can clobber */
993
883
  if (_isClobbered(currentNode)) {
994
884
  _forceRemove(currentNode);
995
885
  return true;
996
886
  }
997
-
998
887
  /* Now let's check the element's type and name */
999
888
  const tagName = transformCaseFunc(currentNode.nodeName);
1000
-
1001
889
  /* Execute a hook if present */
1002
890
  _executeHook('uponSanitizeElement', currentNode, {
1003
891
  tagName,
1004
892
  allowedTags: ALLOWED_TAGS
1005
893
  });
1006
-
1007
894
  /* Detect mXSS attempts abusing namespace confusion */
1008
895
  if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
1009
896
  _forceRemove(currentNode);
1010
897
  return true;
1011
898
  }
1012
-
1013
899
  /* Remove any occurrence of processing instructions */
1014
900
  if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
1015
901
  _forceRemove(currentNode);
1016
902
  return true;
1017
903
  }
1018
-
1019
904
  /* Remove any kind of possibly harmful comments */
1020
905
  if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
1021
906
  _forceRemove(currentNode);
1022
907
  return true;
1023
908
  }
1024
-
1025
909
  /* Remove element if anything forbids its presence */
1026
910
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1027
911
  /* Check if we have a custom element to handle */
@@ -1033,7 +917,6 @@ function createDOMPurify() {
1033
917
  return false;
1034
918
  }
1035
919
  }
1036
-
1037
920
  /* Keep content except for bad-listed elements */
1038
921
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1039
922
  const parentNode = getParentNode(currentNode) || currentNode.parentNode;
@@ -1050,19 +933,16 @@ function createDOMPurify() {
1050
933
  _forceRemove(currentNode);
1051
934
  return true;
1052
935
  }
1053
-
1054
936
  /* Check whether element has a valid namespace */
1055
937
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
1056
938
  _forceRemove(currentNode);
1057
939
  return true;
1058
940
  }
1059
-
1060
941
  /* Make sure that older browsers don't get fallback-tag mXSS */
1061
942
  if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
1062
943
  _forceRemove(currentNode);
1063
944
  return true;
1064
945
  }
1065
-
1066
946
  /* Sanitize element content to be template-safe */
1067
947
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
1068
948
  /* Get the element's text content */
@@ -1077,19 +957,17 @@ function createDOMPurify() {
1077
957
  currentNode.textContent = content;
1078
958
  }
1079
959
  }
1080
-
1081
960
  /* Execute a hook if present */
1082
961
  _executeHook('afterSanitizeElements', currentNode, null);
1083
962
  return false;
1084
963
  };
1085
-
1086
964
  /**
1087
965
  * _isValidAttribute
1088
966
  *
1089
- * @param {string} lcTag Lowercase tag name of containing element.
1090
- * @param {string} lcName Lowercase attribute name.
1091
- * @param {string} value Attribute value.
1092
- * @return {Boolean} Returns true if `value` is valid, otherwise false.
967
+ * @param lcTag Lowercase tag name of containing element.
968
+ * @param lcName Lowercase attribute name.
969
+ * @param value Attribute value.
970
+ * @return Returns true if `value` is valid, otherwise false.
1093
971
  */
1094
972
  // eslint-disable-next-line complexity
1095
973
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
@@ -1097,7 +975,6 @@ function createDOMPurify() {
1097
975
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1098
976
  return false;
1099
977
  }
1100
-
1101
978
  /* Allow valid data-* attributes: At least one character after "-"
1102
979
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1103
980
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
@@ -1119,19 +996,17 @@ function createDOMPurify() {
1119
996
  } else ;
1120
997
  return true;
1121
998
  };
1122
-
1123
999
  /**
1124
1000
  * _isBasicCustomElement
1125
1001
  * checks if at least one dash is included in tagName, and it's not the first char
1126
1002
  * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1127
1003
  *
1128
- * @param {string} tagName name of the tag of the node to sanitize
1129
- * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1004
+ * @param tagName name of the tag of the node to sanitize
1005
+ * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1130
1006
  */
1131
1007
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
1132
1008
  return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
1133
1009
  };
1134
-
1135
1010
  /**
1136
1011
  * _sanitizeAttributes
1137
1012
  *
@@ -1140,7 +1015,7 @@ function createDOMPurify() {
1140
1015
  * @protect removeAttribute
1141
1016
  * @protect setAttribute
1142
1017
  *
1143
- * @param {Node} currentNode to sanitize
1018
+ * @param currentNode to sanitize
1144
1019
  */
1145
1020
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1146
1021
  /* Execute a hook if present */
@@ -1148,7 +1023,6 @@ function createDOMPurify() {
1148
1023
  const {
1149
1024
  attributes
1150
1025
  } = currentNode;
1151
-
1152
1026
  /* Check if we have attributes; if not we might have a text node */
1153
1027
  if (!attributes) {
1154
1028
  return;
@@ -1157,10 +1031,10 @@ function createDOMPurify() {
1157
1031
  attrName: '',
1158
1032
  attrValue: '',
1159
1033
  keepAttr: true,
1160
- allowedAttributes: ALLOWED_ATTR
1034
+ allowedAttributes: ALLOWED_ATTR,
1035
+ forceKeepAttr: undefined
1161
1036
  };
1162
1037
  let l = attributes.length;
1163
-
1164
1038
  /* Go backwards over all attributes; safely remove bad ones */
1165
1039
  while (l--) {
1166
1040
  const attr = attributes[l];
@@ -1171,7 +1045,6 @@ function createDOMPurify() {
1171
1045
  } = attr;
1172
1046
  const lcName = transformCaseFunc(name);
1173
1047
  let value = name === 'value' ? attrValue : stringTrim(attrValue);
1174
-
1175
1048
  /* Execute a hook if present */
1176
1049
  hookEvent.attrName = lcName;
1177
1050
  hookEvent.attrValue = value;
@@ -1179,56 +1052,46 @@ function createDOMPurify() {
1179
1052
  hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
1180
1053
  _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
1181
1054
  value = hookEvent.attrValue;
1182
-
1055
+ /* Full DOM Clobbering protection via namespace isolation,
1056
+ * Prefix id and name attributes with `user-content-`
1057
+ */
1058
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1059
+ // Remove the attribute with this value
1060
+ _removeAttribute(name, currentNode);
1061
+ // Prefix the value and later re-create the attribute with the sanitized value
1062
+ value = SANITIZE_NAMED_PROPS_PREFIX + value;
1063
+ }
1064
+ /* Work around a security issue with comments inside attributes */
1065
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
1066
+ _removeAttribute(name, currentNode);
1067
+ continue;
1068
+ }
1183
1069
  /* Did the hooks approve of the attribute? */
1184
1070
  if (hookEvent.forceKeepAttr) {
1185
1071
  continue;
1186
1072
  }
1187
-
1188
1073
  /* Remove attribute */
1189
1074
  _removeAttribute(name, currentNode);
1190
-
1191
1075
  /* Did the hooks approve of the attribute? */
1192
1076
  if (!hookEvent.keepAttr) {
1193
1077
  continue;
1194
1078
  }
1195
-
1196
1079
  /* Work around a security issue in jQuery 3.0 */
1197
1080
  if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1198
1081
  _removeAttribute(name, currentNode);
1199
1082
  continue;
1200
1083
  }
1201
-
1202
1084
  /* Sanitize attribute content to be template-safe */
1203
1085
  if (SAFE_FOR_TEMPLATES) {
1204
1086
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1205
1087
  value = stringReplace(value, expr, ' ');
1206
1088
  });
1207
1089
  }
1208
-
1209
1090
  /* Is `value` valid for this attribute? */
1210
1091
  const lcTag = transformCaseFunc(currentNode.nodeName);
1211
1092
  if (!_isValidAttribute(lcTag, lcName, value)) {
1212
1093
  continue;
1213
1094
  }
1214
-
1215
- /* Full DOM Clobbering protection via namespace isolation,
1216
- * Prefix id and name attributes with `user-content-`
1217
- */
1218
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1219
- // Remove the attribute with this value
1220
- _removeAttribute(name, currentNode);
1221
-
1222
- // Prefix the value and later re-create the attribute with the sanitized value
1223
- value = SANITIZE_NAMED_PROPS_PREFIX + value;
1224
- }
1225
-
1226
- /* Work around a security issue with comments inside attributes */
1227
- if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
1228
- _removeAttribute(name, currentNode);
1229
- continue;
1230
- }
1231
-
1232
1095
  /* Handle attributes that require Trusted Types */
1233
1096
  if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1234
1097
  if (namespaceURI) ; else {
@@ -1246,7 +1109,6 @@ function createDOMPurify() {
1246
1109
  }
1247
1110
  }
1248
1111
  }
1249
-
1250
1112
  /* Handle invalid data-* attribute set by try-catching it */
1251
1113
  try {
1252
1114
  if (namespaceURI) {
@@ -1262,51 +1124,36 @@ function createDOMPurify() {
1262
1124
  }
1263
1125
  } catch (_) {}
1264
1126
  }
1265
-
1266
1127
  /* Execute a hook if present */
1267
1128
  _executeHook('afterSanitizeAttributes', currentNode, null);
1268
1129
  };
1269
-
1270
1130
  /**
1271
1131
  * _sanitizeShadowDOM
1272
1132
  *
1273
- * @param {DocumentFragment} fragment to iterate over recursively
1133
+ * @param fragment to iterate over recursively
1274
1134
  */
1275
1135
  const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1276
1136
  let shadowNode = null;
1277
1137
  const shadowIterator = _createNodeIterator(fragment);
1278
-
1279
1138
  /* Execute a hook if present */
1280
1139
  _executeHook('beforeSanitizeShadowDOM', fragment, null);
1281
1140
  while (shadowNode = shadowIterator.nextNode()) {
1282
1141
  /* Execute a hook if present */
1283
1142
  _executeHook('uponSanitizeShadowNode', shadowNode, null);
1284
-
1285
1143
  /* Sanitize tags and elements */
1286
1144
  if (_sanitizeElements(shadowNode)) {
1287
1145
  continue;
1288
1146
  }
1289
-
1290
1147
  /* Deep shadow DOM detected */
1291
1148
  if (shadowNode.content instanceof DocumentFragment) {
1292
1149
  _sanitizeShadowDOM(shadowNode.content);
1293
1150
  }
1294
-
1295
1151
  /* Check attributes, sanitize if necessary */
1296
1152
  _sanitizeAttributes(shadowNode);
1297
1153
  }
1298
-
1299
1154
  /* Execute a hook if present */
1300
1155
  _executeHook('afterSanitizeShadowDOM', fragment, null);
1301
1156
  };
1302
-
1303
- /**
1304
- * Sanitize
1305
- * Public method providing core sanitation functionality
1306
- *
1307
- * @param {String|Node} dirty string or DOM node
1308
- * @param {Object} cfg object
1309
- */
1310
1157
  // eslint-disable-next-line complexity
1311
1158
  DOMPurify.sanitize = function (dirty) {
1312
1159
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -1321,7 +1168,6 @@ function createDOMPurify() {
1321
1168
  if (IS_EMPTY_INPUT) {
1322
1169
  dirty = '<!-->';
1323
1170
  }
1324
-
1325
1171
  /* Stringify, in case dirty is an object */
1326
1172
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
1327
1173
  if (typeof dirty.toString === 'function') {
@@ -1333,20 +1179,16 @@ function createDOMPurify() {
1333
1179
  throw typeErrorCreate('toString is not a function');
1334
1180
  }
1335
1181
  }
1336
-
1337
1182
  /* Return dirty HTML if DOMPurify cannot run */
1338
1183
  if (!DOMPurify.isSupported) {
1339
1184
  return dirty;
1340
1185
  }
1341
-
1342
1186
  /* Assign config vars */
1343
1187
  if (!SET_CONFIG) {
1344
1188
  _parseConfig(cfg);
1345
1189
  }
1346
-
1347
1190
  /* Clean up removed elements */
1348
1191
  DOMPurify.removed = [];
1349
-
1350
1192
  /* Check if dirty is correctly typed for IN_PLACE */
1351
1193
  if (typeof dirty === 'string') {
1352
1194
  IN_PLACE = false;
@@ -1380,45 +1222,36 @@ function createDOMPurify() {
1380
1222
  dirty.indexOf('<') === -1) {
1381
1223
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1382
1224
  }
1383
-
1384
1225
  /* Initialize the document to work on */
1385
1226
  body = _initDocument(dirty);
1386
-
1387
1227
  /* Check we have a DOM node from the data */
1388
1228
  if (!body) {
1389
1229
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
1390
1230
  }
1391
1231
  }
1392
-
1393
1232
  /* Remove first element node (ours) if FORCE_BODY is set */
1394
1233
  if (body && FORCE_BODY) {
1395
1234
  _forceRemove(body.firstChild);
1396
1235
  }
1397
-
1398
1236
  /* Get node iterator */
1399
1237
  const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
1400
-
1401
1238
  /* Now start iterating over the created document */
1402
1239
  while (currentNode = nodeIterator.nextNode()) {
1403
1240
  /* Sanitize tags and elements */
1404
1241
  if (_sanitizeElements(currentNode)) {
1405
1242
  continue;
1406
1243
  }
1407
-
1408
1244
  /* Shadow DOM detected, sanitize it */
1409
1245
  if (currentNode.content instanceof DocumentFragment) {
1410
1246
  _sanitizeShadowDOM(currentNode.content);
1411
1247
  }
1412
-
1413
1248
  /* Check attributes, sanitize if necessary */
1414
1249
  _sanitizeAttributes(currentNode);
1415
1250
  }
1416
-
1417
1251
  /* If we sanitized `dirty` in-place, return it. */
1418
1252
  if (IN_PLACE) {
1419
1253
  return dirty;
1420
1254
  }
1421
-
1422
1255
  /* Return sanitized string or DOM */
1423
1256
  if (RETURN_DOM) {
1424
1257
  if (RETURN_DOM_FRAGMENT) {
@@ -1443,12 +1276,10 @@ function createDOMPurify() {
1443
1276
  return returnNode;
1444
1277
  }
1445
1278
  let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1446
-
1447
1279
  /* Serialize doctype if allowed */
1448
1280
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
1449
1281
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
1450
1282
  }
1451
-
1452
1283
  /* Sanitize final string template-safe */
1453
1284
  if (SAFE_FOR_TEMPLATES) {
1454
1285
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
@@ -1457,39 +1288,15 @@ function createDOMPurify() {
1457
1288
  }
1458
1289
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1459
1290
  };
1460
-
1461
- /**
1462
- * Public method to set the configuration once
1463
- * setConfig
1464
- *
1465
- * @param {Object} cfg configuration object
1466
- */
1467
1291
  DOMPurify.setConfig = function () {
1468
1292
  let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1469
1293
  _parseConfig(cfg);
1470
1294
  SET_CONFIG = true;
1471
1295
  };
1472
-
1473
- /**
1474
- * Public method to remove the configuration
1475
- * clearConfig
1476
- *
1477
- */
1478
1296
  DOMPurify.clearConfig = function () {
1479
1297
  CONFIG = null;
1480
1298
  SET_CONFIG = false;
1481
1299
  };
1482
-
1483
- /**
1484
- * Public method to check if an attribute value is valid.
1485
- * Uses last set config, if any. Otherwise, uses config defaults.
1486
- * isValidAttribute
1487
- *
1488
- * @param {String} tag Tag name of containing element.
1489
- * @param {String} attr Attribute name.
1490
- * @param {String} value Attribute value.
1491
- * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1492
- */
1493
1300
  DOMPurify.isValidAttribute = function (tag, attr, value) {
1494
1301
  /* Initialize shared config vars if necessary. */
1495
1302
  if (!CONFIG) {
@@ -1499,14 +1306,6 @@ function createDOMPurify() {
1499
1306
  const lcName = transformCaseFunc(attr);
1500
1307
  return _isValidAttribute(lcTag, lcName, value);
1501
1308
  };
1502
-
1503
- /**
1504
- * AddHook
1505
- * Public method to add DOMPurify hooks
1506
- *
1507
- * @param {String} entryPoint entry point for the hook to add
1508
- * @param {Function} hookFunction function to execute
1509
- */
1510
1309
  DOMPurify.addHook = function (entryPoint, hookFunction) {
1511
1310
  if (typeof hookFunction !== 'function') {
1512
1311
  return;
@@ -1514,37 +1313,16 @@ function createDOMPurify() {
1514
1313
  hooks[entryPoint] = hooks[entryPoint] || [];
1515
1314
  arrayPush(hooks[entryPoint], hookFunction);
1516
1315
  };
1517
-
1518
- /**
1519
- * RemoveHook
1520
- * Public method to remove a DOMPurify hook at a given entryPoint
1521
- * (pops it from the stack of hooks if more are present)
1522
- *
1523
- * @param {String} entryPoint entry point for the hook to remove
1524
- * @return {Function} removed(popped) hook
1525
- */
1526
1316
  DOMPurify.removeHook = function (entryPoint) {
1527
1317
  if (hooks[entryPoint]) {
1528
1318
  return arrayPop(hooks[entryPoint]);
1529
1319
  }
1530
1320
  };
1531
-
1532
- /**
1533
- * RemoveHooks
1534
- * Public method to remove all DOMPurify hooks at a given entryPoint
1535
- *
1536
- * @param {String} entryPoint entry point for the hooks to remove
1537
- */
1538
1321
  DOMPurify.removeHooks = function (entryPoint) {
1539
1322
  if (hooks[entryPoint]) {
1540
1323
  hooks[entryPoint] = [];
1541
1324
  }
1542
1325
  };
1543
-
1544
- /**
1545
- * RemoveAllHooks
1546
- * Public method to remove all DOMPurify hooks
1547
- */
1548
1326
  DOMPurify.removeAllHooks = function () {
1549
1327
  hooks = {};
1550
1328
  };