dompurify 2.2.7 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/purify.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.2.2/LICENSE */
1
+ /*! @license DOMPurify 2.3.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.1/LICENSE */
2
2
 
3
3
  (function (global, factory) {
4
4
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -172,7 +172,7 @@
172
172
 
173
173
  var text = freeze(['#text']);
174
174
 
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']);
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']);
176
176
 
177
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']);
178
178
 
@@ -249,7 +249,7 @@
249
249
  * Version label, exposed for easier checks
250
250
  * if DOMPurify is up to date or not
251
251
  */
252
- DOMPurify.version = '2.2.7';
252
+ DOMPurify.version = '2.3.1';
253
253
 
254
254
  /**
255
255
  * Array of elements that DOMPurify removed during sanitation.
@@ -307,8 +307,8 @@
307
307
  var _document = document,
308
308
  implementation = _document.implementation,
309
309
  createNodeIterator = _document.createNodeIterator,
310
- getElementsByTagName = _document.getElementsByTagName,
311
- createDocumentFragment = _document.createDocumentFragment;
310
+ createDocumentFragment = _document.createDocumentFragment,
311
+ getElementsByTagName = _document.getElementsByTagName;
312
312
  var importNode = originalDocument.importNode;
313
313
 
314
314
 
@@ -415,7 +415,8 @@
415
415
  var USE_PROFILES = {};
416
416
 
417
417
  /* Tags to ignore content of when KEEP_CONTENT is true */
418
- 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']);
418
+ var FORBID_CONTENTS = null;
419
+ var DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
419
420
 
420
421
  /* Tags that are safe for data: URIs */
421
422
  var DATA_URI_TAGS = null;
@@ -423,7 +424,14 @@
423
424
 
424
425
  /* Attributes safe for values like "javascript:" */
425
426
  var URI_SAFE_ATTRIBUTES = null;
426
- var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']);
427
+ var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
428
+
429
+ var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
430
+ var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
431
+ var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
432
+ /* Document namespace */
433
+ var NAMESPACE = HTML_NAMESPACE;
434
+ var IS_EMPTY_INPUT = false;
427
435
 
428
436
  /* Keep a reference to config to pass to hooks */
429
437
  var CONFIG = null;
@@ -457,6 +465,7 @@
457
465
  ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
458
466
  URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
459
467
  DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
468
+ FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS) : DEFAULT_FORBID_CONTENTS;
460
469
  FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
461
470
  FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
462
471
  USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
@@ -474,6 +483,7 @@
474
483
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
475
484
  IN_PLACE = cfg.IN_PLACE || false; // Default false
476
485
  IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
486
+ NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
477
487
  if (SAFE_FOR_TEMPLATES) {
478
488
  ALLOW_DATA_ATTR = false;
479
489
  }
@@ -531,6 +541,14 @@
531
541
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
532
542
  }
533
543
 
544
+ if (cfg.FORBID_CONTENTS) {
545
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
546
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
547
+ }
548
+
549
+ addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS);
550
+ }
551
+
534
552
  /* Add #text in case KEEP_CONTENT is set to true */
