@bigbinary/neeto-payments-frontend 2.5.3 → 2.5.4

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.
@@ -410,7 +410,7 @@ var Configure = function Configure(_ref) {
410
410
  });
411
411
  };
412
412
 
413
- /*! @license DOMPurify 3.0.2 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.2/LICENSE */
413
+ /*! @license DOMPurify 3.1.3 | (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.3/LICENSE */
414
414
 
415
415
  const {
416
416
  entries,
@@ -424,36 +424,30 @@ let {
424
424
  seal,
425
425
  create
426
426
  } = Object; // eslint-disable-line import/no-mutable-exports
427
-
428
427
  let {
429
428
  apply,
430
429
  construct
431
430
  } = typeof Reflect !== 'undefined' && Reflect;
432
-
433
- if (!apply) {
434
- apply = function apply(fun, thisValue, args) {
435
- return fun.apply(thisValue, args);
436
- };
437
- }
438
-
439
431
  if (!freeze) {
440
432
  freeze = function freeze(x) {
441
433
  return x;
442
434
  };
443
435
  }
444
-
445
436
  if (!seal) {
446
437
  seal = function seal(x) {
447
438
  return x;
448
439
  };
449
440
  }
450
-
441
+ if (!apply) {
442
+ apply = function apply(fun, thisValue, args) {
443
+ return fun.apply(thisValue, args);
444
+ };
445
+ }
451
446
  if (!construct) {
452
447
  construct = function construct(Func, args) {
453
448
  return new Func(...args);
454
449
  };
455
450
  }
456
-
457
451
  const arrayForEach = unapply(Array.prototype.forEach);
458
452
  const arrayPop = unapply(Array.prototype.pop);
459
453
  const arrayPush = unapply(Array.prototype.push);
@@ -463,134 +457,178 @@ const stringMatch = unapply(String.prototype.match);
463
457
  const stringReplace = unapply(String.prototype.replace);
464
458
  const stringIndexOf = unapply(String.prototype.indexOf);
465
459
  const stringTrim = unapply(String.prototype.trim);
460
+ const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
466
461
  const regExpTest = unapply(RegExp.prototype.test);
467
462
  const typeErrorCreate = unconstruct(TypeError);
463
+ const numberIsNaN = unapply(Number.isNaN);
464
+
465
+ /**
466
+ * Creates a new function that calls the given function with a specified thisArg and arguments.
467
+ *
468
+ * @param {Function} func - The function to be wrapped and called.
469
+ * @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
470
+ */
468
471
  function unapply(func) {
469
472
  return function (thisArg) {
470
473
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
471
474
  args[_key - 1] = arguments[_key];
472
475
  }
473
-
474
476
  return apply(func, thisArg, args);
475
477
  };
476
478
  }
479
+
480
+ /**
481
+ * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
482
+ *
483
+ * @param {Function} func - The constructor function to be wrapped and called.
484
+ * @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
485
+ */
477
486
  function unconstruct(func) {
478
487
  return function () {
479
488
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
480
489
  args[_key2] = arguments[_key2];
481
490
  }
482
-
483
491
  return construct(func, args);
484
492
  };
485
493
  }
486
- /* Add properties to a lookup table */
487
-
488
- function addToSet(set, array, transformCaseFunc) {
489
- transformCaseFunc = transformCaseFunc ? transformCaseFunc : stringToLowerCase;
490
494
 
495
+ /**
496
+ * Add properties to a lookup table
497
+ *
498
+ * @param {Object} set - The set to which elements will be added.
499
+ * @param {Array} array - The array containing elements to be added to the set.
500
+ * @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
501
+ * @returns {Object} The modified set with added elements.
502
+ */
503
+ function addToSet(set, array) {
504
+ let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
491
505
  if (setPrototypeOf) {
492
506
  // Make 'in' and truthy checks like Boolean(set.constructor)
493
507
  // independent of any properties defined on Object.prototype.
494
508
  // Prevent prototype setters from intercepting set as a this value.
495
509
  setPrototypeOf(set, null);
496
510
  }
497
-
498
511
  let l = array.length;
499
-
500
512
  while (l--) {
501
513
  let element = array[l];
502
-
503
514
  if (typeof element === 'string') {
504
515
  const lcElement = transformCaseFunc(element);
505
-
506
516
  if (lcElement !== element) {
507
517
  // Config presets (e.g. tags.js, attrs.js) are immutable.
508
518
  if (!isFrozen(array)) {
509
519
  array[l] = lcElement;
510
520
  }
511
-
512
521
  element = lcElement;
513
522
  }
514
523
  }
515
-
516
524
  set[element] = true;
517
525
  }
518
-
519
526
  return set;
520
527
  }
521
- /* Shallow clone an object */
522
528
 
529
+ /**
530
+ * Clean up an array to harden against CSPP
531
+ *
532
+ * @param {Array} array - The array to be cleaned.
533
+ * @returns {Array} The cleaned version of the array
534
+ */
535
+ function cleanArray(array) {
536
+ for (let index = 0; index < array.length; index++) {
537
+ const isPropertyExist = objectHasOwnProperty(array, index);
538
+ if (!isPropertyExist) {
539
+ array[index] = null;
540
+ }
541
+ }
542
+ return array;
543
+ }
544
+
545
+ /**
546
+ * Shallow clone an object
547
+ *
548
+ * @param {Object} object - The object to be cloned.
549
+ * @returns {Object} A new object that copies the original.
550
+ */
523
551
  function clone(object) {
524
552
  const newObject = create(null);
525
-
526
553
  for (const [property, value] of entries(object)) {
527
- newObject[property] = value;
554
+ const isPropertyExist = objectHasOwnProperty(object, property);
555
+ if (isPropertyExist) {
556
+ if (Array.isArray(value)) {
557
+ newObject[property] = cleanArray(value);
558
+ } else if (value && typeof value === 'object' && value.constructor === Object) {
559
+ newObject[property] = clone(value);
560
+ } else {
561
+ newObject[property] = value;
562
+ }
563
+ }
528
564
  }
529
-
530
565
  return newObject;
531
566
  }
532
- /* This method automatically checks if the prop is function
533
- * or getter and behaves accordingly. */
534
567
 
568
+ /**
569
+ * This method automatically checks if the prop is function or getter and behaves accordingly.
570
+ *
571
+ * @param {Object} object - The object to look up the getter function in its prototype chain.
572
+ * @param {String} prop - The property name for which to find the getter function.
573
+ * @returns {Function} The getter function found in the prototype chain or a fallback function.
574
+ */
535
575
  function lookupGetter(object, prop) {
536
576
  while (object !== null) {
537
577
  const desc = getOwnPropertyDescriptor(object, prop);
538
-
539
578
  if (desc) {
540
579
  if (desc.get) {
541
580
  return unapply(desc.get);
542
581
  }
543
-
544
582
  if (typeof desc.value === 'function') {
545
583
  return unapply(desc.value);
546
584
  }
547
585
  }
548
-
549
586
  object = getPrototypeOf(object);
550
587
  }
551
-
552
- function fallbackValue(element) {
553
- console.warn('fallback value for', element);
588
+ function fallbackValue() {
554
589
  return null;
555
590
  }
556
-
557
591
  return fallbackValue;
558
592
  }
559
593
 
560
- 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']); // SVG
594
+ 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']);
561
595
 
596
+ // SVG
562
597
  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']);
563
- const 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.
598
+ 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']);
599
+
600
+ // List of SVG elements that are disallowed by default.
564
601
  // We still need to know them so that we can do namespace
565
602
  // checks properly in case one wants to add them to
566
603
  // allow-list.
604
+ 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']);
605
+ 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']);
567
606
 
568
- const 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']);
569
- 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']); // Similarly to SVG, we want to know all MathML elements,
607
+ // Similarly to SVG, we want to know all MathML elements,
570
608
  // even those that we disallow by default.
571
-
572
609
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
573
610
  const text = freeze(['#text']);
574
611
 
575
- const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']);
612
+ const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
576
613
  const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
577
614
  const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
578
615
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
579
616
 
617
+ // eslint-disable-next-line unicorn/better-regex
580
618
  const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
581
-
582
619
  const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
583
620
  const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
584
621
  const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
585
-
586
622
  const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
587
-
588
623
  const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
589
624
  );
625
+
590
626
  const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
591
627
  const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
592
628
  );
629
+
593
630
  const DOCTYPE_NAME = seal(/^html$/i);
631
+ const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
594
632
 
595
633
  var EXPRESSIONS = /*#__PURE__*/Object.freeze({
596
634
  __proto__: null,
@@ -602,47 +640,62 @@ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
602
640
  IS_ALLOWED_URI: IS_ALLOWED_URI,
603
641
  IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
604
642
  ATTR_WHITESPACE: ATTR_WHITESPACE,
605
- DOCTYPE_NAME: DOCTYPE_NAME
643
+ DOCTYPE_NAME: DOCTYPE_NAME,
644
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT
606
645
  });
607
646
 
