@sapui5/sap.ui.richtexteditor 1.96.36 → 1.96.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/package.json +1 -1
  2. package/src/sap/ui/richtexteditor/.library +2 -2
  3. package/src/sap/ui/richtexteditor/RTESplitButton.js +1 -1
  4. package/src/sap/ui/richtexteditor/js/tiny_mce6/models/dom/model.js +1 -1
  5. package/src/sap/ui/richtexteditor/js/tiny_mce6/models/dom/model.min.js +1 -1
  6. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/accordion/plugin.js +1 -1
  7. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/accordion/plugin.min.js +1 -1
  8. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/advlist/plugin.js +1 -1
  9. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/advlist/plugin.min.js +1 -1
  10. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/anchor/plugin.js +1 -1
  11. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/anchor/plugin.min.js +1 -1
  12. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/autolink/plugin.js +1 -1
  13. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/autolink/plugin.min.js +1 -1
  14. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/autoresize/plugin.js +1 -1
  15. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/autoresize/plugin.min.js +1 -1
  16. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/autosave/plugin.js +1 -1
  17. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/autosave/plugin.min.js +1 -1
  18. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/charmap/plugin.js +1 -1
  19. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/charmap/plugin.min.js +1 -1
  20. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/code/plugin.js +1 -1
  21. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/code/plugin.min.js +1 -1
  22. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/codesample/plugin.js +1 -1
  23. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/codesample/plugin.min.js +1 -1
  24. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/directionality/plugin.js +1 -1
  25. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/directionality/plugin.min.js +1 -1
  26. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/emoticons/plugin.js +1 -1
  27. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/emoticons/plugin.min.js +1 -1
  28. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/fullscreen/plugin.js +1 -1
  29. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/fullscreen/plugin.min.js +1 -1
  30. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/help/plugin.js +1 -1
  31. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/help/plugin.min.js +1 -1
  32. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/image/plugin.js +1 -1
  33. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/image/plugin.min.js +1 -1
  34. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/importcss/plugin.js +1 -1
  35. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/importcss/plugin.min.js +1 -1
  36. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/insertdatetime/plugin.js +1 -1
  37. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/insertdatetime/plugin.min.js +1 -1
  38. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/link/plugin.js +1 -1
  39. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/link/plugin.min.js +1 -1
  40. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/lists/plugin.js +1 -1
  41. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/lists/plugin.min.js +1 -1
  42. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/media/plugin.js +1 -1
  43. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/media/plugin.min.js +1 -1
  44. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/nonbreaking/plugin.js +1 -1
  45. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/nonbreaking/plugin.min.js +1 -1
  46. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/pagebreak/plugin.js +1 -1
  47. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/pagebreak/plugin.min.js +1 -1
  48. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/powerpaste/CHANGELOG.md +2 -0
  49. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/powerpaste/js/wordimport.js +1 -1
  50. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/powerpaste/plugin.js +1 -1
  51. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/powerpaste/plugin.min.js +1 -1
  52. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/powerpaste/readme.txt +1 -1
  53. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/powerpaste/version.txt +1 -1
  54. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/preview/plugin.js +1 -1
  55. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/preview/plugin.min.js +1 -1
  56. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/quickbars/plugin.js +1 -1
  57. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/quickbars/plugin.min.js +1 -1
  58. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/save/plugin.js +1 -1
  59. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/save/plugin.min.js +1 -1
  60. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/searchreplace/plugin.js +1 -1
  61. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/searchreplace/plugin.min.js +1 -1
  62. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/table/plugin.js +1 -1
  63. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/table/plugin.min.js +1 -1
  64. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/template/plugin.js +1 -1
  65. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/template/plugin.min.js +1 -1
  66. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/visualblocks/plugin.js +1 -1
  67. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/visualblocks/plugin.min.js +1 -1
  68. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/visualchars/plugin.js +1 -1
  69. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/visualchars/plugin.min.js +1 -1
  70. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/wordcount/plugin.js +1 -1
  71. package/src/sap/ui/richtexteditor/js/tiny_mce6/plugins/wordcount/plugin.min.js +1 -1
  72. package/src/sap/ui/richtexteditor/js/tiny_mce6/readme.txt +1 -1
  73. package/src/sap/ui/richtexteditor/js/tiny_mce6/themes/silver/theme.js +886 -865
  74. package/src/sap/ui/richtexteditor/js/tiny_mce6/themes/silver/theme.min.js +382 -2
  75. package/src/sap/ui/richtexteditor/js/tiny_mce6/tinymce.js +889 -867
  76. package/src/sap/ui/richtexteditor/js/tiny_mce6/tinymce.min.js +382 -2
  77. package/src/sap/ui/richtexteditor/js/tiny_mce6/version.txt +1 -1
  78. package/src/sap/ui/richtexteditor/library.js +1 -1
@@ -4,7 +4,7 @@
4
4
  * Copyright (c) 2024 Ephox Corporation DBA Tiny Technologies, Inc.
5
5
  * Licensed under the Tiny commercial license. See https://www.tiny.cloud/legal/
6
6
  *
7
- * Version: 6.8.4
7
+ * Version: 6.8.5
8
8
  */
9
9
 
10
10
  (function () {
@@ -8106,14 +8106,24 @@
8106
8106
  };
8107
8107
  };
8108
8108
 
8109
- const {entries, setPrototypeOf, isFrozen, getPrototypeOf, getOwnPropertyDescriptor} = Object;
8110
- let {freeze, seal, create: create$1} = Object;
8111
- let {apply, construct} = typeof Reflect !== 'undefined' && Reflect;
8112
- if (!apply) {
8113
- apply = function apply(fun, thisValue, args) {
8114
- return fun.apply(thisValue, args);
8115
- };
8116
- }
8109
+ /*! @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 */
8110
+
8111
+ const {
8112
+ entries,
8113
+ setPrototypeOf,
8114
+ isFrozen,
8115
+ getPrototypeOf,
8116
+ getOwnPropertyDescriptor
8117
+ } = Object;
8118
+ let {
8119
+ freeze,
8120
+ seal,
8121
+ create: create$1
8122
+ } = Object; // eslint-disable-line import/no-mutable-exports
8123
+ let {
8124
+ apply,
8125
+ construct
8126
+ } = typeof Reflect !== 'undefined' && Reflect;
8117
8127
  if (!freeze) {
8118
8128
  freeze = function freeze(x) {
8119
8129
  return x;
@@ -8124,6 +8134,11 @@
8124
8134
  return x;
8125
8135
  };
8126
8136
  }
