dompurify 2.3.0 → 2.3.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.
package/dist/purify.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @license DOMPurify 2.3.0 | (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.0/LICENSE */
1
+ /*! @license DOMPurify 2.3.4 | (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.4/LICENSE */
2
2
 
3
3
  (function (global, factory) {
4
4
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -156,13 +156,13 @@
156
156
  // SVG
157
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']);
158
158
 
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']);
159
+ var 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']);
160
160
 
161
161
  // List of SVG elements that are disallowed by default.
162
162
  // We still need to know them so that we can do namespace
163
163
  // checks properly in case one wants to add them to
164
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']);
165
+ var 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']);
166
166
 
167
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']);
168
168
 
@@ -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', 'slot']);
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', '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']);
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.3.0';
252
+ DOMPurify.version = '2.3.4';
253
253
 
254
254
  /**
255
255
  * Array of elements that DOMPurify removed during sanitation.
@@ -275,8 +275,7 @@
275
275
  NodeFilter = window.NodeFilter,
276
276
  _window$NamedNodeMap = window.NamedNodeMap,
277
277
  NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
278
- Text = window.Text,
279
- Comment = window.Comment,
278
+ HTMLFormElement = window.HTMLFormElement,
280
279
  DOMParser = window.DOMParser,
281
280
  trustedTypes = window.trustedTypes;
282
281
 
@@ -346,6 +345,33 @@
346
345
  var ALLOWED_ATTR = null;
347
346
  var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml)));
348
347
 
348
+ /*
349
+ * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
350
+ * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
351
+ * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
352
+ * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
353
+ */
354
+ var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
355
+ tagNameCheck: {
356
+ writable: true,
357
+ configurable: false,
358
+ enumerable: true,
359
+ value: null
360
+ },
361
+ attributeNameCheck: {
362
+ writable: true,
363
+ configurable: false,
364
+ enumerable: true,
365
+ value: null
366
+ },
367
+ allowCustomizedBuiltInElements: {
368
+ writable: true,
369
+ configurable: false,
370
+ enumerable: true,
371
+ value: false
372
+ }
373
+ }));
374
+
349
375
  /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
350
376
  var FORBID_TAGS = null;
351
377
 
@@ -386,17 +412,6 @@
386
412
  * string (or a TrustedHTML object if Trusted Types are supported) */
387
413
  var RETURN_DOM_FRAGMENT = false;
388
414
 
389
- /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM
390
- * `Node` is imported into the current `Document`. If this flag is not enabled the
391
- * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by
392
- * DOMPurify.
393
- *
394
- * This defaults to `true` starting DOMPurify 2.2.0. Note that setting it to `false`
395
- * might cause XSS from attacks hidden in closed shadowroots in case the browser
396
- * supports Declarative Shadow: DOM https://web.dev/declarative-shadow-dom/
397
- */
398
- var RETURN_DOM_IMPORT = true;
399
-
400
415
  /* Try to return a Trusted Type object instead of a string, return a string in
401
416
  * case Trusted Types are not supported */
402
417
  var RETURN_TRUSTED_TYPE = false;
@@ -415,7 +430,8 @@
415
430
  var USE_PROFILES = {};
416
431
 
417
432
  /* 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']);
433
+ var FORBID_CONTENTS = null;
434
+ 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
435
 
420
436
  /* Tags that are safe for data: URIs */
421
437
  var DATA_URI_TAGS = null;
@@ -423,7 +439,7 @@
423
439
 
424
440
  /* Attributes safe for values like "javascript:" */
425
441
  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']);
442
+ var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
427
443
 
428
444
  var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
429
445
  var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
@@ -432,6 +448,12 @@
432
448
  var NAMESPACE = HTML_NAMESPACE;
433
449
  var IS_EMPTY_INPUT = false;
434
450
 
451
+ /* Parsing of strict XHTML documents */
452
+ var PARSER_MEDIA_TYPE = void 0;
453
+ var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
454
+ var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
455
+ var transformCaseFunc = void 0;
456
+
435
457
  /* Keep a reference to config to pass to hooks */
436
458
  var CONFIG = null;
437
459
 
@@ -440,6 +462,10 @@
440
462
 
441
463
  var formElement = document.createElement('form');
442
464
 
465
+ var isRegexOrFunction = function isRegexOrFunction(testValue) {
466
+ return testValue instanceof RegExp || testValue instanceof Function;
467
+ };
468
+
443
469
  /**
444
470
  * _parseConfig
445
471
  *
@@ -464,6 +490,7 @@
464
490
  ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
465
491
  URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
466
492
  DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
493
+ FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS) : DEFAULT_FORBID_CONTENTS;
467
494
  FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
468
495
  FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
469
496
  USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
@@ -474,7 +501,6 @@
474
501
  WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
475
502
  RETURN_DOM = cfg.RETURN_DOM || false; // Default false
476
503
  RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
477
- RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true
478
504
  RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
479
505
  FORCE_BODY = cfg.FORCE_BODY || false; // Default false
480
506
  SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
@@ -482,6 +508,27 @@
482
508
  IN_PLACE = cfg.IN_PLACE || false; // Default false
483
509
  IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
484
510
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
511
+ if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
512
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
513
+ }
514
+
515
+ if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
516
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
517
+ }
518
+
519
+ if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
520
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
521
+ }
522
+
523
+ PARSER_MEDIA_TYPE =
524
+ // eslint-disable-next-line unicorn/prefer-includes
525
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
526
+
527
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
528
+ transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {
529
+ return x;
530
+ } : stringToLowerCase;
531
+
485
532
  if (SAFE_FOR_TEMPLATES) {
486
533
  ALLOW_DATA_ATTR = false;
487
534
  }
@@ -539,6 +586,14 @@
539
586
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
540
587
  }
541
588
 
589
+ if (cfg.FORBID_CONTENTS) {
590
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
591
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
592
+ }
593
+
594
+ addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS);
595
+ }
596
+
542
597
  /* Add #text in case KEEP_CONTENT is set to true */
