dompurify 2.2.4 → 2.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/purify.js CHANGED
@@ -10,7 +10,9 @@
10
10
 
11
11
  var hasOwnProperty = Object.hasOwnProperty,
12
12
  setPrototypeOf = Object.setPrototypeOf,
13
- isFrozen = Object.isFrozen;
13
+ isFrozen = Object.isFrozen,
14
+ getPrototypeOf = Object.getPrototypeOf,
15
+ getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
14
16
  var freeze = Object.freeze,
15
17
  seal = Object.seal,
16
18
  create = Object.create; // eslint-disable-line import/no-mutable-exports
@@ -121,18 +123,56 @@
121
123
  return newObject;
122
124
  }
123
125
 
126
+ /* IE10 doesn't support __lookupGetter__ so lets'
127
+ * simulate it. It also automatically checks
128
+ * if the prop is function or getter and behaves
129
+ * accordingly. */
130
+ function lookupGetter(object, prop) {
131
+ while (object !== null) {
132
+ var desc = getOwnPropertyDescriptor(object, prop);
133
+ if (desc) {
134
+ if (desc.get) {
135
+ return unapply(desc.get);
136
+ }
137
+
138
+ if (typeof desc.value === 'function') {
139
+ return unapply(desc.value);
140
+ }
141
+ }
142
+
143
+ object = getPrototypeOf(object);
144
+ }
145
+
146
+ function fallbackValue(element) {
147
+ console.warn('fallback value for', element);
148
+ return null;
149
+ }
150
+
151
+ return fallbackValue;
152
+ }
153
+
124
154
  var html = 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']);
125
155
 
126
156
  // SVG
127
- var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'audio', 'canvas', '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', 'video', 'view', 'vkern']);
157
+ var svg = 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']);
128
158
 
129
159
  var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
130
160
 
161
+ // List of SVG elements that are disallowed by default.
162
+ // We still need to know them so that we can do namespace
163
+ // checks properly in case one wants to add them to
164
+ // allow-list.
165
+ var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'feimage', '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']);
166
+
131
167
  var mathMl = 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']);
132
168
 
169
+ // Similarly to SVG, we want to know all MathML elements,
170
+ // even those that we disallow by default.
171
+ var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
172
+
133
173
  var text = freeze(['#text']);
134
174
 
135
- var html$1 = 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', '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']);
175
+ var html$1 = 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', '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']);
136
176
 
137
177
  var svg$1 = 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', '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']);
138
178
 
@@ -209,7 +249,7 @@
209
249
  * Version label, exposed for easier checks
210
250
  * if DOMPurify is up to date or not
211
251
  */
212
- DOMPurify.version = '2.2.4';
252
+ DOMPurify.version = '2.2.8';
213
253
 
214
254
  /**
215
255
  * Array of elements that DOMPurify removed during sanitation.
@@ -231,6 +271,7 @@
231
271
  var DocumentFragment = window.DocumentFragment,
232
272
  HTMLTemplateElement = window.HTMLTemplateElement,
233
273
  Node = window.Node,
274
+ Element = window.Element,
234
275
  NodeFilter = window.NodeFilter,
235
276
  _window$NamedNodeMap = window.NamedNodeMap,
236
277
  NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
@@ -239,13 +280,20 @@
239
280
  DOMParser = window.DOMParser,
240
281
  trustedTypes = window.trustedTypes;
241
282
 
283
+
284
+ var ElementPrototype = Element.prototype;
285
+
286
+ var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
287
+ var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
288
+ var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
289
+ var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
290
+
242
291
  // As per issue #47, the web-components registry is inherited by a
243
292
  // new document created via createHTMLDocument. As per the spec
244
293
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
245
294
  // a new empty registry is used when creating a template contents owner
246
295
  // document, so we use that as our parent document to ensure nothing
247
296
  // is inherited.
248
-
249
297
  if (typeof HTMLTemplateElement === 'function') {
250
298
  var template = document.createElement('template');
251
299
  if (template.content && template.content.ownerDocument) {
@@ -259,7 +307,6 @@
259
307
  var _document = document,
260
308
  implementation = _document.implementation,
261
309
  createNodeIterator = _document.createNodeIterator,
262
- getElementsByTagName = _document.getElementsByTagName,
263
310
  createDocumentFragment = _document.createDocumentFragment;
264
311
  var importNode = originalDocument.importNode;
265
312
 
@@ -274,7 +321,7 @@
274
321
  /**
275
322
  * Expose whether this browser supports running the full DOMPurify.
276
323
  */
277
- DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
324
+ DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
278
325
 
279
326
  var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
280
327
  ERB_EXPR$$1 = ERB_EXPR,