8137
+ if (!apply) {
8138
+ apply = function apply(fun, thisValue, args) {
8139
+ return fun.apply(thisValue, args);
8140
+ };
8141
+ }
8127
8142
  if (!construct) {
8128
8143
  construct = function construct(Func, args) {
8129
8144
  return new Func(...args);
@@ -8138,8 +8153,16 @@
8138
8153
  const stringReplace = unapply(String.prototype.replace);
8139
8154
  const stringIndexOf = unapply(String.prototype.indexOf);
8140
8155
  const stringTrim = unapply(String.prototype.trim);
8156
+ const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
8141
8157
  const regExpTest = unapply(RegExp.prototype.test);
8142
8158
  const typeErrorCreate = unconstruct(TypeError);
8159
+
8160
+ /**
8161
+ * Creates a new function that calls the given function with a specified thisArg and arguments.
8162
+ *
8163
+ * @param {Function} func - The function to be wrapped and called.
8164
+ * @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
8165
+ */
8143
8166
  function unapply(func) {
8144
8167
  return function (thisArg) {
8145
8168
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
@@ -8148,6 +8171,13 @@
8148
8171
  return apply(func, thisArg, args);
8149
8172
  };
8150
8173
  }
8174
+
8175
+ /**
8176
+ * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
8177
+ *
8178
+ * @param {Function} func - The constructor function to be wrapped and called.
8179
+ * @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
8180
+ */
8151
8181
  function unconstruct(func) {
8152
8182
  return function () {
8153
8183
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
@@ -8156,10 +8186,21 @@
8156
8186
  return construct(func, args);
8157
8187
  };
8158
8188
  }
8159
- function addToSet(set, array, transformCaseFunc) {
8160
- var _transformCaseFunc;
8161
- transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase;
8189
+
8190
+ /**
8191
+ * Add properties to a lookup table
8192
+ *
8193
+ * @param {Object} set - The set to which elements will be added.
8194
+ * @param {Array} array - The array containing elements to be added to the set.
8195
+ * @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
8196
+ * @returns {Object} The modified set with added elements.
8197
+ */
8198
+ function addToSet(set, array) {
8199
+ let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
8162
8200
  if (setPrototypeOf) {
8201
+ // Make 'in' and truthy checks like Boolean(set.constructor)
8202
+ // independent of any properties defined on Object.prototype.
8203
+ // Prevent prototype setters from intercepting set as a this value.
8163
8204
  setPrototypeOf(set, null);
8164
8205
  }
8165
8206
  let l = array.length;
@@ -8168,6 +8209,7 @@
8168
8209
  if (typeof element === 'string') {
8169
8210
  const lcElement = transformCaseFunc(element);
8170
8211
  if (lcElement !== element) {
8212
+ // Config presets (e.g. tags.js, attrs.js) are immutable.
8171
8213
  if (!isFrozen(array)) {
8172
8214
  array[l] = lcElement;
8173
8215
  }
@@ -8178,13 +8220,53 @@
8178
8220
  }
8179
8221
  return set;
8180
8222
  }
8223
+
8224
+ /**
8225
+ * Clean up an array to harden against CSPP
8226
+ *
8227
+ * @param {Array} array - The array to be cleaned.
8228
+ * @returns {Array} The cleaned version of the array
8229
+ */
8230
+ function cleanArray(array) {
8231
+ for (let index = 0; index < array.length; index++) {
8232
+ const isPropertyExist = objectHasOwnProperty(array, index);
8233
+ if (!isPropertyExist) {
8234
+ array[index] = null;
8235
+ }
8236
+ }
8237
+ return array;
8238
+ }
8239
+
8240
+ /**
8241
+ * Shallow clone an object
8242
+ *
8243
+ * @param {Object} object - The object to be cloned.
8244
+ * @returns {Object} A new object that copies the original.
8245
+ */
8181
8246
  function clone(object) {
8182
8247
  const newObject = create$1(null);
8183
8248
  for (const [property, value] of entries(object)) {
8184
- newObject[property] = value;
8249
+ const isPropertyExist = objectHasOwnProperty(object, property);
8250
+ if (isPropertyExist) {
8251
+ if (Array.isArray(value)) {
8252
+ newObject[property] = cleanArray(value);
8253
+ } else if (value && typeof value === 'object' && value.constructor === Object) {
8254
+ newObject[property] = clone(value);
8255
+ } else {
8256
+ newObject[property] = value;
8257
+ }
8258
+ }
8185
8259
  }
8186
8260
  return newObject;
8187
8261
  }
8262
+
8263
+ /**
8264
+ * This method automatically checks if the prop is function or getter and behaves accordingly.
8265
+ *
8266
+ * @param {Object} object - The object to look up the getter function in its prototype chain.
8267
+ * @param {String} prop - The property name for which to find the getter function.
8268
+ * @returns {Function} The getter function found in the prototype chain or a fallback function.
8269
+ */
8188
8270
  function lookupGetter(object, prop) {
8189
8271
  while (object !== null) {
8190
8272
  const desc = getOwnPropertyDescriptor(object, prop);
@@ -8198,644 +8280,50 @@
8198
8280
  }
8199
8281
  object = getPrototypeOf(object);
8200
8282
  }
8201
- function fallbackValue(element) {
8202
- console.warn('fallback value for', element);
8283
+ function fallbackValue() {
8203
8284
  return null;
8204
8285
  }
8205
8286
  return fallbackValue;
8206
8287
  }
8207
- const html$1 = freeze([
8208
- 'a',
8209
- 'abbr',
8210
- 'acronym',
8211
- 'address',
8212
- 'area',
8213
- 'article',
8214
- 'aside',
8215
- 'audio',
8216
- 'b',
8217
- 'bdi',
8218
- 'bdo',
8219
- 'big',
8220
- 'blink',
8221
- 'blockquote',
8222
- 'body',
8223
- 'br',
8224
- 'button',
8225
- 'canvas',
8226
- 'caption',
8227
- 'center',
8228
- 'cite',
8229
- 'code',
8230
- 'col',
8231
- 'colgroup',
8232
- 'content',
8233
- 'data',
8234
- 'datalist',
8235
- 'dd',
8236
- 'decorator',
8237
- 'del',
8238
- 'details',
8239
- 'dfn',
8240
- 'dialog',
8241
- 'dir',
8242
- 'div',
8243
- 'dl',
8244
- 'dt',
8245
- 'element',
8246
- 'em',
8247
- 'fieldset',
8248
- 'figcaption',
8249
- 'figure',
8250
- 'font',
8251
- 'footer',
8252
- 'form',
8253
- 'h1',
8254
- 'h2',
8255
- 'h3',
8256
- 'h4',
8257
- 'h5',
8258
- 'h6',
8259
- 'head',
8260
- 'header',
8261
- 'hgroup',
8262
- 'hr',
8263
- 'html',
8264
- 'i',
8265
- 'img',
8266
- 'input',
8267
- 'ins',
8268
- 'kbd',
8269
- 'label',
8270
- 'legend',
8271
- 'li',
8272
- 'main',
8273
- 'map',
8274
- 'mark',
8275
- 'marquee',
8276
- 'menu',
8277
- 'menuitem',
8278
- 'meter',
8279
- 'nav',
8280
- 'nobr',
8281
- 'ol',
8282
- 'optgroup',
8283
- 'option',
8284
- 'output',
8285
- 'p',
8286
- 'picture',
8287
- 'pre',
8288
- 'progress',
8289
- 'q',
8290
- 'rp',
8291
- 'rt',
8292
- 'ruby',
8293
- 's',
8294
- 'samp',
8295
- 'section',
8296
- 'select',
8297
- 'shadow',
8298
- 'small',
8299
- 'source',
8300
- 'spacer',
8301
- 'span',
8302
- 'strike',
8303
- 'strong',
8304
- 'style',
8305
- 'sub',
8306
- 'summary',
8307
- 'sup',
8308
- 'table',
8309
- 'tbody',
8310
- 'td',
8311
- 'template',
8312
- 'textarea',
8313
- 'tfoot',
8314
- 'th',
8315
- 'thead',
8316
- 'time',
8317
- 'tr',
8318
- 'track',
8319
- 'tt',
8320
- 'u',
8321
- 'ul',
8322
- 'var',
8323
- 'video',
8324
- 'wbr'
8325
- ]);
8326
- const svg$1 = freeze([
8327
- 'svg',
8328
- 'a',
8329
- 'altglyph',
8330
- 'altglyphdef',
8331
- 'altglyphitem',
8332
- 'animatecolor',
8333
- 'animatemotion',
8334
- 'animatetransform',
8335
- 'circle',
8336
- 'clippath',
8337
- 'defs',
8338
- 'desc',
8339
- 'ellipse',
8340
- 'filter',
8341
- 'font',
8342
- 'g',
8343
- 'glyph',
8344
- 'glyphref',
8345
- 'hkern',
8346
- 'image',
8347
- 'line',
8348
- 'lineargradient',
8349
- 'marker',
8350
- 'mask',
8351
- 'metadata',
8352
- 'mpath',
8353
- 'path',
8354
- 'pattern',
8355
- 'polygon',
8356
- 'polyline',
8357
- 'radialgradient',
8358
- 'rect',
8359
- 'stop',
8360
- 'style',
8361
- 'switch',
8362
- 'symbol',
8363
- 'text',
8364
- 'textpath',
8365
- 'title',
8366
- 'tref',
8367
- 'tspan',
8368
- 'view',
8369
- 'vkern'
8370
- ]);
8371
- const svgFilters = freeze([
8372
- 'feBlend',
8373
- 'feColorMatrix',
8374
- 'feComponentTransfer',
8375
- 'feComposite',
8376
- 'feConvolveMatrix',
8377
- 'feDiffuseLighting',
8378
- 'feDisplacementMap',
8379
- 'feDistantLight',
8380
- 'feDropShadow',
8381
- 'feFlood',
8382
- 'feFuncA',
8383
- 'feFuncB',
8384
- 'feFuncG',
8385
- 'feFuncR',
8386
- 'feGaussianBlur',
8387
- 'feImage',
8388
- 'feMerge',
8389
- 'feMergeNode',
8390
- 'feMorphology',
8391
- 'feOffset',
8392
- 'fePointLight',
8393
- 'feSpecularLighting',
8394
- 'feSpotLight',
8395
- 'feTile',
8396
- 'feTurbulence'
8397
- ]);
8398
- const svgDisallowed = freeze([
8399
- 'animate',
8400
- 'color-profile',
8401
- 'cursor',
8402
- 'discard',
8403
- 'font-face',
8404
- 'font-face-format',
8405
- 'font-face-name',
8406
- 'font-face-src',
8407
- 'font-face-uri',
8408
- 'foreignobject',
8409
- 'hatch',
8410
- 'hatchpath',
8411
- 'mesh',
8412
- 'meshgradient',
8413
- 'meshpatch',
8414
- 'meshrow',
8415
- 'missing-glyph',
8416
- 'script',
8417
- 'set',
8418
- 'solidcolor',
8419
- 'unknown',
8420
- 'use'
8421
- ]);
8422
- const mathMl$1 = freeze([
8423
- 'math',
8424
- 'menclose',
8425
- 'merror',
8426
- 'mfenced',
8427
- 'mfrac',
8428
- 'mglyph',
8429
- 'mi',
8430
- 'mlabeledtr',
8431
- 'mmultiscripts',
8432
- 'mn',
8433
- 'mo',
8434
- 'mover',
8435
- 'mpadded',
8436
- 'mphantom',
8437
- 'mroot',
8438
- 'mrow',
8439
- 'ms',
8440
- 'mspace',
8441
- 'msqrt',
8442
- 'mstyle',
8443
- 'msub',
8444
- 'msup',
8445
- 'msubsup',
8446
- 'mtable',
8447
- 'mtd',
8448
- 'mtext',
8449
- 'mtr',
8450
- 'munder',
8451
- 'munderover',
8452
- 'mprescripts'
8453
- ]);
8454
- const mathMlDisallowed = freeze([
8455
- 'maction',
8456
- 'maligngroup',
8457
- 'malignmark',
8458
- 'mlongdiv',
8459
- 'mscarries',
8460
- 'mscarry',
8461
- 'msgroup',
8462
- 'mstack',
8463
- 'msline',
8464
- 'msrow',
8465
- 'semantics',
8466
- 'annotation',
8467
- 'annotation-xml',
8468
- 'mprescripts',
8469
- 'none'
8470
- ]);
8288
+
8289
+ 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']);
8290
+
8291
+ // SVG
8292
+ 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']);
8293
+ 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']);
8294
+
8295
+ // List of SVG elements that are disallowed by default.
8296
+ // We still need to know them so that we can do namespace
8297
+ // checks properly in case one wants to add them to
8298
+ // allow-list.
8299
+ 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']);
8300
+ 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']);
8301
+
8302
+ // Similarly to SVG, we want to know all MathML elements,
8303
+ // even those that we disallow by default.
8304
+ const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
8471
8305
  const text$1 = freeze(['#text']);
8472
- const html = freeze([
8473
- 'accept',
8474
- 'action',
8475
- 'align',
8476
- 'alt',
8477
- 'autocapitalize',
8478
- 'autocomplete',
8479
- 'autopictureinpicture',
8480
- 'autoplay',
8481
- 'background',
8482
- 'bgcolor',
8483
- 'border',
8484
- 'capture',
8485
- 'cellpadding',
8486
- 'cellspacing',
8487
- 'checked',
8488
- 'cite',
8489
- 'class',
8490
- 'clear',
8491
- 'color',
8492
- 'cols',
8493
- 'colspan',
8494
- 'controls',
8495
- 'controlslist',
8496
- 'coords',
8497
- 'crossorigin',
8498
- 'datetime',
8499
- 'decoding',
8500
- 'default',
8501
- 'dir',
8502
- 'disabled',
8503
- 'disablepictureinpicture',
8504
- 'disableremoteplayback',
8505
- 'download',
8506
- 'draggable',
8507
- 'enctype',
8508
- 'enterkeyhint',
8509
- 'face',
8510
- 'for',
8511
- 'headers',
8512
- 'height',
8513
- 'hidden',
8514
- 'high',
8515
- 'href',
8516
- 'hreflang',
8517
- 'id',
8518
- 'inputmode',
8519
- 'integrity',
8520
- 'ismap',
8521
- 'kind',
8522
- 'label',
8523
- 'lang',
8524
- 'list',
8525
- 'loading',
8526
- 'loop',
8527
- 'low',
8528
- 'max',
8529
- 'maxlength',
8530
- 'media',
8531
- 'method',
8532
- 'min',
8533
- 'minlength',
8534
- 'multiple',
8535
- 'muted',
8536
- 'name',
8537
- 'nonce',
8538
- 'noshade',
8539
- 'novalidate',
8540
- 'nowrap',
8541
- 'open',
8542
- 'optimum',
8543
- 'pattern',
8544
- 'placeholder',
8545
- 'playsinline',
8546
- 'poster',
8547
- 'preload',
8548
- 'pubdate',
8549
- 'radiogroup',
8550
- 'readonly',
8551
- 'rel',
8552
- 'required',
8553
- 'rev',
8554
- 'reversed',
8555
- 'role',
8556
- 'rows',
8557
- 'rowspan',
8558
- 'spellcheck',
8559
- 'scope',
8560
- 'selected',
8561
- 'shape',
8562
- 'size',
8563
- 'sizes',
8564
- 'span',
8565
- 'srclang',
8566
- 'start',
8567
- 'src',
8568
- 'srcset',
8569
- 'step',
8570
- 'style',
8571
- 'summary',
8572
- 'tabindex',
8573
- 'title',
8574
- 'translate',
8575
- 'type',
8576
- 'usemap',
8577
- 'valign',
8578
- 'value',
8579
- 'width',
8580
- 'xmlns',
8581
- 'slot'
8582
- ]);
8583
- const svg = freeze([
8584
- 'accent-height',
8585
- 'accumulate',
8586
- 'additive',
8587
- 'alignment-baseline',
8588
- 'ascent',
8589
- 'attributename',
8590
- 'attributetype',
8591
- 'azimuth',
8592
- 'basefrequency',
8593
- 'baseline-shift',
8594
- 'begin',
8595
- 'bias',
8596
- 'by',
8597
- 'class',
8598
- 'clip',
8599
- 'clippathunits',
8600
- 'clip-path',
8601
- 'clip-rule',
8602
- 'color',
8603
- 'color-interpolation',
8604
- 'color-interpolation-filters',
8605
- 'color-profile',
8606
- 'color-rendering',
8607
- 'cx',
8608
- 'cy',
8609
- 'd',
8610
- 'dx',
8611
- 'dy',
8612
- 'diffuseconstant',
8613
- 'direction',
8614
- 'display',
8615
- 'divisor',
8616
- 'dur',
8617
- 'edgemode',
8618
- 'elevation',
8619
- 'end',
8620
- 'fill',
8621
- 'fill-opacity',
8622
- 'fill-rule',
8623
- 'filter',
8624
- 'filterunits',
8625
- 'flood-color',
8626
- 'flood-opacity',
8627
- 'font-family',
8628
- 'font-size',
8629
- 'font-size-adjust',
8630
- 'font-stretch',
8631
- 'font-style',
8632
- 'font-variant',
8633
- 'font-weight',
8634
- 'fx',
8635
- 'fy',
8636
- 'g1',
8637
- 'g2',
8638
- 'glyph-name',
8639
- 'glyphref',
8640
- 'gradientunits',
8641
- 'gradienttransform',
8642
- 'height',
8643
- 'href',
8644
- 'id',
8645
- 'image-rendering',
8646
- 'in',
8647
- 'in2',
8648
- 'k',
8649
- 'k1',
8650
- 'k2',
8651
- 'k3',
8652
- 'k4',
8653
- 'kerning',
8654
- 'keypoints',
8655
- 'keysplines',
8656
- 'keytimes',
8657
- 'lang',
8658
- 'lengthadjust',
8659
- 'letter-spacing',
8660
- 'kernelmatrix',
8661
- 'kernelunitlength',
8662
- 'lighting-color',
8663
- 'local',
8664
- 'marker-end',
8665
- 'marker-mid',
8666
- 'marker-start',
8667
- 'markerheight',
8668
- 'markerunits',
8669
- 'markerwidth',
8670
- 'maskcontentunits',
8671
- 'maskunits',
8672
- 'max',
8673
- 'mask',
8674
- 'media',
8675
- 'method',
8676
- 'mode',
8677
- 'min',
8678
- 'name',
8679
- 'numoctaves',
8680
- 'offset',
8681
- 'operator',
8682
- 'opacity',
8683
- 'order',
8684
- 'orient',
8685
- 'orientation',
8686
- 'origin',
8687
- 'overflow',
8688
- 'paint-order',
8689
- 'path',
8690
- 'pathlength',
8691
- 'patterncontentunits',
8692
- 'patterntransform',
8693
- 'patternunits',
8694
- 'points',
8695
- 'preservealpha',
8696
- 'preserveaspectratio',
8697
- 'primitiveunits',
8698
- 'r',
8699
- 'rx',
8700
- 'ry',
8701
- 'radius',
8702
- 'refx',
8703
- 'refy',
8704
- 'repeatcount',
8705
- 'repeatdur',
8706
- 'restart',
8707
- 'result',
8708
- 'rotate',
8709
- 'scale',
8710
- 'seed',
8711
- 'shape-rendering',
8712
- 'specularconstant',
8713
- 'specularexponent',
8714
- 'spreadmethod',
8715
- 'startoffset',
8716
- 'stddeviation',
8717
- 'stitchtiles',
8718
- 'stop-color',
8719
- 'stop-opacity',
8720
- 'stroke-dasharray',
8721
- 'stroke-dashoffset',
8722
- 'stroke-linecap',
8723
- 'stroke-linejoin',
8724
- 'stroke-miterlimit',
8725
- 'stroke-opacity',
8726
- 'stroke',
8727
- 'stroke-width',
8728
- 'style',
8729
- 'surfacescale',
8730
- 'systemlanguage',
8731
- 'tabindex',
8732
- 'targetx',
8733
- 'targety',
8734
- 'transform',
8735
- 'transform-origin',
8736
- 'text-anchor',
8737
- 'text-decoration',
8738
- 'text-rendering',
8739
- 'textlength',
8740
- 'type',
8741
- 'u1',
8742
- 'u2',
8743
- 'unicode',
8744
- 'values',
8745
- 'viewbox',
8746
- 'visibility',
8747
- 'version',
8748
- 'vert-adv-y',
8749
- 'vert-origin-x',
8750
- 'vert-origin-y',
8751
- 'width',
8752
- 'word-spacing',
8753
- 'wrap',
8754
- 'writing-mode',
8755
- 'xchannelselector',
8756
- 'ychannelselector',
8757
- 'x',
8758
- 'x1',
8759
- 'x2',
8760
- 'xmlns',
8761
- 'y',
8762
- 'y1',
8763
- 'y2',
8764
- 'z',
8765
- 'zoomandpan'
8766
- ]);
8767
- const mathMl = freeze([
8768
- 'accent',
8769
- 'accentunder',
8770
- 'align',
8771
- 'bevelled',
8772
- 'close',
8773
- 'columnsalign',
8774
- 'columnlines',
8775
- 'columnspan',
8776
- 'denomalign',
8777
- 'depth',
8778
- 'dir',
8779
- 'display',
8780
- 'displaystyle',
8781
- 'encoding',
8782
- 'fence',
8783
- 'frame',
8784
- 'height',
8785
- 'href',
8786
- 'id',
8787
- 'largeop',
8788
- 'length',
8789
- 'linethickness',
8790
- 'lspace',
8791
- 'lquote',
8792
- 'mathbackground',
8793
- 'mathcolor',
8794
- 'mathsize',
8795
- 'mathvariant',
8796
- 'maxsize',
8797
- 'minsize',
8798
- 'movablelimits',
8799
- 'notation',
8800
- 'numalign',
8801
- 'open',
8802
- 'rowalign',
8803
- 'rowlines',
8804
- 'rowspacing',
8805
- 'rowspan',
8806
- 'rspace',
8807
- 'rquote',
8808
- 'scriptlevel',
8809
- 'scriptminsize',
8810
- 'scriptsizemultiplier',
8811
- 'selection',
8812
- 'separator',
8813
- 'separators',
8814
- 'stretchy',
8815
- 'subscriptshift',
8816
- 'supscriptshift',
8817
- 'symmetric',
8818
- 'voffset',
8819
- 'width',
8820
- 'xmlns'
8821
- ]);
8822
- const xml = freeze([
8823
- 'xlink:href',
8824
- 'xml:id',
8825
- 'xlink:title',
8826
- 'xml:space',
8827
- 'xmlns:xlink'
8828
- ]);
8829
- const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm);
8306
+
8307
+ 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']);
8308
+ 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']);
8309
+ 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']);
8310
+ const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
8311
+
8312
+ // eslint-disable-next-line unicorn/better-regex
8313
+ const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
8830
8314
  const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
8831
8315
  const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
8832
- const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/);
8833
- const ARIA_ATTR = seal(/^aria-[\-\w]+$/);
8834
- const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i);
8316
+ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
8317
+ const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
8318
+ 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
8319
+ );
8835
8320
  const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
