dompurify 2.2.8 → 2.3.2

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.es.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.2 | (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.2/LICENSE */
2
2
 
3
3
  function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
4
4
 
@@ -243,7 +243,7 @@ function createDOMPurify() {
243
243
  * Version label, exposed for easier checks
244
244
  * if DOMPurify is up to date or not
245
245
  */
246
- DOMPurify.version = '2.2.8';
246
+ DOMPurify.version = '2.3.2';
247
247
 
248
248
  /**
249
249
  * Array of elements that DOMPurify removed during sanitation.
@@ -301,7 +301,8 @@ function createDOMPurify() {
301
301
  var _document = document,
302
302
  implementation = _document.implementation,
303
303
  createNodeIterator = _document.createNodeIterator,
304
- createDocumentFragment = _document.createDocumentFragment;
304
+ createDocumentFragment = _document.createDocumentFragment,
305
+ getElementsByTagName = _document.getElementsByTagName;
305
306
  var importNode = originalDocument.importNode;
306
307
 
307
308
 
@@ -408,7 +409,8 @@ function createDOMPurify() {
408
409
  var USE_PROFILES = {};
409
410
 
410
411
  /* Tags to ignore content of when KEEP_CONTENT is true */
411
- 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']);
412
+ var FORBID_CONTENTS = null;
413
+ 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']);
412
414
 
413
415
  /* Tags that are safe for data: URIs */
414
416
  var DATA_URI_TAGS = null;
@@ -416,13 +418,20 @@ function createDOMPurify() {
416
418
 
417
419
  /* Attributes safe for values like "javascript:" */
418
420
  var URI_SAFE_ATTRIBUTES = null;
419
- var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']);
421
+ var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
420
422
 
421
423
  var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
422
424
  var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
423
425
  var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
424
426
  /* Document namespace */
425
427
  var NAMESPACE = HTML_NAMESPACE;
428
+ var IS_EMPTY_INPUT = false;
429
+
430
+ /* Parsing of strict XHTML documents */
431
+ var PARSER_MEDIA_TYPE = void 0;
432
+ var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
433
+ var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
434
+ var transformCaseFunc = void 0;
426
435
 
427
436
  /* Keep a reference to config to pass to hooks */
428
437
  var CONFIG = null;
@@ -456,6 +465,7 @@ function createDOMPurify() {
456
465
  ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
457
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;
458
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;
459
469
  FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
460
470
  FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
461
471
  USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
@@ -473,7 +483,13 @@ function createDOMPurify() {
473
483
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
474
484
  IN_PLACE = cfg.IN_PLACE || false; // Default false
475
485
  IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
476
- NAMESPACE = cfg.NAMESPACE || NAMESPACE;
486
+ NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
487
+ PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE in SUPPORTED_PARSER_MEDIA_TYPES ? cfg.PARSER_MEDIA_TYPE : DEFAULT_PARSER_MEDIA_TYPE;
488
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
489
+ transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {
490
+ return x;
491
+ } : stringToLowerCase;
492
+
477
493
  if (SAFE_FOR_TEMPLATES) {
478
494
  ALLOW_DATA_ATTR = false;
479
495
  }
@@ -531,6 +547,14 @@ function createDOMPurify() {
531
547
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
532
548
  }
533
549
 
550
+ if (cfg.FORBID_CONTENTS) {
551
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
552
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
553
+ }
554
+
555
+ addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS);
556
+ }
557
+
534
558
  /* Add #text in case KEEP_CONTENT is set to true */
535
559
  if (KEEP_CONTENT) {
536
560
  ALLOWED_TAGS['#text'] = true;
@@ -669,6 +693,7 @@ function createDOMPurify() {
669
693
  var _forceRemove = function _forceRemove(node) {
670
694
  arrayPush(DOMPurify.removed, { element: node });
671
695
  try {
696
+ // eslint-disable-next-line unicorn/prefer-dom-node-remove
672
697
  node.parentNode.removeChild(node);
673
698
  } catch (_) {
674
699
  try {
@@ -733,6 +758,11 @@ function createDOMPurify() {
733
758
  leadingWhitespace = matches && matches[0];
734
759
  }
735
760
 
761
+ if (PARSER_MEDIA_TYPE === 'application/xhtml+xml') {
762
+ // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
763
+ dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
764
+ }
765
+
736
766
  var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
737
767
  /*
738
768
  * Use the DOMParser API by default, fallback later if needs be
@@ -740,14 +770,18 @@ function createDOMPurify() {
740
770
  */
741
771
  if (NAMESPACE === HTML_NAMESPACE) {
742
772
  try {
743
- doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
773
+ doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
744
774
  } catch (_) {}
745
775
  }
746
776
 
747
777
  /* Use createHTMLDocument in case DOMParser is not available */
748
778
  if (!doc || !doc.documentElement) {
749
779
  doc = implementation.createDocument(NAMESPACE, 'template', null);
750
- doc.documentElement.innerHTML = dirtyPayload;
780
+ try {
781
+ doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload;
782
+ } catch (_) {
783
+ // Syntax error if dirtyPayload is invalid xml
784
+ }
751
785
  }
752
786
 
753
787
  var body = doc.body || doc.documentElement;
@@ -757,6 +791,10 @@ function createDOMPurify() {
757
791
  }
758
792
 
759
793
  /* Work on whole document or just its body */
794
+ if (NAMESPACE === HTML_NAMESPACE) {
795
+ return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
796
+ }
797
+
760
798
  return WHOLE_DOCUMENT ? doc.documentElement : body;
761
799
  };
762
800
 
@@ -767,9 +805,7 @@ function createDOMPurify() {
767
805
  * @return {Iterator} iterator instance
768
806
  */
769
807
  var _createIterator = function _createIterator(root) {
770
- return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () {
771
- return NodeFilter.FILTER_ACCEPT;
772
- }, false);
808
+ return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
773
809
  };
774
810
 
775
811
  /**
@@ -847,7 +883,7 @@ function createDOMPurify() {
847
883
  }
848
884
 
849
885
  /* Now let's check the element's type and name */
850
- var tagName = stringToLowerCase(currentNode.nodeName);
886
+ var tagName = transformCaseFunc(currentNode.nodeName);
851
887
 
852
888
  /* Execute a hook if present */
853
889
  _executeHook('uponSanitizeElement', currentNode, {
@@ -861,6 +897,12 @@ function createDOMPurify() {
861
897
  return true;
862
898
  }
863
899
 
900
+ /* Mitigate a problem with templates inside select */
901
+ if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
902
+ _forceRemove(currentNode);
903
+ return true;
904
+ }
905
+
864
906
  /* Remove element if anything forbids its presence */
865
907
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
866
908
  /* Keep content except for bad-listed elements */
@@ -929,7 +971,7 @@ function createDOMPurify() {
929
971
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
930
972
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
931
973
  We don't need to check the value; it's always URI safe. */
932
- 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]) {
974
+ 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]) {
933
975
  return false;
934
976
 
935
977
  /* Check value is safe. First, is attr inert? If so, is safe */
@@ -982,7 +1024,7 @@ function createDOMPurify() {
982
1024
  namespaceURI = _attr.namespaceURI;
983
1025
 
984
1026
  value = stringTrim(attr.value);
985
- lcName = stringToLowerCase(name);
1027
+ lcName = transformCaseFunc(name);
986
1028
 
987
1029
  /* Execute a hook if present */
988
1030
  hookEvent.attrName = lcName;
@@ -1017,7 +1059,7 @@ function createDOMPurify() {
1017
1059
  }
1018
1060
 
1019
1061
  /* Is `value` valid for this attribute? */
1020
- var lcTag = currentNode.nodeName.toLowerCase();
1062
+ var lcTag = transformCaseFunc(currentNode.nodeName);
1021
1063
  if (!_isValidAttribute(lcTag, lcName, value)) {
1022
1064
  continue;
1023
1065
  }
@@ -1090,7 +1132,8 @@ function createDOMPurify() {
1090
1132
  /* Make sure we have a string to sanitize.
1091
1133
  DO NOT return early, as this will return the wrong type if
1092
1134
  the user has requested a DOM object rather than a string */
1093
- if (!dirty) {
1135
+ IS_EMPTY_INPUT = !dirty;
1136
+ if (IS_EMPTY_INPUT) {
1094
1137
  dirty = '<!-->';
1095
1138
  }
1096
1139
 
@@ -1146,7 +1189,7 @@ function createDOMPurify() {
1146
1189
  } else if (importedNode.nodeName === 'HTML') {
1147
1190
  body = importedNode;
1148
1191
  } else {
1149
- // eslint-disable-next-line unicorn/prefer-node-append
1192
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
1150
1193
  body.appendChild(importedNode);
1151
1194
  }
1152
1195
  } else {
@@ -1210,7 +1253,7 @@ function createDOMPurify() {
1210
1253
  returnNode = createDocumentFragment.call(body.ownerDocument);
1211
1254
 
1212
1255
  while (body.firstChild) {
1213
- // eslint-disable-next-line unicorn/prefer-node-append
1256
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
1214
1257
  returnNode.appendChild(body.firstChild);
1215
1258
  }
1216
1259
  } else {
@@ -1279,8 +1322,8 @@ function createDOMPurify() {
1279
1322
  _parseConfig({});
1280
1323
  }
1281
1324
 
1282
- var lcTag = stringToLowerCase(tag);
1283
- var lcName = stringToLowerCase(attr);
1325
+ var lcTag = transformCaseFunc(tag);
1326
+ var lcName = transformCaseFunc(attr);
1284
1327
  return _isValidAttribute(lcTag, lcName, value);
1285
1328
  };
1286
1329