tinymce-rails 7.4.0 → 7.4.1

Sign up to get free protection for your applications and to get access to all the features.
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,