8836
- const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g);
8321
+ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
8322
+ );
8837
8323
  const DOCTYPE_NAME = seal(/^html$/i);
8838
- var EXPRESSIONS = Object.freeze({
8324
+ const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
8325
+
8326
+ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
8839
8327
  __proto__: null,
8840
8328
  MUSTACHE_EXPR: MUSTACHE_EXPR,
8841
8329
  ERB_EXPR: ERB_EXPR,
@@ -8845,13 +8333,47 @@
8845
8333
  IS_ALLOWED_URI: IS_ALLOWED_URI,
8846
8334
  IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
8847
8335
  ATTR_WHITESPACE: ATTR_WHITESPACE,
8848
- DOCTYPE_NAME: DOCTYPE_NAME
8849
- });
8850
- const getGlobal = () => typeof window === 'undefined' ? null : window;
8336
+ DOCTYPE_NAME: DOCTYPE_NAME,
8337
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT
8338
+ });
8339
+
8340
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
8341
+ const NODE_TYPE = {
8342
+ element: 1,
8343
+ attribute: 2,
8344
+ text: 3,
8345
+ cdataSection: 4,
8346
+ entityReference: 5,
8347
+ // Deprecated
8348
+ entityNode: 6,
8349
+ // Deprecated
8350
+ progressingInstruction: 7,
8351
+ comment: 8,
8352
+ document: 9,
8353
+ documentType: 10,
8354
+ documentFragment: 11,
8355
+ notation: 12 // Deprecated
8356
+ };
8357
+ const getGlobal = function getGlobal() {
8358
+ return typeof window === 'undefined' ? null : window;
8359
+ };
8360
+
8361
+ /**
8362
+ * Creates a no-op policy for internal use only.
8363
+ * Don't export this function outside this module!
8364
+ * @param {TrustedTypePolicyFactory} trustedTypes The policy factory.
8365
+ * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
8366
+ * @return {TrustedTypePolicy} The policy created (or null, if Trusted Types
8367
+ * are not supported or creating the policy failed).
8368
+ */
8851
8369
  const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
8852
8370
  if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
8853
8371
  return null;
8854
8372
  }
