dompurify 2.2.9 → 2.3.3

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.3 | (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.3/LICENSE */
2
2
 
3
3
  (function (global, factory) {
4
4
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -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.9';
252
+ DOMPurify.version = '2.3.3';
253
253
 
254
254
  /**
255
255
  * Array of elements that DOMPurify removed during sanitation.
@@ -307,7 +307,8 @@
307
307
  var _document = document,
308
308
  implementation = _document.implementation,
309
309
  createNodeIterator = _document.createNodeIterator,
310
- createDocumentFragment = _document.createDocumentFragment;
310
+ createDocumentFragment = _document.createDocumentFragment,
311
+ getElementsByTagName = _document.getElementsByTagName;
311
312
  var importNode = originalDocument.importNode;
312
313
 
313
314
 
@@ -414,7 +415,8 @@
414
415
  var USE_PROFILES = {};
415
416
 
416
417
  /* Tags to ignore content of when KEEP_CONTENT is true */
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']);
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']);
418
420
 
419
421
  /* Tags that are safe for data: URIs */
420
422
  var DATA_URI_TAGS = null;
@@ -422,7 +424,7 @@
422
424
 
423
425
  /* Attributes safe for values like "javascript:" */
424
426
  var URI_SAFE_ATTRIBUTES = null;
425
- 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']);
426
428
 
427
429
  var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
428
430
  var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
@@ -431,6 +433,12 @@
431
433
  var NAMESPACE = HTML_NAMESPACE;
432
434
  var IS_EMPTY_INPUT = false;
433
435
 
436
+ /* Parsing of strict XHTML documents */
437
+ var PARSER_MEDIA_TYPE = void 0;
438
+ var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
439
+ var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
440
+ var transformCaseFunc = void 0;
441
+
434
442
  /* Keep a reference to config to pass to hooks */
435
443
  var CONFIG = null;
436
444
 
@@ -463,6 +471,7 @@
463
471
  ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
464
472
  URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
465
473
  DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
474
+ FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS) : DEFAULT_FORBID_CONTENTS;
466
475
  FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
467
476
  FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
468
477
  USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
@@ -481,6 +490,16 @@
481
490
  IN_PLACE = cfg.IN_PLACE || false; // Default false
482
491
  IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
483
492
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
493
+
494
+ PARSER_MEDIA_TYPE =
495
+ // eslint-disable-next-line unicorn/prefer-includes
496
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
497
+
498
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
499
+ transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {
500
+ return x;
501
+ } : stringToLowerCase;
502
+
484
503
  if (SAFE_FOR_TEMPLATES) {
485
504
  ALLOW_DATA_ATTR = false;
486
505
  }
@@ -538,6 +557,14 @@
538
557
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
539
558
  }
540
559
 
560
+ if (cfg.FORBID_CONTENTS) {
561
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
562
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
563
+ }
564
+
565
+ addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS);
566
+ }
567
+
541
568
  /* Add #text in case KEEP_CONTENT is set to true */
542
569
  if (KEEP_CONTENT) {
543
570
  ALLOWED_TAGS['#text'] = true;
@@ -741,6 +768,11 @@
741
768
  leadingWhitespace = matches && matches[0];
742
769
  }
743
770
 
771
+ if (PARSER_MEDIA_TYPE === 'application/xhtml+xml') {
772
+ // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
773
+ dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
774
+ }
775
+
744
776
  var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
745
777
  /*
746
778
  * Use the DOMParser API by default, fallback later if needs be
@@ -748,7 +780,7 @@
748
780
  */
749
781
  if (NAMESPACE === HTML_NAMESPACE) {
750
782
  try {
751
- doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
783
+ doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
752
784
  } catch (_) {}
753
785
  }
754
786
 
@@ -769,6 +801,10 @@
769
801
  }
770
802
 
771
803
  /* Work on whole document or just its body */
804
+ if (NAMESPACE === HTML_NAMESPACE) {
805
+ return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
806
+ }
807
+
772
808
  return WHOLE_DOCUMENT ? doc.documentElement : body;
773
809
  };
774
810
 
@@ -857,7 +893,7 @@
857
893
  }
858
894
 
859
895
  /* Now let's check the element's type and name */
860
- var tagName = stringToLowerCase(currentNode.nodeName);
896
+ var tagName = transformCaseFunc(currentNode.nodeName);
861
897
 
862
898
  /* Execute a hook if present */
863
899
  _executeHook('uponSanitizeElement', currentNode, {
@@ -871,6 +907,12 @@
871
907
  return true;
872
908
  }
873
909
 
910
+ /* Mitigate a problem with templates inside select */
911
+ if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
912
+ _forceRemove(currentNode);
913
+ return true;
914
+ }
915
+
874
916
  /* Remove element if anything forbids its presence */
875
917
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
876
918
  /* Keep content except for bad-listed elements */
@@ -939,7 +981,7 @@
939
981
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
940
982
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
941
983
  We don't need to check the value; it's always URI safe. */
942
- 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]) {
984
+ 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]) {
943
985
  return false;
944
986
 
945
987
  /* Check value is safe. First, is attr inert? If so, is safe */
@@ -992,7 +1034,7 @@
992
1034
  namespaceURI = _attr.namespaceURI;
993
1035
 
994
1036
  value = stringTrim(attr.value);
995
- lcName = stringToLowerCase(name);
1037
+ lcName = transformCaseFunc(name);
996
1038
 
997
1039
  /* Execute a hook if present */
998
1040
  hookEvent.attrName = lcName;
@@ -1027,7 +1069,7 @@
1027
1069
  }
1028
1070
 
1029
1071
  /* Is `value` valid for this attribute? */
1030
- var lcTag = currentNode.nodeName.toLowerCase();
1072
+ var lcTag = transformCaseFunc(currentNode.nodeName);
1031
1073
  if (!_isValidAttribute(lcTag, lcName, value)) {
1032
1074
  continue;
1033
1075
  }
@@ -1290,8 +1332,8 @@
1290
1332
  _parseConfig({});
1291
1333
  }
1292
1334
 
1293
- var lcTag = stringToLowerCase(tag);
1294
- var lcName = stringToLowerCase(attr);
1335
+ var lcTag = transformCaseFunc(tag);
1336
+ var lcName = transformCaseFunc(attr);
1295
1337
  return _isValidAttribute(lcTag, lcName, value);
1296
1338
  };
1297
1339