tinymce-rails 7.4.0 → 7.4.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/source/tinymce/tinymce.js +891 -867
  3. data/lib/tinymce/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/tinymce/models/dom/model.js +1 -1
  5. data/vendor/assets/javascripts/tinymce/plugins/accordion/plugin.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/plugins/advlist/plugin.js +1 -1
  7. data/vendor/assets/javascripts/tinymce/plugins/anchor/plugin.js +1 -1
  8. data/vendor/assets/javascripts/tinymce/plugins/autolink/plugin.js +1 -1
  9. data/vendor/assets/javascripts/tinymce/plugins/autoresize/plugin.js +1 -1
  10. data/vendor/assets/javascripts/tinymce/plugins/autosave/plugin.js +1 -1
  11. data/vendor/assets/javascripts/tinymce/plugins/charmap/plugin.js +1 -1
  12. data/vendor/assets/javascripts/tinymce/plugins/code/plugin.js +1 -1
  13. data/vendor/assets/javascripts/tinymce/plugins/codesample/plugin.js +1 -1
  14. data/vendor/assets/javascripts/tinymce/plugins/directionality/plugin.js +1 -1
  15. data/vendor/assets/javascripts/tinymce/plugins/emoticons/plugin.js +1 -1
  16. data/vendor/assets/javascripts/tinymce/plugins/fullscreen/plugin.js +1 -1
  17. data/vendor/assets/javascripts/tinymce/plugins/help/plugin.js +1 -1
  18. data/vendor/assets/javascripts/tinymce/plugins/image/plugin.js +1 -1
  19. data/vendor/assets/javascripts/tinymce/plugins/importcss/plugin.js +1 -1
  20. data/vendor/assets/javascripts/tinymce/plugins/insertdatetime/plugin.js +1 -1
  21. data/vendor/assets/javascripts/tinymce/plugins/link/plugin.js +1 -1
  22. data/vendor/assets/javascripts/tinymce/plugins/lists/plugin.js +1 -1
  23. data/vendor/assets/javascripts/tinymce/plugins/media/plugin.js +1 -1
  24. data/vendor/assets/javascripts/tinymce/plugins/nonbreaking/plugin.js +1 -1
  25. data/vendor/assets/javascripts/tinymce/plugins/pagebreak/plugin.js +1 -1
  26. data/vendor/assets/javascripts/tinymce/plugins/preview/plugin.js +1 -1
  27. data/vendor/assets/javascripts/tinymce/plugins/quickbars/plugin.js +1 -1
  28. data/vendor/assets/javascripts/tinymce/plugins/save/plugin.js +1 -1
  29. data/vendor/assets/javascripts/tinymce/plugins/searchreplace/plugin.js +1 -1
  30. data/vendor/assets/javascripts/tinymce/plugins/table/plugin.js +1 -1
  31. data/vendor/assets/javascripts/tinymce/plugins/visualblocks/plugin.js +1 -1
  32. data/vendor/assets/javascripts/tinymce/plugins/visualchars/plugin.js +1 -1
  33. data/vendor/assets/javascripts/tinymce/plugins/wordcount/plugin.js +1 -1
  34. data/vendor/assets/javascripts/tinymce/themes/silver/theme.js +384 -2
  35. data/vendor/assets/javascripts/tinymce/tinymce.js +383 -2
  36. metadata +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TinyMCE version 7.4.0 (2024-10-09)
2
+ * TinyMCE version 7.4.1 (TBD)
3
3
  */
4
4
 
5
5
  (function () {
@@ -15274,14 +15274,24 @@
15274
15274
  }
15275
15275
  };
15276
15276
 
15277
- const {entries, setPrototypeOf, isFrozen, getPrototypeOf, getOwnPropertyDescriptor} = Object;
15278
- let {freeze, seal, create: create$7} = Object;
15279
- let {apply, construct} = typeof Reflect !== 'undefined' && Reflect;
15280
- if (!apply) {
15281
- apply = function apply(fun, thisValue, args) {
15282
- return fun.apply(thisValue, args);
15283
- };
15284
- }
15277
+ /*! @license DOMPurify 3.1.7 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.1.7/LICENSE */
15278
+
15279
+ const {
15280
+ entries,
15281
+ setPrototypeOf,
15282
+ isFrozen,
15283
+ getPrototypeOf,
15284
+ getOwnPropertyDescriptor
15285
+ } = Object;
15286
+ let {
15287
+ freeze,
15288
+ seal,
15289
+ create: create$7
15290
+ } = Object; // eslint-disable-line import/no-mutable-exports
15291
+ let {
15292
+ apply,
15293
+ construct
15294
+ } = typeof Reflect !== 'undefined' && Reflect;
15285
15295
  if (!freeze) {
15286
15296
  freeze = function freeze(x) {
15287
15297
  return x;
@@ -15292,6 +15302,11 @@
15292
15302
  return x;
15293
15303
  };
15294
15304
  }