8373
+
8374
+ // Allow the callers to control the unique policy name
8375
+ // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
8376
+ // Policy creation with duplicate names throws in Trusted Types.
8855
8377
  let suffix = null;
8856
8378
  const ATTR_NAME = 'data-tt-policy-suffix';
8857
8379
  if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
@@ -8868,6 +8390,9 @@
8868
8390
  }
8869
8391
  });
8870
8392
  } catch (_) {
8393
+ // Policy creation failed (most likely another DOMPurify script has
8394
+ // already run). Skip creating the policy, as this will only cause errors
8395
+ // if TT are enforced.
8871
8396
  console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
8872
8397
  return null;
8873
8398
  }
@@ -8875,21 +8400,53 @@
8875
8400
  function createDOMPurify() {
8876
8401
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
8877
8402
  const DOMPurify = root => createDOMPurify(root);
8878
- DOMPurify.version = '3.0.5';
8403
+
8404
+ /**
8405
+ * Version label, exposed for easier checks
8406
+ * if DOMPurify is up to date or not
8407
+ */
8408
+ DOMPurify.version = '3.1.7';
8409
+
8410
+ /**
8411
+ * Array of elements that DOMPurify removed during sanitation.
8412
+ * Empty if nothing was removed.
8413
+ */
8879
8414
  DOMPurify.removed = [];
8880
- if (!window || !window.document || window.document.nodeType !== 9) {
8415
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
8416
+ // Not running in a browser, provide a factory function
8417
+ // so that you can pass your own Window
8881
8418
  DOMPurify.isSupported = false;
8882
8419
  return DOMPurify;
8883
8420
  }
8884
- const originalDocument = window.document;
8421
+ let {
8422
+ document
8423
+ } = window;
8424
+ const originalDocument = document;
8885
8425
  const currentScript = originalDocument.currentScript;
8886
- let {document} = window;
8887
- const {DocumentFragment, HTMLTemplateElement, Node, Element, NodeFilter, NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap, HTMLFormElement, DOMParser, trustedTypes} = window;
8426
+ const {
8427
+ DocumentFragment,
8428
+ HTMLTemplateElement,
8429
+ Node,
8430
+ Element,
8431
+ NodeFilter,
8432
+ NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
8433
+ HTMLFormElement,
8434
+ DOMParser,
8435
+ trustedTypes
8436
+ } = window;
8888
8437
  const ElementPrototype = Element.prototype;
8889
8438
  const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
8439
+ const remove = lookupGetter(ElementPrototype, 'remove');
8890
8440
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
8891
8441
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
8892
8442
  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
8443
+
8444
+ // As per issue #47, the web-components registry is inherited by a
8445
+ // new document created via createHTMLDocument. As per the spec
8446
+ // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
8447
+ // a new empty registry is used when creating a template contents owner
8448
+ // document, so we use that as our parent document to ensure nothing
8449
+ // is inherited.
8893
8450
  if (typeof HTMLTemplateElement === 'function') {
8894
8451
  const template = document.createElement('template');
8895
8452
  if (template.content && template.content.ownerDocument) {
@@ -8898,28 +8455,55 @@
8898
8455
  }
8899
8456
  let trustedTypesPolicy;
8900
8457
  let emptyHTML = '';
8901
- const {implementation, createNodeIterator, createDocumentFragment, getElementsByTagName} = document;
8902
- const {importNode} = originalDocument;
8458
+ const {
8459
+ implementation,
8460
+ createNodeIterator,
8461
+ createDocumentFragment,
8462
+ getElementsByTagName
8463
+ } = document;
8464
+ const {
8465
+ importNode
8466
+ } = originalDocument;
8903
8467
  let hooks = {};
8468
+
8469
+ /**
8470
+ * Expose whether this browser supports running the full DOMPurify.
8471
+ */
8904
8472
  DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
8905
- const {MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR, DATA_ATTR, ARIA_ATTR, IS_SCRIPT_OR_DATA, ATTR_WHITESPACE} = EXPRESSIONS;
8906
- let {IS_ALLOWED_URI: IS_ALLOWED_URI$1} = EXPRESSIONS;
8473
+ const {
8474
+ MUSTACHE_EXPR,
8475
+ ERB_EXPR,
8476
+ TMPLIT_EXPR,
8477
+ DATA_ATTR,
8478
+ ARIA_ATTR,
8479
+ IS_SCRIPT_OR_DATA,
8480
+ ATTR_WHITESPACE,
8481
+ CUSTOM_ELEMENT
8482
+ } = EXPRESSIONS;
8483
+ let {
8484
+ IS_ALLOWED_URI: IS_ALLOWED_URI$1
8485
+ } = EXPRESSIONS;
8486
+
8487
+ /**
8488
+ * We consider the elements and attributes below to be safe. Ideally
8489
+ * don't add any new ones but feel free to remove unwanted ones.
8490
+ */
8491
+
8492
+ /* allowed element names */
8907
8493
  let ALLOWED_TAGS = null;
8908
- const DEFAULT_ALLOWED_TAGS = addToSet({}, [
8909
- ...html$1,
8910
- ...svg$1,
8911
- ...svgFilters,
8912
- ...mathMl$1,
8913
- ...text$1
8914
- ]);
8494
+ const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text$1]);
8495
+
8496
+ /* Allowed attribute names */
8915
8497
  let ALLOWED_ATTR = null;
8916
- const DEFAULT_ALLOWED_ATTR = addToSet({}, [
8917
- ...html,
8918
- ...svg,
8919
- ...mathMl,
8920
- ...xml
8921
- ]);
8922
- let CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
8498
+ const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
8499
+
8500
+ /*
8501
+ * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
8502
+ * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
8503
+ * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
8504
+ * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
8505
+ */
8506
+ let CUSTOM_ELEMENT_HANDLING = Object.seal(create$1(null, {
8923
8507
  tagNameCheck: {
8924
8508
  writable: true,
8925
8509
  configurable: false,
@@ -8939,135 +8523,193 @@
8939
8523
  value: false
8940
8524
  }
8941
8525
  }));
8526
+
8527
+ /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
8942
8528
  let FORBID_TAGS = null;
8529
+
8530
+ /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
8943
8531
  let FORBID_ATTR = null;
8532
+
8533
+ /* Decide if ARIA attributes are okay */
8944
8534
  let ALLOW_ARIA_ATTR = true;
8535
+
8536
+ /* Decide if custom data attributes are okay */
8945
8537
  let ALLOW_DATA_ATTR = true;
8538
+
8539
+ /* Decide if unknown protocols are okay */
8946
8540
  let ALLOW_UNKNOWN_PROTOCOLS = false;
8541
+
8542
+ /* Decide if self-closing tags in attributes are allowed.
8543
+ * Usually removed due to a mXSS issue in jQuery 3.0 */
8947
8544
  let ALLOW_SELF_CLOSE_IN_ATTR = true;
8545
+
8546
+ /* Output should be safe for common template engines.
8547
+ * This means, DOMPurify removes data attributes, mustaches and ERB
8548
+ */
8948
8549
  let SAFE_FOR_TEMPLATES = false;
8550
+
8551
+ /* Output should be safe even for XML used within HTML and alike.
8552
+ * This means, DOMPurify removes comments when containing risky content.
8553
+ */
8554
+ let SAFE_FOR_XML = true;
8555
+
8556
+ /* Decide if document with <html>... should be returned */
8949
8557
  let WHOLE_DOCUMENT = false;
8558
+
8559
+ /* Track whether config is already set on this instance of DOMPurify. */
8950
8560
  let SET_CONFIG = false;
8561
+
8562
+ /* Decide if all elements (e.g. style, script) must be children of
8563
+ * document.body. By default, browsers might move them to document.head */
8951
8564
  let FORCE_BODY = false;
8565
+
8566
+ /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
8567
+ * string (or a TrustedHTML object if Trusted Types are supported).
8568
+ * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
8569
+ */
8952
8570
  let RETURN_DOM = false;
8571
+
8572
+ /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
8573
+ * string (or a TrustedHTML object if Trusted Types are supported) */
8953
8574
  let RETURN_DOM_FRAGMENT = false;
8575
+
8576
+ /* Try to return a Trusted Type object instead of a string, return a string in
8577
+ * case Trusted Types are not supported */
8954
8578
  let RETURN_TRUSTED_TYPE = false;
8579
+
8580
+ /* Output should be free from DOM clobbering attacks?
8581
+ * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
8582
+ */
8955
8583
  let SANITIZE_DOM = true;
8584
+
8585
+ /* Achieve full DOM Clobbering protection by isolating the namespace of named
8586
+ * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
8587
+ *
8588
+ * HTML/DOM spec rules that enable DOM Clobbering:
8589
+ * - Named Access on Window (§7.3.3)
8590
+ * - DOM Tree Accessors (§3.1.5)
8591
+ * - Form Element Parent-Child Relations (§4.10.3)
8592
+ * - Iframe srcdoc / Nested WindowProxies (§4.8.5)
8593
+ * - HTMLCollection (§4.2.10.2)
8594
+ *
8595
+ * Namespace isolation is implemented by prefixing `id` and `name` attributes
8596
+ * with a constant string, i.e., `user-content-`
8597
+ */
8956
8598
  let SANITIZE_NAMED_PROPS = false;
8957
8599
  const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
8600
+
8601
+ /* Keep element content when removing element? */
8958
8602
  let KEEP_CONTENT = true;
8603
+
8604
+ /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
8605
+ * of importing it into a new Document and returning a sanitized copy */
8959
8606
  let IN_PLACE = false;
8607
+
8608
+ /* Allow usage of profiles like html, svg and mathMl */
8960
8609
  let USE_PROFILES = {};
8610
+
8611
+ /* Tags to ignore content of when KEEP_CONTENT is true */
8961
8612
  let FORBID_CONTENTS = null;
8962
- const DEFAULT_FORBID_CONTENTS = addToSet({}, [
8963
- 'annotation-xml',
8964
- 'audio',
8965
- 'colgroup',
8966
- 'desc',
8967
- 'foreignobject',
8968
- 'head',
8969
- 'iframe',
8970
- 'math',
8971
- 'mi',
8972
- 'mn',
8973
- 'mo',
8974
- 'ms',
8975
- 'mtext',
8976
- 'noembed',
8977
- 'noframes',
8978
- 'noscript',
8979
- 'plaintext',
8980
- 'script',
8981
- 'style',
8982
- 'svg',
8983
- 'template',
8984
- 'thead',
8985
- 'title',
8986
- 'video',
8987
- 'xmp'
8988
- ]);
8613
+ 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']);
8614
+
8615
+ /* Tags that are safe for data: URIs */
8989
8616
  let DATA_URI_TAGS = null;
