tinymce-rails 6.8.4 → 6.8.5

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