608
- const getGlobal = () => typeof window === 'undefined' ? null : window;
647
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
648
+ const NODE_TYPE = {
649
+ element: 1,
650
+ attribute: 2,
651
+ text: 3,
652
+ cdataSection: 4,
653
+ entityReference: 5,
654
+ // Deprecated
655
+ entityNode: 6,
656
+ // Deprecated
657
+ progressingInstruction: 7,
658
+ comment: 8,
659
+ document: 9,
660
+ documentType: 10,
661
+ documentFragment: 11,
662
+ notation: 12 // Deprecated
663
+ };
664
+
665
+ const getGlobal = function getGlobal() {
666
+ return typeof window === 'undefined' ? null : window;
667
+ };
668
+
609
669
  /**
610
670
  * Creates a no-op policy for internal use only.
611
671
  * Don't export this function outside this module!
612
- * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
613
- * @param {Document} document The document object (to determine policy name suffix)
614
- * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
615
- * are not supported).
672
+ * @param {TrustedTypePolicyFactory} trustedTypes The policy factory.
673
+ * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
674
+ * @return {TrustedTypePolicy} The policy created (or null, if Trusted Types
675
+ * are not supported or creating the policy failed).
616
676
  */
617
-
618
-
619
- const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
677
+ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
620
678
  if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
621
679
  return null;
622
- } // Allow the callers to control the unique policy name
680
+ }
681
+
682
+ // Allow the callers to control the unique policy name
623
683
  // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
624
684
  // Policy creation with duplicate names throws in Trusted Types.
625
-
626
-
627
685
  let suffix = null;
628
686
  const ATTR_NAME = 'data-tt-policy-suffix';
629
-
630
- if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
631
- suffix = document.currentScript.getAttribute(ATTR_NAME);
687
+ if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
688
+ suffix = purifyHostElement.getAttribute(ATTR_NAME);
632
689
  }
633
-
634
690
  const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
635
-
636
691
  try {
637
692
  return trustedTypes.createPolicy(policyName, {
638
693
  createHTML(html) {
639
694
  return html;
640
695
  },
641
-
642
696
  createScriptURL(scriptUrl) {
643
697
  return scriptUrl;
644
698
  }
645
-
646
699
  });
647
700
  } catch (_) {
648
701
  // Policy creation failed (most likely another DOMPurify script has
@@ -652,36 +705,32 @@ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedType
652
705
  return null;
653
706
  }
654
707
  };
655
-
656
708
  function createDOMPurify() {
657
709
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
658
-
659
710
  const DOMPurify = root => createDOMPurify(root);
711
+
660
712
  /**
661
713
  * Version label, exposed for easier checks
662
714
  * if DOMPurify is up to date or not
663
715
  */
716
+ DOMPurify.version = '3.1.3';
664
717
 
665
-
666
- DOMPurify.version = '3.0.2';
667
718
  /**
668
719
  * Array of elements that DOMPurify removed during sanitation.
669
720
  * Empty if nothing was removed.
670
721
  */
671
-
672
722
  DOMPurify.removed = [];
673
-
674
- if (!window || !window.document || window.document.nodeType !== 9) {
723
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
675
724
  // Not running in a browser, provide a factory function
676
725
  // so that you can pass your own Window
677
726
  DOMPurify.isSupported = false;
678
727
  return DOMPurify;
679
728
  }
680
-
681
- const originalDocument = window.document;
682
729
  let {
683
730
  document
684
731
  } = window;
732
+ const originalDocument = document;
733
+ const currentScript = originalDocument.currentScript;
685
734
  const {
686
735
  DocumentFragment,
687
736
  HTMLTemplateElement,
@@ -697,24 +746,22 @@ function createDOMPurify() {
697
746
  const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
698
747
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
699
748
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
700
- const getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a
749
+ const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
750
+
751
+ // As per issue #47, the web-components registry is inherited by a
701
752
  // new document created via createHTMLDocument. As per the spec
702
753
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
703
754
  // a new empty registry is used when creating a template contents owner
704
755
  // document, so we use that as our parent document to ensure nothing
705
756
  // is inherited.
706
-
707
757
  if (typeof HTMLTemplateElement === 'function') {
708
758
  const template = document.createElement('template');
709
-
710
759
  if (template.content && template.content.ownerDocument) {
711
760
  document = template.content.ownerDocument;
712
761
  }
713
762
  }
714
-
715
- const trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
716
-
717
- const emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
763
+ let trustedTypesPolicy;
764
+ let emptyHTML = '';
718
765
  const {
719
766
  implementation,
720
767
  createNodeIterator,
@@ -725,11 +772,11 @@ function createDOMPurify() {
725
772
  importNode
726
773
  } = originalDocument;
727
774
  let hooks = {};
775
+
728
776
  /**
729
777
  * Expose whether this browser supports running the full DOMPurify.
730
778
  */
731
-
732
- DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined';
779
+ DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
733
780
  const {
734
781
  MUSTACHE_EXPR,
735
782
  ERB_EXPR,
@@ -737,32 +784,33 @@ function createDOMPurify() {
737
784
  DATA_ATTR,
738
785
  ARIA_ATTR,
739
786
  IS_SCRIPT_OR_DATA,
740
- ATTR_WHITESPACE
787
+ ATTR_WHITESPACE,
788
+ CUSTOM_ELEMENT
741
789
  } = EXPRESSIONS;
742
790
  let {
743
791
  IS_ALLOWED_URI: IS_ALLOWED_URI$1
744
792
  } = EXPRESSIONS;
793
+
745
794
  /**
746
795
  * We consider the elements and attributes below to be safe. Ideally
747
796
  * don't add any new ones but feel free to remove unwanted ones.
748
797
  */
749
798
 
750
799
  /* allowed element names */
751
-
752
800
  let ALLOWED_TAGS = null;
753
801
  const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
754
- /* Allowed attribute names */
755
802
 
803
+ /* Allowed attribute names */
756
804
  let ALLOWED_ATTR = null;
757
805
  const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
806
+
758
807
  /*
759
808
  * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
760
809
  * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
761
810
  * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
762
811
  * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
763
812
  */
764
-
765
- let CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
813
+ let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
766
814
  tagNameCheck: {
767
815
  writable: true,
768
816
  configurable: false,
@@ -782,59 +830,65 @@ function createDOMPurify() {
782
830
  value: false
783
831
  }
784
832
  }));
785
- /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
786
833
 
834
+ /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
787
835
  let FORBID_TAGS = null;
788
- /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
789
836
 
837
+ /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
790
838
  let FORBID_ATTR = null;
791
- /* Decide if ARIA attributes are okay */
792
839
 
840
+ /* Decide if ARIA attributes are okay */
793
841
  let ALLOW_ARIA_ATTR = true;
794
- /* Decide if custom data attributes are okay */
795
842
 
843
+ /* Decide if custom data attributes are okay */
796
844
  let ALLOW_DATA_ATTR = true;
797
- /* Decide if unknown protocols are okay */
798
845
 
846
+ /* Decide if unknown protocols are okay */
799
847
  let ALLOW_UNKNOWN_PROTOCOLS = false;
848
+
800
849
  /* Decide if self-closing tags in attributes are allowed.
801
850
  * Usually removed due to a mXSS issue in jQuery 3.0 */
802
-
803
851
  let ALLOW_SELF_CLOSE_IN_ATTR = true;
852
+
804
853
  /* Output should be safe for common template engines.
805
854
  * This means, DOMPurify removes data attributes, mustaches and ERB
806
855
  */
807
-
808
856
  let SAFE_FOR_TEMPLATES = false;
809
- /* Decide if document with <html>... should be returned */
810
857
 
858
+ /* Output should be safe even for XML used within HTML and alike.
859
+ * This means, DOMPurify removes comments when containing risky content.
860
+ */
861
+ let SAFE_FOR_XML = true;
862
+
863
+ /* Decide if document with <html>... should be returned */
811
864
  let WHOLE_DOCUMENT = false;
812
- /* Track whether config is already set on this instance of DOMPurify. */
813
865
 
866
+ /* Track whether config is already set on this instance of DOMPurify. */
814
867
  let SET_CONFIG = false;
868
+
815
869
  /* Decide if all elements (e.g. style, script) must be children of
816
870
  * document.body. By default, browsers might move them to document.head */
817
-
818
871
  let FORCE_BODY = false;
872
+
819
873
  /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
820
874
  * string (or a TrustedHTML object if Trusted Types are supported).
821
875
  * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
822
876
  */
823
-
824
877
  let RETURN_DOM = false;
878
+
825
879
  /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
826
880
  * string (or a TrustedHTML object if Trusted Types are supported) */
827
-
828
881
  let RETURN_DOM_FRAGMENT = false;
882
+
829
883
  /* Try to return a Trusted Type object instead of a string, return a string in
830
884
  * case Trusted Types are not supported */
831
-
832
885
  let RETURN_TRUSTED_TYPE = false;
886
+
833
887
  /* Output should be free from DOM clobbering attacks?
834
888
  * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
835
889
  */
836
-
837
890
  let SANITIZE_DOM = true;
891
+
838
892
  /* Achieve full DOM Clobbering protection by isolating the namespace of named
839
893
  * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
840
894
  *
@@ -848,327 +902,312 @@ function createDOMPurify() {
848
902
  * Namespace isolation is implemented by prefixing `id` and `name` attributes
849
903
  * with a constant string, i.e., `user-content-`
850
904
  */
851
-
852
905
  let SANITIZE_NAMED_PROPS = false;
853
906
  const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
854
- /* Keep element content when removing element? */
855
907
 
908
+ /* Keep element content when removing element? */
856
909
  let KEEP_CONTENT = true;
910
+
857
911
  /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
858
912
  * of importing it into a new Document and returning a sanitized copy */
859
-
860
913
  let IN_PLACE = false;
861
- /* Allow usage of profiles like html, svg and mathMl */
862
914
 
915
+ /* Allow usage of profiles like html, svg and mathMl */
863
916
  let USE_PROFILES = {};
864
- /* Tags to ignore content of when KEEP_CONTENT is true */
865
917
 
918
+ /* Tags to ignore content of when KEEP_CONTENT is true */
866
919
  let FORBID_CONTENTS = null;
867
920
  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']);
868
- /* Tags that are safe for data: URIs */
869
921
 
922
+ /* Tags that are safe for data: URIs */
870
923
  let DATA_URI_TAGS = null;
871
924
  const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
872
- /* Attributes safe for values like "javascript:" */
873
925
 
926
+ /* Attributes safe for values like "javascript:" */
874
927
  let URI_SAFE_ATTRIBUTES = null;
875
928
  const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
876
929
  const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
877
930
  const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
878
931
  const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
879
932
  /* Document namespace */
880
-
881
933
  let NAMESPACE = HTML_NAMESPACE;
882
934
  let IS_EMPTY_INPUT = false;
883
- /* Allowed XHTML+XML namespaces */
884
935
 
936
+ /* Allowed XHTML+XML namespaces */
885
937
  let ALLOWED_NAMESPACES = null;
886
938
  const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
887
- /* Parsing of strict XHTML documents */
888
939
 
889
- let PARSER_MEDIA_TYPE;
940
+ /* Parsing of strict XHTML documents */
941
+ let PARSER_MEDIA_TYPE = null;
890
942
  const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
891
943
  const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
892
- let transformCaseFunc;
893
- /* Keep a reference to config to pass to hooks */
944
+ let transformCaseFunc = null;
894
945
 
946
+ /* Keep a reference to config to pass to hooks */
895
947
  let CONFIG = null;
896
- /* Ideally, do not touch anything below this line */
897
948
 
949
+ /* Specify the maximum element nesting depth to prevent mXSS */
950
+ const MAX_NESTING_DEPTH = 255;
951
+
952
+ /* Ideally, do not touch anything below this line */
898
953
  /* ______________________________________________ */
899
954
 
900
955
  const formElement = document.createElement('form');
901
-
902
956
  const isRegexOrFunction = function isRegexOrFunction(testValue) {
903
957
  return testValue instanceof RegExp || testValue instanceof Function;
904
958
  };
959
+
905
960
  /**
906
961
  * _parseConfig
907
962
  *
908
963
  * @param {Object} cfg optional config literal
909
964
  */
910
965
  // eslint-disable-next-line complexity
911
-
912
-
913
- const _parseConfig = function _parseConfig(cfg) {
966
+ const _parseConfig = function _parseConfig() {
967
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
914
968
  if (CONFIG && CONFIG === cfg) {
915
969
  return;
916
970
  }
917
- /* Shield configuration object from tampering */
918
-
919
971
 
972
+ /* Shield configuration object from tampering */
920
973
  if (!cfg || typeof cfg !== 'object') {
921
974
  cfg = {};
922
975
  }
923
- /* Shield configuration object from prototype pollution */
924
-
925
976
 
977
+ /* Shield configuration object from prototype pollution */
926
978
  cfg = clone(cfg);
927
- PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
928
- 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.
979
+ PARSER_MEDIA_TYPE =
980
+ // eslint-disable-next-line unicorn/prefer-includes
981
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
929
982
 
983
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
930
984
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
931
- /* Set configuration parameters */
932
985
 
933
- ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
934
- ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
935
- ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
936
- URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent
937
- cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent
986
+ /* Set configuration parameters */
987
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
988
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
989
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
990
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
991
+ // eslint-disable-line indent
992
+ cfg.ADD_URI_SAFE_ATTR,
993
+ // eslint-disable-line indent
938
994
  transformCaseFunc // eslint-disable-line indent
939
995
  ) // eslint-disable-line indent
940
996
  : DEFAULT_URI_SAFE_ATTRIBUTES;
941
- DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent
942
- cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent
997
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
998
+ // eslint-disable-line indent
999
+ cfg.ADD_DATA_URI_TAGS,
1000
+ // eslint-disable-line indent
943
1001
  transformCaseFunc // eslint-disable-line indent
944
1002
  ) // eslint-disable-line indent
945
1003
  : DEFAULT_DATA_URI_TAGS;
946
- FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
947
- FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
948
- FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
949
- USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
1004
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
1005
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
1006
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
1007
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
950
1008
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
951
-
952
1009
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
953
-
954
1010
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
955
-
956
1011
  ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
957
-
958
1012
  SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
959
-
1013
+ SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
960
1014
  WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
961
-
962
1015
  RETURN_DOM = cfg.RETURN_DOM || false; // Default false
963
-
964
1016
  RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
965
-
966
1017
  RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
967
-
968
1018
  FORCE_BODY = cfg.FORCE_BODY || false; // Default false
969
-
970
1019
  SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
971
-
972
1020
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
973
-
974
1021
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
975
-
976
1022
  IN_PLACE = cfg.IN_PLACE || false; // Default false
977
-
978
1023
  IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
979
1024
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
980
1025
  CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
981
-
982
1026
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
983
1027
  CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
984
1028
  }
985
-
986
1029
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
987
1030
  CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
988
1031
  }
989
-
990
1032
  if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
991
1033
  CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
992
1034
  }