8990
- const DEFAULT_DATA_URI_TAGS = addToSet({}, [
8991
- 'audio',
8992
- 'video',
8993
- 'img',
8994
- 'source',
8995
- 'image',
8996
- 'track'
8997
- ]);
8617
+ const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
8618
+
8619
+ /* Attributes safe for values like "javascript:" */
8998
8620
  let URI_SAFE_ATTRIBUTES = null;
8999
- const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [
9000
- 'alt',
9001
- 'class',
9002
- 'for',
9003
- 'id',
9004
- 'label',
9005
- 'name',
9006
- 'pattern',
9007
- 'placeholder',
9008
- 'role',
9009
- 'summary',
9010
- 'title',
9011
- 'value',
9012
- 'style',
9013
- 'xmlns'
9014
- ]);
8621
+ const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
9015
8622
  const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
9016
8623
  const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
9017
8624
  const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
8625
+ /* Document namespace */
9018
8626
  let NAMESPACE = HTML_NAMESPACE;
9019
8627
  let IS_EMPTY_INPUT = false;
8628
+
8629
+ /* Allowed XHTML+XML namespaces */
9020
8630
  let ALLOWED_NAMESPACES = null;
9021
- const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [
9022
- MATHML_NAMESPACE,
9023
- SVG_NAMESPACE,
9024
- HTML_NAMESPACE
9025
- ], stringToString);
9026
- let PARSER_MEDIA_TYPE;
9027
- const SUPPORTED_PARSER_MEDIA_TYPES = [
9028
- 'application/xhtml+xml',
9029
- 'text/html'
9030
- ];
8631
+ const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
8632
+
8633
+ /* Parsing of strict XHTML documents */
8634
+ let PARSER_MEDIA_TYPE = null;
8635
+ const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
9031
8636
  const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
9032
- let transformCaseFunc;
8637
+ let transformCaseFunc = null;
8638
+
8639
+ /* Keep a reference to config to pass to hooks */
9033
8640
  let CONFIG = null;
8641
+
8642
+ /* Ideally, do not touch anything below this line */
8643
+ /* ______________________________________________ */
8644
+
9034
8645
  const formElement = document.createElement('form');
9035
8646
  const isRegexOrFunction = function isRegexOrFunction(testValue) {
9036
8647
  return testValue instanceof RegExp || testValue instanceof Function;
9037
8648
  };
9038
- const _parseConfig = function _parseConfig(cfg) {
8649
+
8650
+ /**
8651
+ * _parseConfig
8652
+ *
8653
+ * @param {Object} cfg optional config literal
8654
+ */
8655
+ // eslint-disable-next-line complexity
8656
+ const _parseConfig = function _parseConfig() {
8657
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
9039
8658
  if (CONFIG && CONFIG === cfg) {
9040
8659
  return;
9041
8660
  }
8661
+
8662
+ /* Shield configuration object from tampering */
9042
8663
  if (!cfg || typeof cfg !== 'object') {
9043
8664
  cfg = {};
9044
8665
  }
8666
+
8667
+ /* Shield configuration object from prototype pollution */
9045
8668
  cfg = clone(cfg);
9046
- 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;
8669
+ PARSER_MEDIA_TYPE =
8670
+ // eslint-disable-next-line unicorn/prefer-includes
8671
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
8672
+
8673
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
9047
8674
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
9048
- ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
9049
- ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
9050
- ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
9051
- 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;
9052
- 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;
9053
- FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
9054
- FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
9055
- FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
9056
- USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
9057
- ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false;
9058
- ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false;
9059
- ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false;
9060
- ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false;
9061
- SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false;
9062
- WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false;
9063
- RETURN_DOM = cfg.RETURN_DOM || false;
9064
- RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false;
9065
- RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false;
9066
- FORCE_BODY = cfg.FORCE_BODY || false;
9067
- SANITIZE_DOM = cfg.SANITIZE_DOM !== false;
9068
- SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false;
9069
- KEEP_CONTENT = cfg.KEEP_CONTENT !== false;
9070
- IN_PLACE = cfg.IN_PLACE || false;
8675
+
8676
+ /* Set configuration parameters */
8677
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
8678
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
8679
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
8680
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
8681
+ // eslint-disable-line indent
8682
+ cfg.ADD_URI_SAFE_ATTR,
8683
+ // eslint-disable-line indent
8684
+ transformCaseFunc // eslint-disable-line indent
8685
+ ) // eslint-disable-line indent
8686
+ : DEFAULT_URI_SAFE_ATTRIBUTES;
8687
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
8688
+ // eslint-disable-line indent
8689
+ cfg.ADD_DATA_URI_TAGS,
8690
+ // eslint-disable-line indent
8691
+ transformCaseFunc // eslint-disable-line indent
8692
+ ) // eslint-disable-line indent
8693
+ : DEFAULT_DATA_URI_TAGS;
8694
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
8695
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
8696
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
8697
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
8698
+ ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
8699
+ ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
8700
+ ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
8701
+ ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
8702
+ SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
8703
+ SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
8704
+ WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
8705
+ RETURN_DOM = cfg.RETURN_DOM || false; // Default false
8706
+ RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
8707
+ RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
8708
+ FORCE_BODY = cfg.FORCE_BODY || false; // Default false
8709
+ SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
8710
+ SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
8711
+ KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
8712
+ IN_PLACE = cfg.IN_PLACE || false; // Default false
9071
8713
  IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
9072
8714
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
9073
8715
  CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
@@ -9086,8 +8728,10 @@
9086
8728
  if (RETURN_DOM_FRAGMENT) {
9087
8729
  RETURN_DOM = true;
9088
8730
  }
8731
+
8732
+ /* Parse profile info */
9089
8733
  if (USE_PROFILES) {
9090
- ALLOWED_TAGS = addToSet({}, [...text$1]);
8734
+ ALLOWED_TAGS = addToSet({}, text$1);
9091
8735
  ALLOWED_ATTR = [];
9092
8736
  if (USE_PROFILES.html === true) {
9093
8737
  addToSet(ALLOWED_TAGS, html$1);
@@ -9109,6 +8753,8 @@
9109
8753
  addToSet(ALLOWED_ATTR, xml);
9110
8754
  }
9111
8755
  }
8756
+
8757
+ /* Merge configuration parameters */
9112
8758
  if (cfg.ADD_TAGS) {
9113
8759
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
9114
8760
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
@@ -9130,16 +8776,18 @@
9130
8776
  }
9131
8777
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
9132
8778
  }
8779
+
8780
+ /* Add #text in case KEEP_CONTENT is set to true */
9133
8781
  if (KEEP_CONTENT) {
9134
8782
  ALLOWED_TAGS['#text'] = true;
9135
8783
  }
8784
+
8785
+ /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
9136
8786
  if (WHOLE_DOCUMENT) {
9137
- addToSet(ALLOWED_TAGS, [
9138
- 'html',
9139
- 'head',
9140
- 'body'
9141
- ]);
8787
+ addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
9142
8788
  }
8789
+
8790
+ /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
9143
8791
  if (ALLOWED_TAGS.table) {
9144
8792
  addToSet(ALLOWED_TAGS, ['tbody']);
9145
8793
  delete FORBID_TAGS.tbody;
@@ -9151,48 +8799,57 @@
9151
8799
  if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
9152
8800
  throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
9153
8801
  }
8802
+
8803
+ // Overwrite existing TrustedTypes policy.
9154
8804
  trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
8805
+
8806
+ // Sign local variables required by `sanitize`.
9155
8807
  emptyHTML = trustedTypesPolicy.createHTML('');
9156
8808
  } else {
8809
+ // Uninitialized policy, attempt to initialize the internal dompurify policy.
9157
8810
  if (trustedTypesPolicy === undefined) {
9158
8811
  trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
9159
8812
  }
8813
+
8814
+ // If creating the internal policy succeeded sign internal variables.
9160
8815
  if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
9161
8816
  emptyHTML = trustedTypesPolicy.createHTML('');
9162
8817
  }
9163
8818
  }
8819
+
8820
+ // Prevent further manipulation of configuration.
8821
+ // Not available in IE8, Safari 5, etc.
9164
8822
  if (freeze) {
9165
8823
  freeze(cfg);
9166
8824
  }
9167
8825
  CONFIG = cfg;
9168
8826
  };
9169
- const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [
9170
- 'mi',
9171
- 'mo',
9172
- 'mn',
9173
- 'ms',
9174
- 'mtext'
9175
- ]);
9176
- const HTML_INTEGRATION_POINTS = addToSet({}, [
9177
- 'foreignobject',
9178
- 'desc',
9179
- 'title',
9180
- 'annotation-xml'
9181
- ]);
9182
- const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [
9183
- 'title',
9184
- 'style',
9185
- 'font',
9186
- 'a',
9187
- 'script'
9188
- ]);
9189
- const ALL_SVG_TAGS = addToSet({}, svg$1);
9190
- addToSet(ALL_SVG_TAGS, svgFilters);
9191
- addToSet(ALL_SVG_TAGS, svgDisallowed);
9192
- const ALL_MATHML_TAGS = addToSet({}, mathMl$1);
9193
- addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
8827
+ const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
8828
+ const HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
8829
+
8830
+ // Certain elements are allowed in both SVG and HTML
8831
+ // namespace. We need to specify them explicitly
8832
+ // so that they don't get erroneously deleted from
8833
+ // HTML namespace.
8834
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
8835
+
8836
+ /* Keep track of all possible SVG and MathML tags
8837
+ * so that we can perform the namespace checks
8838
+ * correctly. */
8839
+ const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
8840
+ const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
8841
+
8842
+ /**
8843
+ * @param {Element} element a DOM element whose namespace is being checked
8844
+ * @returns {boolean} Return false if the element has a
8845
+ * namespace that a spec-compliant parser would never
8846
+ * return. Return true otherwise.
8847
+ */
9194
8848
  const _checkValidNamespace = function _checkValidNamespace(element) {
9195
8849
  let parent = getParentNode(element);
8850
+
8851
+ // In JSDOM, if we're inside shadow DOM, then parentNode
8852
+ // can be null. We just simulate parent in this case.
9196
8853
  if (!parent || !parent.tagName) {
9197
8854
  parent = {
9198
8855
  namespaceURI: NAMESPACE,
@@ -9205,45 +8862,93 @@
9205
8862
  return false;
9206
8863
  }
9207
8864
  if (element.namespaceURI === SVG_NAMESPACE) {
8865
+ // The only way to switch from HTML namespace to SVG
8866
+ // is via <svg>. If it happens via any other tag, then
8867
+ // it should be killed.
9208
8868
  if (parent.namespaceURI === HTML_NAMESPACE) {
9209
8869
  return tagName === 'svg';
9210
8870
  }
8871
+
8872
+ // The only way to switch from MathML to SVG is via`
8873
+ // svg if parent is either <annotation-xml> or MathML
8874
+ // text integration points.
9211
8875
  if (parent.namespaceURI === MATHML_NAMESPACE) {
9212
8876
  return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
9213
8877
  }
8878
+
8879
+ // We only allow elements that are defined in SVG
8880
+ // spec. All others are disallowed in SVG namespace.
9214
8881
  return Boolean(ALL_SVG_TAGS[tagName]);
9215
8882
  }
9216
8883
  if (element.namespaceURI === MATHML_NAMESPACE) {
8884
+ // The only way to switch from HTML namespace to MathML
8885
+ // is via <math>. If it happens via any other tag, then
8886
+ // it should be killed.
9217
8887
  if (parent.namespaceURI === HTML_NAMESPACE) {
9218
8888
  return tagName === 'math';
9219
8889
  }
8890
+
8891
+ // The only way to switch from SVG to MathML is via
8892
+ // <math> and HTML integration points
9220
8893
  if (parent.namespaceURI === SVG_NAMESPACE) {
9221
8894
  return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
9222
8895
  }
8896
+
8897
+ // We only allow elements that are defined in MathML
8898
+ // spec. All others are disallowed in MathML namespace.
9223
8899
  return Boolean(ALL_MATHML_TAGS[tagName]);
9224
8900
  }
9225
8901
  if (element.namespaceURI === HTML_NAMESPACE) {
8902
+ // The only way to switch from SVG to HTML is via
8903
+ // HTML integration points, and from MathML to HTML
8904
+ // is via MathML text integration points
9226
8905
  if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
9227
8906
  return false;
9228
8907
  }
9229
8908
  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
9230
8909
  return false;
9231
8910
  }
8911
+
8912
+ // We disallow tags that are specific for MathML
8913
+ // or SVG and should never appear in HTML namespace
9232
8914
  return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
9233
8915
  }
