tinymce-rails 6.8.4 → 6.8.5

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