15305
+ if (!apply) {
15306
+ apply = function apply(fun, thisValue, args) {
15307
+ return fun.apply(thisValue, args);
15308
+ };
15309
+ }
15295
15310
  if (!construct) {
15296
15311
  construct = function construct(Func, args) {
15297
15312
  return new Func(...args);
@@ -15306,8 +15321,16 @@
15306
15321
  const stringReplace = unapply(String.prototype.replace);
15307
15322
  const stringIndexOf = unapply(String.prototype.indexOf);
15308
15323
  const stringTrim = unapply(String.prototype.trim);
15324
+ const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
15309
15325
  const regExpTest = unapply(RegExp.prototype.test);
15310
15326
  const typeErrorCreate = unconstruct(TypeError);
15327
+
15328
+ /**
15329
+ * Creates a new function that calls the given function with a specified thisArg and arguments.
15330
+ *
15331
+ * @param {Function} func - The function to be wrapped and called.
15332
+ * @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
15333
+ */
15311
15334
  function unapply(func) {
15312
15335
  return function (thisArg) {
15313
15336
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
@@ -15316,6 +15339,13 @@
15316
15339
  return apply(func, thisArg, args);
15317
15340
  };
15318
15341
  }
15342
+
15343
+ /**
15344
+ * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
15345
+ *
15346
+ * @param {Function} func - The constructor function to be wrapped and called.
15347
+ * @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
15348
+ */
15319
15349
  function unconstruct(func) {
15320
15350
  return function () {
15321
15351
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
@@ -15324,10 +15354,21 @@
15324
15354
  return construct(func, args);
15325
15355
  };
15326
15356
  }
15327
- function addToSet(set, array, transformCaseFunc) {
15328
- var _transformCaseFunc;
15329
- transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase;
15357
+
15358
+ /**
15359
+ * Add properties to a lookup table
15360
+ *
15361
+ * @param {Object} set - The set to which elements will be added.
15362
+ * @param {Array} array - The array containing elements to be added to the set.
15363
+ * @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
15364
+ * @returns {Object} The modified set with added elements.
15365
+ */
15366
+ function addToSet(set, array) {
15367
+ let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
15330
15368
  if (setPrototypeOf) {
15369
+ // Make 'in' and truthy checks like Boolean(set.constructor)
15370
+ // independent of any properties defined on Object.prototype.
15371
+ // Prevent prototype setters from intercepting set as a this value.
15331
15372
  setPrototypeOf(set, null);
15332
15373
  }
15333
15374
  let l = array.length;
@@ -15336,6 +15377,7 @@
15336
15377
  if (typeof element === 'string') {
15337
15378
  const lcElement = transformCaseFunc(element);
15338
15379
  if (lcElement !== element) {
15380
+ // Config presets (e.g. tags.js, attrs.js) are immutable.
15339
15381
  if (!isFrozen(array)) {
15340
15382
  array[l] = lcElement;
15341
15383
  }
@@ -15346,13 +15388,53 @@
15346
15388
  }
15347
15389
  return set;
15348
15390
  }
15391
+
15392
+ /**
15393
+ * Clean up an array to harden against CSPP
15394
+ *
15395
+ * @param {Array} array - The array to be cleaned.
15396
+ * @returns {Array} The cleaned version of the array
15397
+ */
15398
+ function cleanArray(array) {
15399
+ for (let index = 0; index < array.length; index++) {
15400
+ const isPropertyExist = objectHasOwnProperty(array, index);
15401
+ if (!isPropertyExist) {
15402
+ array[index] = null;
15403
+ }
15404
+ }
15405
+ return array;
15406
+ }
15407
+
15408
+ /**
15409
+ * Shallow clone an object
15410
+ *
15411
+ * @param {Object} object - The object to be cloned.
15412
+ * @returns {Object} A new object that copies the original.
15413
+ */
15349
15414
  function clone(object) {
15350
15415
  const newObject = create$7(null);
15351
15416
  for (const [property, value] of entries(object)) {
15352
- newObject[property] = value;
15417
+ const isPropertyExist = objectHasOwnProperty(object, property);
15418
+ if (isPropertyExist) {
15419
+ if (Array.isArray(value)) {
15420
+ newObject[property] = cleanArray(value);
15421
+ } else if (value && typeof value === 'object' && value.constructor === Object) {
15422
+ newObject[property] = clone(value);
15423
+ } else {
15424
+ newObject[property] = value;
15425
+ }
15426
+ }
15353
15427
  }
15354
15428
  return newObject;
15355
15429
  }
15430
+
15431
+ /**
15432
+ * This method automatically checks if the prop is function or getter and behaves accordingly.
15433
+ *
15434
+ * @param {Object} object - The object to look up the getter function in its prototype chain.
15435
+ * @param {String} prop - The property name for which to find the getter function.
15436
+ * @returns {Function} The getter function found in the prototype chain or a fallback function.
15437
+ */
15356
15438
  function lookupGetter(object, prop) {
15357
15439
  while (object !== null) {
15358
15440
  const desc = getOwnPropertyDescriptor(object, prop);
@@ -15366,644 +15448,50 @@
15366
15448
  }
15367
15449
  object = getPrototypeOf(object);
15368
15450
  }
15369
- function fallbackValue(element) {
15370
- console.warn('fallback value for', element);
15451
+ function fallbackValue() {
15371
15452
  return null;
15372
15453
  }
15373
15454
  return fallbackValue;
15374
15455
  }
15375
- const html$1 = freeze([
15376
- 'a',
15377
- 'abbr',
15378
- 'acronym',
15379
- 'address',
15380
- 'area',
15381
- 'article',
15382
- 'aside',
15383
- 'audio',
15384
- 'b',
15385
- 'bdi',
15386
- 'bdo',
15387
- 'big',
15388
- 'blink',
15389
- 'blockquote',
15390
- 'body',
15391
- 'br',
15392
- 'button',
15393
- 'canvas',
15394
- 'caption',
15395
- 'center',
15396
- 'cite',
15397
- 'code',
15398
- 'col',
15399
- 'colgroup',
15400
- 'content',
15401
- 'data',
15402
- 'datalist',
15403
- 'dd',
15404
- 'decorator',
15405
- 'del',
15406
- 'details',
15407
- 'dfn',
15408
- 'dialog',
15409
- 'dir',
15410
- 'div',
15411
- 'dl',
15412
- 'dt',
15413
- 'element',
15414
- 'em',
15415
- 'fieldset',
15416
- 'figcaption',
15417
- 'figure',
15418
- 'font',
15419
- 'footer',
15420
- 'form',
15421
- 'h1',
15422
- 'h2',
15423
- 'h3',
15424
- 'h4',
15425
- 'h5',
15426
- 'h6',
15427
- 'head',
15428
- 'header',
15429
- 'hgroup',
15430
- 'hr',
15431
- 'html',
15432
- 'i',
15433
- 'img',
15434
- 'input',
15435
- 'ins',
15436
- 'kbd',
15437
- 'label',
15438
- 'legend',
15439
- 'li',
15440
- 'main',
15441
- 'map',
15442
- 'mark',
15443
- 'marquee',
15444
- 'menu',
15445
- 'menuitem',
15446
- 'meter',
15447
- 'nav',
15448
- 'nobr',
15449
- 'ol',
15450
- 'optgroup',
15451
- 'option',
15452
- 'output',
15453
- 'p',
15454
- 'picture',
15455
- 'pre',
15456
- 'progress',
15457
- 'q',
15458
- 'rp',
15459
- 'rt',
15460
- 'ruby',
15461
- 's',
15462
- 'samp',
15463
- 'section',
15464
- 'select',
15465
- 'shadow',
15466
- 'small',
15467
- 'source',
15468
- 'spacer',
15469
- 'span',
15470
- 'strike',
15471
- 'strong',
15472
- 'style',
15473
- 'sub',
15474
- 'summary',
15475
- 'sup',
15476
- 'table',
15477
- 'tbody',
15478
- 'td',
15479
- 'template',
15480
- 'textarea',
15481
- 'tfoot',
15482
- 'th',
15483
- 'thead',
15484
- 'time',
15485
- 'tr',
15486
- 'track',
15487
- 'tt',
15488
- 'u',
15489
- 'ul',
15490
- 'var',
15491
- 'video',
15492
- 'wbr'
15493
- ]);
15494
- const svg$1 = freeze([
15495
- 'svg',
15496
- 'a',
15497
- 'altglyph',
15498
- 'altglyphdef',
15499
- 'altglyphitem',
15500
- 'animatecolor',
15501
- 'animatemotion',
15502
- 'animatetransform',
15503
- 'circle',
15504
- 'clippath',
15505
- 'defs',
15506
- 'desc',
15507
- 'ellipse',
15508
- 'filter',
15509
- 'font',
15510
- 'g',
15511
- 'glyph',
15512
- 'glyphref',
15513
- 'hkern',
15514
- 'image',
15515
- 'line',
15516
- 'lineargradient',
15517
- 'marker',
15518
- 'mask',
15519
- 'metadata',
15520
- 'mpath',
15521
- 'path',
15522
- 'pattern',
15523
- 'polygon',
15524
- 'polyline',
15525
- 'radialgradient',
15526
- 'rect',
15527
- 'stop',
15528
- 'style',
15529
- 'switch',
15530
- 'symbol',
15531
- 'text',
15532
- 'textpath',
15533
- 'title',
15534
- 'tref',
15535
- 'tspan',
15536
- 'view',
15537
- 'vkern'
15538
- ]);
15539
- const svgFilters = freeze([
15540
- 'feBlend',
15541
- 'feColorMatrix',
15542
- 'feComponentTransfer',
15543
- 'feComposite',
15544
- 'feConvolveMatrix',
15545
- 'feDiffuseLighting',
15546
- 'feDisplacementMap',
15547
- 'feDistantLight',
15548
- 'feDropShadow',
15549
- 'feFlood',
15550
- 'feFuncA',
15551
- 'feFuncB',
15552
- 'feFuncG',
15553
- 'feFuncR',
15554
- 'feGaussianBlur',
15555
- 'feImage',
15556
- 'feMerge',
15557
- 'feMergeNode',
15558
- 'feMorphology',
15559
- 'feOffset',
15560
- 'fePointLight',
15561
- 'feSpecularLighting',
15562
- 'feSpotLight',
15563
- 'feTile',
15564
- 'feTurbulence'
15565
- ]);
15566
- const svgDisallowed = freeze([
15567
- 'animate',
15568
- 'color-profile',
15569
- 'cursor',
15570
- 'discard',
15571
- 'font-face',
15572
- 'font-face-format',
15573
- 'font-face-name',
15574
- 'font-face-src',
15575
- 'font-face-uri',
15576
- 'foreignobject',
15577
- 'hatch',
15578
- 'hatchpath',
15579
- 'mesh',
15580
- 'meshgradient',
15581
- 'meshpatch',
15582
- 'meshrow',
15583
- 'missing-glyph',
15584
- 'script',
15585
- 'set',
15586
- 'solidcolor',
15587
- 'unknown',
15588
- 'use'
15589
- ]);
15590
- const mathMl$1 = freeze([
15591
- 'math',
15592
- 'menclose',
15593
- 'merror',
15594
- 'mfenced',
15595
- 'mfrac',
15596
- 'mglyph',
15597
- 'mi',
15598
- 'mlabeledtr',
15599
- 'mmultiscripts',
15600
- 'mn',
15601
- 'mo',
15602
- 'mover',
15603
- 'mpadded',
15604
- 'mphantom',
15605
- 'mroot',
15606
- 'mrow',
15607
- 'ms',
15608
- 'mspace',
15609
- 'msqrt',
15610
- 'mstyle',
15611
- 'msub',
15612
- 'msup',
15613
- 'msubsup',
15614
- 'mtable',
15615
- 'mtd',
15616
- 'mtext',
15617
- 'mtr',
15618
- 'munder',
15619
- 'munderover',
15620
- 'mprescripts'
15621
- ]);
15622
- const mathMlDisallowed = freeze([
15623
- 'maction',
15624
- 'maligngroup',
15625
- 'malignmark',
15626
- 'mlongdiv',
15627
- 'mscarries',
15628
- 'mscarry',
15629
- 'msgroup',
15630
- 'mstack',
15631
- 'msline',
15632
- 'msrow',
15633
- 'semantics',
15634
- 'annotation',
15635
- 'annotation-xml',
15636
- 'mprescripts',
15637
- 'none'
15638
- ]);
15456
+
15457
+ const html$1 = 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']);
15458
+
15459
+ // SVG
15460
+ const svg$1 = 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']);
15461
+ const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
15462
+
15463
+ // List of SVG elements that are disallowed by default.
15464
+ // We still need to know them so that we can do namespace
15465
+ // checks properly in case one wants to add them to
15466
+ // allow-list.
15467
+ const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', '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']);
15468
+ const mathMl$1 = 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', 'mprescripts']);
15469
+
15470
+ // Similarly to SVG, we want to know all MathML elements,
15471
+ // even those that we disallow by default.
15472
+ const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
15639
15473
  const text = freeze(['#text']);
15640
- const html = freeze([
15641
- 'accept',
15642
- 'action',
15643
- 'align',
15644
- 'alt',
15645
- 'autocapitalize',
15646
- 'autocomplete',
15647
- 'autopictureinpicture',
15648
- 'autoplay',
15649
- 'background',
15650
- 'bgcolor',
15651
- 'border',
15652
- 'capture',
15653
- 'cellpadding',
15654
- 'cellspacing',
15655
- 'checked',
15656
- 'cite',
15657
- 'class',
15658
- 'clear',
15659
- 'color',
15660
- 'cols',
15661
- 'colspan',
15662
- 'controls',
15663
- 'controlslist',
15664
- 'coords',
15665
- 'crossorigin',
15666
- 'datetime',
15667
- 'decoding',
15668
- 'default',
15669
- 'dir',
15670
- 'disabled',
15671
- 'disablepictureinpicture',
15672
- 'disableremoteplayback',
15673
- 'download',
15674
- 'draggable',
15675
- 'enctype',
15676
- 'enterkeyhint',
15677
- 'face',
15678
- 'for',
15679
- 'headers',
15680
- 'height',
15681
- 'hidden',
15682
- 'high',
15683
- 'href',
15684
- 'hreflang',
15685
- 'id',
15686
- 'inputmode',
15687
- 'integrity',
15688
- 'ismap',
15689
- 'kind',
15690
- 'label',
15691
- 'lang',
15692
- 'list',
15693
- 'loading',
15694
- 'loop',
15695
- 'low',
15696
- 'max',
15697
- 'maxlength',
15698
- 'media',
15699
- 'method',
15700
- 'min',
15701
- 'minlength',
15702
- 'multiple',
15703
- 'muted',
15704
- 'name',
15705
- 'nonce',
15706
- 'noshade',
15707
- 'novalidate',
15708
- 'nowrap',
15709
- 'open',
15710
- 'optimum',
15711
- 'pattern',
15712
- 'placeholder',
15713
- 'playsinline',
15714
- 'poster',
15715
- 'preload',
15716
- 'pubdate',
15717
- 'radiogroup',
15718
- 'readonly',
15719
- 'rel',
15720
- 'required',
15721
- 'rev',
15722
- 'reversed',
15723
- 'role',
15724
- 'rows',
15725
- 'rowspan',
15726
- 'spellcheck',
15727
- 'scope',
15728
- 'selected',
15729
- 'shape',
15730
- 'size',
15731
- 'sizes',
15732
- 'span',
15733
- 'srclang',
15734
- 'start',
15735
- 'src',
15736
- 'srcset',
15737
- 'step',
15738
- 'style',
15739
- 'summary',
15740
- 'tabindex',
15741
- 'title',
15742
- 'translate',
15743
- 'type',
15744
- 'usemap',
15745
- 'valign',
15746
- 'value',
15747
- 'width',
15748
- 'xmlns',
15749
- 'slot'
15750
- ]);
15751
- const svg = freeze([
15752
- 'accent-height',
15753
- 'accumulate',
15754
- 'additive',
15755
- 'alignment-baseline',
15756
- 'ascent',
15757
- 'attributename',
15758
- 'attributetype',
15759
- 'azimuth',
15760
- 'basefrequency',
15761
- 'baseline-shift',
15762
- 'begin',
15763
- 'bias',
15764
- 'by',
15765
- 'class',
15766
- 'clip',
15767
- 'clippathunits',
15768
- 'clip-path',
15769
- 'clip-rule',
15770
- 'color',
15771
- 'color-interpolation',
15772
- 'color-interpolation-filters',
15773
- 'color-profile',
15774
- 'color-rendering',
15775
- 'cx',
15776
- 'cy',
15777
- 'd',
15778
- 'dx',
15779
- 'dy',
15780
- 'diffuseconstant',
15781
- 'direction',
15782
- 'display',
15783
- 'divisor',
15784
- 'dur',
15785
- 'edgemode',
15786
- 'elevation',
15787
- 'end',
15788
- 'fill',
15789
- 'fill-opacity',
15790
- 'fill-rule',
15791
- 'filter',
15792
- 'filterunits',
15793
- 'flood-color',
15794
- 'flood-opacity',
15795
- 'font-family',
15796
- 'font-size',
15797
- 'font-size-adjust',
15798
- 'font-stretch',
15799
- 'font-style',
15800
- 'font-variant',
15801
- 'font-weight',
15802
- 'fx',
15803
- 'fy',
15804
- 'g1',
15805
- 'g2',
15806
- 'glyph-name',
15807
- 'glyphref',
15808
- 'gradientunits',
15809
- 'gradienttransform',
15810
- 'height',
15811
- 'href',
15812
- 'id',
15813
- 'image-rendering',
15814
- 'in',
15815
- 'in2',
15816
- 'k',
15817
- 'k1',
15818
- 'k2',
15819
- 'k3',
15820
- 'k4',
15821
- 'kerning',
15822
- 'keypoints',
15823
- 'keysplines',
15824
- 'keytimes',
15825
- 'lang',
15826
- 'lengthadjust',
15827
- 'letter-spacing',
15828
- 'kernelmatrix',
15829
- 'kernelunitlength',
15830
- 'lighting-color',
15831
- 'local',
15832
- 'marker-end',
15833
- 'marker-mid',
15834
- 'marker-start',
15835
- 'markerheight',
15836
- 'markerunits',
15837
- 'markerwidth',
15838
- 'maskcontentunits',
15839
- 'maskunits',
15840
- 'max',
15841
- 'mask',
15842
- 'media',
15843
- 'method',
15844
- 'mode',
15845
- 'min',
15846
- 'name',
15847
- 'numoctaves',
15848
- 'offset',
15849
- 'operator',
15850
- 'opacity',
15851
- 'order',
15852
- 'orient',
15853
- 'orientation',
15854
- 'origin',
15855
- 'overflow',
15856
- 'paint-order',
15857
- 'path',
15858
- 'pathlength',
15859
- 'patterncontentunits',
15860
- 'patterntransform',
15861
- 'patternunits',
15862
- 'points',
15863
- 'preservealpha',
15864
- 'preserveaspectratio',
15865
- 'primitiveunits',
15866
- 'r',
15867
- 'rx',
15868
- 'ry',
15869
- 'radius',
15870
- 'refx',
15871
- 'refy',
15872
- 'repeatcount',
15873
- 'repeatdur',
15874
- 'restart',
15875
- 'result',
15876
- 'rotate',
15877
- 'scale',
15878
- 'seed',
15879
- 'shape-rendering',
15880
- 'specularconstant',
15881
- 'specularexponent',
15882
- 'spreadmethod',
15883
- 'startoffset',
15884
- 'stddeviation',
15885
- 'stitchtiles',
15886
- 'stop-color',
15887
- 'stop-opacity',
15888
- 'stroke-dasharray',
15889
- 'stroke-dashoffset',
15890
- 'stroke-linecap',
15891
- 'stroke-linejoin',
15892
- 'stroke-miterlimit',
15893
- 'stroke-opacity',
15894
- 'stroke',
15895
- 'stroke-width',
15896
- 'style',
15897
- 'surfacescale',
15898
- 'systemlanguage',
15899
- 'tabindex',
15900
- 'targetx',
15901
- 'targety',
15902
- 'transform',
15903
- 'transform-origin',
15904
- 'text-anchor',
15905
- 'text-decoration',
15906
- 'text-rendering',
15907
- 'textlength',
15908
- 'type',
15909
- 'u1',
15910
- 'u2',
15911
- 'unicode',
15912
- 'values',
15913
- 'viewbox',
15914
- 'visibility',
15915
- 'version',
15916
- 'vert-adv-y',
15917
- 'vert-origin-x',
15918
- 'vert-origin-y',
15919
- 'width',
15920
- 'word-spacing',
15921
- 'wrap',
15922
- 'writing-mode',
15923
- 'xchannelselector',
15924
- 'ychannelselector',
15925
- 'x',
15926
- 'x1',
15927
- 'x2',
15928
- 'xmlns',
15929
- 'y',
15930
- 'y1',
15931
- 'y2',
15932
- 'z',
15933
- 'zoomandpan'
15934
- ]);
15935
- const mathMl = freeze([
15936
- 'accent',
15937
- 'accentunder',
15938
- 'align',
15939
- 'bevelled',
15940
- 'close',
15941
- 'columnsalign',
15942
- 'columnlines',
15943
- 'columnspan',
15944
- 'denomalign',
15945
- 'depth',
15946
- 'dir',
15947
- 'display',
15948
- 'displaystyle',
15949
- 'encoding',
15950
- 'fence',
15951
- 'frame',
15952
- 'height',
15953
- 'href',
15954
- 'id',
15955
- 'largeop',
15956
- 'length',
15957
- 'linethickness',
15958
- 'lspace',
15959
- 'lquote',
15960
- 'mathbackground',
15961
- 'mathcolor',
15962
- 'mathsize',
15963
- 'mathvariant',
15964
- 'maxsize',
15965
- 'minsize',
15966
- 'movablelimits',
15967
- 'notation',
15968
- 'numalign',
15969
- 'open',
15970
- 'rowalign',
15971
- 'rowlines',
15972
- 'rowspacing',
15973
- 'rowspan',
15974
- 'rspace',
15975
- 'rquote',
15976
- 'scriptlevel',
15977
- 'scriptminsize',
15978
- 'scriptsizemultiplier',
15979
- 'selection',
15980
- 'separator',
15981
- 'separators',
15982
- 'stretchy',
15983
- 'subscriptshift',
15984
- 'supscriptshift',
15985
- 'symmetric',
15986
- 'voffset',
15987
- 'width',
15988
- 'xmlns'
15989
- ]);
15990
- const xml = freeze([
15991
- 'xlink:href',
15992
- 'xml:id',
15993
- 'xlink:title',
15994
- 'xml:space',
15995
- 'xmlns:xlink'
15996
- ]);
15997
- const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm);
15474
+
15475
+ const html = 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', 'popover', 'popovertarget', 'popovertargetaction', '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', 'wrap', 'xmlns', 'slot']);
15476
+ const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', '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', 'exponent', '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', 'intercept', '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', 'slope', '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', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', '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']);
15477
+ const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
15478
+ const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
15479
+
15480
+ // eslint-disable-next-line unicorn/better-regex
15481
+ const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
15998
15482
  const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
15999
15483
  const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
16000
- const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/);
16001
- const ARIA_ATTR = seal(/^aria-[\-\w]+$/);
16002
- const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i);
15484
+ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
15485
+ const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
15486
+ const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
15487
+ );
16003
15488
  const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