8916
+
8917
+ // For XHTML and XML documents that support custom namespaces
9234
8918
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
9235
8919
  return true;
9236
8920
  }
8921
+
8922
+ // The code should never reach this place (this means
8923
+ // that the element somehow got namespace that is not
8924
+ // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
8925
+ // Return false just in case.
9237
8926
  return false;
9238
8927
  };
8928
+
8929
+ /**
8930
+ * _forceRemove
8931
+ *
8932
+ * @param {Node} node a DOM node
8933
+ */
9239
8934
  const _forceRemove = function _forceRemove(node) {
9240
- arrayPush(DOMPurify.removed, { element: node });
8935
+ arrayPush(DOMPurify.removed, {
8936
+ element: node
8937
+ });
9241
8938
  try {
9242
- node.parentNode.removeChild(node);
8939
+ // eslint-disable-next-line unicorn/prefer-dom-node-remove
8940
+ getParentNode(node).removeChild(node);
9243
8941
  } catch (_) {
9244
- node.remove();
8942
+ remove(node);
9245
8943
  }
9246
8944
  };
8945
+
8946
+ /**
8947
+ * _removeAttribute
8948
+ *
8949
+ * @param {String} name an Attribute name
8950
+ * @param {Node} node a DOM node
8951
+ */
9247
8952
  const _removeAttribute = function _removeAttribute(name, node) {
9248
8953
  try {
9249
8954
  arrayPush(DOMPurify.removed, {
@@ -9257,64 +8962,114 @@
9257
8962
  });
9258
8963
  }
9259
8964
  node.removeAttribute(name);
8965
+
8966
+ // We void attribute values for unremovable "is"" attributes
9260
8967
  if (name === 'is' && !ALLOWED_ATTR[name]) {
9261
8968
  if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
9262
8969
  try {
9263
8970
  _forceRemove(node);
9264
- } catch (_) {
9265
- }
8971
+ } catch (_) {}
9266
8972
  } else {
9267
8973
  try {
9268
8974
  node.setAttribute(name, '');
9269
- } catch (_) {
9270
- }
8975
+ } catch (_) {}
9271
8976
  }
9272
8977
  }
9273
8978
  };
8979
+
8980
+ /**
8981
+ * _initDocument
8982
+ *
8983
+ * @param {String} dirty a string of dirty markup
8984
+ * @return {Document} a DOM, filled with the dirty markup
8985
+ */
9274
8986
  const _initDocument = function _initDocument(dirty) {
9275
- let doc;
9276
- let leadingWhitespace;
8987
+ /* Create a HTML document */
8988
+ let doc = null;
8989
+ let leadingWhitespace = null;
9277
8990
  if (FORCE_BODY) {
9278
8991
  dirty = '<remove></remove>' + dirty;
9279
8992
  } else {
8993
+ /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
9280
8994
  const matches = stringMatch(dirty, /^[\r\n\t ]+/);
9281
8995
  leadingWhitespace = matches && matches[0];
9282
8996
  }
9283
8997
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
8998
+ // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
9284
8999
  dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
9285
9000
  }
9286
9001
  const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
9002
+ /*
9003
+ * Use the DOMParser API by default, fallback later if needs be
9004
+ * DOMParser not work for svg when has multiple root element.
9005
+ */
9287
9006
  if (NAMESPACE === HTML_NAMESPACE) {
9288
9007
  try {
9289
9008
  doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
9290
- } catch (_) {
9291
- }
9009
+ } catch (_) {}
9292
9010
  }
9011
+
9012
+ /* Use createHTMLDocument in case DOMParser is not available */
9293
9013
  if (!doc || !doc.documentElement) {
9294
9014
  doc = implementation.createDocument(NAMESPACE, 'template', null);
9295
9015
  try {
9296
9016
  doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
9297
9017
  } catch (_) {
9018
+ // Syntax error if dirtyPayload is invalid xml
9298
9019
  }
9299
9020
  }
9300
9021
  const body = doc.body || doc.documentElement;
9301
9022
  if (dirty && leadingWhitespace) {
9302
9023
  body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
9303
9024
  }
9025
+
9026
+ /* Work on whole document or just its body */
9304
9027
  if (NAMESPACE === HTML_NAMESPACE) {
9305
9028
  return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
9306
9029
  }
9307
9030
  return WHOLE_DOCUMENT ? doc.documentElement : body;
9308
9031
  };
9309
- const _createIterator = function _createIterator(root) {
9310
- return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
9311
- };
9032
+
9033
+ /**
9034
+ * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
9035
+ *
9036
+ * @param {Node} root The root element or node to start traversing on.
9037
+ * @return {NodeIterator} The created NodeIterator
9038
+ */
9039
+ const _createNodeIterator = function _createNodeIterator(root) {
9040
+ return createNodeIterator.call(root.ownerDocument || root, root,
9041
+ // eslint-disable-next-line no-bitwise
9042
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
9043
+ };
9044
+
9045
+ /**
9046
+ * _isClobbered
9047
+ *
9048
+ * @param {Node} elm element to check for clobbering attacks
9049
+ * @return {Boolean} true if clobbered, false if safe
9050
+ */
9312
9051
  const _isClobbered = function _isClobbered(elm) {
9313
9052
  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');
9314
9053
  };
9054
+
9055
+ /**
9056
+ * Checks whether the given object is a DOM node.
9057
+ *
9058
+ * @param {Node} object object to check whether it's a DOM node
9059
+ * @return {Boolean} true is object is a DOM node
9060
+ */
9315
9061
  const _isNode = function _isNode(object) {
9316
- return typeof Node === 'object' ? object instanceof Node : object && typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
9062
+ return typeof Node === 'function' && object instanceof Node;
9317
9063
  };
9064
+
9065
+ /**
9066
+ * _executeHook
9067
+ * Execute user configurable hooks
9068
+ *
9069
+ * @param {String} entryPoint Name of the hook's entry point
9070
+ * @param {Node} currentNode node to work on with the hook
9071
+ * @param {Object} data additional hook parameters
9072
+ */
9318
9073
  const _executeHook = function _executeHook(entryPoint, currentNode, data) {
9319
9074
  if (!hooks[entryPoint]) {
9320
9075
  return;
@@ -9323,93 +9078,184 @@
9323
9078
  hook.call(DOMPurify, currentNode, data, CONFIG);
9324
9079
  });
9325
9080
  };
9081
+
9082
+ /**
9083
+ * _sanitizeElements
9084
+ *
9085
+ * @protect nodeName
9086
+ * @protect textContent
9087
+ * @protect removeChild
9088
+ *
9089
+ * @param {Node} currentNode to check for permission to exist
9090
+ * @return {Boolean} true if node was killed, false if left alive
9091
+ */
9326
9092
  const _sanitizeElements = function _sanitizeElements(currentNode) {
9327
- let content;
9093
+ let content = null;
9094
+
9095
+ /* Execute a hook if present */
9328
9096
  _executeHook('beforeSanitizeElements', currentNode, null);
9097
+
9098
+ /* Check if element is clobbered or can clobber */
9329
9099
  if (_isClobbered(currentNode)) {
9330
9100
  _forceRemove(currentNode);
9331
9101
  return true;
9332
9102
  }
9103
+
9104
+ /* Now let's check the element's type and name */
9333
9105
  const tagName = transformCaseFunc(currentNode.nodeName);
9106
+
9107
+ /* Execute a hook if present */
9334
9108
  _executeHook('uponSanitizeElement', currentNode, {
9335
9109
  tagName,
9336
9110
  allowedTags: ALLOWED_TAGS
9337
9111
  });
9338
- if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
9112
+
9113
+ /* Detect mXSS attempts abusing namespace confusion */
9114
+ if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
9115
+ _forceRemove(currentNode);
9116
+ return true;
9117
+ }
9118
+
9119
+ /* Remove any occurrence of processing instructions */
9120
+ if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
9121
+ _forceRemove(currentNode);
9122
+ return true;
9123
+ }
9124
+
9125
+ /* Remove any kind of possibly harmful comments */
9126
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
9339
9127
  _forceRemove(currentNode);
9340
9128
  return true;
9341
9129
  }
9130
+
9131
+ /* Remove element if anything forbids its presence */
9342
9132
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
9343
- if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
9344
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName))
9133
+ /* Check if we have a custom element to handle */
9134
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
9135
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
9345
9136
  return false;
9346
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName))
9137
+ }
9138
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
9347
9139
  return false;
9140
+ }
9348
9141
  }
9142
+
9143
+ /* Keep content except for bad-listed elements */
9349
9144
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
9350
9145
  const parentNode = getParentNode(currentNode) || currentNode.parentNode;
9351
9146
  const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