993
-
994
1035
  if (SAFE_FOR_TEMPLATES) {
995
1036
  ALLOW_DATA_ATTR = false;
996
1037
  }
997
-
998
1038
  if (RETURN_DOM_FRAGMENT) {
999
1039
  RETURN_DOM = true;
1000
1040
  }
1001
- /* Parse profile info */
1002
-
1003
1041
 
1042
+ /* Parse profile info */
1004
1043
  if (USE_PROFILES) {
1005
- ALLOWED_TAGS = addToSet({}, [...text]);
1044
+ ALLOWED_TAGS = addToSet({}, text);
1006
1045
  ALLOWED_ATTR = [];
1007
-
1008
1046
  if (USE_PROFILES.html === true) {
1009
1047
  addToSet(ALLOWED_TAGS, html$1);
1010
1048
  addToSet(ALLOWED_ATTR, html);
1011
1049
  }
1012
-
1013
1050
  if (USE_PROFILES.svg === true) {
1014
1051
  addToSet(ALLOWED_TAGS, svg$1);
1015
1052
  addToSet(ALLOWED_ATTR, svg);
1016
1053
  addToSet(ALLOWED_ATTR, xml);
1017
1054
  }
1018
-
1019
1055
  if (USE_PROFILES.svgFilters === true) {
1020
1056
  addToSet(ALLOWED_TAGS, svgFilters);
1021
1057
  addToSet(ALLOWED_ATTR, svg);
1022
1058
  addToSet(ALLOWED_ATTR, xml);
1023
1059
  }
1024
-
1025
1060
  if (USE_PROFILES.mathMl === true) {
1026
1061
  addToSet(ALLOWED_TAGS, mathMl$1);
1027
1062
  addToSet(ALLOWED_ATTR, mathMl);
1028
1063
  addToSet(ALLOWED_ATTR, xml);
1029
1064
  }
1030
1065
  }
1031
- /* Merge configuration parameters */
1032
-
1033
1066
 
1067
+ /* Merge configuration parameters */
1034
1068
  if (cfg.ADD_TAGS) {
1035
1069
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
1036
1070
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
1037
1071
  }
1038
-
1039
1072
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
1040
1073
  }
1041
-
1042
1074
  if (cfg.ADD_ATTR) {
1043
1075
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
1044
1076
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
1045
1077
  }
1046
-
1047
1078
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
1048
1079
  }
1049
-
1050
1080
  if (cfg.ADD_URI_SAFE_ATTR) {
1051
1081
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
1052
1082
  }
1053
-
1054
1083
  if (cfg.FORBID_CONTENTS) {
1055
1084
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
1056
1085
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
1057
1086
  }
1058
-
1059
1087
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
1060
1088
  }
1061
- /* Add #text in case KEEP_CONTENT is set to true */
1062
-
1063
1089
 