16004
- const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g);
15489
+ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
15490
+ );
16005
15491
  const DOCTYPE_NAME = seal(/^html$/i);
16006
- var EXPRESSIONS = Object.freeze({
15492
+ const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
15493
+
15494
+ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
16007
15495
  __proto__: null,
16008
15496
  MUSTACHE_EXPR: MUSTACHE_EXPR,
16009
15497
  ERB_EXPR: ERB_EXPR,
@@ -16013,13 +15501,47 @@
16013
15501
  IS_ALLOWED_URI: IS_ALLOWED_URI,
16014
15502
  IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
16015
15503
  ATTR_WHITESPACE: ATTR_WHITESPACE,
16016
- DOCTYPE_NAME: DOCTYPE_NAME
15504
+ DOCTYPE_NAME: DOCTYPE_NAME,
15505
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT
16017
15506
  });
16018
- const getGlobal = () => typeof window === 'undefined' ? null : window;
15507
+
15508
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
15509
+ const NODE_TYPE = {
15510
+ element: 1,
15511
+ attribute: 2,
15512
+ text: 3,
15513
+ cdataSection: 4,
15514
+ entityReference: 5,
15515
+ // Deprecated
15516
+ entityNode: 6,
15517
+ // Deprecated
15518
+ progressingInstruction: 7,
15519
+ comment: 8,
15520
+ document: 9,
15521
+ documentType: 10,
15522
+ documentFragment: 11,
15523
+ notation: 12 // Deprecated
15524
+ };
15525
+ const getGlobal = function getGlobal() {
15526
+ return typeof window === 'undefined' ? null : window;
15527
+ };
15528
+
15529
+ /**
15530
+ * Creates a no-op policy for internal use only.
15531
+ * Don't export this function outside this module!
15532
+ * @param {TrustedTypePolicyFactory} trustedTypes The policy factory.
15533
+ * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
15534
+ * @return {TrustedTypePolicy} The policy created (or null, if Trusted Types
15535
+ * are not supported or creating the policy failed).
15536
+ */
16019
15537
  const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
16020
15538
  if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
16021
15539
  return null;
16022
15540
  }
15541
+
15542
+ // Allow the callers to control the unique policy name
15543
+ // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
15544
+ // Policy creation with duplicate names throws in Trusted Types.
16023
15545
  let suffix = null;
16024
15546
  const ATTR_NAME = 'data-tt-policy-suffix';
16025
15547
  if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
@@ -16036,6 +15558,9 @@
16036
15558
  }
16037
15559
  });
16038
15560
  } catch (_) {
15561
+ // Policy creation failed (most likely another DOMPurify script has
15562
+ // already run). Skip creating the policy, as this will only cause errors
15563
+ // if TT are enforced.
16039
15564
  console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
16040
15565
  return null;
16041
15566
  }