9352
9147
  if (childNodes && parentNode) {
9353
9148
  const childCount = childNodes.length;
9354
9149
  for (let i = childCount - 1; i >= 0; --i) {
9355
- parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
9150
+ const childClone = cloneNode(childNodes[i], true);
9151
+ childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
9152
+ parentNode.insertBefore(childClone, getNextSibling(currentNode));
9356
9153
  }
9357
9154
  }
9358
9155
  }
9359
9156
  _forceRemove(currentNode);
9360
9157
  return true;
9361
9158
  }
9159
+
9160
+ /* Check whether element has a valid namespace */
9362
9161
  if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
9363
9162
  _forceRemove(currentNode);
9364
9163
  return true;
9365
9164
  }
9165
+
9166
+ /* Make sure that older browsers don't get fallback-tag mXSS */
9366
9167
  if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
9367
9168
  _forceRemove(currentNode);
9368
9169
  return true;
9369
9170
  }
9370
- if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
9171
+
9172
+ /* Sanitize element content to be template-safe */
9173
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
9174
+ /* Get the element's text content */
9371
9175
  content = currentNode.textContent;
9372
- content = stringReplace(content, MUSTACHE_EXPR, ' ');
9373
- content = stringReplace(content, ERB_EXPR, ' ');
9374
- content = stringReplace(content, TMPLIT_EXPR, ' ');
9176
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
9177
+ content = stringReplace(content, expr, ' ');
9178
+ });
9375
9179
  if (currentNode.textContent !== content) {
9376
- arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
9180
+ arrayPush(DOMPurify.removed, {
9181
+ element: currentNode.cloneNode()
9182
+ });
9377
9183
  currentNode.textContent = content;
9378
9184
  }
9379
9185
  }
9186
+
9187
+ /* Execute a hook if present */
9380
9188
  _executeHook('afterSanitizeElements', currentNode, null);
9381
9189
  return false;
9382
9190
  };
9191
+
9192
+ /**
9193
+ * _isValidAttribute
9194
+ *
9195
+ * @param {string} lcTag Lowercase tag name of containing element.
9196
+ * @param {string} lcName Lowercase attribute name.
9197
+ * @param {string} value Attribute value.
9198
+ * @return {Boolean} Returns true if `value` is valid, otherwise false.
9199
+ */
9200
+ // eslint-disable-next-line complexity
9383
9201
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
9202
+ /* Make sure attribute cannot clobber */
9384
9203
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
9385
9204
  return false;
9386
9205
  }
9387
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName));
9388
- else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName));
9389
- else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
9390
- 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)));
9391
- else {
9206
+
9207
+ /* Allow valid data-* attributes: At least one character after "-"
9208
+ (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
9209
+ XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
9210
+ We don't need to check the value; it's always URI safe. */
9211
+ 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]) {
9212
+ if (
9213
+ // First condition does a very basic check if a) it's basically a valid custom element tagname AND
9214
+ // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
9215
+ // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
9216
+ _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)) ||
9217
+ // Alternative, second condition checks if it's an `is`-attribute, AND
9218
+ // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
9219
+ 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 {
9392
9220
  return false;
9393
9221
  }
9394
- } else if (URI_SAFE_ATTRIBUTES[lcName]);
9395
- else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, '')));
9396
- else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]);
9397
- else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, '')));
9398
- else if (value) {
9222
+ /* Check value is safe. First, is attr inert? If so, is safe */
9223
+ } 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) {
9399
9224
  return false;
9400
9225
  } else ;
9401
9226
  return true;
9402
9227
  };
9403
- const _basicCustomElementTest = function _basicCustomElementTest(tagName) {
9404
- return tagName.indexOf('-') > 0;
9405
- };
9228
+
9229
+ /**
9230
+ * _isBasicCustomElement
9231
+ * checks if at least one dash is included in tagName, and it's not the first char
9232
+ * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
9233
+ *
9234
+ * @param {string} tagName name of the tag of the node to sanitize
9235
+ * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
9236
+ */
9237
+ const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
9238
+ return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
9239
+ };
9240
+
9241
+ /**
9242
+ * _sanitizeAttributes
9243
+ *
9244
+ * @protect attributes
9245
+ * @protect nodeName
9246
+ * @protect removeAttribute
9247
+ * @protect setAttribute
9248
+ *
9249
+ * @param {Node} currentNode to sanitize
9250
+ */
9406
9251
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
9407
- let attr;
9408
- let value;
9409
- let lcName;
9410
- let l;
9252
+ /* Execute a hook if present */
9411
9253
  _executeHook('beforeSanitizeAttributes', currentNode, null);
9412
- const {attributes} = currentNode;
9254
+ const {
9255
+ attributes
9256
+ } = currentNode;
9257
+
9258
+ /* Check if we have attributes; if not we might have a text node */
9413
9259
  if (!attributes) {
9414
9260
  return;
9415
9261
  }
@@ -9419,99 +9265,172 @@
9419
9265
  keepAttr: true,
9420
9266
  allowedAttributes: ALLOWED_ATTR
9421
9267
  };
9422
- l = attributes.length;
9268
+ let l = attributes.length;
9269
+
9270
+ /* Go backwards over all attributes; safely remove bad ones */
9423
9271
  while (l--) {
9424
- attr = attributes[l];
9425
- const {name, namespaceURI} = attr;
9426
- value = name === 'value' ? attr.value : stringTrim(attr.value);
9272
+ const attr = attributes[l];
9273
+ const {
9274
+ name,
9275
+ namespaceURI,
9276
+ value: attrValue
9277
+ } = attr;
9278
+ const lcName = transformCaseFunc(name);
9279
+ let value = name === 'value' ? attrValue : stringTrim(attrValue);
9427
9280
  const initValue = value;
9428
- lcName = transformCaseFunc(name);
9281
+
9282
+ /* Execute a hook if present */
9429
9283
  hookEvent.attrName = lcName;
9430
9284
  hookEvent.attrValue = value;
9431
9285
  hookEvent.keepAttr = true;
9432
- hookEvent.forceKeepAttr = undefined;
9286
+ hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
9433
9287
  _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
9434
9288
  value = hookEvent.attrValue;
9289
+
9290
+ /* Did the hooks approve of the attribute? */
9435
9291
  if (hookEvent.forceKeepAttr) {
9436
9292
  continue;
9437
9293
  }
9294
+
9295
+ /* Did the hooks approve of the attribute? */
9438
9296
  if (!hookEvent.keepAttr) {
9439
9297
  _removeAttribute(name, currentNode);
9440
9298
  continue;
9441
9299
  }
9300
+
9301
+ /* Work around a security issue in jQuery 3.0 */
9442
9302
  if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
9443
9303
  _removeAttribute(name, currentNode);
9444
9304
  continue;
9445
9305
  }
9306
+
9307
+ /* Sanitize attribute content to be template-safe */
9446
9308
  if (SAFE_FOR_TEMPLATES) {
9447
- value = stringReplace(value, MUSTACHE_EXPR, ' ');
9448
- value = stringReplace(value, ERB_EXPR, ' ');
9449
- value = stringReplace(value, TMPLIT_EXPR, ' ');
9309
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
9310
+ value = stringReplace(value, expr, ' ');
9311
+ });
9450
9312
  }
9313
+
9314
+ /* Is `value` valid for this attribute? */
9451
9315
  const lcTag = transformCaseFunc(currentNode.nodeName);
9452
9316
  if (!_isValidAttribute(lcTag, lcName, value)) {
9453
9317
  _removeAttribute(name, currentNode);
9454
9318
  continue;
9455
9319
  }
9320
+
9321
+ /* Full DOM Clobbering protection via namespace isolation,
9322
+ * Prefix id and name attributes with `user-content-`
9323
+ */
9456
9324
  if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
9325
+ // Remove the attribute with this value
9457
9326
  _removeAttribute(name, currentNode);
9327
+
9328
+ // Prefix the value and later re-create the attribute with the sanitized value
9458
9329
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
9459
9330
  }
9331
+
9332
+ /* Work around a security issue with comments inside attributes */
9333
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
9334
+ _removeAttribute(name, currentNode);
9335
+ continue;
9336
+ }
9337
+
9338
+ /* Handle attributes that require Trusted Types */
9460
9339
  if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
9461
- if (namespaceURI);
9462
- else {
9340
+ if (namespaceURI) ; else {
9463
9341
  switch (trustedTypes.getAttributeType(lcTag, lcName)) {
9464
- case 'TrustedHTML': {
9465
- value = trustedTypesPolicy.createHTML(value);
9466
- break;
9467
- }
9468
- case 'TrustedScriptURL': {
9469
- value = trustedTypesPolicy.createScriptURL(value);
9470
- break;
9471
- }
9342
+ case 'TrustedHTML':
9343
+ {
9344
+ value = trustedTypesPolicy.createHTML(value);
9345
+ break;
9346
+ }
9347
+ case 'TrustedScriptURL':
9348
+ {
9349
+ value = trustedTypesPolicy.createScriptURL(value);
9350
+ break;
9351
+ }
9472
9352
  }
9473
9353
  }
9474
9354
  }
9355
+
9356
+ /* Handle invalid data-* attribute set by try-catching it */
9475
9357
  if (value !== initValue) {
9476
9358
  try {
9477
9359
  if (namespaceURI) {
9478
9360
  currentNode.setAttributeNS(namespaceURI, name, value);
9479
9361
  } else {
9362
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
9480
9363
  currentNode.setAttribute(name, value);
9481
9364
  }
9482
- } catch (_) {
9483
- _removeAttribute(name, currentNode);
9484
- }
9365
+ if (_isClobbered(currentNode)) {
9366
+ _forceRemove(currentNode);
9367
+ } else {
9368
+ arrayPop(DOMPurify.removed);
9369
+ }
9370
+ } catch (_) {}
9485
9371
  }
9486
9372
  }
9373
+
9374
+ /* Execute a hook if present */
9487
9375
  _executeHook('afterSanitizeAttributes', currentNode, null);
9488
9376
  };
9377
+
9378
+ /**
9379
+ * _sanitizeShadowDOM
9380
+ *
9381
+ * @param {DocumentFragment} fragment to iterate over recursively
9382
+ */
9489
9383
  const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
9490
- let shadowNode;
9491
- const shadowIterator = _createIterator(fragment);
9384
+ let shadowNode = null;
9385
+ const shadowIterator = _createNodeIterator(fragment);
9386
+
9387
+ /* Execute a hook if present */
9492
9388
  _executeHook('beforeSanitizeShadowDOM', fragment, null);