535
553
  if (KEEP_CONTENT) {
536
554
  ALLOWED_TAGS['#text'] = true;
@@ -570,10 +588,6 @@
570
588
  var ALL_MATHML_TAGS = addToSet({}, mathMl);
571
589
  addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
572
590
 
573
- var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
574
- var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
575
- var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
576
-
577
591
  /**
578
592
  *
579
593
  *
@@ -673,6 +687,7 @@
673
687
  var _forceRemove = function _forceRemove(node) {
674
688
  arrayPush(DOMPurify.removed, { element: node });
675
689
  try {
690
+ // eslint-disable-next-line unicorn/prefer-dom-node-remove
676
691
  node.parentNode.removeChild(node);
677
692
  } catch (_) {
678
693
  try {
@@ -738,27 +753,38 @@
738
753
  }
739
754
 
740
755
  var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
741
- /* Use the DOMParser API by default, fallback later if needs be */
742
- try {
743
- doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
744
- } catch (_) {}
756
+ /*
757
+ * Use the DOMParser API by default, fallback later if needs be
758
+ * DOMParser not work for svg when has multiple root element.
759
+ */
760
+ if (NAMESPACE === HTML_NAMESPACE) {
761
+ try {
762
+ doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
763
+ } catch (_) {}
764
+ }
745
765
 
746
766
  /* Use createHTMLDocument in case DOMParser is not available */
747
767
  if (!doc || !doc.documentElement) {
748
- doc = implementation.createHTMLDocument('');
749
- var _doc = doc,
750
- body = _doc.body;
751
-
752
- body.parentNode.removeChild(body.parentNode.firstElementChild);
753
- body.outerHTML = dirtyPayload;
768
+ doc = implementation.createDocument(NAMESPACE, 'template', null);
769
+ try {
770
+ doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload;
771
+ } catch (_) {
772
+ // Syntax error if dirtyPayload is invalid xml
773
+ }
754
774
  }
755
775
 
776
+ var body = doc.body || doc.documentElement;
777
+
756
778
  if (dirty && leadingWhitespace) {
757
- doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
779
+ body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
758
780
  }
759
781
 
760
782
  /* Work on whole document or just its body */
761
- return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
783
+ if (NAMESPACE === HTML_NAMESPACE) {
784
+ return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
785
+ }
786
+
787
+ return WHOLE_DOCUMENT ? doc.documentElement : body;
762
788
  };
763
789
 
764
790
  /**
@@ -768,9 +794,7 @@
768
794
  * @return {Iterator} iterator instance
769
795
  */
770
796
  var _createIterator = function _createIterator(root) {
771
- return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () {
772
- return NodeFilter.FILTER_ACCEPT;
773
- }, false);
797
+ return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
774
798
  };
775
799
 
776
800
  /**
@@ -862,12 +886,18 @@
862
886
  return true;
863
887
  }
864
888
 
889
+ /* Mitigate a problem with templates inside select */
890
+ if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
891
+ _forceRemove(currentNode);
892
+ return true;
893
+ }
894
+
865
895
  /* Remove element if anything forbids its presence */
866
896
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
867
897
  /* Keep content except for bad-listed elements */
868
898
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
869
- var parentNode = getParentNode(currentNode);
870
- var childNodes = getChildNodes(currentNode);
899
+ var parentNode = getParentNode(currentNode) || currentNode.parentNode;
900
+ var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
871
901
 
872
902
  if (childNodes && parentNode) {
873
903
  var childCount = childNodes.length;
@@ -930,7 +960,7 @@
930
960
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
931
961
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
932
962
  We don't need to check the value; it's always URI safe. */
933
- if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
963
+ if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
934
964
  return false;
935
965
 
936
966
  /* Check value is safe. First, is attr inert? If so, is safe */
@@ -1091,7 +1121,8 @@
1091
1121
  /* Make sure we have a string to sanitize.
1092
1122
  DO NOT return early, as this will return the wrong type if
1093
1123
  the user has requested a DOM object rather than a string */
1094
- if (!dirty) {
1124
+ IS_EMPTY_INPUT = !dirty;
1125
+ if (IS_EMPTY_INPUT) {
1095
1126
  dirty = '<!-->';
1096
1127
  }
1097
1128
 
@@ -1147,7 +1178,7 @@
1147
1178
  } else if (importedNode.nodeName === 'HTML') {
1148
1179
  body = importedNode;
1149
1180
  } else {
1150
- // eslint-disable-next-line unicorn/prefer-node-append
1181
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
1151
1182
  body.appendChild(importedNode);
1152
1183
  }
1153
1184
  } else {
@@ -1211,7 +1242,7 @@
1211
1242
  returnNode = createDocumentFragment.call(body.ownerDocument);
1212
1243
 
1213
1244
  while (body.firstChild) {
1214
- // eslint-disable-next-line unicorn/prefer-node-append
1245
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
1215
1246
  returnNode.appendChild(body.firstChild);
1216
1247
  }
1217
1248
  } else {