@@ -16043,21 +15568,53 @@
16043
15568
  function createDOMPurify() {
16044
15569
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
16045
15570
  const DOMPurify = root => createDOMPurify(root);
16046
- DOMPurify.version = '3.0.5';
15571
+
15572
+ /**
15573
+ * Version label, exposed for easier checks
15574
+ * if DOMPurify is up to date or not
15575
+ */
15576
+ DOMPurify.version = '3.1.7';
15577
+
15578
+ /**
15579
+ * Array of elements that DOMPurify removed during sanitation.
15580
+ * Empty if nothing was removed.
15581
+ */
16047
15582
  DOMPurify.removed = [];
16048
- if (!window || !window.document || window.document.nodeType !== 9) {
15583
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
15584
+ // Not running in a browser, provide a factory function
15585
+ // so that you can pass your own Window
16049
15586
  DOMPurify.isSupported = false;
16050
15587
  return DOMPurify;
16051
15588
  }
16052
- const originalDocument = window.document;
15589
+ let {
15590
+ document
15591
+ } = window;
15592
+ const originalDocument = document;
16053
15593
  const currentScript = originalDocument.currentScript;
16054
- let {document} = window;
16055
- const {DocumentFragment, HTMLTemplateElement, Node, Element, NodeFilter, NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap, HTMLFormElement, DOMParser, trustedTypes} = window;
15594
+ const {
15595
+ DocumentFragment,
15596
+ HTMLTemplateElement,
15597
+ Node,
15598
+ Element,
15599
+ NodeFilter,
15600
+ NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
15601
+ HTMLFormElement,
15602
+ DOMParser,
15603
+ trustedTypes
15604
+ } = window;
16056
15605
  const ElementPrototype = Element.prototype;
16057
15606
  const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
15607
+ const remove = lookupGetter(ElementPrototype, 'remove');
16058
15608
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
16059
15609
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
16060
15610
  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
15611
+
15612
+ // As per issue #47, the web-components registry is inherited by a
15613
+ // new document created via createHTMLDocument. As per the spec
15614
+ // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
15615
+ // a new empty registry is used when creating a template contents owner
15616
+ // document, so we use that as our parent document to ensure nothing
15617
+ // is inherited.
16061
15618
  if (typeof HTMLTemplateElement === 'function') {
16062
15619
  const template = document.createElement('template');
16063
15620
  if (template.content && template.content.ownerDocument) {
@@ -16066,28 +15623,55 @@
16066
15623
  }
16067
15624
  let trustedTypesPolicy;
16068
15625
  let emptyHTML = '';
16069
- const {implementation, createNodeIterator, createDocumentFragment, getElementsByTagName} = document;
16070
- const {importNode} = originalDocument;
15626
+ const {
15627
+ implementation,
15628
+ createNodeIterator,
15629
+ createDocumentFragment,
15630
+ getElementsByTagName
15631
+ } = document;
15632
+ const {
15633
+ importNode
15634
+ } = originalDocument;
16071
15635
  let hooks = {};
15636
+
15637
+ /**
15638
+ * Expose whether this browser supports running the full DOMPurify.
15639
+ */
16072
15640
  DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
16073
- const {MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR, DATA_ATTR, ARIA_ATTR, IS_SCRIPT_OR_DATA, ATTR_WHITESPACE} = EXPRESSIONS;
16074
- let {IS_ALLOWED_URI: IS_ALLOWED_URI$1} = EXPRESSIONS;
15641
+ const {
15642
+ MUSTACHE_EXPR,
15643
+ ERB_EXPR,
15644
+ TMPLIT_EXPR,
15645
+ DATA_ATTR,
15646
+ ARIA_ATTR,
15647
+ IS_SCRIPT_OR_DATA,
15648
+ ATTR_WHITESPACE,
15649
+ CUSTOM_ELEMENT
15650
+ } = EXPRESSIONS;
15651
+ let {
15652
+ IS_ALLOWED_URI: IS_ALLOWED_URI$1
15653
+ } = EXPRESSIONS;
15654
+
15655
+ /**
15656
+ * We consider the elements and attributes below to be safe. Ideally
15657
+ * don't add any new ones but feel free to remove unwanted ones.
15658
+ */
15659
+
15660
+ /* allowed element names */
16075
15661
  let ALLOWED_TAGS = null;
16076
- const DEFAULT_ALLOWED_TAGS = addToSet({}, [
16077
- ...html$1,
16078
- ...svg$1,
16079
- ...svgFilters,
16080
- ...mathMl$1,
16081
- ...text
16082
- ]);
15662
+ const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
15663
+
15664
+ /* Allowed attribute names */
16083
15665
  let ALLOWED_ATTR = null;
16084
- const DEFAULT_ALLOWED_ATTR = addToSet({}, [
16085
- ...html,
16086
- ...svg,
16087
- ...mathMl,
16088
- ...xml
16089
- ]);
16090
- let CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
15666
+ const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
15667
+
15668
+ /*
15669
+ * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
15670
+ * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
15671
+ * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
15672
+ * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
15673
+ */
15674
+ let CUSTOM_ELEMENT_HANDLING = Object.seal(create$7(null, {
16091
15675
  tagNameCheck: {
16092
15676
  writable: true,
16093
15677
  configurable: false,
@@ -16107,135 +15691,193 @@
16107
15691
  value: false
16108
15692
  }
16109
15693
  }));
15694
+
15695
+ /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
16110
15696
  let FORBID_TAGS = null;
15697
+
15698
+ /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
16111
15699
  let FORBID_ATTR = null;
15700
+
15701
+ /* Decide if ARIA attributes are okay */
16112
15702
  let ALLOW_ARIA_ATTR = true;
15703
+
15704
+ /* Decide if custom data attributes are okay */
16113
15705
  let ALLOW_DATA_ATTR = true;
15706
+
15707
+ /* Decide if unknown protocols are okay */
16114
15708
  let ALLOW_UNKNOWN_PROTOCOLS = false;
15709
+
15710
+ /* Decide if self-closing tags in attributes are allowed.
15711
+ * Usually removed due to a mXSS issue in jQuery 3.0 */
16115
15712
  let ALLOW_SELF_CLOSE_IN_ATTR = true;
15713
+
15714
+ /* Output should be safe for common template engines.
15715
+ * This means, DOMPurify removes data attributes, mustaches and ERB
15716
+ */
16116
15717
  let SAFE_FOR_TEMPLATES = false;
15718
+
15719
+ /* Output should be safe even for XML used within HTML and alike.
15720
+ * This means, DOMPurify removes comments when containing risky content.
15721
+ */
15722
+ let SAFE_FOR_XML = true;
15723
+
15724
+ /* Decide if document with <html>... should be returned */
16117
15725
  let WHOLE_DOCUMENT = false;
15726
+
15727
+ /* Track whether config is already set on this instance of DOMPurify. */
16118
15728
  let SET_CONFIG = false;
15729
+
15730
+ /* Decide if all elements (e.g. style, script) must be children of
15731
+ * document.body. By default, browsers might move them to document.head */
16119
15732
  let FORCE_BODY = false;
15733
+
15734
+ /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
15735
+ * string (or a TrustedHTML object if Trusted Types are supported).
15736
+ * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
15737
+ */
16120
15738
  let RETURN_DOM = false;
15739
+
15740
+ /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
15741
+ * string (or a TrustedHTML object if Trusted Types are supported) */
16121
15742
  let RETURN_DOM_FRAGMENT = false;
15743
+
15744
+ /* Try to return a Trusted Type object instead of a string, return a string in
15745
+ * case Trusted Types are not supported */
16122
15746
  let RETURN_TRUSTED_TYPE = false;
15747
+
15748
+ /* Output should be free from DOM clobbering attacks?
15749
+ * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
15750
+ */
16123
15751
  let SANITIZE_DOM = true;
15752
+
15753
+ /* Achieve full DOM Clobbering protection by isolating the namespace of named
15754
+ * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
15755
+ *
15756
+ * HTML/DOM spec rules that enable DOM Clobbering:
15757
+ * - Named Access on Window (§7.3.3)
15758
+ * - DOM Tree Accessors (§3.1.5)
15759
+ * - Form Element Parent-Child Relations (§4.10.3)
15760
+ * - Iframe srcdoc / Nested WindowProxies (§4.8.5)
15761
+ * - HTMLCollection (§4.2.10.2)
15762
+ *
15763
+ * Namespace isolation is implemented by prefixing `id` and `name` attributes
15764
+ * with a constant string, i.e., `user-content-`
15765
+ */
16124
15766
  let SANITIZE_NAMED_PROPS = false;
16125
15767
  const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
15768
+
15769
+ /* Keep element content when removing element? */
16126
15770
  let KEEP_CONTENT = true;
15771
+
15772
+ /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
15773
+ * of importing it into a new Document and returning a sanitized copy */
16127
15774
  let IN_PLACE = false;
15775
+
15776
+ /* Allow usage of profiles like html, svg and mathMl */
16128
15777
  let USE_PROFILES = {};
15778
+
15779
+ /* Tags to ignore content of when KEEP_CONTENT is true */
16129
15780
  let FORBID_CONTENTS = null;
16130
- const DEFAULT_FORBID_CONTENTS = addToSet({}, [
16131
- 'annotation-xml',
16132
- 'audio',
16133
- 'colgroup',
16134
- 'desc',
16135
- 'foreignobject',
16136
- 'head',
16137
- 'iframe',
16138
- 'math',
16139
- 'mi',
16140
- 'mn',
16141
- 'mo',
16142
- 'ms',
16143
- 'mtext',
16144
- 'noembed',
16145
- 'noframes',
16146
- 'noscript',
16147
- 'plaintext',
16148
- 'script',
16149
- 'style',
16150
- 'svg',
16151
- 'template',
16152
- 'thead',
16153
- 'title',
16154
- 'video',
16155
- 'xmp'
16156
- ]);
15781
+ const 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']);
15782
+
15783
+ /* Tags that are safe for data: URIs */
16157
15784
  let DATA_URI_TAGS = null;
16158
- const DEFAULT_DATA_URI_TAGS = addToSet({}, [
16159
- 'audio',
16160
- 'video',
16161
- 'img',
16162
- 'source',
16163
- 'image',
16164
- 'track'
16165
- ]);
15785
+ const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
15786
+
15787
+ /* Attributes safe for values like "javascript:" */
16166
15788
  let URI_SAFE_ATTRIBUTES = null;
16167
- const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [
16168
- 'alt',
16169
- 'class',
16170
- 'for',
16171
- 'id',
16172
- 'label',
16173
- 'name',
16174
- 'pattern',
16175
- 'placeholder',
16176
- 'role',
16177
- 'summary',
16178
- 'title',
16179
- 'value',
16180
- 'style',
16181
- 'xmlns'
16182
- ]);
15789
+ const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
16183
15790
  const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
16184
15791
  const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
16185
15792
  const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
15793
+ /* Document namespace */
16186
15794
  let NAMESPACE = HTML_NAMESPACE;
16187
15795
  let IS_EMPTY_INPUT = false;
15796
+
15797
+ /* Allowed XHTML+XML namespaces */
16188
15798
  let ALLOWED_NAMESPACES = null;
16189
- const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [
16190
- MATHML_NAMESPACE,
16191
- SVG_NAMESPACE,
16192
- HTML_NAMESPACE
16193
- ], stringToString);
16194
- let PARSER_MEDIA_TYPE;
16195
- const SUPPORTED_PARSER_MEDIA_TYPES = [
16196
- 'application/xhtml+xml',
16197
- 'text/html'
16198
- ];
15799
+ const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
15800
+
15801
+ /* Parsing of strict XHTML documents */
15802
+ let PARSER_MEDIA_TYPE = null;
15803
+ const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
16199
15804
  const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