@@ -367,7 +414,7 @@
367
414
  var USE_PROFILES = {};
368
415
 
369
416
  /* Tags to ignore content of when KEEP_CONTENT is true */
370
- var FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
417
+ var 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']);
371
418
 
372
419
  /* Tags that are safe for data: URIs */
373
420
  var DATA_URI_TAGS = null;
@@ -377,6 +424,12 @@
377
424
  var URI_SAFE_ATTRIBUTES = null;
378
425
  var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']);
379
426
 
427
+ var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
428
+ var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
429
+ var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
430
+ /* Document namespace */
431
+ var NAMESPACE = HTML_NAMESPACE;
432
+
380
433
  /* Keep a reference to config to pass to hooks */
381
434
  var CONFIG = null;
382
435
 
@@ -426,6 +479,7 @@
426
479
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
427
480
  IN_PLACE = cfg.IN_PLACE || false; // Default false
428
481
  IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
482
+ NAMESPACE = cfg.NAMESPACE || NAMESPACE;
429
483
  if (SAFE_FOR_TEMPLATES) {
430
484
  ALLOW_DATA_ATTR = false;
431
485
  }
@@ -508,6 +562,111 @@
508
562
  CONFIG = cfg;
509
563
  };
510
564
 
565
+ var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
566
+
567
+ var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
568
+
569
+ /* Keep track of all possible SVG and MathML tags
570
+ * so that we can perform the namespace checks
571
+ * correctly. */
572
+ var ALL_SVG_TAGS = addToSet({}, svg);
573
+ addToSet(ALL_SVG_TAGS, svgFilters);
574
+ addToSet(ALL_SVG_TAGS, svgDisallowed);
575
+
576
+ var ALL_MATHML_TAGS = addToSet({}, mathMl);
577
+ addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
578
+
579
+ /**
580
+ *
581
+ *
582
+ * @param {Element} element a DOM element whose namespace is being checked
583
+ * @returns {boolean} Return false if the element has a
584
+ * namespace that a spec-compliant parser would never
585
+ * return. Return true otherwise.
586
+ */
587
+ var _checkValidNamespace = function _checkValidNamespace(element) {
588
+ var parent = getParentNode(element);
589
+
590
+ // In JSDOM, if we're inside shadow DOM, then parentNode
591
+ // can be null. We just simulate parent in this case.
592
+ if (!parent || !parent.tagName) {
593
+ parent = {
594
+ namespaceURI: HTML_NAMESPACE,
595
+ tagName: 'template'
596
+ };
597
+ }
598
+
599
+ var tagName = stringToLowerCase(element.tagName);
600
+ var parentTagName = stringToLowerCase(parent.tagName);
601
+
602
+ if (element.namespaceURI === SVG_NAMESPACE) {
603
+ // The only way to switch from HTML namespace to SVG
604
+ // is via <svg>. If it happens via any other tag, then
605
+ // it should be killed.
606
+ if (parent.namespaceURI === HTML_NAMESPACE) {
607
+ return tagName === 'svg';
608
+ }
609
+
610
+ // The only way to switch from MathML to SVG is via
611
+ // svg if parent is either <annotation-xml> or MathML
612
+ // text integration points.
613
+ if (parent.namespaceURI === MATHML_NAMESPACE) {
614
+ return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
615
+ }
616
+
617
+ // We only allow elements that are defined in SVG
618
+ // spec. All others are disallowed in SVG namespace.
619
+ return Boolean(ALL_SVG_TAGS[tagName]);
620
+ }
621
+
622
+ if (element.namespaceURI === MATHML_NAMESPACE) {
623
+ // The only way to switch from HTML namespace to MathML
624
+ // is via <math>. If it happens via any other tag, then
625
+ // it should be killed.
626
+ if (parent.namespaceURI === HTML_NAMESPACE) {
627
+ return tagName === 'math';
628
+ }
629
+
630
+ // The only way to switch from SVG to MathML is via
631
+ // <math> and HTML integration points
632
+ if (parent.namespaceURI === SVG_NAMESPACE) {
633
+ return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
634
+ }
635
+
636
+ // We only allow elements that are defined in MathML
637
+ // spec. All others are disallowed in MathML namespace.
638
+ return Boolean(ALL_MATHML_TAGS[tagName]);
639
+ }
640
+
641
+ if (element.namespaceURI === HTML_NAMESPACE) {
642
+ // The only way to switch from SVG to HTML is via
643
+ // HTML integration points, and from MathML to HTML
644
+ // is via MathML text integration points
645
+ if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
646
+ return false;
647
+ }
648
+
649
+ if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
650
+ return false;
651
+ }
652
+
653
+ // Certain elements are allowed in both SVG and HTML
654
+ // namespace. We need to specify them explicitly
655
+ // so that they don't get erronously deleted from
656
+ // HTML namespace.
657
+ var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
658
+
659
+ // We disallow tags that are specific for MathML
660
+ // or SVG and should never appear in HTML namespace
661
+ return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]);
662
+ }
663
+
664
+ // The code should never reach this place (this means
665
+ // that the element somehow got namespace that is not
666
+ // HTML, SVG or MathML). Return false just in case.
667
+ return false;
668
+ };
669
+
511
670
  /**
512
671
  * _forceRemove
513
672
  *
@@ -546,6 +705,19 @@
546
705
  }
547
706
 
548
707
  node.removeAttribute(name);
708
+
709
+ // We void attribute values for unremovable "is"" attributes
710
+ if (name === 'is' && !ALLOWED_ATTR[name]) {
711
+ if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
712
+ try {
713
+ _forceRemove(node);
714
+ } catch (_) {}
715
+ } else {
716
+ try {
717
+ node.setAttribute(name, '');
718
+ } catch (_) {}
719
+ }
720
+ }
549
721
  };
550
722
 
551
723
  /**
@@ -568,27 +740,30 @@
568
740
  }
569
741
 
570
742
  var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
571
- /* Use the DOMParser API by default, fallback later if needs be */
572
- try {
573
- doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
574
- } catch (_) {}
743
+ /*
744
+ * Use the DOMParser API by default, fallback later if needs be
745
+ * DOMParser not work for svg when has multiple root element.
746
+ */
747
+ if (NAMESPACE === HTML_NAMESPACE) {
748
+ try {
749
+ doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
750
+ } catch (_) {}
751
+ }
575
752
 