1090
+ /* Add #text in case KEEP_CONTENT is set to true */
1064
1091
  if (KEEP_CONTENT) {
1065
1092
  ALLOWED_TAGS['#text'] = true;
1066
1093
  }
1067
- /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
1068
-
1069
1094
 
1095
+ /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
1070
1096
  if (WHOLE_DOCUMENT) {
1071
1097
  addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
1072
1098
  }
1073
- /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
1074
-
1075
1099
 
1100
+ /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
1076
1101
  if (ALLOWED_TAGS.table) {
1077
1102
  addToSet(ALLOWED_TAGS, ['tbody']);
1078
1103
  delete FORBID_TAGS.tbody;
1079
- } // Prevent further manipulation of configuration.
1080
- // Not available in IE8, Safari 5, etc.
1104
+ }
1105
+ if (cfg.TRUSTED_TYPES_POLICY) {
1106
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
1107
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
1108
+ }
1109
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
1110
+ throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
1111
+ }
1112
+
1113
+ // Overwrite existing TrustedTypes policy.
1114
+ trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
1115
+
1116
+ // Sign local variables required by `sanitize`.
1117
+ emptyHTML = trustedTypesPolicy.createHTML('');
1118
+ } else {
1119
+ // Uninitialized policy, attempt to initialize the internal dompurify policy.
1120
+ if (trustedTypesPolicy === undefined) {
1121
+ trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
1122
+ }
1081
1123
 
1124
+ // If creating the internal policy succeeded sign internal variables.
1125
+ if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
1126
+ emptyHTML = trustedTypesPolicy.createHTML('');
1127
+ }
1128
+ }
1082
1129
 
1130
+ // Prevent further manipulation of configuration.
1131
+ // Not available in IE8, Safari 5, etc.
1083
1132
  if (freeze) {
1084
1133
  freeze(cfg);
1085
1134
  }
1086
-
1087
1135
  CONFIG = cfg;
1088
1136
  };
1089
-
1090
1137
  const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
1091
- const HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML
1138
+ const HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'annotation-xml']);
1139
+
1140
+ // Certain elements are allowed in both SVG and HTML
1092
1141
  // namespace. We need to specify them explicitly
1093
1142
  // so that they don't get erroneously deleted from
1094
1143
  // HTML namespace.
1095
-
1096
1144
  const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
1145
+
1097
1146
  /* Keep track of all possible SVG and MathML tags
1098
1147
  * so that we can perform the namespace checks
1099
1148
  * correctly. */
1149
+ const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
1150
+ const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
1100
1151
 
1101
- const ALL_SVG_TAGS = addToSet({}, svg$1);
1102
- addToSet(ALL_SVG_TAGS, svgFilters);
1103
- addToSet(ALL_SVG_TAGS, svgDisallowed);
1104
- const ALL_MATHML_TAGS = addToSet({}, mathMl$1);
1105
- addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
1106
1152
  /**
1107
- *
1108
- *
1109
1153
  * @param {Element} element a DOM element whose namespace is being checked
1110
1154
  * @returns {boolean} Return false if the element has a
1111
1155
  * namespace that a spec-compliant parser would never
1112
1156
  * return. Return true otherwise.
1113
1157
  */
1114
-
1115
1158
  const _checkValidNamespace = function _checkValidNamespace(element) {
1116
- let parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode
1117
- // can be null. We just simulate parent in this case.
1159
+ let parent = getParentNode(element);
1118
1160
 
1161
+ // In JSDOM, if we're inside shadow DOM, then parentNode
1162
+ // can be null. We just simulate parent in this case.
1119
1163
  if (!parent || !parent.tagName) {
1120
1164
  parent = {
1121
1165
  namespaceURI: NAMESPACE,
1122
1166
  tagName: 'template'
1123
1167
  };
1124
1168
  }
1125
-
1126
1169
  const tagName = stringToLowerCase(element.tagName);
1127
1170
  const parentTagName = stringToLowerCase(parent.tagName);
1128
-
1129
1171
  if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
1130
1172
  return false;
1131
1173
  }
1132
-
1133
1174
  if (element.namespaceURI === SVG_NAMESPACE) {
1134
1175
  // The only way to switch from HTML namespace to SVG
1135
1176
  // is via <svg>. If it happens via any other tag, then
1136
1177
  // it should be killed.
1137
1178
  if (parent.namespaceURI === HTML_NAMESPACE) {
1138
1179
  return tagName === 'svg';
1139
- } // The only way to switch from MathML to SVG is via`
1180
+ }
1181
+
1182
+ // The only way to switch from MathML to SVG is via`
1140
1183
  // svg if parent is either <annotation-xml> or MathML
1141
1184
  // text integration points.
1142
-
1143
-
1144
1185
  if (parent.namespaceURI === MATHML_NAMESPACE) {
1145
1186
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
1146
- } // We only allow elements that are defined in SVG
1147
- // spec. All others are disallowed in SVG namespace.
1148
-
1187
+ }
1149
1188
 
1189
+ // We only allow elements that are defined in SVG
1190
+ // spec. All others are disallowed in SVG namespace.
1150
1191
  return Boolean(ALL_SVG_TAGS[tagName]);
1151
1192
  }
1152
-
1153
1193
  if (element.namespaceURI === MATHML_NAMESPACE) {
1154
1194
  // The only way to switch from HTML namespace to MathML
1155
1195
  // is via <math>. If it happens via any other tag, then
1156
1196
  // it should be killed.
1157
1197
  if (parent.namespaceURI === HTML_NAMESPACE) {
1158
1198
  return tagName === 'math';
1159
- } // The only way to switch from SVG to MathML is via
1160
- // <math> and HTML integration points
1161
-
1199
+ }
1162
1200
 
1201
+ // The only way to switch from SVG to MathML is via
1202
+ // <math> and HTML integration points
1163
1203
  if (parent.namespaceURI === SVG_NAMESPACE) {
1164
1204
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
1165
- } // We only allow elements that are defined in MathML
1166
- // spec. All others are disallowed in MathML namespace.
1167
-
1205
+ }
1168
1206
 
1207
+ // We only allow elements that are defined in MathML
1208
+ // spec. All others are disallowed in MathML namespace.
1169
1209
  return Boolean(ALL_MATHML_TAGS[tagName]);
1170
1210
  }
1171
-
1172
1211
  if (element.namespaceURI === HTML_NAMESPACE) {
1173
1212
  // The only way to switch from SVG to HTML is via
1174
1213
  // HTML integration points, and from MathML to HTML
@@ -1176,39 +1215,36 @@ function createDOMPurify() {
1176
1215
  if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
1177
1216
  return false;
1178
1217
  }
1179
-
1180
1218
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
1181
1219
  return false;
1182
- } // We disallow tags that are specific for MathML
1183
- // or SVG and should never appear in HTML namespace
1184
-
1220
+ }
1185
1221
 
1222
+ // We disallow tags that are specific for MathML
1223
+ // or SVG and should never appear in HTML namespace
1186
1224
  return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
1187
- } // For XHTML and XML documents that support custom namespaces
1188
-
1225
+ }
1189
1226
 
1227
+ // For XHTML and XML documents that support custom namespaces
1190
1228
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
1191
1229
  return true;
1192
- } // The code should never reach this place (this means
1193
- // that the element somehow got namespace that is not
1230
+ }
1231
+
1232
+ // The code should never reach this place (this means
1233
+ // that the element somehow got namespace that is not
1194
1234
  // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
1195
1235
  // Return false just in case.
1196
-
1197
-
1198
1236
  return false;
1199
1237
  };
1238
+
1200
1239
  /**
1201
1240
  * _forceRemove
1202
1241
  *
1203
1242
  * @param {Node} node a DOM node
1204
1243
  */