16200
- let transformCaseFunc;
15805
+ let transformCaseFunc = null;
15806
+
15807
+ /* Keep a reference to config to pass to hooks */
16201
15808
  let CONFIG = null;
15809
+
15810
+ /* Ideally, do not touch anything below this line */
15811
+ /* ______________________________________________ */
15812
+
16202
15813
  const formElement = document.createElement('form');
16203
15814
  const isRegexOrFunction = function isRegexOrFunction(testValue) {
16204
15815
  return testValue instanceof RegExp || testValue instanceof Function;
16205
15816
  };
16206
- const _parseConfig = function _parseConfig(cfg) {
15817
+
15818
+ /**
15819
+ * _parseConfig
15820
+ *
15821
+ * @param {Object} cfg optional config literal
15822
+ */
15823
+ // eslint-disable-next-line complexity
15824
+ const _parseConfig = function _parseConfig() {
15825
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
16207
15826
  if (CONFIG && CONFIG === cfg) {
16208
15827
  return;
16209
15828
  }
15829
+
15830
+ /* Shield configuration object from tampering */
16210
15831
  if (!cfg || typeof cfg !== 'object') {
16211
15832
  cfg = {};
16212
15833
  }
15834
+
15835
+ /* Shield configuration object from prototype pollution */
16213
15836
  cfg = clone(cfg);
16214
- PARSER_MEDIA_TYPE = SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
15837
+ PARSER_MEDIA_TYPE =
15838
+ // eslint-disable-next-line unicorn/prefer-includes
15839
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
15840
+
15841
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
16215
15842
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
16216
- ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
16217
- ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
16218
- ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
16219
- URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
16220
- DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
16221
- FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
16222
- FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
16223
- FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
16224
- USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
16225
- ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false;
16226
- ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false;
16227
- ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false;
16228
- ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false;
16229
- SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false;
16230
- WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false;
16231
- RETURN_DOM = cfg.RETURN_DOM || false;
16232
- RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false;
16233
- RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false;
16234
- FORCE_BODY = cfg.FORCE_BODY || false;
16235
- SANITIZE_DOM = cfg.SANITIZE_DOM !== false;
16236
- SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false;
16237
- KEEP_CONTENT = cfg.KEEP_CONTENT !== false;
16238
- IN_PLACE = cfg.IN_PLACE || false;
15843
+
15844
+ /* Set configuration parameters */
15845
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
15846
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
15847
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
15848
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
15849
+ // eslint-disable-line indent
15850
+ cfg.ADD_URI_SAFE_ATTR,
15851
+ // eslint-disable-line indent
15852
+ transformCaseFunc // eslint-disable-line indent
15853
+ ) // eslint-disable-line indent
15854
+ : DEFAULT_URI_SAFE_ATTRIBUTES;
15855
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
15856
+ // eslint-disable-line indent
15857
+ cfg.ADD_DATA_URI_TAGS,
15858
+ // eslint-disable-line indent
15859
+ transformCaseFunc // eslint-disable-line indent
15860
+ ) // eslint-disable-line indent
15861
+ : DEFAULT_DATA_URI_TAGS;
15862
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
15863
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
15864
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
15865
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
15866
+ ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
15867
+ ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
15868
+ ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
15869
+ ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
15870
+ SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
15871
+ SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
15872
+ WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
15873
+ RETURN_DOM = cfg.RETURN_DOM || false; // Default false
15874
+ RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
15875
+ RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
15876
+ FORCE_BODY = cfg.FORCE_BODY || false; // Default false
15877
+ SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
15878
+ SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
15879
+ KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
15880
+ IN_PLACE = cfg.IN_PLACE || false; // Default false
16239
15881
  IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
16240
15882
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
16241
15883
  CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
@@ -16254,8 +15896,10 @@
16254
15896
  if (RETURN_DOM_FRAGMENT) {
16255
15897
  RETURN_DOM = true;
16256
15898
  }
15899
+
15900
+ /* Parse profile info */
16257
15901
  if (USE_PROFILES) {
16258
- ALLOWED_TAGS = addToSet({}, [...text]);
15902
+ ALLOWED_TAGS = addToSet({}, text);
16259
15903
  ALLOWED_ATTR = [];
16260
15904
  if (USE_PROFILES.html === true) {
16261
15905
  addToSet(ALLOWED_TAGS, html$1);
@@ -16277,6 +15921,8 @@
16277
15921
  addToSet(ALLOWED_ATTR, xml);
16278
15922
  }
16279
15923
  }
15924
+
15925
+ /* Merge configuration parameters */
16280
15926
  if (cfg.ADD_TAGS) {
16281
15927
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
16282
15928
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
@@ -16298,16 +15944,18 @@
16298
15944
  }
16299
15945
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
16300
15946
  }
15947
+
15948
+ /* Add #text in case KEEP_CONTENT is set to true */
16301
15949
  if (KEEP_CONTENT) {
16302
15950
  ALLOWED_TAGS['#text'] = true;
16303
15951
  }
15952
+
15953
+ /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
16304
15954
  if (WHOLE_DOCUMENT) {
16305
- addToSet(ALLOWED_TAGS, [
16306
- 'html',
16307
- 'head',
16308
- 'body'
16309
- ]);
15955
+ addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
16310
15956
  }
15957
+
15958
+ /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
16311
15959
  if (ALLOWED_TAGS.table) {
16312
15960
  addToSet(ALLOWED_TAGS, ['tbody']);
16313
15961
  delete FORBID_TAGS.tbody;
@@ -16319,48 +15967,57 @@
16319
15967
  if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
16320
15968
  throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
16321
15969
  }
15970
+
15971
+ // Overwrite existing TrustedTypes policy.
16322
15972
  trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
15973
+
15974
+ // Sign local variables required by `sanitize`.
16323
15975
  emptyHTML = trustedTypesPolicy.createHTML('');
16324
15976
  } else {
15977
+ // Uninitialized policy, attempt to initialize the internal dompurify policy.
16325
15978
  if (trustedTypesPolicy === undefined) {
16326
15979
  trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
16327
15980
  }
15981
+
15982
+ // If creating the internal policy succeeded sign internal variables.
16328
15983
  if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
16329
15984
  emptyHTML = trustedTypesPolicy.createHTML('');
16330
15985
  }
16331
15986
  }
15987
+
15988
+ // Prevent further manipulation of configuration.
15989
+ // Not available in IE8, Safari 5, etc.
16332
15990
  if (freeze) {
16333
15991
  freeze(cfg);
16334
15992
  }
16335
15993
  CONFIG = cfg;
16336
15994
  };
16337
- const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [
16338
- 'mi',
16339
- 'mo',
16340
- 'mn',
16341
- 'ms',
16342
- 'mtext'
16343
- ]);
16344
- const HTML_INTEGRATION_POINTS = addToSet({}, [
16345
- 'foreignobject',
16346
- 'desc',
16347
- 'title',
16348
- 'annotation-xml'
16349
- ]);
16350
- const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [
16351
- 'title',
16352
- 'style',
16353
- 'font',
16354
- 'a',
16355
- 'script'
16356
- ]);
16357
- const ALL_SVG_TAGS = addToSet({}, svg$1);
16358
- addToSet(ALL_SVG_TAGS, svgFilters);
16359
- addToSet(ALL_SVG_TAGS, svgDisallowed);
16360
- const ALL_MATHML_TAGS = addToSet({}, mathMl$1);
16361
- addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
15995
+ const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
15996
+ const HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
15997
+
15998
+ // Certain elements are allowed in both SVG and HTML
15999
+ // namespace. We need to specify them explicitly
16000
+ // so that they don't get erroneously deleted from
16001
+ // HTML namespace.
16002
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
16003
+
16004
+ /* Keep track of all possible SVG and MathML tags
16005
+ * so that we can perform the namespace checks
16006
+ * correctly. */
16007
+ const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
16008
+ const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
16009
+
16010
+ /**
16011
+ * @param {Element} element a DOM element whose namespace is being checked
16012
+ * @returns {boolean} Return false if the element has a
16013
+ * namespace that a spec-compliant parser would never
16014
+ * return. Return true otherwise.
16015
+ */
16362
16016
  const _checkValidNamespace = function _checkValidNamespace(element) {
16363
16017
  let parent = getParentNode(element);
16018
+
16019
+ // In JSDOM, if we're inside shadow DOM, then parentNode
16020
+ // can be null. We just simulate parent in this case.
16364
16021
  if (!parent || !parent.tagName) {
16365
16022
  parent = {
16366
16023
  namespaceURI: NAMESPACE,
@@ -16373,45 +16030,93 @@
16373
16030
  return false;
16374
16031
  }
16375
16032
  if (element.namespaceURI === SVG_NAMESPACE) {
16033
+ // The only way to switch from HTML namespace to SVG
16034
+ // is via <svg>. If it happens via any other tag, then
16035
+ // it should be killed.
16376
16036
  if (parent.namespaceURI === HTML_NAMESPACE) {
16377
16037
  return tagName === 'svg';
16378
16038
  }
16039
+
16040
+ // The only way to switch from MathML to SVG is via`
16041
+ // svg if parent is either <annotation-xml> or MathML
16042
+ // text integration points.
16379
16043
  if (parent.namespaceURI === MATHML_NAMESPACE) {
16380
16044
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
16381
16045
  }
16046
+
16047
+ // We only allow elements that are defined in SVG
16048
+ // spec. All others are disallowed in SVG namespace.
16382
16049
  return Boolean(ALL_SVG_TAGS[tagName]);
16383
16050
  }
16384
16051
  if (element.namespaceURI === MATHML_NAMESPACE) {
16052
+ // The only way to switch from HTML namespace to MathML
16053
+ // is via <math>. If it happens via any other tag, then
16054
+ // it should be killed.
16385
16055
  if (parent.namespaceURI === HTML_NAMESPACE) {
16386
16056
  return tagName === 'math';
16387
16057
  }
16058
+
16059
+ // The only way to switch from SVG to MathML is via
16060
+ // <math> and HTML integration points
16388
16061
  if (parent.namespaceURI === SVG_NAMESPACE) {
16389
16062
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
16390
16063
  }
16064
+
16065
+ // We only allow elements that are defined in MathML
16066
+ // spec. All others are disallowed in MathML namespace.
16391
16067
  return Boolean(ALL_MATHML_TAGS[tagName]);
16392
16068
  }
16393
16069
  if (element.namespaceURI === HTML_NAMESPACE) {
16070
+ // The only way to switch from SVG to HTML is via
16071
+ // HTML integration points, and from MathML to HTML
16072
+ // is via MathML text integration points
16394
16073
  if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
16395
16074
  return false;
16396
16075
  }
16397
16076
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
16398
16077
  return false;
16399
16078
  }
16079
+
16080
+ // We disallow tags that are specific for MathML
16081
+ // or SVG and should never appear in HTML namespace
16400
16082
  return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
16401
16083
  }