543
598
  if (KEEP_CONTENT) {
544
599
  ALLOWED_TAGS['#text'] = true;
@@ -742,6 +797,11 @@
742
797
  leadingWhitespace = matches && matches[0];
743
798
  }
744
799
 
800
+ if (PARSER_MEDIA_TYPE === 'application/xhtml+xml') {
801
+ // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
802
+ dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
803
+ }
804
+
745
805
  var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
746
806
  /*
747
807
  * Use the DOMParser API by default, fallback later if needs be
@@ -749,7 +809,7 @@
749
809
  */
750
810
  if (NAMESPACE === HTML_NAMESPACE) {
751
811
  try {
752
- doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
812
+ doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
753
813
  } catch (_) {}
754
814
  }
755
815
 
@@ -794,15 +854,7 @@
794
854
  * @return {Boolean} true if clobbered, false if safe
795
855
  */
796
856
  var _isClobbered = function _isClobbered(elm) {
797
- if (elm instanceof Text || elm instanceof Comment) {
798
- return false;
799
- }
800
-
801
- 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') {
802
- return true;
803
- }
804
-
805
- return false;
857
+ 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');
806
858
  };
807
859
 
808
860
  /**
@@ -862,7 +914,7 @@
862
914
  }
863
915
 
864
916
  /* Now let's check the element's type and name */
865
- var tagName = stringToLowerCase(currentNode.nodeName);
917
+ var tagName = transformCaseFunc(currentNode.nodeName);
866
918
 
867
919
  /* Execute a hook if present */
868
920
  _executeHook('uponSanitizeElement', currentNode, {
@@ -876,6 +928,12 @@
876
928
  return true;
877
929
  }
878
930
 
931
+ /* Mitigate a problem with templates inside select */
932
+ if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
933
+ _forceRemove(currentNode);
934
+ return true;
935
+ }
936
+
879
937
  /* Remove element if anything forbids its presence */
880
938
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
881
939
  /* Keep content except for bad-listed elements */
@@ -892,6 +950,11 @@
892
950
  }
893
951
  }
894
952
 
953
+ if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
954
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
955
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
956
+ }
957
+
895
958
  _forceRemove(currentNode);
896
959
  return true;
897
960
  }
@@ -945,8 +1008,16 @@
945
1008
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
946
1009
  We don't need to check the value; it's always URI safe. */
947
1010
  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]) {
948
- return false;
949
-
1011
+ if (
1012
+ // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1013
+ // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1014
+ // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1015
+ _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)) ||
1016
+ // Alternative, second condition checks if it's an `is`-attribute, AND
1017
+ // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1018
+ 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 {
1019
+ return false;
1020
+ }
950
1021
  /* Check value is safe. First, is attr inert? If so, is safe */
951
1022
  } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; 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$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if (!value) ; else {
952
1023
  return false;
@@ -955,6 +1026,16 @@
955
1026
  return true;
956
1027
  };
957
1028
 
1029
+ /**
1030
+ * _basicCustomElementCheck
1031
+ * checks if at least one dash is included in tagName, and it's not the first char
1032
+ * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1033
+ * @param {string} tagName name of the tag of the node to sanitize
1034
+ */
1035
+ var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
1036
+ return tagName.indexOf('-') > 0;
1037
+ };
1038
+
958
1039
  /**
959
1040
  * _sanitizeAttributes
960
1041
  *
@@ -997,7 +1078,7 @@
997
1078
  namespaceURI = _attr.namespaceURI;
998
1079
 
999
1080
  value = stringTrim(attr.value);
1000
- lcName = stringToLowerCase(name);
1081
+ lcName = transformCaseFunc(name);
1001
1082
 
1002
1083
  /* Execute a hook if present */
1003
1084
  hookEvent.attrName = lcName;
@@ -1032,7 +1113,7 @@
1032
1113
  }
1033
1114
 
1034
1115
  /* Is `value` valid for this attribute? */
1035
- var lcTag = currentNode.nodeName.toLowerCase();
1116
+ var lcTag = transformCaseFunc(currentNode.nodeName);
1036
1117
  if (!_isValidAttribute(lcTag, lcName, value)) {
1037
1118
  continue;
1038
1119
  }
@@ -1233,7 +1314,7 @@
1233
1314
  returnNode = body;
1234
1315
  }
1235
1316
 
1236
- if (RETURN_DOM_IMPORT) {
1317
+ if (ALLOWED_ATTR.shadowroot) {
1237
1318
  /*
1238
1319
  AdoptNode() is not used because internal state is not reset
1239
1320
  (e.g. the past names map of a HTMLFormElement), this is safe
@@ -1295,8 +1376,8 @@
1295
1376
  _parseConfig({});
1296
1377
  }
1297
1378
 
1298
- var lcTag = stringToLowerCase(tag);
1299
- var lcName = stringToLowerCase(attr);
1379
+ var lcTag = transformCaseFunc(tag);
1380
+ var lcName = transformCaseFunc(attr);
1300
1381
  return _isValidAttribute(lcTag, lcName, value);
1301
1382
  };
1302
1383