1205
-
1206
-
1207
1244
  const _forceRemove = function _forceRemove(node) {
1208
1245
  arrayPush(DOMPurify.removed, {
1209
1246
  element: node
1210
1247
  });
1211
-
1212
1248
  try {
1213
1249
  // eslint-disable-next-line unicorn/prefer-dom-node-remove
1214
1250
  node.parentNode.removeChild(node);
@@ -1216,14 +1252,13 @@ function createDOMPurify() {
1216
1252
  node.remove();
1217
1253
  }
1218
1254
  };
1255
+
1219
1256
  /**
1220
1257
  * _removeAttribute
1221
1258
  *
1222
1259
  * @param {String} name an Attribute name
1223
1260
  * @param {Node} node a DOM node
1224
1261
  */
1225
-
1226
-
1227
1262
  const _removeAttribute = function _removeAttribute(name, node) {
1228
1263
  try {
1229
1264
  arrayPush(DOMPurify.removed, {
@@ -1236,9 +1271,9 @@ function createDOMPurify() {
1236
1271
  from: node
1237
1272
  });
1238
1273
  }
1274
+ node.removeAttribute(name);
1239
1275
 
1240
- node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes
1241
-
1276
+ // We void attribute values for unremovable "is"" attributes
1242
1277
  if (name === 'is' && !ALLOWED_ATTR[name]) {
1243
1278
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
1244
1279
  try {
@@ -1251,19 +1286,17 @@ function createDOMPurify() {
1251
1286
  }
1252
1287
  }
1253
1288
  };
1289
+
1254
1290
  /**
1255
1291
  * _initDocument
1256
1292
  *
1257
1293
  * @param {String} dirty a string of dirty markup
1258
1294
  * @return {Document} a DOM, filled with the dirty markup
1259
1295
  */
1260
-
1261
-
1262
1296
  const _initDocument = function _initDocument(dirty) {
1263
1297
  /* Create a HTML document */
1264
- let doc;
1265
- let leadingWhitespace;
1266
-
1298
+ let doc = null;
1299
+ let leadingWhitespace = null;
1267
1300
  if (FORCE_BODY) {
1268
1301
  dirty = '<remove></remove>' + dirty;
1269
1302
  } else {
@@ -1271,83 +1304,78 @@ function createDOMPurify() {
1271
1304
  const matches = stringMatch(dirty, /^[\r\n\t ]+/);
1272
1305
  leadingWhitespace = matches && matches[0];
1273
1306
  }
1274
-
1275
1307
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
1276
1308
  // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
1277
1309
  dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
1278
1310
  }
1279
-
1280
1311
  const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
1281
1312
  /*
1282
1313
  * Use the DOMParser API by default, fallback later if needs be
1283
1314
  * DOMParser not work for svg when has multiple root element.
1284
1315
  */
1285
-
1286
1316
  if (NAMESPACE === HTML_NAMESPACE) {
1287
1317
  try {
1288
1318
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
1289
1319
  } catch (_) {}
1290
1320
  }
1291
- /* Use createHTMLDocument in case DOMParser is not available */
1292
-
1293
1321
 
1322
+ /* Use createHTMLDocument in case DOMParser is not available */
1294
1323
  if (!doc || !doc.documentElement) {
1295
1324
  doc = implementation.createDocument(NAMESPACE, 'template', null);
1296
-
1297
1325
  try {
1298
1326
  doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
1299
- } catch (_) {// Syntax error if dirtyPayload is invalid xml
1327
+ } catch (_) {
1328
+ // Syntax error if dirtyPayload is invalid xml
1300
1329
  }
1301
1330
  }
1302
-
1303
1331
  const body = doc.body || doc.documentElement;
1304
-
1305
1332
  if (dirty && leadingWhitespace) {
1306
1333
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
1307
1334
  }
1308
- /* Work on whole document or just its body */
1309
-
1310
1335
 
1336
+ /* Work on whole document or just its body */
1311
1337
  if (NAMESPACE === HTML_NAMESPACE) {
1312
1338
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
1313
1339
  }
1314
-
1315
1340
  return WHOLE_DOCUMENT ? doc.documentElement : body;
1316
1341
  };
1342
+
1317
1343
  /**
1318
- * _createIterator
1344
+ * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
1319
1345
  *
1320
- * @param {Document} root document/fragment to create iterator for
1321
- * @return {Iterator} iterator instance
1346
+ * @param {Node} root The root element or node to start traversing on.
1347
+ * @return {NodeIterator} The created NodeIterator
1322
1348
  */
1323
-
1324
-
1325
- const _createIterator = function _createIterator(root) {
1326
- return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
1327
- NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
1349
+ const _createNodeIterator = function _createNodeIterator(root) {
1350
+ return createNodeIterator.call(root.ownerDocument || root, root,
1351
+ // eslint-disable-next-line no-bitwise
1352
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
1328
1353
  };
1354
+
1329
1355
  /**
1330
1356
  * _isClobbered
1331
1357
  *
1332
1358
  * @param {Node} elm element to check for clobbering attacks
1333
1359
  * @return {Boolean} true if clobbered, false if safe
1334
1360
  */
1335
-
1336
-
1337
1361
  const _isClobbered = function _isClobbered(elm) {
1338
- 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');
1362
+ return elm instanceof HTMLFormElement && (
1363
+ // eslint-disable-next-line unicorn/no-typeof-undefined
1364
+ typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' ||
1365
+ // eslint-disable-next-line unicorn/no-typeof-undefined
1366
+ typeof elm.__removalCount !== 'undefined' && typeof elm.__removalCount !== 'number' || 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');
1339
1367
  };
1368
+
1340
1369
  /**
1341
- * _isNode
1370
+ * Checks whether the given object is a DOM node.
1342
1371
  *
1343
- * @param {Node} obj object to check whether it's a DOM node
1372
+ * @param {Node} object object to check whether it's a DOM node
1344
1373
  * @return {Boolean} true is object is a DOM node
1345
1374
  */
1346
-
1347
-
1348
1375
  const _isNode = function _isNode(object) {
1349
- return typeof Node === 'object' ? object instanceof Node : object && typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
1376
+ return typeof Node === 'function' && object instanceof Node;
1350
1377
  };
1378
+
1351
1379
  /**
1352
1380
  * _executeHook
1353
1381
  * Execute user configurable hooks
@@ -1356,17 +1384,15 @@ function createDOMPurify() {
1356
1384
  * @param {Node} currentNode node to work on with the hook
1357
1385
  * @param {Object} data additional hook parameters
1358
1386
  */
1359
-
1360
-
1361
1387
  const _executeHook = function _executeHook(entryPoint, currentNode, data) {
1362
1388
  if (!hooks[entryPoint]) {
1363
1389
  return;
1364
1390
  }
1365
-
1366
1391
  arrayForEach(hooks[entryPoint], hook => {
1367
1392
  hook.call(DOMPurify, currentNode, data, CONFIG);
1368
1393
  });
1369
1394
  };
1395
+
1370
1396
  /**
1371
1397
  * _sanitizeElements
1372
1398
  *
@@ -1377,94 +1403,93 @@ function createDOMPurify() {
1377
1403
  * @param {Node} currentNode to check for permission to exist
1378
1404
  * @return {Boolean} true if node was killed, false if left alive
1379
1405
  */
1380
-
1381
-
1382
1406
  const _sanitizeElements = function _sanitizeElements(currentNode) {
1383
- let content;
1384
- /* Execute a hook if present */
1407
+ let content = null;
1385
1408
 
1409
+ /* Execute a hook if present */
1386
1410
  _executeHook('beforeSanitizeElements', currentNode, null);
1387
- /* Check if element is clobbered or can clobber */
1388
-
1389
1411
 
1412
+ /* Check if element is clobbered or can clobber */
1390
1413
  if (_isClobbered(currentNode)) {
1391
1414
  _forceRemove(currentNode);
1392
-
1393
1415
  return true;
1394
1416
  }
1395
- /* Now let's check the element's type and name */
1396
-
1397
1417
 
1418
+ /* Now let's check the element's type and name */
1398
1419
  const tagName = transformCaseFunc(currentNode.nodeName);
1399
- /* Execute a hook if present */
1400
1420
 
1421
+ /* Execute a hook if present */
1401
1422
  _executeHook('uponSanitizeElement', currentNode, {
1402
1423
  tagName,
1403
1424
  allowedTags: ALLOWED_TAGS
1404
1425
  });
1405
- /* Detect mXSS attempts abusing namespace confusion */
1406
-
1407
1426
 
1408
- if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
1427
+ /* Detect mXSS attempts abusing namespace confusion */
1428
+ if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
1409
1429
  _forceRemove(currentNode);
1430
+ return true;
1431
+ }
1410
1432
 
1433
+ /* Remove any ocurrence of processing instructions */
1434
+ if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
1435
+ _forceRemove(currentNode);
1411
1436
  return true;
1412
1437
  }
1413
- /* Remove element if anything forbids its presence */
1414
1438
 
1439
+ /* Remove any kind of possibly harmful comments */
1440
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
1441
+ _forceRemove(currentNode);
1442
+ return true;
1443
+ }
1415
1444
 
1445
+ /* Remove element if anything forbids its presence */
1416
1446
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1417
1447
  /* Check if we have a custom element to handle */
1418
- if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
1419
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
1420
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
1448
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
1449
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
1450
+ return false;
1451
+ }
1452
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
1453
+ return false;
1454
+ }
1421
1455
  }
1422
- /* Keep content except for bad-listed elements */
1423
-
1424
1456
 
1457
+ /* Keep content except for bad-listed elements */
1425
1458
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1426
1459
  const parentNode = getParentNode(currentNode) || currentNode.parentNode;
1427
1460
  const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
1428
-
1429
1461
  if (childNodes && parentNode) {
1430
1462
  const childCount = childNodes.length;
1431
-
1432
1463
  for (let i = childCount - 1; i >= 0; --i) {
1433
- parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
1464
+ const childClone = cloneNode(childNodes[i], true);
1465
+ childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
1466
+ parentNode.insertBefore(childClone, getNextSibling(currentNode));
1434
1467
  }
1435
1468
  }
1436
1469
  }
1437
-
1438
1470
  _forceRemove(currentNode);