16084
+
16085
+ // For XHTML and XML documents that support custom namespaces
16402
16086
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
16403
16087
  return true;
16404
16088
  }
16089
+
16090
+ // The code should never reach this place (this means
16091
+ // that the element somehow got namespace that is not
16092
+ // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
16093
+ // Return false just in case.
16405
16094
  return false;
16406
16095
  };
16096
+
16097
+ /**
16098
+ * _forceRemove
16099
+ *
16100
+ * @param {Node} node a DOM node
16101
+ */
16407
16102
  const _forceRemove = function _forceRemove(node) {
16408
- arrayPush(DOMPurify.removed, { element: node });
16103
+ arrayPush(DOMPurify.removed, {
16104
+ element: node
16105
+ });
16409
16106
  try {
16410
- node.parentNode.removeChild(node);
16107
+ // eslint-disable-next-line unicorn/prefer-dom-node-remove
16108
+ getParentNode(node).removeChild(node);
16411
16109
  } catch (_) {
16412
- node.remove();
16110
+ remove(node);
16413
16111
  }
16414
16112
  };
16113
+
16114
+ /**
16115
+ * _removeAttribute
16116
+ *
16117
+ * @param {String} name an Attribute name
16118
+ * @param {Node} node a DOM node
16119
+ */
16415
16120
  const _removeAttribute = function _removeAttribute(name, node) {
16416
16121
  try {
16417
16122
  arrayPush(DOMPurify.removed, {
@@ -16425,64 +16130,114 @@
16425
16130
  });
16426
16131
  }
16427
16132
  node.removeAttribute(name);
16133
+
16134
+ // We void attribute values for unremovable "is"" attributes
16428
16135
  if (name === 'is' && !ALLOWED_ATTR[name]) {
16429
16136
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
16430
16137
  try {
16431
16138
  _forceRemove(node);
16432
- } catch (_) {
16433
- }
16139
+ } catch (_) {}
16434
16140
  } else {
16435
16141
  try {
16436
16142
  node.setAttribute(name, '');
16437
- } catch (_) {
16438
- }
16143
+ } catch (_) {}
16439
16144
  }
16440
16145
  }
16441
16146
  };
16147
+
16148
+ /**
16149
+ * _initDocument
16150
+ *
16151
+ * @param {String} dirty a string of dirty markup
16152
+ * @return {Document} a DOM, filled with the dirty markup
16153
+ */
16442
16154
  const _initDocument = function _initDocument(dirty) {
16443
- let doc;
16444
- let leadingWhitespace;
16155
+ /* Create a HTML document */
16156
+ let doc = null;
16157
+ let leadingWhitespace = null;
16445
16158
  if (FORCE_BODY) {
16446
16159
  dirty = '<remove></remove>' + dirty;
16447
16160
  } else {
16161
+ /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
16448
16162
  const matches = stringMatch(dirty, /^[\r\n\t ]+/);
16449
16163
  leadingWhitespace = matches && matches[0];
16450
16164
  }
16451
16165
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
16166
+ // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
16452
16167
  dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
16453
16168
  }
16454
16169
  const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
16170
+ /*
16171
+ * Use the DOMParser API by default, fallback later if needs be
16172
+ * DOMParser not work for svg when has multiple root element.
16173
+ */
16455
16174
  if (NAMESPACE === HTML_NAMESPACE) {
16456
16175
  try {
16457
16176
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
16458
- } catch (_) {
16459
- }
16177
+ } catch (_) {}
16460
16178
  }
16179
+
16180
+ /* Use createHTMLDocument in case DOMParser is not available */
16461
16181
  if (!doc || !doc.documentElement) {
16462
16182
  doc = implementation.createDocument(NAMESPACE, 'template', null);
16463
16183
  try {
16464
16184
  doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
16465
16185
  } catch (_) {
16186
+ // Syntax error if dirtyPayload is invalid xml
16466
16187
  }
16467
16188
  }
16468
16189
  const body = doc.body || doc.documentElement;
16469
16190
  if (dirty && leadingWhitespace) {
16470
16191
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
16471
16192
  }
16193
+
16194
+ /* Work on whole document or just its body */
16472
16195
  if (NAMESPACE === HTML_NAMESPACE) {
16473
16196
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
16474
16197
  }
16475
16198
  return WHOLE_DOCUMENT ? doc.documentElement : body;
16476
16199
  };
16477
- const _createIterator = function _createIterator(root) {
16478
- return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
16479
- };
16200
+
16201
+ /**
16202
+ * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
16203
+ *
16204
+ * @param {Node} root The root element or node to start traversing on.
16205
+ * @return {NodeIterator} The created NodeIterator
16206
+ */
16207
+ const _createNodeIterator = function _createNodeIterator(root) {
16208
+ return createNodeIterator.call(root.ownerDocument || root, root,
16209
+ // eslint-disable-next-line no-bitwise
16210
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
16211
+ };
16212
+
16213
+ /**
16214
+ * _isClobbered
16215
+ *
16216
+ * @param {Node} elm element to check for clobbering attacks
16217
+ * @return {Boolean} true if clobbered, false if safe
16218
+ */
16480
16219
  const _isClobbered = function _isClobbered(elm) {
16481
16220
  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' || typeof elm.hasChildNodes !== 'function');
16482
16221
  };
16222
+
16223
+ /**
16224
+ * Checks whether the given object is a DOM node.
16225
+ *
16226
+ * @param {Node} object object to check whether it's a DOM node
16227
+ * @return {Boolean} true is object is a DOM node
16228
+ */
16483
16229
  const _isNode = function _isNode(object) {
16484
- return typeof Node === 'object' ? object instanceof Node : object && typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
16230
+ return typeof Node === 'function' && object instanceof Node;
16485
16231
  };
16232
+
16233
+ /**
16234
+ * _executeHook
16235
+ * Execute user configurable hooks
16236
+ *
16237
+ * @param {String} entryPoint Name of the hook's entry point
16238
+ * @param {Node} currentNode node to work on with the hook
16239
+ * @param {Object} data additional hook parameters
16240
+ */
16486
16241
  const _executeHook = function _executeHook(entryPoint, currentNode, data) {
16487
16242
  if (!hooks[entryPoint]) {
16488
16243
  return;
@@ -16491,93 +16246,184 @@
16491
16246
  hook.call(DOMPurify, currentNode, data, CONFIG);
16492
16247
  });
16493
16248
  };
16249
+
16250
+ /**
16251
+ * _sanitizeElements
16252
+ *
16253
+ * @protect nodeName
16254
+ * @protect textContent
16255
+ * @protect removeChild
16256
+ *
16257
+ * @param {Node} currentNode to check for permission to exist
16258
+ * @return {Boolean} true if node was killed, false if left alive
16259
+ */
16494
16260
  const _sanitizeElements = function _sanitizeElements(currentNode) {
16495
- let content;
16261
+ let content = null;
16262
+
16263
+ /* Execute a hook if present */
16496
16264
  _executeHook('beforeSanitizeElements', currentNode, null);
16265
+
16266
+ /* Check if element is clobbered or can clobber */
16497
16267
  if (_isClobbered(currentNode)) {
16498
16268
  _forceRemove(currentNode);
16499
16269
  return true;
16500
16270
  }
16271
+
16272
+ /* Now let's check the element's type and name */
16501
16273
  const tagName = transformCaseFunc(currentNode.nodeName);
16274
+
16275
+ /* Execute a hook if present */
16502
16276
  _executeHook('uponSanitizeElement', currentNode, {
16503
16277
  tagName,
16504
16278
  allowedTags: ALLOWED_TAGS
16505
16279
  });
16506
- if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
16280
+
16281
+ /* Detect mXSS attempts abusing namespace confusion */
16282
+ if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
16507
16283
  _forceRemove(currentNode);
16508
16284
  return true;
16509
16285
  }
16286
+
16287
+ /* Remove any occurrence of processing instructions */
16288
+ if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
16289
+ _forceRemove(currentNode);
16290
+ return true;
16291
+ }
16292
+
16293
+ /* Remove any kind of possibly harmful comments */
16294
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
16295
+ _forceRemove(currentNode);
16296
+ return true;
16297
+ }
16298
+
16299
+ /* Remove element if anything forbids its presence */
16510
16300
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
16511
- if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
16512
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName))
16301
+ /* Check if we have a custom element to handle */
16302
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
16303
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
16513
16304
  return false;
16514
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName))
16305
+ }
16306
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
16515
16307
  return false;
16308
+ }
16516
16309
  }
16310
+
16311
+ /* Keep content except for bad-listed elements */
16517
16312
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
16518
16313
  const parentNode = getParentNode(currentNode) || currentNode.parentNode;
16519
16314
  const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
16520
16315
  if (childNodes && parentNode) {
16521
16316
  const childCount = childNodes.length;
16522
16317
  for (let i = childCount - 1; i >= 0; --i) {
16523
- parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
16318
+ const childClone = cloneNode(childNodes[i], true);
16319
+ childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
16320
+ parentNode.insertBefore(childClone, getNextSibling(currentNode));
16524
16321
  }
16525
16322
  }
16526
16323
  }
16527
16324
  _forceRemove(currentNode);
16528
16325
  return true;
16529
16326
  }
16327
+
16328
+ /* Check whether element has a valid namespace */
16530
16329
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
16531
16330
  _forceRemove(currentNode);
16532
16331
  return true;
16533
16332
  }
16333
+
16334
+ /* Make sure that older browsers don't get fallback-tag mXSS */
16534
16335
  if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
16535
16336
  _forceRemove(currentNode);
16536
16337
  return true;
16537
16338
  }
16538
- if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
16339
+
16340
+ /* Sanitize element content to be template-safe */
16341
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
16342
+ /* Get the element's text content */
16539
16343
  content = currentNode.textContent;