9493
9389
  while (shadowNode = shadowIterator.nextNode()) {
9390
+ /* Execute a hook if present */
9494
9391
  _executeHook('uponSanitizeShadowNode', shadowNode, null);
9392
+
9393
+ /* Sanitize tags and elements */
9495
9394
  if (_sanitizeElements(shadowNode)) {
9496
9395
  continue;
9497
9396
  }
9397
+
9398
+ /* Deep shadow DOM detected */
9498
9399
  if (shadowNode.content instanceof DocumentFragment) {
9499
9400
  _sanitizeShadowDOM(shadowNode.content);
9500
9401
  }
9402
+
9403
+ /* Check attributes, sanitize if necessary */
9501
9404
  _sanitizeAttributes(shadowNode);
9502
9405
  }
9406
+
9407
+ /* Execute a hook if present */
9503
9408
  _executeHook('afterSanitizeShadowDOM', fragment, null);
9504
9409
  };
9410
+
9411
+ /**
9412
+ * Sanitize
9413
+ * Public method providing core sanitation functionality
9414
+ *
9415
+ * @param {String|Node} dirty string or DOM node
9416
+ * @param {Object} cfg object
9417
+ */
9418
+ // eslint-disable-next-line complexity
9505
9419
  DOMPurify.sanitize = function (dirty) {
9506
9420
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
9507
- let body;
9508
- let importedNode;
9509
- let currentNode;
9510
- let returnNode;
9421
+ let body = null;
9422
+ let importedNode = null;
9423
+ let currentNode = null;
9424
+ let returnNode = null;
9425
+ /* Make sure we have a string to sanitize.
9426
+ DO NOT return early, as this will return the wrong type if
9427
+ the user has requested a DOM object rather than a string */
9511
9428
  IS_EMPTY_INPUT = !dirty;
9512
9429
  if (IS_EMPTY_INPUT) {
9513
9430
  dirty = '<!-->';
9514
9431
  }
9432
+
9433
+ /* Stringify, in case dirty is an object */
9515
9434
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
9516
9435
  if (typeof dirty.toString === 'function') {
9517
9436
  dirty = dirty.toString();
@@ -9522,17 +9441,26 @@
9522
9441
  throw typeErrorCreate('toString is not a function');
9523
9442
  }
9524
9443
  }
9444
+
9445
+ /* Return dirty HTML if DOMPurify cannot run */
9525
9446
  if (!DOMPurify.isSupported) {
9526
9447
  return dirty;
9527
9448
  }
9449
+
9450
+ /* Assign config vars */
9528
9451
  if (!SET_CONFIG) {
9529
9452
  _parseConfig(cfg);
9530
9453
  }
9454
+
9455
+ /* Clean up removed elements */
9531
9456
  DOMPurify.removed = [];
9457
+
9458
+ /* Check if dirty is correctly typed for IN_PLACE */
9532
9459
  if (typeof dirty === 'string') {
9533
9460
  IN_PLACE = false;
9534
9461
  }
9535
9462
  if (IN_PLACE) {
9463
+ /* Do some early pre-sanitization to avoid unsafe root nodes */
9536
9464
  if (dirty.nodeName) {
9537
9465
  const tagName = transformCaseFunc(dirty.nodeName);
9538
9466
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
@@ -9540,74 +9468,138 @@
9540
9468
  }
9541
9469
  }
9542
9470
  } else if (dirty instanceof Node) {
9471
+ /* If dirty is a DOM element, append to an empty document to avoid
9472
+ elements being stripped by the parser */
9543
9473
  body = _initDocument('<!---->');
9544
9474
  importedNode = body.ownerDocument.importNode(dirty, true);
9545
- if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
9475
+ if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
9476
+ /* Node is already a body, use as is */
9546
9477
  body = importedNode;
9547
9478
  } else if (importedNode.nodeName === 'HTML') {
9548
9479
  body = importedNode;
9549
9480
  } else {
9481
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
9550
9482
  body.appendChild(importedNode);
9551
9483
  }
9552
9484
  } else {
9553
- if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) {
9485
+ /* Exit directly if we have nothing to do */
9486
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
9487
+ // eslint-disable-next-line unicorn/prefer-includes
9488
+ dirty.indexOf('<') === -1) {
9554
9489
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
9555
9490
  }
9491
+
9492
+ /* Initialize the document to work on */
9556
9493
  body = _initDocument(dirty);
9494
+
9495
+ /* Check we have a DOM node from the data */
9557
9496
  if (!body) {
9558
9497
  return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
9559
9498
  }
9560
9499
  }
9500
+
9501
+ /* Remove first element node (ours) if FORCE_BODY is set */
9561
9502
  if (body && FORCE_BODY) {
9562
9503
  _forceRemove(body.firstChild);
9563
9504
  }
9564
- const nodeIterator = _createIterator(IN_PLACE ? dirty : body);
9505
+
9506
+ /* Get node iterator */
9507
+ const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
9508
+
9509
+ /* Now start iterating over the created document */
9565
9510
  while (currentNode = nodeIterator.nextNode()) {
9511
+ /* Sanitize tags and elements */
9566
9512
  if (_sanitizeElements(currentNode)) {
9567
9513
  continue;
9568
9514
  }
9515
+
9516
+ /* Shadow DOM detected, sanitize it */
9569
9517
  if (currentNode.content instanceof DocumentFragment) {
9570
9518
  _sanitizeShadowDOM(currentNode.content);
9571
9519
  }
9520
+
9521
+ /* Check attributes, sanitize if necessary */
9572
9522
  _sanitizeAttributes(currentNode);
9573
9523
  }
9524
+
9525
+ /* If we sanitized `dirty` in-place, return it. */
9574
9526
  if (IN_PLACE) {
9575
9527
  return dirty;
9576
9528
  }
9529
+
9530
+ /* Return sanitized string or DOM */
9577
9531
  if (RETURN_DOM) {
9578
9532
  if (RETURN_DOM_FRAGMENT) {
9579
9533
  returnNode = createDocumentFragment.call(body.ownerDocument);
9580
9534
  while (body.firstChild) {
9535
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
9581
9536
  returnNode.appendChild(body.firstChild);
9582
9537
  }
9583
9538
  } else {
9584
9539
  returnNode = body;
9585
9540
  }
9586
9541
  if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
9542
+ /*
9543
+ AdoptNode() is not used because internal state is not reset
9544
+ (e.g. the past names map of a HTMLFormElement), this is safe
9545
+ in theory but we would rather not risk another attack vector.
9546
+ The state that is cloned by importNode() is explicitly defined
9547
+ by the specs.
9548
+ */
9587
9549
  returnNode = importNode.call(originalDocument, returnNode, true);
9588
9550
  }
9589
9551
  return returnNode;
9590
9552
  }
9591
9553
  let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
9554
+
9555
+ /* Serialize doctype if allowed */
9592
9556
  if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
9593
9557
  serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
9594
9558
  }
9559
+
9560
+ /* Sanitize final string template-safe */
9595
9561
  if (SAFE_FOR_TEMPLATES) {
9596
- serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR, ' ');
9597
- serializedHTML = stringReplace(serializedHTML, ERB_EXPR, ' ');
9598
- serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR, ' ');
9562
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
9563
+ serializedHTML = stringReplace(serializedHTML, expr, ' ');
9564
+ });
9599
9565
  }
9600
9566
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
9601
9567
  };
9602
- DOMPurify.setConfig = function (cfg) {
9568
+
9569
+ /**
9570
+ * Public method to set the configuration once
9571
+ * setConfig
9572
+ *
9573
+ * @param {Object} cfg configuration object
9574
+ */
9575
+ DOMPurify.setConfig = function () {
9576
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
9603
9577
  _parseConfig(cfg);
9604
9578
  SET_CONFIG = true;
9605
9579
  };
9580
+
9581
+ /**
9582
+ * Public method to remove the configuration
9583
+ * clearConfig
9584
+ *
9585
+ */
9606
9586
  DOMPurify.clearConfig = function () {
9607
9587
  CONFIG = null;
9608
9588
  SET_CONFIG = false;
9609
9589
  };
9590
+
9591
+ /**
9592
+ * Public method to check if an attribute value is valid.
9593
+ * Uses last set config, if any. Otherwise, uses config defaults.
9594
+ * isValidAttribute
9595
+ *
9596
+ * @param {String} tag Tag name of containing element.
9597
+ * @param {String} attr Attribute name.
9598
+ * @param {String} value Attribute value.
9599
+ * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
9600
+ */
9610
9601
  DOMPurify.isValidAttribute = function (tag, attr, value) {
9602
+ /* Initialize shared config vars if necessary. */
9611
9603
  if (!CONFIG) {
9612
9604
  _parseConfig({});
9613
9605
  }
@@ -9615,6 +9607,14 @@
9615
9607
  const lcName = transformCaseFunc(attr);
9616
9608
  return _isValidAttribute(lcTag, lcName, value);
9617
9609
  };
9610
+
9611
+ /**
9612
+ * AddHook
9613
+ * Public method to add DOMPurify hooks
9614
+ *
9615
+ * @param {String} entryPoint entry point for the hook to add
9616
+ * @param {Function} hookFunction function to execute
9617
+ */
9618
9618
  DOMPurify.addHook = function (entryPoint, hookFunction) {
9619
9619
  if (typeof hookFunction !== 'function') {
9620
9620
  return;
@@ -9622,16 +9622,37 @@
9622
9622
  hooks[entryPoint] = hooks[entryPoint] || [];
9623
9623
  arrayPush(hooks[entryPoint], hookFunction);
9624
9624
  };
9625
+
9626
+ /**
9627
+ * RemoveHook
9628
+ * Public method to remove a DOMPurify hook at a given entryPoint
9629
+ * (pops it from the stack of hooks if more are present)
9630
+ *
9631
+ * @param {String} entryPoint entry point for the hook to remove
9632
+ * @return {Function} removed(popped) hook
9633
+ */
9625
9634
  DOMPurify.removeHook = function (entryPoint) {
9626
9635
  if (hooks[entryPoint]) {
9627
9636
  return arrayPop(hooks[entryPoint]);
9628
9637
  }
9629
9638
  };
9639
+
9640
+ /**
9641
+ * RemoveHooks
9642
+ * Public method to remove all DOMPurify hooks at a given entryPoint
9643
+ *
9644
+ * @param {String} entryPoint entry point for the hooks to remove
9645
+ */
9630
9646
  DOMPurify.removeHooks = function (entryPoint) {
9631
9647
  if (hooks[entryPoint]) {
9632
9648
  hooks[entryPoint] = [];
9633
9649
  }
9634
9650
  };
9651
+
9652
+ /**
9653
+ * RemoveAllHooks
9654
+ * Public method to remove all DOMPurify hooks
9655
+ */
9635
9656
  DOMPurify.removeAllHooks = function () {
9636
9657
  hooks = {};
9637
9658
  };