1439
-
1440
1471
  return true;
1441
1472
  }
1442
- /* Check whether element has a valid namespace */
1443
-
1444
1473
 
1474
+ /* Check whether element has a valid namespace */
1445
1475
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
1446
1476
  _forceRemove(currentNode);
1447
-
1448
1477
  return true;
1449
1478
  }
1450
- /* Make sure that older browsers don't get noscript mXSS */
1451
-
1452
1479
 
1453
- if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
1480
+ /* Make sure that older browsers don't get fallback-tag mXSS */
1481
+ if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
1454
1482
  _forceRemove(currentNode);
1455
-
1456
1483
  return true;
1457
1484
  }
1458
- /* Sanitize element content to be template-safe */
1459
1485
 
1460
-
1461
- if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
1486
+ /* Sanitize element content to be template-safe */
1487
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
1462
1488
  /* Get the element's text content */
1463
1489
  content = currentNode.textContent;
1464
- content = stringReplace(content, MUSTACHE_EXPR, ' ');
1465
- content = stringReplace(content, ERB_EXPR, ' ');
1466
- content = stringReplace(content, TMPLIT_EXPR, ' ');
1467
-
1490
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1491
+ content = stringReplace(content, expr, ' ');
1492
+ });
1468
1493
  if (currentNode.textContent !== content) {
1469
1494
  arrayPush(DOMPurify.removed, {
1470
1495
  element: currentNode.cloneNode()
@@ -1472,13 +1497,12 @@ function createDOMPurify() {
1472
1497
  currentNode.textContent = content;
1473
1498
  }
1474
1499
  }
1475
- /* Execute a hook if present */
1476
-
1477
1500
 
1501
+ /* Execute a hook if present */
1478
1502
  _executeHook('afterSanitizeElements', currentNode, null);
1479
-
1480
1503
  return false;
1481
1504
  };
1505
+
1482
1506
  /**
1483
1507
  * _isValidAttribute
1484
1508
  *
@@ -1488,47 +1512,46 @@ function createDOMPurify() {
1488
1512
  * @return {Boolean} Returns true if `value` is valid, otherwise false.
1489
1513
  */
1490
1514
  // eslint-disable-next-line complexity
1491
-
1492
-
1493
1515
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1494
1516
  /* Make sure attribute cannot clobber */
1495
- if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1517
+ if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement || value === '__depth' || value === '__removalCount')) {
1496
1518
  return false;
1497
1519
  }
1520
+
1498
1521
  /* Allow valid data-* attributes: At least one character after "-"
1499
1522
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1500
1523
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1501
1524
  We don't need to check the value; it's always URI safe. */
1502
-
1503
-
1504
1525
  if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
1505
- if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1526
+ if (
1527
+ // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1506
1528
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1507
1529
  // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1508
- _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
1530
+ _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) ||
1531
+ // Alternative, second condition checks if it's an `is`-attribute, AND
1509
1532
  // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1510
1533
  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 {
1511
1534
  return false;
1512
1535
  }
1513
1536
  /* Check value is safe. First, is attr inert? If so, is safe */
1514
-
1515
- } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (!value) ; else {
1537
+ } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
1516
1538
  return false;
1517
- }
1518
-
1539
+ } else ;
1519
1540
  return true;
1520
1541
  };
1542
+
1521
1543
  /**
1522
- * _basicCustomElementCheck
1544
+ * _isBasicCustomElement
1523
1545
  * checks if at least one dash is included in tagName, and it's not the first char
1524
1546
  * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1547
+ *
1525
1548
  * @param {string} tagName name of the tag of the node to sanitize
1549
+ * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1526
1550
  */
1527
-
1528
-
1529
- const _basicCustomElementTest = function _basicCustomElementTest(tagName) {
1530
- return tagName.indexOf('-') > 0;
1551
+ const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
1552
+ return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
1531
1553
  };
1554
+
1532
1555
  /**
1533
1556
  * _sanitizeAttributes
1534
1557
  *
@@ -1539,123 +1562,111 @@ function createDOMPurify() {
1539
1562
  *
1540
1563
  * @param {Node} currentNode to sanitize
1541
1564
  */
1542
-
1543
-
1544
1565
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1545
- let attr;
1546
- let value;
1547
- let lcName;
1548
- let l;
1549
1566
  /* Execute a hook if present */
1550
-
1551
1567
  _executeHook('beforeSanitizeAttributes', currentNode, null);
1552
-
1553
1568
  const {
1554
1569
  attributes
1555
1570
  } = currentNode;
1556
- /* Check if we have attributes; if not we might have a text node */
1557
1571
 
1572
+ /* Check if we have attributes; if not we might have a text node */
1558
1573
  if (!attributes) {
1559
1574
  return;
1560
1575
  }
1561
-
1562
1576
  const hookEvent = {
1563
1577
  attrName: '',
1564
1578
  attrValue: '',
1565
1579
  keepAttr: true,
1566
1580
  allowedAttributes: ALLOWED_ATTR
1567
1581
  };
1568
- l = attributes.length;
1569
- /* Go backwards over all attributes; safely remove bad ones */
1582
+ let l = attributes.length;
1570
1583
 
1584
+ /* Go backwards over all attributes; safely remove bad ones */
1571
1585
  while (l--) {
1572
- attr = attributes[l];
1586
+ const attr = attributes[l];
1573
1587
  const {
1574
1588
  name,
1575
- namespaceURI
1589
+ namespaceURI,
1590
+ value: attrValue
1576
1591
  } = attr;
1577
- value = name === 'value' ? attr.value : stringTrim(attr.value);
1578
- lcName = transformCaseFunc(name);
1579
- /* Execute a hook if present */
1592
+ const lcName = transformCaseFunc(name);
1593
+ let value = name === 'value' ? attrValue : stringTrim(attrValue);
1580
1594
 
1595
+ /* Execute a hook if present */
1581
1596
  hookEvent.attrName = lcName;
1582
1597
  hookEvent.attrValue = value;
1583
1598
  hookEvent.keepAttr = true;
1584
1599
  hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
1585
-
1586
1600
  _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
1587
-
1588
1601
  value = hookEvent.attrValue;
1589
1602
  /* Did the hooks approve of the attribute? */
1590
-
1591
1603
  if (hookEvent.forceKeepAttr) {
1592
1604
  continue;
1593
1605
  }
1594
- /* Remove attribute */
1595
-
1596
1606
 
1607
+ /* Remove attribute */
1597
1608
  _removeAttribute(name, currentNode);
1598
- /* Did the hooks approve of the attribute? */
1599
-
1600
1609
 
1610
+ /* Did the hooks approve of the attribute? */
1601
1611
  if (!hookEvent.keepAttr) {
1602
1612
  continue;
1603
1613
  }
1604
- /* Work around a security issue in jQuery 3.0 */
1605
-
1606
1614
 
1615
+ /* Work around a security issue in jQuery 3.0 */
1607
1616
  if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1608
1617
  _removeAttribute(name, currentNode);
1609
-
1610
1618
  continue;
1611
1619
  }
1612
- /* Sanitize attribute content to be template-safe */
1613
1620
 
1621
+ /* Work around a security issue with comments inside attributes */
1622
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
1623
+ _removeAttribute(name, currentNode);
1624
+ continue;
1625
+ }
1614
1626
 
1627
+ /* Sanitize attribute content to be template-safe */
1615
1628
  if (SAFE_FOR_TEMPLATES) {
1616
- value = stringReplace(value, MUSTACHE_EXPR, ' ');
1617
- value = stringReplace(value, ERB_EXPR, ' ');
1618
- value = stringReplace(value, TMPLIT_EXPR, ' ');
1629
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1630
+ value = stringReplace(value, expr, ' ');
1631
+ });
1619
1632
  }
1620
- /* Is `value` valid for this attribute? */
1621
-
1622
1633
 
1634
+ /* Is `value` valid for this attribute? */
1623
1635
  const lcTag = transformCaseFunc(currentNode.nodeName);
1624
-
1625
1636
  if (!_isValidAttribute(lcTag, lcName, value)) {
1626
1637
  continue;
1627
1638
  }
1639
+
1628
1640
  /* Full DOM Clobbering protection via namespace isolation,
1629
1641
  * Prefix id and name attributes with `user-content-`
1630
1642
  */
1631
-
1632
-
1633
1643
  if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1634
1644
  // Remove the attribute with this value
1635
- _removeAttribute(name, currentNode); // Prefix the value and later re-create the attribute with the sanitized value
1636
-
1645
+ _removeAttribute(name, currentNode);
1637
1646
 
1647
+ // Prefix the value and later re-create the attribute with the sanitized value
1638
1648
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
1639
1649
  }
1640
- /* Handle attributes that require Trusted Types */
1641
-
1642
1650
 
1651
+ /* Handle attributes that require Trusted Types */
1643
1652
  if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1644