16540
- content = stringReplace(content, MUSTACHE_EXPR, ' ');
16541
- content = stringReplace(content, ERB_EXPR, ' ');
16542
- content = stringReplace(content, TMPLIT_EXPR, ' ');
16344
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
16345
+ content = stringReplace(content, expr, ' ');
16346
+ });
16543
16347
  if (currentNode.textContent !== content) {
16544
- arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
16348
+ arrayPush(DOMPurify.removed, {
16349
+ element: currentNode.cloneNode()
16350
+ });
16545
16351
  currentNode.textContent = content;
16546
16352
  }
16547
16353
  }
16354
+
16355
+ /* Execute a hook if present */
16548
16356
  _executeHook('afterSanitizeElements', currentNode, null);
16549
16357
  return false;
16550
16358
  };
16359
+
16360
+ /**
16361
+ * _isValidAttribute
16362
+ *
16363
+ * @param {string} lcTag Lowercase tag name of containing element.
16364
+ * @param {string} lcName Lowercase attribute name.
16365
+ * @param {string} value Attribute value.
16366
+ * @return {Boolean} Returns true if `value` is valid, otherwise false.
16367
+ */
16368
+ // eslint-disable-next-line complexity
16551
16369
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
16370
+ /* Make sure attribute cannot clobber */
16552
16371
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
16553
16372
  return false;
16554
16373
  }
16555
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName));
16556
- else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName));
16557
- else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
16558
- if (_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)) || 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)));
16559
- else {
16374
+
16375
+ /* Allow valid data-* attributes: At least one character after "-"
16376
+ (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
16377
+ XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
16378
+ We don't need to check the value; it's always URI safe. */
16379
+ if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
16380
+ if (
16381
+ // First condition does a very basic check if a) it's basically a valid custom element tagname AND
16382
+ // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
16383
+ // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
16384
+ _isBasicCustomElement(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)) ||
16385
+ // Alternative, second condition checks if it's an `is`-attribute, AND
16386
+ // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
16387
+ 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 {
16560
16388
  return false;
16561
16389
  }
16562
- } else if (URI_SAFE_ATTRIBUTES[lcName]);
16563
- else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, '')));
16564
- else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]);
16565
- else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, '')));
16566
- else if (value) {
16390
+ /* Check value is safe. First, is attr inert? If so, is safe */
16391
+ } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; 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, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
16567
16392
  return false;
16568
16393
  } else ;
16569
16394
  return true;
16570
16395
  };
16571
- const _basicCustomElementTest = function _basicCustomElementTest(tagName) {
16572
- return tagName.indexOf('-') > 0;
16573
- };
16396
+
16397
+ /**
16398
+ * _isBasicCustomElement
16399
+ * checks if at least one dash is included in tagName, and it's not the first char
16400
+ * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
16401
+ *
16402
+ * @param {string} tagName name of the tag of the node to sanitize
16403
+ * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
16404
+ */
16405
+ const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
16406
+ return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
16407
+ };
16408
+
16409
+ /**
16410
+ * _sanitizeAttributes
16411
+ *
16412
+ * @protect attributes
16413
+ * @protect nodeName
16414
+ * @protect removeAttribute
16415
+ * @protect setAttribute
16416
+ *
16417
+ * @param {Node} currentNode to sanitize
16418
+ */
16574
16419
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
16575
- let attr;
16576
- let value;
16577
- let lcName;
16578
- let l;
16420
+ /* Execute a hook if present */
16579
16421
  _executeHook('beforeSanitizeAttributes', currentNode, null);
16580
- const {attributes} = currentNode;
16422
+ const {
16423
+ attributes
16424
+ } = currentNode;
16425
+
16426
+ /* Check if we have attributes; if not we might have a text node */
16581
16427
  if (!attributes) {
16582
16428
  return;
16583
16429
  }
@@ -16587,99 +16433,174 @@
16587
16433
  keepAttr: true,
16588
16434
  allowedAttributes: ALLOWED_ATTR
16589
16435
  };
16590
- l = attributes.length;
16436
+ let l = attributes.length;
16437
+
16438
+ /* Go backwards over all attributes; safely remove bad ones */
16591
16439
  while (l--) {
16592
- attr = attributes[l];
16593
- const {name, namespaceURI} = attr;
16594
- value = name === 'value' ? attr.value : stringTrim(attr.value);
16440
+ const attr = attributes[l];
16441
+ const {
16442
+ name,
16443
+ namespaceURI,
16444
+ value: attrValue
16445
+ } = attr;
16446
+ const lcName = transformCaseFunc(name);
16447
+ let value = name === 'value' ? attrValue : stringTrim(attrValue);
16595
16448
  const initValue = value;
16596
- lcName = transformCaseFunc(name);
16449
+
16450
+ /* Execute a hook if present */
16597
16451
  hookEvent.attrName = lcName;
16598
16452
  hookEvent.attrValue = value;
16599
16453
  hookEvent.keepAttr = true;
16600
- hookEvent.forceKeepAttr = undefined;
16454
+ hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
16601
16455
  _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
16602
16456
  value = hookEvent.attrValue;
16457
+
16458
+ /* Did the hooks approve of the attribute? */
16603
16459
  if (hookEvent.forceKeepAttr) {
16604
16460
  continue;
16605
16461
  }
16462
+
16463
+ /* Remove attribute */
16464
+
16465
+ /* Did the hooks approve of the attribute? */
16606
16466
  if (!hookEvent.keepAttr) {
16607
16467
  _removeAttribute(name, currentNode);
16608
16468
  continue;
16609
16469
  }
16470
+
16471
+ /* Work around a security issue in jQuery 3.0 */
16610
16472
  if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
16611
16473
  _removeAttribute(name, currentNode);
16612
16474
  continue;
16613
16475
  }
16476
+
16477
+ /* Sanitize attribute content to be template-safe */
16614
16478
  if (SAFE_FOR_TEMPLATES) {
16615
- value = stringReplace(value, MUSTACHE_EXPR, ' ');
16616
- value = stringReplace(value, ERB_EXPR, ' ');
16617
- value = stringReplace(value, TMPLIT_EXPR, ' ');
16479
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
16480
+ value = stringReplace(value, expr, ' ');
16481
+ });
16618
16482
  }
16483
+
16484
+ /* Is `value` valid for this attribute? */
16619
16485
  const lcTag = transformCaseFunc(currentNode.nodeName);
16620
16486
  if (!_isValidAttribute(lcTag, lcName, value)) {
16621
16487
  _removeAttribute(name, currentNode);
16622
16488
  continue;
16623
16489
  }
16490
+
16491
+ /* Full DOM Clobbering protection via namespace isolation,
16492
+ * Prefix id and name attributes with `user-content-`
16493
+ */
16624
16494
  if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
16495
+ // Remove the attribute with this value
16625
16496
  _removeAttribute(name, currentNode);
16497
+
16498
+ // Prefix the value and later re-create the attribute with the sanitized value
16626
16499
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
16627
16500
  }
16501
+
16502
+ /* Work around a security issue with comments inside attributes */
16503
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
16504
+ _removeAttribute(name, currentNode);
16505
+ continue;
16506
+ }
16507
+
16508
+ /* Handle attributes that require Trusted Types */
16628
16509
  if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
16629
- if (namespaceURI);
16630
- else {
16510
+ if (namespaceURI) ; else {
16631
16511
  switch (trustedTypes.getAttributeType(lcTag, lcName)) {
16632
- case 'TrustedHTML': {
16633
- value = trustedTypesPolicy.createHTML(value);
16634
- break;
16635
- }
16636
- case 'TrustedScriptURL': {
16637
- value = trustedTypesPolicy.createScriptURL(value);
16638
- break;
16639
- }
16512
+ case 'TrustedHTML':
16513
+ {
16514
+ value = trustedTypesPolicy.createHTML(value);
16515
+ break;
16516
+ }
16517
+ case 'TrustedScriptURL':
16518
+ {
16519
+ value = trustedTypesPolicy.createScriptURL(value);
16520
+ break;
16521
+ }
16640
16522
  }
16641
16523
  }
16642
16524
  }
16525
+
16526
+ /* Handle invalid data-* attribute set by try-catching it */
16643
16527
  if (value !== initValue) {
16644
16528
  try {
16645
16529
  if (namespaceURI) {
16646
16530
  currentNode.setAttributeNS(namespaceURI, name, value);
16647
16531
  } else {
16532
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
16648
16533
  currentNode.setAttribute(name, value);
16649
16534
  }
16650
- } catch (_) {
16651
- _removeAttribute(name, currentNode);
16652
- }
16535
+ if (_isClobbered(currentNode)) {
16536
+ _forceRemove(currentNode);
16537
+ } else {
16538
+ arrayPop(DOMPurify.removed);
16539
+ }
16540
+ } catch (_) {}
16653
16541
  }
16654
16542
  }
16543
+
16544
+ /* Execute a hook if present */
16655
16545
  _executeHook('afterSanitizeAttributes', currentNode, null);
16656
16546
  };
16547
+
16548
+ /**
16549
+ * _sanitizeShadowDOM
16550
+ *
16551
+ * @param {DocumentFragment} fragment to iterate over recursively
16552
+ */
16657
16553
  const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
16658
- let shadowNode;
16659
- const shadowIterator = _createIterator(fragment);
16554
+ let shadowNode = null;
16555
+ const shadowIterator = _createNodeIterator(fragment);
16556
+
16557
+ /* Execute a hook if present */
16660
16558
  _executeHook('beforeSanitizeShadowDOM', fragment, null);
16661
16559
  while (shadowNode = shadowIterator.nextNode()) {
16560
+ /* Execute a hook if present */
16662
16561
  _executeHook('uponSanitizeShadowNode', shadowNode, null);
16562
+
16563
+ /* Sanitize tags and elements */
16663
16564
  if (_sanitizeElements(shadowNode)) {
16664
16565
  continue;
16665
16566
  }
16567
+
16568
+ /* Deep shadow DOM detected */
16666
16569
  if (shadowNode.content instanceof DocumentFragment) {
16667
16570
  _sanitizeShadowDOM(shadowNode.content);
16668
16571
  }
16572
+
16573
+ /* Check attributes, sanitize if necessary */
16669
16574
  _sanitizeAttributes(shadowNode);
16670
16575
  }
16576
+
16577
+ /* Execute a hook if present */
16671
16578
  _executeHook('afterSanitizeShadowDOM', fragment, null);
16672
16579
  };