576
753
  /* Use createHTMLDocument in case DOMParser is not available */
577
754
  if (!doc || !doc.documentElement) {
578
- doc = implementation.createHTMLDocument('');
579
- var _doc = doc,
580
- body = _doc.body;
581
-
582
- body.parentNode.removeChild(body.parentNode.firstElementChild);
583
- body.outerHTML = dirtyPayload;
755
+ doc = implementation.createDocument(NAMESPACE, 'template', null);
756
+ doc.documentElement.innerHTML = dirtyPayload;
584
757
  }
585
758
 
759
+ var body = doc.body || doc.documentElement;
760
+
586
761
  if (dirty && leadingWhitespace) {
587
- doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
762
+ body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
588
763
  }
589
764
 
590
765
  /* Work on whole document or just its body */
591
- return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
766
+ return WHOLE_DOCUMENT ? doc.documentElement : body;
592
767
  };
593
768
 
594
769
  /**
@@ -614,7 +789,7 @@
614
789
  return false;
615
790
  }
616
791
 
617
- if (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') {
792
+ if (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') {
618
793
  return true;
619
794
  }
620
795
 
@@ -695,29 +870,30 @@
695
870
  /* Remove element if anything forbids its presence */
696
871
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
697
872
  /* Keep content except for bad-listed elements */
698
- if (KEEP_CONTENT && !FORBID_CONTENTS[tagName] && typeof currentNode.insertAdjacentHTML === 'function') {
699
- try {
700
- var htmlToInsert = currentNode.innerHTML;
701
- currentNode.insertAdjacentHTML('AfterEnd', trustedTypesPolicy ? trustedTypesPolicy.createHTML(htmlToInsert) : htmlToInsert);
702
- } catch (_) {}
703
- }
873
+ if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
874
+ var parentNode = getParentNode(currentNode) || currentNode.parentNode;
875
+ var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
704
876
 
705
- _forceRemove(currentNode);
706
- return true;
707
- }
877
+ if (childNodes && parentNode) {
878
+ var childCount = childNodes.length;
879
+
880
+ for (var i = childCount - 1; i >= 0; --i) {
881
+ parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
882
+ }
883
+ }
884
+ }
708
885
 
709
- if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
710
886
  _forceRemove(currentNode);
711
887
  return true;
712
888
  }
713
889
 
714
- if (tagName === 'math' && _isNode(currentNode.firstElementChild) && currentNode.querySelectorAll(':not(' + mathMl.join('):not(') + ')').length > 0) {
890
+ /* Check whether element has a valid namespace */
891
+ if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
715
892
  _forceRemove(currentNode);
716
893
  return true;
717
894
  }
718
895
 
719
- /* Take care of an mXSS using HTML inside SVG affecting old Chrome */
720
- if (tagName === 'svg' && currentNode.querySelectorAll('p, br, table, form, noscript').length > 0) {
896
+ if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
721
897
  _forceRemove(currentNode);
722
898
  return true;
723
899
  }