1653
  if (namespaceURI) ; else {
1645
1654
  switch (trustedTypes.getAttributeType(lcTag, lcName)) {
1646
1655
  case 'TrustedHTML':
1647
- value = trustedTypesPolicy.createHTML(value);
1648
- break;
1649
-
1656
+ {
1657
+ value = trustedTypesPolicy.createHTML(value);
1658
+ break;
1659
+ }
1650
1660
  case 'TrustedScriptURL':
1651
- value = trustedTypesPolicy.createScriptURL(value);
1652
- break;
1661
+ {
1662
+ value = trustedTypesPolicy.createScriptURL(value);
1663
+ break;
1664
+ }
1653
1665
  }
1654
1666
  }
1655
1667
  }
1656
- /* Handle invalid data-* attribute set by try-catching it */
1657
-
1658
1668
 
1669
+ /* Handle invalid data-* attribute set by try-catching it */
1659
1670
  try {
1660
1671
  if (namespaceURI) {
1661
1672
  currentNode.setAttributeNS(namespaceURI, name, value);
@@ -1663,123 +1674,129 @@ function createDOMPurify() {
1663
1674
  /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1664
1675
  currentNode.setAttribute(name, value);
1665
1676
  }
1666
-
1667
- arrayPop(DOMPurify.removed);
1677
+ if (_isClobbered(currentNode)) {
1678
+ _forceRemove(currentNode);
1679
+ } else {
1680
+ arrayPop(DOMPurify.removed);
1681
+ }
1668
1682
  } catch (_) {}
1669
1683
  }
1670
- /* Execute a hook if present */
1671
-
1672
1684
 
1685
+ /* Execute a hook if present */
1673
1686
  _executeHook('afterSanitizeAttributes', currentNode, null);
1674
1687
  };
1688
+
1675
1689
  /**
1676
1690
  * _sanitizeShadowDOM
1677
1691
  *
1678
1692
  * @param {DocumentFragment} fragment to iterate over recursively
1679
1693
  */
1680
-
1681
-
1682
1694
  const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1683
- let shadowNode;
1695
+ let shadowNode = null;
1696
+ const shadowIterator = _createNodeIterator(fragment);
1684
1697
 
1685
- const shadowIterator = _createIterator(fragment);
1686
1698
  /* Execute a hook if present */
1687
-
1688
-
1689
1699
  _executeHook('beforeSanitizeShadowDOM', fragment, null);
1690
-
1691
1700
  while (shadowNode = shadowIterator.nextNode()) {
1692
1701
  /* Execute a hook if present */
1693
1702
  _executeHook('uponSanitizeShadowNode', shadowNode, null);
1694
- /* Sanitize tags and elements */
1695
-
1696
1703
 
1704
+ /* Sanitize tags and elements */
1697
1705
  if (_sanitizeElements(shadowNode)) {
1698
1706
  continue;
1699
1707
  }
1700
- /* Deep shadow DOM detected */
1708
+ const parentNode = getParentNode(shadowNode);
1709
+
1710
+ /* Set the nesting depth of an element */
1711
+ if (shadowNode.nodeType === NODE_TYPE.element) {
1712
+ if (parentNode && parentNode.__depth) {
1713
+ /*
1714
+ We want the depth of the node in the original tree, which can
1715
+ change when it's removed from its parent.
1716
+ */
1717
+ shadowNode.__depth = (shadowNode.__removalCount || 0) + parentNode.__depth + 1;
1718
+ } else {
1719
+ shadowNode.__depth = 1;
1720
+ }
1721
+ }
1701
1722
 
1723
+ /*
1724
+ * Remove an element if nested too deeply to avoid mXSS
1725
+ * or if the __depth might have been tampered with
1726
+ */
1727
+ if (shadowNode.__depth >= MAX_NESTING_DEPTH || shadowNode.__depth < 0 || numberIsNaN(shadowNode.__depth)) {
1728
+ _forceRemove(shadowNode);
1729
+ }
1702
1730
 
1731
+ /* Deep shadow DOM detected */
1703
1732
  if (shadowNode.content instanceof DocumentFragment) {
1733
+ shadowNode.content.__depth = shadowNode.__depth;
1704
1734
  _sanitizeShadowDOM(shadowNode.content);
1705
1735
  }
1706
- /* Check attributes, sanitize if necessary */
1707
-
1708
1736
 
1737
+ /* Check attributes, sanitize if necessary */
1709
1738
  _sanitizeAttributes(shadowNode);
1710
1739
  }
1711
- /* Execute a hook if present */
1712
-
1713
1740
 
1741
+ /* Execute a hook if present */
1714
1742
  _executeHook('afterSanitizeShadowDOM', fragment, null);
1715
1743
  };
1744
+
1716
1745
  /**
1717
1746
  * Sanitize
1718
1747
  * Public method providing core sanitation functionality
1719
1748
  *
1720
1749
  * @param {String|Node} dirty string or DOM node
1721
- * @param {Object} configuration object
1750
+ * @param {Object} cfg object
1722
1751
  */
1723
1752
  // eslint-disable-next-line complexity