16580
+
16581
+ /**
16582
+ * Sanitize
16583
+ * Public method providing core sanitation functionality
16584
+ *
16585
+ * @param {String|Node} dirty string or DOM node
16586
+ * @param {Object} cfg object
16587
+ */
16588
+ // eslint-disable-next-line complexity
16673
16589
  DOMPurify.sanitize = function (dirty) {
16674
16590
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
16675
- let body;
16676
- let importedNode;
16677
- let currentNode;
16678
- let returnNode;
16591
+ let body = null;
16592
+ let importedNode = null;
16593
+ let currentNode = null;
16594
+ let returnNode = null;
16595
+ /* Make sure we have a string to sanitize.
16596
+ DO NOT return early, as this will return the wrong type if
16597
+ the user has requested a DOM object rather than a string */
16679
16598
  IS_EMPTY_INPUT = !dirty;
16680
16599
  if (IS_EMPTY_INPUT) {
16681
16600
  dirty = '<!-->';
16682
16601
  }
16602
+
16603
+ /* Stringify, in case dirty is an object */
16683
16604
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
16684
16605
  if (typeof dirty.toString === 'function') {
16685
16606
  dirty = dirty.toString();
@@ -16690,17 +16611,26 @@
16690
16611
  throw typeErrorCreate('toString is not a function');
16691
16612
  }
16692
16613
  }
16614
+
16615
+ /* Return dirty HTML if DOMPurify cannot run */
16693
16616
  if (!DOMPurify.isSupported) {
16694
16617
  return dirty;
16695
16618
  }
16619
+
16620
+ /* Assign config vars */
16696
16621
  if (!SET_CONFIG) {
16697
16622
  _parseConfig(cfg);
16698
16623
  }
16624
+
16625
+ /* Clean up removed elements */
16699
16626
  DOMPurify.removed = [];
16627
+
16628
+ /* Check if dirty is correctly typed for IN_PLACE */
16700
16629
  if (typeof dirty === 'string') {
16701
16630
  IN_PLACE = false;
16702
16631
  }
16703
16632
  if (IN_PLACE) {
16633
+ /* Do some early pre-sanitization to avoid unsafe root nodes */
16704
16634
  if (dirty.nodeName) {
16705
16635
  const tagName = transformCaseFunc(dirty.nodeName);
16706
16636
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
@@ -16708,74 +16638,138 @@
16708
16638
  }
16709
16639
  }
16710
16640
  } else if (dirty instanceof Node) {
16641
+ /* If dirty is a DOM element, append to an empty document to avoid
16642
+ elements being stripped by the parser */
16711
16643
  body = _initDocument('<!---->');
16712
16644
  importedNode = body.ownerDocument.importNode(dirty, true);
16713
- if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
16645
+ if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
16646
+ /* Node is already a body, use as is */
16714
16647
  body = importedNode;
16715
16648
  } else if (importedNode.nodeName === 'HTML') {
16716
16649
  body = importedNode;
16717
16650
  } else {
16651
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
16718
16652
  body.appendChild(importedNode);
16719
16653
  }
16720
16654
  } else {
16721
- if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) {
16655
+ /* Exit directly if we have nothing to do */
16656
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
16657
+ // eslint-disable-next-line unicorn/prefer-includes
16658
+ dirty.indexOf('<') === -1) {
16722
16659
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
16723
16660
  }
16661
+
16662
+ /* Initialize the document to work on */
16724
16663
  body = _initDocument(dirty);
16664
+
16665
+ /* Check we have a DOM node from the data */
16725
16666
  if (!body) {
16726
16667
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
16727
16668
  }
16728
16669
  }
16670
+
16671
+ /* Remove first element node (ours) if FORCE_BODY is set */
16729
16672
  if (body && FORCE_BODY) {
16730
16673
  _forceRemove(body.firstChild);
16731
16674
  }
16732
- const nodeIterator = _createIterator(IN_PLACE ? dirty : body);
16675
+
16676
+ /* Get node iterator */
16677
+ const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
16678
+
16679
+ /* Now start iterating over the created document */
16733
16680
  while (currentNode = nodeIterator.nextNode()) {
16681
+ /* Sanitize tags and elements */
16734
16682
  if (_sanitizeElements(currentNode)) {
16735
16683
  continue;
16736
16684
  }
16685
+
16686
+ /* Shadow DOM detected, sanitize it */
16737
16687
  if (currentNode.content instanceof DocumentFragment) {
16738
16688
  _sanitizeShadowDOM(currentNode.content);
16739
16689
  }
16690
+
16691
+ /* Check attributes, sanitize if necessary */
16740
16692
  _sanitizeAttributes(currentNode);
16741
16693
  }
16694
+
16695
+ /* If we sanitized `dirty` in-place, return it. */
16742
16696
  if (IN_PLACE) {
16743
16697
  return dirty;
16744
16698
  }
16699
+
16700
+ /* Return sanitized string or DOM */
16745
16701
  if (RETURN_DOM) {
16746
16702
  if (RETURN_DOM_FRAGMENT) {
16747
16703
  returnNode = createDocumentFragment.call(body.ownerDocument);
16748
16704
  while (body.firstChild) {
16705
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
16749
16706
  returnNode.appendChild(body.firstChild);
16750
16707
  }
16751
16708
  } else {
16752
16709
  returnNode = body;
16753
16710
  }
16754
16711
  if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
16712
+ /*
16713
+ AdoptNode() is not used because internal state is not reset
16714
+ (e.g. the past names map of a HTMLFormElement), this is safe
16715
+ in theory but we would rather not risk another attack vector.
16716
+ The state that is cloned by importNode() is explicitly defined
16717
+ by the specs.
16718
+ */
16755
16719
  returnNode = importNode.call(originalDocument, returnNode, true);
16756
16720
  }
16757
16721
  return returnNode;
16758
16722
  }
16759
16723
  let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
16724
+
16725
+ /* Serialize doctype if allowed */
16760
16726
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
16761
16727
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
16762
16728
  }
16729
+
16730
+ /* Sanitize final string template-safe */
16763
16731
  if (SAFE_FOR_TEMPLATES) {
16764
- serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR, ' ');
16765
- serializedHTML = stringReplace(serializedHTML, ERB_EXPR, ' ');
16766
- serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR, ' ');
16732
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
16733
+ serializedHTML = stringReplace(serializedHTML, expr, ' ');
16734
+ });
16767
16735
  }
16768
16736
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
16769
16737
  };
16770
- DOMPurify.setConfig = function (cfg) {
16738
+
16739
+ /**
16740
+ * Public method to set the configuration once
16741
+ * setConfig
16742
+ *
16743
+ * @param {Object} cfg configuration object
16744
+ */
16745
+ DOMPurify.setConfig = function () {
16746
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
16771
16747
  _parseConfig(cfg);
16772
16748
  SET_CONFIG = true;
16773
16749
  };
16750
+
16751
+ /**
16752
+ * Public method to remove the configuration
16753
+ * clearConfig
16754
+ *
16755
+ */
16774
16756
  DOMPurify.clearConfig = function () {
16775
16757
  CONFIG = null;
16776
16758
  SET_CONFIG = false;
16777
16759
  };
16760
+
16761
+ /**
16762
+ * Public method to check if an attribute value is valid.
16763
+ * Uses last set config, if any. Otherwise, uses config defaults.
16764
+ * isValidAttribute
16765
+ *
16766
+ * @param {String} tag Tag name of containing element.
16767
+ * @param {String} attr Attribute name.
16768
+ * @param {String} value Attribute value.
16769
+ * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
16770
+ */
16778
16771
  DOMPurify.isValidAttribute = function (tag, attr, value) {
16772
+ /* Initialize shared config vars if necessary. */
16779
16773
  if (!CONFIG) {
16780
16774
  _parseConfig({});
16781
16775
  }
@@ -16783,6 +16777,14 @@
16783
16777
  const lcName = transformCaseFunc(attr);
16784
16778
  return _isValidAttribute(lcTag, lcName, value);
16785
16779
  };
16780
+
16781
+ /**
16782
+ * AddHook
16783
+ * Public method to add DOMPurify hooks
16784
+ *
16785
+ * @param {String} entryPoint entry point for the hook to add
16786
+ * @param {Function} hookFunction function to execute
16787
+ */
16786
16788
  DOMPurify.addHook = function (entryPoint, hookFunction) {
16787
16789
  if (typeof hookFunction !== 'function') {
16788
16790
  return;
@@ -16790,16 +16792,37 @@
16790
16792
  hooks[entryPoint] = hooks[entryPoint] || [];
16791
16793
  arrayPush(hooks[entryPoint], hookFunction);
16792
16794
  };
16795
+
16796
+ /**
16797
+ * RemoveHook
16798
+ * Public method to remove a DOMPurify hook at a given entryPoint
16799
+ * (pops it from the stack of hooks if more are present)
16800
+ *
16801
+ * @param {String} entryPoint entry point for the hook to remove
16802
+ * @return {Function} removed(popped) hook
16803
+ */
16793
16804
  DOMPurify.removeHook = function (entryPoint) {
16794
16805
  if (hooks[entryPoint]) {
16795
16806
  return arrayPop(hooks[entryPoint]);
16796
16807
  }
16797
16808
  };
16809
+
16810
+ /**
16811
+ * RemoveHooks
16812
+ * Public method to remove all DOMPurify hooks at a given entryPoint
16813
+ *
16814
+ * @param {String} entryPoint entry point for the hooks to remove
16815
+ */
16798
16816
  DOMPurify.removeHooks = function (entryPoint) {
16799
16817
  if (hooks[entryPoint]) {
16800
16818
  hooks[entryPoint] = [];
16801
16819
  }
16802
16820
  };
16821
+
16822
+ /**
16823
+ * RemoveAllHooks
16824
+ * Public method to remove all DOMPurify hooks
16825
+ */
16803
16826
  DOMPurify.removeAllHooks = function () {
16804
16827
  hooks = {};
16805
16828
  };
@@ -17235,7 +17258,8 @@
17235
17258
  '#cdata-section',
17236
17259
  'body'
17237
17260
  ],
17238
- ALLOWED_ATTR: []
17261
+ ALLOWED_ATTR: [],
17262
+ SAFE_FOR_XML: false
17239
17263
  };
17240
17264
  const config = { ...basePurifyConfig };
17241
17265
  config.PARSER_MEDIA_TYPE = mimeType;
@@ -31592,8 +31616,8 @@
31592
31616
  documentBaseURL: null,
31593
31617
  suffix: null,
31594
31618
  majorVersion: '7',
31595
- minorVersion: '4.0',
31596
- releaseDate: '2024-10-09',
31619
+ minorVersion: '4.1',
31620
+ releaseDate: 'TBD',
31597
31621
  i18n: I18n,
31598
31622
  activeEditor: null,
31599
31623
  focusedEditor: null,