1724
-
1725
-
1726
1753
  DOMPurify.sanitize = function (dirty) {
1727
1754
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1728
- let body;
1729
- let importedNode;
1730
- let currentNode;
1731
- let returnNode;
1755
+ let body = null;
1756
+ let importedNode = null;
1757
+ let currentNode = null;
1758
+ let returnNode = null;
1732
1759
  /* Make sure we have a string to sanitize.
1733
1760
  DO NOT return early, as this will return the wrong type if
1734
1761
  the user has requested a DOM object rather than a string */
1735
-
1736
1762
  IS_EMPTY_INPUT = !dirty;
1737
-
1738
1763
  if (IS_EMPTY_INPUT) {
1739
1764
  dirty = '<!-->';
1740
1765
  }
1741
- /* Stringify, in case dirty is an object */
1742
-
1743
1766
 
1767
+ /* Stringify, in case dirty is an object */
1744
1768
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
1745
- // eslint-disable-next-line no-negated-condition
1746
- if (typeof dirty.toString !== 'function') {
1747
- throw typeErrorCreate('toString is not a function');
1748
- } else {
1769
+ if (typeof dirty.toString === 'function') {
1749
1770
  dirty = dirty.toString();
1750
-
1751
1771
  if (typeof dirty !== 'string') {
1752
1772
  throw typeErrorCreate('dirty is not a string, aborting');
1753
1773
  }
1774
+ } else {
1775
+ throw typeErrorCreate('toString is not a function');
1754
1776
  }
1755
1777
  }
1756
- /* Return dirty HTML if DOMPurify cannot run */
1757
-
1758
1778
 
1779
+ /* Return dirty HTML if DOMPurify cannot run */
1759
1780
  if (!DOMPurify.isSupported) {
1760
1781
  return dirty;
1761
1782
  }
1762
- /* Assign config vars */
1763
-
1764
1783
 
1784
+ /* Assign config vars */
1765
1785
  if (!SET_CONFIG) {
1766
1786
  _parseConfig(cfg);
1767
1787
  }
1768
- /* Clean up removed elements */
1769
-
1770
1788
 
1789
+ /* Clean up removed elements */
1771
1790
  DOMPurify.removed = [];
1772
- /* Check if dirty is correctly typed for IN_PLACE */
1773
1791
 
1792
+ /* Check if dirty is correctly typed for IN_PLACE */
1774
1793
  if (typeof dirty === 'string') {
1775
1794
  IN_PLACE = false;
1776
1795
  }
1777
-
1778
1796
  if (IN_PLACE) {
1779
1797
  /* Do some early pre-sanitization to avoid unsafe root nodes */
1780
1798
  if (dirty.nodeName) {
1781
1799
  const tagName = transformCaseFunc(dirty.nodeName);
1782
-
1783
1800
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1784
1801
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1785
1802
  }
@@ -1789,8 +1806,7 @@ function createDOMPurify() {
1789
1806
  elements being stripped by the parser */
1790
1807
  body = _initDocument('<!---->');
1791
1808
  importedNode = body.ownerDocument.importNode(dirty, true);
1792
-
1793
- if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1809
+ if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
1794
1810
  /* Node is already a body, use as is */
1795
1811
  body = importedNode;
1796
1812
  } else if (importedNode.nodeName === 'HTML') {
@@ -1801,62 +1817,77 @@ function createDOMPurify() {
1801
1817
  }
1802
1818
  } else {
1803
1819
  /* Exit directly if we have nothing to do */
1804
- if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
1820
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
1821
+ // eslint-disable-next-line unicorn/prefer-includes
1805
1822
  dirty.indexOf('<') === -1) {
1806
1823
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1807
1824
  }
1808
- /* Initialize the document to work on */
1809
-
1810
1825
 
1826
+ /* Initialize the document to work on */
1811
1827
  body = _initDocument(dirty);
1812
- /* Check we have a DOM node from the data */
1813
1828
 
1829
+ /* Check we have a DOM node from the data */
1814
1830
  if (!body) {
1815
1831
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
1816
1832
  }
1817
1833
  }
1818
- /* Remove first element node (ours) if FORCE_BODY is set */
1819
-
1820
1834
 
1835
+ /* Remove first element node (ours) if FORCE_BODY is set */
1821
1836
  if (body && FORCE_BODY) {
1822
1837
  _forceRemove(body.firstChild);
1823
1838
  }
1824
- /* Get node iterator */
1825
1839
 
1840
+ /* Get node iterator */
1841
+ const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
1826
1842
 
1827
- const nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1828
1843
  /* Now start iterating over the created document */
1829
-
1830
-
1831
1844
  while (currentNode = nodeIterator.nextNode()) {
1832
1845
  /* Sanitize tags and elements */
1833
1846
  if (_sanitizeElements(currentNode)) {
1834
1847
  continue;
1835
1848
  }
1836
- /* Shadow DOM detected, sanitize it */
1849
+ const parentNode = getParentNode(currentNode);
1850
+
1851
+ /* Set the nesting depth of an element */
1852
+ if (currentNode.nodeType === NODE_TYPE.element) {
1853
+ if (parentNode && parentNode.__depth) {
1854
+ /*
1855
+ We want the depth of the node in the original tree, which can
1856
+ change when it's removed from its parent.
1857
+ */
1858
+ currentNode.__depth = (currentNode.__removalCount || 0) + parentNode.__depth + 1;
1859
+ } else {
1860
+ currentNode.__depth = 1;
1861
+ }
1862
+ }
1837
1863
 
1864
+ /*
1865
+ * Remove an element if nested too deeply to avoid mXSS
1866
+ * or if the __depth might have been tampered with
1867
+ */
1868
+ if (currentNode.__depth >= MAX_NESTING_DEPTH || currentNode.__depth < 0 || numberIsNaN(currentNode.__depth)) {
1869
+ _forceRemove(currentNode);
1870
+ }
1838
1871
 
1872
+ /* Shadow DOM detected, sanitize it */
1839
1873
  if (currentNode.content instanceof DocumentFragment) {
1874
+ currentNode.content.__depth = currentNode.__depth;
1840
1875
  _sanitizeShadowDOM(currentNode.content);
1841
1876
  }
1842
- /* Check attributes, sanitize if necessary */
1843
-
1844
1877
 
1878
+ /* Check attributes, sanitize if necessary */
1845
1879
  _sanitizeAttributes(currentNode);
1846
1880
  }
1847
- /* If we sanitized `dirty` in-place, return it. */
1848
-
1849
1881
 
1882
+ /* If we sanitized `dirty` in-place, return it. */
1850
1883
  if (IN_PLACE) {
1851
1884
  return dirty;
1852
1885
  }
1853
- /* Return sanitized string or DOM */
1854
-
1855
1886
 
1887
+ /* Return sanitized string or DOM */
1856
1888
  if (RETURN_DOM) {
1857
1889
  if (RETURN_DOM_FRAGMENT) {
1858
1890
  returnNode = createDocumentFragment.call(body.ownerDocument);
1859
-
1860
1891
  while (body.firstChild) {
1861
1892
  // eslint-disable-next-line unicorn/prefer-dom-node-append
1862
1893
  returnNode.appendChild(body.firstChild);
@@ -1864,8 +1895,7 @@ function createDOMPurify() {
1864
1895
  } else {
1865
1896
  returnNode = body;
1866
1897
  }
1867
-
1868
- if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
1898
+ if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
1869
1899
  /*
1870
1900
  AdoptNode() is not used because internal state is not reset
1871
1901
  (e.g. the past names map of a HTMLFormElement), this is safe
@@ -1875,73 +1905,66 @@ function createDOMPurify() {
1875
1905
  */
1876
1906
  returnNode = importNode.call(originalDocument, returnNode, true);
1877
1907
  }
1878
-
1879
1908
  return returnNode;
1880
1909
  }
1881
-
1882
1910
  let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1883
- /* Serialize doctype if allowed */
1884
1911
 
1912
+ /* Serialize doctype if allowed */
1885
1913
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
1886
1914
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
1887
1915
  }
1888
- /* Sanitize final string template-safe */
1889
-
1890
1916
 
1917
+ /* Sanitize final string template-safe */
1891
1918
  if (SAFE_FOR_TEMPLATES) {
1892
- serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR, ' ');
1893
- serializedHTML = stringReplace(serializedHTML, ERB_EXPR, ' ');
1894
- serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR, ' ');
1919
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1920
+ serializedHTML = stringReplace(serializedHTML, expr, ' ');
1921
+ });
1895
1922
  }
1896
-
1897
1923
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1898
1924
  };
1925
+
1899
1926
  /**
1900
1927
  * Public method to set the configuration once
1901
1928
  * setConfig
1902
1929
  *
1903
1930
  * @param {Object} cfg configuration object
1904
1931
  */
1905
-
1906
-
1907
- DOMPurify.setConfig = function (cfg) {
1932
+ DOMPurify.setConfig = function () {
1933
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1908
1934
  _parseConfig(cfg);
1909
-
1910
1935
  SET_CONFIG = true;
1911
1936
  };
1937
+
1912
1938
  /**
1913
1939
  * Public method to remove the configuration
1914
1940
  * clearConfig
1915
1941
  *
1916
1942
  */
1917
-
1918
-
1919
1943
  DOMPurify.clearConfig = function () {
1920
1944
  CONFIG = null;
1921
1945
  SET_CONFIG = false;
1922
1946
  };
1947
+
1923
1948
  /**
1924
1949
  * Public method to check if an attribute value is valid.
1925
1950
  * Uses last set config, if any. Otherwise, uses config defaults.
1926
1951
  * isValidAttribute
1927
1952
  *
1928
- * @param {string} tag Tag name of containing element.
1929
- * @param {string} attr Attribute name.
1930
- * @param {string} value Attribute value.
1953
+ * @param {String} tag Tag name of containing element.
1954
+ * @param {String} attr Attribute name.
1955
+ * @param {String} value Attribute value.
1931
1956
  * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1932
1957
  */
1933
-
1934
-
1935
1958
  DOMPurify.isValidAttribute = function (tag, attr, value) {
1936
1959
  /* Initialize shared config vars if necessary. */
1937
1960
  if (!CONFIG) {
1938
1961
  _parseConfig({});
1939
1962
  }
1940
-
1941
1963
  const lcTag = transformCaseFunc(tag);
1942
1964
  const lcName = transformCaseFunc(attr);
1943
1965
  return _isValidAttribute(lcTag, lcName, value);
1944
1966
  };
1967
+
1945
1968
  /**
1946
1969
  * AddHook
1947
1970
  * Public method to add DOMPurify hooks
@@ -1949,16 +1972,14 @@ function createDOMPurify() {
1949
1972
  * @param {String} entryPoint entry point for the hook to add
1950
1973
  * @param {Function} hookFunction function to execute
1951
1974
  */
1952
-
1953
-
1954
1975
  DOMPurify.addHook = function (entryPoint, hookFunction) {
1955
1976
  if (typeof hookFunction !== 'function') {
1956
1977
  return;
1957
1978
  }
1958
-
1959
1979
  hooks[entryPoint] = hooks[entryPoint] || [];
1960
1980
  arrayPush(hooks[entryPoint], hookFunction);
1961
1981
  };
1982
+
1962
1983
  /**
1963
1984
  * RemoveHook
1964
1985
  * Public method to remove a DOMPurify hook at a given entryPoint
@@ -1967,40 +1988,33 @@ function createDOMPurify() {
1967
1988
  * @param {String} entryPoint entry point for the hook to remove
1968
1989
  * @return {Function} removed(popped) hook
1969
1990
  */
1970
-
1971
-
1972
1991
  DOMPurify.removeHook = function (entryPoint) {
1973
1992
  if (hooks[entryPoint]) {
1974
1993
  return arrayPop(hooks[entryPoint]);
1975
1994
  }
1976
1995
  };
1996
+
1977
1997
  /**
1978
1998
  * RemoveHooks
1979
1999
  * Public method to remove all DOMPurify hooks at a given entryPoint
1980
2000
  *
1981
2001
  * @param {String} entryPoint entry point for the hooks to remove
1982
2002
  */
1983
-
1984
-
1985
2003
  DOMPurify.removeHooks = function (entryPoint) {
1986
2004
  if (hooks[entryPoint]) {
1987
2005
  hooks[entryPoint] = [];
1988
2006
  }
1989
2007
  };
2008
+
1990
2009
  /**
1991
2010
  * RemoveAllHooks
1992
2011
  * Public method to remove all DOMPurify hooks
1993
- *
1994
2012
  */
1995
-
1996
-
1997
2013
  DOMPurify.removeAllHooks = function () {
1998
2014
  hooks = {};
1999
2015
  };
2000
-
2001
2016
  return DOMPurify;
2002
2017
  }
2003
-
2004
2018
  var purify = createDOMPurify();
2005
2019
 
2006
2020
  var getInitialTab = function getInitialTab(stripeAccount) {