lightning-base-components 1.21.9-alpha → 1.22.2-alpha

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 (122) hide show
  1. package/metadata/raptor.json +9 -0
  2. package/package.json +3 -5
  3. package/scopedImports/@salesforce-internal-core.appVersion.js +1 -1
  4. package/src/lightning/accordionSection/button.slds.css +18 -10
  5. package/src/lightning/baseCombobox/dropdown.slds.css +7 -7
  6. package/src/lightning/baseCombobox/input-text.slds.css +7 -6
  7. package/src/lightning/baseComboboxFormattedText/baseComboboxFormattedText.js-meta.xml +1 -1
  8. package/src/lightning/button/button.slds.css +18 -10
  9. package/src/lightning/buttonIcon/button-icon.slds.css +23 -15
  10. package/src/lightning/buttonIcon/buttonIcon.js +29 -26
  11. package/src/lightning/buttonIconStateful/button-icon.slds.css +23 -15
  12. package/src/lightning/buttonIconStateful/button.slds.css +18 -10
  13. package/src/lightning/buttonMenu/button-icon.slds.css +23 -15
  14. package/src/lightning/buttonMenu/button.slds.css +18 -10
  15. package/src/lightning/buttonMenu/dropdown.slds.css +7 -7
  16. package/src/lightning/buttonStateful/button-stateful.slds.css +23 -0
  17. package/src/lightning/buttonStateful/button.slds.css +18 -10
  18. package/src/lightning/buttonStateful/buttonStateful.js-meta.xml +3 -0
  19. package/src/lightning/calendar/calendar.lbc.native.css +1 -0
  20. package/src/lightning/calendar/calendar.slds.css +13 -10
  21. package/src/lightning/calendar/dropdown.slds.css +7 -7
  22. package/src/lightning/calendar/keyboard.js +11 -3
  23. package/src/lightning/colorPickerCustom/input-text.slds.css +7 -6
  24. package/src/lightning/colorPickerPanel/colorPickerPanel.js +1 -11
  25. package/src/lightning/combobox/combobox.html +1 -1
  26. package/src/lightning/combobox/combobox.js +5 -13
  27. package/src/lightning/combobox/form-element.slds.css +1 -1
  28. package/src/lightning/datatable/__examples__/withVirtualization/withVirtualization.js +0 -1
  29. package/src/lightning/datatable/autoWidthStrategy.js +32 -44
  30. package/src/lightning/datatable/columns.js +4 -1
  31. package/src/lightning/datatable/datatable.js +15 -34
  32. package/src/lightning/datatable/keyboard.js +13 -12
  33. package/src/lightning/datatable/renderManager.js +1 -3
  34. package/src/lightning/datatable/rows.js +17 -9
  35. package/src/lightning/datatable/state.js +0 -1
  36. package/src/lightning/datatable/virtualization.js +11 -14
  37. package/src/lightning/datepicker/form-element.slds.css +1 -1
  38. package/src/lightning/datepicker/input-text.slds.css +7 -6
  39. package/src/lightning/datetimepicker/form-element.slds.css +1 -1
  40. package/src/lightning/datetimepicker/input-text.slds.css +7 -6
  41. package/src/lightning/dualListbox/form-element.slds.css +1 -1
  42. package/src/lightning/formattedAddress/formattedAddress.js +3 -2
  43. package/src/lightning/formattedDateTime/formattedDateTime.js +3 -2
  44. package/src/lightning/formattedDateTime/formattedDateTime.js-meta.xml +1 -1
  45. package/src/lightning/formattedLocation/formattedLocation.js +3 -2
  46. package/src/lightning/formattedPhone/formattedPhone.js +3 -2
  47. package/src/lightning/formattedTime/formattedTime.js +3 -2
  48. package/src/lightning/formattedUrl/formattedUrl.js +3 -2
  49. package/src/lightning/groupedCombobox/form-element.slds.css +1 -1
  50. package/src/lightning/groupedCombobox/groupedCombobox.js +21 -28
  51. package/src/lightning/groupedCombobox/input-text.slds.css +7 -6
  52. package/src/lightning/helptext/button-icon.slds.css +23 -15
  53. package/src/lightning/helptext/form-element.slds.css +1 -1
  54. package/src/lightning/helptext/helptext.html +2 -1
  55. package/src/lightning/helptext/helptext.js +0 -12
  56. package/src/lightning/helptext/helptext.js-meta.xml +3 -0
  57. package/src/lightning/input/form-element.slds.css +1 -1
  58. package/src/lightning/inputAddress/__docs__/inputAddress.md +44 -3
  59. package/src/lightning/inputAddress/form-element.slds.css +1 -1
  60. package/src/lightning/inputAddress/input-text.slds.css +7 -6
  61. package/src/lightning/inputAddress/inputAddress.js +8 -6
  62. package/src/lightning/inputLocation/form-element.slds.css +1 -1
  63. package/src/lightning/inputLocation/input-text.slds.css +7 -6
  64. package/src/lightning/inputName/form-element.slds.css +1 -1
  65. package/src/lightning/inputName/input-text.slds.css +7 -6
  66. package/src/lightning/inputName/inputName.js-meta.xml +3 -0
  67. package/src/lightning/interactiveDialogBase/button.slds.css +515 -0
  68. package/src/lightning/interactiveDialogBase/interactive-dialog-base.slds.css +45 -17
  69. package/src/lightning/interactiveDialogBase/interactiveDialogBase.lbc.native.css +6 -1
  70. package/src/lightning/interactiveDialogBase/modal-base.slds.css +242 -0
  71. package/src/lightning/interactiveDialogBase/modal-body.slds.css +60 -0
  72. package/src/lightning/interactiveDialogBase/modal-footer.slds.css +79 -0
  73. package/src/lightning/interactiveDialogBase/modal-header.slds.css +76 -0
  74. package/src/lightning/lookupAddress/form-element.slds.css +1 -1
  75. package/src/lightning/lookupAddress/lookupAddress.js-meta.xml +3 -0
  76. package/src/lightning/menuDivider/menuDivider.js-meta.xml +3 -0
  77. package/src/lightning/menuSubheader/menuSubheader.js-meta.xml +3 -0
  78. package/src/lightning/modal/modal.js-meta.xml +3 -0
  79. package/src/lightning/modalBase/modalBase.js +7 -5
  80. package/src/lightning/modalBody/modalBody.js-meta.xml +3 -0
  81. package/src/lightning/modalFooter/modalFooter.js-meta.xml +3 -0
  82. package/src/lightning/modalHeader/modalHeader.js-meta.xml +3 -0
  83. package/src/lightning/picklist/picklist.js +3 -2
  84. package/src/lightning/pill/pill.js +1 -1
  85. package/src/lightning/pillContainer/button.slds.css +18 -10
  86. package/src/lightning/primitiveBubble/primitiveBubble.js +9 -4
  87. package/src/lightning/primitiveColorpickerButton/color-picker-button.slds.css +1 -1
  88. package/src/lightning/primitiveDatatableTooltipBubble/primitiveDatatableTooltipBubble.html +1 -1
  89. package/src/lightning/primitiveFileDroppableZone/primitiveFileDroppableZone.js +3 -2
  90. package/src/lightning/primitiveHeaderActions/primitiveHeaderActions.html +1 -0
  91. package/src/lightning/primitiveInputCheckbox/form-element.slds.css +1 -1
  92. package/src/lightning/primitiveInputCheckboxButton/form-element.slds.css +1 -1
  93. package/src/lightning/primitiveInputColor/form-element.slds.css +1 -1
  94. package/src/lightning/primitiveInputColor/input-text.slds.css +7 -6
  95. package/src/lightning/primitiveInputFile/button.slds.css +18 -10
  96. package/src/lightning/primitiveInputFile/form-element.slds.css +1 -1
  97. package/src/lightning/primitiveInputFile/input-file.slds.css +3 -2
  98. package/src/lightning/primitiveInputSimple/form-element.slds.css +1 -1
  99. package/src/lightning/primitiveInputSimple/input-text.slds.css +7 -6
  100. package/src/lightning/primitiveInputToggle/form-element.slds.css +1 -1
  101. package/src/lightning/primitiveInputToggle/input-toggle.slds.css +13 -7
  102. package/src/lightning/progressBar/progressBar.js-meta.xml +3 -0
  103. package/src/lightning/progressRing/progressRing.js-meta.xml +3 -0
  104. package/src/lightning/prompt/form-element.slds.css +319 -0
  105. package/src/lightning/prompt/input-text.slds.css +507 -0
  106. package/src/lightning/prompt/prompt.lbc.native.css +2 -0
  107. package/src/lightning/purifyLib/purify.js +838 -586
  108. package/src/lightning/radioGroup/form-element.slds.css +1 -1
  109. package/src/lightning/radioGroup/radioGroup.js-meta.xml +3 -0
  110. package/src/lightning/select/form-element.slds.css +1 -1
  111. package/src/lightning/select/select.slds.css +1 -1
  112. package/src/lightning/sldsUtilsThemes/sldsUtilsThemes.css +6 -4
  113. package/src/lightning/tabBar/tab-bar.slds.css +17 -3
  114. package/src/lightning/textarea/form-element.slds.css +1 -1
  115. package/src/lightning/textarea/textarea.js-meta.xml +3 -0
  116. package/src/lightning/timepicker/form-element.slds.css +1 -1
  117. package/src/lightning/toast/button-icon.slds.css +23 -15
  118. package/src/lightning/toast/toast.js-meta.xml +3 -0
  119. package/src/lightning/toastContainer/toastContainer.js-meta.xml +3 -0
  120. package/src/lightning/tooltipLibrary/tooltipLibrary.js +15 -4
  121. package/src/lightning/utilsPrivate/utilsPrivate.js +4 -4
  122. package/src/lightning/verticalNavigationOverflow/button.slds.css +18 -10
@@ -1,3 +1,4 @@
1
+ /*! @license DOMPurify 3.0.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.11/LICENSE */
1
2
  /**
2
3
  * This library is copied from https://github.com/cure53/DOMPurify
3
4
  *
@@ -7,7 +8,7 @@
7
8
  *
8
9
  * How?
9
10
  * In order to update this library, follow the steps:
10
- * Current version: 2.2.6
11
+ * Current version: 3.0.11
11
12
  *
12
13
  * 1. Check if there is a new version published at https://github.com/cure53/DOMPurify/releases or find the specific release you need.
13
14
  * 2. Download the Source Code zip of that release.
@@ -18,169 +19,197 @@
18
19
  * 5.2 Default export the variable `purify`
19
20
  */
20
21
 
21
- function _toConsumableArray(arr) {
22
- if (Array.isArray(arr)) {
23
- for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
24
- arr2[i] = arr[i];
25
- }
26
- return arr2;
27
- } else {
28
- return Array.from(arr);
29
- }
30
- }
31
-
32
- var hasOwnProperty = Object.hasOwnProperty,
33
- setPrototypeOf = Object.setPrototypeOf,
34
- isFrozen = Object.isFrozen,
35
- getPrototypeOf = Object.getPrototypeOf,
36
- getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
37
- var freeze = Object.freeze,
38
- seal = Object.seal,
39
- create = Object.create; // eslint-disable-line import/no-mutable-exports
40
-
41
- var _ref = typeof Reflect !== 'undefined' && Reflect,
42
- apply = _ref.apply,
43
- construct = _ref.construct;
44
-
45
- if (!apply) {
46
- apply = function apply(fun, thisValue, args) {
47
- return fun.apply(thisValue, args);
48
- };
49
- }
50
-
22
+ 'use strict';
23
+
24
+ const {
25
+ entries,
26
+ setPrototypeOf,
27
+ isFrozen,
28
+ getPrototypeOf,
29
+ getOwnPropertyDescriptor,
30
+ } = Object;
31
+ let { freeze, seal, create } = Object; // eslint-disable-line import/no-mutable-exports
32
+ let { apply, construct } = typeof Reflect !== 'undefined' && Reflect;
51
33
  if (!freeze) {
52
34
  freeze = function freeze(x) {
53
35
  return x;
54
36
  };
55
37
  }
56
-
57
38
  if (!seal) {
58
39
  seal = function seal(x) {
59
40
  return x;
60
41
  };
61
42
  }
62
-
43
+ if (!apply) {
44
+ apply = function apply(fun, thisValue, args) {
45
+ return fun.apply(thisValue, args);
46
+ };
47
+ }
63
48
  if (!construct) {
64
49
  construct = function construct(Func, args) {
65
- return new (Function.prototype.bind.apply(
66
- Func,
67
- [null].concat(_toConsumableArray(args))
68
- ))();
50
+ return new Func(...args);
69
51
  };
70
52
  }
53
+ const arrayForEach = unapply(Array.prototype.forEach);
54
+ const arrayPop = unapply(Array.prototype.pop);
55
+ const arrayPush = unapply(Array.prototype.push);
56
+ const stringToLowerCase = unapply(String.prototype.toLowerCase);
57
+ const stringToString = unapply(String.prototype.toString);
58
+ const stringMatch = unapply(String.prototype.match);
59
+ const stringReplace = unapply(String.prototype.replace);
60
+ const stringIndexOf = unapply(String.prototype.indexOf);
61
+ const stringTrim = unapply(String.prototype.trim);
62
+ const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
63
+ const regExpTest = unapply(RegExp.prototype.test);
64
+ const typeErrorCreate = unconstruct(TypeError);
71
65
 
72
- var arrayForEach = unapply(Array.prototype.forEach);
73
- var arrayPop = unapply(Array.prototype.pop);
74
- var arrayPush = unapply(Array.prototype.push);
75
-
76
- var stringToLowerCase = unapply(String.prototype.toLowerCase);
77
- var stringMatch = unapply(String.prototype.match);
78
- var stringReplace = unapply(String.prototype.replace);
79
- var stringIndexOf = unapply(String.prototype.indexOf);
80
- var stringTrim = unapply(String.prototype.trim);
81
-
82
- var regExpTest = unapply(RegExp.prototype.test);
83
-
84
- var typeErrorCreate = unconstruct(TypeError);
85
-
66
+ /**
67
+ * Creates a new function that calls the given function with a specified thisArg and arguments.
68
+ *
69
+ * @param {Function} func - The function to be wrapped and called.
70
+ * @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
71
+ */
86
72
  function unapply(func) {
87
73
  return function (thisArg) {
88
74
  for (
89
75
  var _len = arguments.length,
90
- args = Array(_len > 1 ? _len - 1 : 0),
76
+ args = new Array(_len > 1 ? _len - 1 : 0),
91
77
  _key = 1;
92
78
  _key < _len;
93
79
  _key++
94
80
  ) {
95
81
  args[_key - 1] = arguments[_key];
96
82
  }
97
-
98
83
  return apply(func, thisArg, args);
99
84
  };
100
85
  }
101
86
 
87
+ /**
88
+ * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
89
+ *
90
+ * @param {Function} func - The constructor function to be wrapped and called.
91
+ * @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
92
+ */
102
93
  function unconstruct(func) {
103
94
  return function () {
104
95
  for (
105
- var _len2 = arguments.length, args = Array(_len2), _key2 = 0;
96
+ var _len2 = arguments.length, args = new Array(_len2), _key2 = 0;
106
97
  _key2 < _len2;
107
98
  _key2++
108
99
  ) {
109
100
  args[_key2] = arguments[_key2];
110
101
  }
111
-
112
102
  return construct(func, args);
113
103
  };
114
104
  }
115
105
 
116
- /* Add properties to a lookup table */
106
+ /**
107
+ * Add properties to a lookup table
108
+ *
109
+ * @param {Object} set - The set to which elements will be added.
110
+ * @param {Array} array - The array containing elements to be added to the set.
111
+ * @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
112
+ * @returns {Object} The modified set with added elements.
113
+ */
117
114
  function addToSet(set, array) {
115
+ let transformCaseFunc =
116
+ arguments.length > 2 && arguments[2] !== undefined
117
+ ? arguments[2]
118
+ : stringToLowerCase;
118
119
  if (setPrototypeOf) {
119
120
  // Make 'in' and truthy checks like Boolean(set.constructor)
120
121
  // independent of any properties defined on Object.prototype.
121
122
  // Prevent prototype setters from intercepting set as a this value.
122
123
  setPrototypeOf(set, null);
123
124
  }
124
-
125
- var l = array.length;
125
+ let l = array.length;
126
126
  while (l--) {
127
- var element = array[l];
127
+ let element = array[l];
128
128
  if (typeof element === 'string') {
129
- var lcElement = stringToLowerCase(element);
129
+ const lcElement = transformCaseFunc(element);
130
130
  if (lcElement !== element) {
131
131
  // Config presets (e.g. tags.js, attrs.js) are immutable.
132
132
  if (!isFrozen(array)) {
133
133
  array[l] = lcElement;
134
134
  }
135
-
136
135
  element = lcElement;
137
136
  }
138
137
  }
139
-
140
138
  set[element] = true;
141
139
  }
142
-
143
140
  return set;
144
141
  }
145
142
 
146
- /* Shallow clone an object */
147
- function clone(object) {
148
- var newObject = create(null);
149
-
150
- var property = void 0;
151
- for (property in object) {
152
- if (apply(hasOwnProperty, object, [property])) {
153
- newObject[property] = object[property];
143
+ /**
144
+ * Clean up an array to harden against CSPP
145
+ *
146
+ * @param {Array} array - The array to be cleaned.
147
+ * @returns {Array} The cleaned version of the array
148
+ */
149
+ function cleanArray(array) {
150
+ for (let index = 0; index < array.length; index++) {
151
+ const isPropertyExist = objectHasOwnProperty(array, index);
152
+ if (!isPropertyExist) {
153
+ array[index] = null;
154
154
  }
155
155
  }
156
+ return array;
157
+ }
156
158
 
159
+ /**
160
+ * Shallow clone an object
161
+ *
162
+ * @param {Object} object - The object to be cloned.
163
+ * @returns {Object} A new object that copies the original.
164
+ */
165
+ function clone(object) {
166
+ const newObject = create(null);
167
+ for (const [property, value] of entries(object)) {
168
+ const isPropertyExist = objectHasOwnProperty(object, property);
169
+ if (isPropertyExist) {
170
+ if (Array.isArray(value)) {
171
+ newObject[property] = cleanArray(value);
172
+ } else if (
173
+ value &&
174
+ typeof value === 'object' &&
175
+ value.constructor === Object
176
+ ) {
177
+ newObject[property] = clone(value);
178
+ } else {
179
+ newObject[property] = value;
180
+ }
181
+ }
182
+ }
157
183
  return newObject;
158
184
  }
159
185
 
160
- /* IE10 doesn't support __lookupGetter__ so lets'
161
- * simulate it. It also automatically checks
162
- * if the prop is function or getter and behaves
163
- * accordingly. */
186
+ /**
187
+ * This method automatically checks if the prop is function or getter and behaves accordingly.
188
+ *
189
+ * @param {Object} object - The object to look up the getter function in its prototype chain.
190
+ * @param {String} prop - The property name for which to find the getter function.
191
+ * @returns {Function} The getter function found in the prototype chain or a fallback function.
192
+ */
164
193
  function lookupGetter(object, prop) {
165
194
  while (object !== null) {
166
- var desc = getOwnPropertyDescriptor(object, prop);
195
+ const desc = getOwnPropertyDescriptor(object, prop);
167
196
  if (desc) {
168
197
  if (desc.get) {
169
198
  return unapply(desc.get);
170
199
  }
171
-
172
200
  if (typeof desc.value === 'function') {
173
201
  return unapply(desc.value);
174
202
  }
175
203
  }
176
-
177
204
  object = getPrototypeOf(object);
178
205
  }
179
-
180
- return null;
206
+ function fallbackValue() {
207
+ return null;
208
+ }
209
+ return fallbackValue;
181
210
  }
182
211
 
183
- var html = freeze([
212
+ const html$1 = freeze([
184
213
  'a',
185
214
  'abbr',
186
215
  'acronym',
@@ -301,7 +330,7 @@ var html = freeze([
301
330
  ]);
302
331
 
303
332
  // SVG
304
- var svg = freeze([
333
+ const svg$1 = freeze([
305
334
  'svg',
306
335
  'a',
307
336
  'altglyph',
@@ -346,8 +375,7 @@ var svg = freeze([
346
375
  'view',
347
376
  'vkern',
348
377
  ]);
349
-
350
- var svgFilters = freeze([
378
+ const svgFilters = freeze([
351
379
  'feBlend',
352
380
  'feColorMatrix',
353
381
  'feComponentTransfer',
@@ -356,12 +384,14 @@ var svgFilters = freeze([
356
384
  'feDiffuseLighting',
357
385
  'feDisplacementMap',
358
386
  'feDistantLight',
387
+ 'feDropShadow',
359
388
  'feFlood',
360
389
  'feFuncA',
361
390
  'feFuncB',
362
391
  'feFuncG',
363
392
  'feFuncR',
364
393
  'feGaussianBlur',
394
+ 'feImage',
365
395
  'feMerge',
366
396
  'feMergeNode',
367
397
  'feMorphology',
@@ -377,13 +407,11 @@ var svgFilters = freeze([
377
407
  // We still need to know them so that we can do namespace
378
408
  // checks properly in case one wants to add them to
379
409
  // allow-list.
380
- var svgDisallowed = freeze([
410
+ const svgDisallowed = freeze([
381
411
  'animate',
382
412
  'color-profile',
383
413
  'cursor',
384
414
  'discard',
385
- 'fedropshadow',
386
- 'feimage',
387
415
  'font-face',
388
416
  'font-face-format',
389
417
  'font-face-name',
@@ -403,8 +431,7 @@ var svgDisallowed = freeze([
403
431
  'unknown',
404
432
  'use',
405
433
  ]);
406
-
407
- var mathMl = freeze([
434
+ const mathMl$1 = freeze([
408
435
  'math',
409
436
  'menclose',
410
437
  'merror',
@@ -434,11 +461,12 @@ var mathMl = freeze([
434
461
  'mtr',
435
462
  'munder',
436
463
  'munderover',
464
+ 'mprescripts',
437
465
  ]);
438
466
 
439
467
  // Similarly to SVG, we want to know all MathML elements,
440
468
  // even those that we disallow by default.
441
- var mathMlDisallowed = freeze([
469
+ const mathMlDisallowed = freeze([
442
470
  'maction',
443
471
  'maligngroup',
444
472
  'malignmark',
@@ -455,10 +483,9 @@ var mathMlDisallowed = freeze([
455
483
  'mprescripts',
456
484
  'none',
457
485
  ]);
486
+ const text = freeze(['#text']);
458
487
 
459
- var text = freeze(['#text']);
460
-
461
- var html$1 = freeze([
488
+ const html = freeze([
462
489
  'accept',
463
490
  'action',
464
491
  'align',
@@ -523,6 +550,7 @@ var html$1 = freeze([
523
550
  'multiple',
524
551
  'muted',
525
552
  'name',
553
+ 'nonce',
526
554
  'noshade',
527
555
  'novalidate',
528
556
  'nowrap',
@@ -566,9 +594,9 @@ var html$1 = freeze([
566
594
  'value',
567
595
  'width',
568
596
  'xmlns',
597
+ 'slot',
569
598
  ]);
570
-
571
- var svg$1 = freeze([
599
+ const svg = freeze([
572
600
  'accent-height',
573
601
  'accumulate',
574
602
  'additive',
@@ -720,6 +748,7 @@ var svg$1 = freeze([
720
748
  'targetx',
721
749
  'targety',
722
750
  'transform',
751
+ 'transform-origin',
723
752
  'text-anchor',
724
753
  'text-decoration',
725
754
  'text-rendering',
@@ -751,8 +780,7 @@ var svg$1 = freeze([
751
780
  'z',
752
781
  'zoomandpan',
753
782
  ]);
754
-
755
- var mathMl$1 = freeze([
783
+ const mathMl = freeze([
756
784
  'accent',
757
785
  'accentunder',
758
786
  'align',
@@ -807,8 +835,7 @@ var mathMl$1 = freeze([
807
835
  'width',
808
836
  'xmlns',
809
837
  ]);
810
-
811
- var xml = freeze([
838
+ const xml = freeze([
812
839
  'xlink:href',
813
840
  'xml:id',
814
841
  'xlink:title',
@@ -817,63 +844,55 @@ var xml = freeze([
817
844
  ]);
818
845
 
819
846
  // eslint-disable-next-line unicorn/better-regex
820
- var MUSTACHE_EXPR = seal(/\{\{[\s\S]*|[\s\S]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
821
- var ERB_EXPR = seal(/<%[\s\S]*|[\s\S]*%>/gm);
822
- var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
823
- var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
824
- var IS_ALLOWED_URI = seal(
825
- /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
847
+ const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
848
+ const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
849
+ const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
850
+ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
851
+ const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
852
+ const IS_ALLOWED_URI = seal(
853
+ /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
826
854
  );
827
- var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
828
- var ATTR_WHITESPACE = seal(
855
+
856
+ const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
857
+ const ATTR_WHITESPACE = seal(
829
858
  /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
830
859
  );
831
860
 
832
- var _typeof =
833
- typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'
834
- ? function (obj) {
835
- return typeof obj;
836
- }
837
- : function (obj) {
838
- return obj &&
839
- typeof Symbol === 'function' &&
840
- obj.constructor === Symbol &&
841
- obj !== Symbol.prototype
842
- ? 'symbol'
843
- : typeof obj;
844
- };
845
-
846
- function _toConsumableArray$1(arr) {
847
- if (Array.isArray(arr)) {
848
- for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
849
- arr2[i] = arr[i];
850
- }
851
- return arr2;
852
- } else {
853
- return Array.from(arr);
854
- }
855
- }
856
-
857
- var getGlobal = function getGlobal() {
861
+ const DOCTYPE_NAME = seal(/^html$/i);
862
+ const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
863
+
864
+ var EXPRESSIONS = /*#__PURE__*/ Object.freeze({
865
+ __proto__: null,
866
+ MUSTACHE_EXPR: MUSTACHE_EXPR,
867
+ ERB_EXPR: ERB_EXPR,
868
+ TMPLIT_EXPR: TMPLIT_EXPR,
869
+ DATA_ATTR: DATA_ATTR,
870
+ ARIA_ATTR: ARIA_ATTR,
871
+ IS_ALLOWED_URI: IS_ALLOWED_URI,
872
+ IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
873
+ ATTR_WHITESPACE: ATTR_WHITESPACE,
874
+ DOCTYPE_NAME: DOCTYPE_NAME,
875
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT,
876
+ });
877
+
878
+ const getGlobal = function getGlobal() {
858
879
  return typeof window === 'undefined' ? null : window;
859
880
  };
860
881
 
861
882
  /**
862
883
  * Creates a no-op policy for internal use only.
863
884
  * Don't export this function outside this module!
864
- * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
865
- * @param {Document} document The document object (to determine policy name suffix)
866
- * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
867
- * are not supported).
885
+ * @param {TrustedTypePolicyFactory} trustedTypes The policy factory.
886
+ * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
887
+ * @return {TrustedTypePolicy} The policy created (or null, if Trusted Types
888
+ * are not supported or creating the policy failed).
868
889
  */
869
- var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(
890
+ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(
870
891
  trustedTypes,
871
- document
892
+ purifyHostElement
872
893
  ) {
873
894
  if (
874
- (typeof trustedTypes === 'undefined'
875
- ? 'undefined'
876
- : _typeof(trustedTypes)) !== 'object' ||
895
+ typeof trustedTypes !== 'object' ||
877
896
  typeof trustedTypes.createPolicy !== 'function'
878
897
  ) {
879
898
  return null;
@@ -882,21 +901,19 @@ var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(
882
901
  // Allow the callers to control the unique policy name
883
902
  // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
884
903
  // Policy creation with duplicate names throws in Trusted Types.
885
- var suffix = null;
886
- var ATTR_NAME = 'data-tt-policy-suffix';
887
- if (
888
- document.currentScript &&
889
- document.currentScript.hasAttribute(ATTR_NAME)
890
- ) {
891
- suffix = document.currentScript.getAttribute(ATTR_NAME);
904
+ let suffix = null;
905
+ const ATTR_NAME = 'data-tt-policy-suffix';
906
+ if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
907
+ suffix = purifyHostElement.getAttribute(ATTR_NAME);
892
908
  }
893
-
894
- var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
895
-
909
+ const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
896
910
  try {
897
911
  return trustedTypes.createPolicy(policyName, {
898
- createHTML: function createHTML(html$$1) {
899
- return html$$1;
912
+ createHTML(html) {
913
+ return html;
914
+ },
915
+ createScriptURL(scriptUrl) {
916
+ return scriptUrl;
900
917
  },
901
918
  });
902
919
  } catch (_) {
@@ -909,61 +926,49 @@ var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(
909
926
  return null;
910
927
  }
911
928
  };
912
-
913
929
  function createDOMPurify() {
914
- var window =
930
+ let window =
915
931
  arguments.length > 0 && arguments[0] !== undefined
916
932
  ? arguments[0]
917
933
  : getGlobal();
918
-
919
- var DOMPurify = function DOMPurify(root) {
920
- return createDOMPurify(root);
921
- };
934
+ const DOMPurify = (root) => createDOMPurify(root);
922
935
 
923
936
  /**
924
937
  * Version label, exposed for easier checks
925
938
  * if DOMPurify is up to date or not
926
939
  */
927
- DOMPurify.version = '2.2.6';
940
+ DOMPurify.version = '3.0.11';
928
941
 
929
942
  /**
930
943
  * Array of elements that DOMPurify removed during sanitation.
931
944
  * Empty if nothing was removed.
932
945
  */
933
946
  DOMPurify.removed = [];
934
-
935
947
  if (!window || !window.document || window.document.nodeType !== 9) {
936
948
  // Not running in a browser, provide a factory function
937
949
  // so that you can pass your own Window
938
950
  DOMPurify.isSupported = false;
939
-
940
951
  return DOMPurify;
941
952
  }
942
-
943
- var originalDocument = window.document;
944
-
945
- var document = window.document;
946
- var DocumentFragment = window.DocumentFragment,
947
- HTMLTemplateElement = window.HTMLTemplateElement,
948
- Node = window.Node,
949
- Element = window.Element,
950
- NodeFilter = window.NodeFilter,
951
- _window$NamedNodeMap = window.NamedNodeMap,
952
- NamedNodeMap =
953
- _window$NamedNodeMap === undefined
954
- ? window.NamedNodeMap || window.MozNamedAttrMap
955
- : _window$NamedNodeMap,
956
- Text = window.Text,
957
- Comment = window.Comment,
958
- DOMParser = window.DOMParser,
959
- trustedTypes = window.trustedTypes;
960
-
961
- var ElementPrototype = Element.prototype;
962
-
963
- var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
964
- var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
965
- var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
966
- var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
953
+ let { document } = window;
954
+ const originalDocument = document;
955
+ const currentScript = originalDocument.currentScript;
956
+ const {
957
+ DocumentFragment,
958
+ HTMLTemplateElement,
959
+ Node,
960
+ Element,
961
+ NodeFilter,
962
+ NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
963
+ HTMLFormElement,
964
+ DOMParser,
965
+ trustedTypes,
966
+ } = window;
967
+ const ElementPrototype = Element.prototype;
968
+ const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
969
+ const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
970
+ const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
971
+ const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
967
972
 
968
973
  // As per issue #47, the web-components registry is inherited by a
969
974
  // new document created via createHTMLDocument. As per the spec
@@ -972,52 +977,41 @@ function createDOMPurify() {
972
977
  // document, so we use that as our parent document to ensure nothing
973
978
  // is inherited.
974
979
  if (typeof HTMLTemplateElement === 'function') {
975
- var template = document.createElement('template');
980
+ const template = document.createElement('template');
976
981
  if (template.content && template.content.ownerDocument) {
977
982
  document = template.content.ownerDocument;
978
983
  }
979
984
  }
980
-
981
- var trustedTypesPolicy = _createTrustedTypesPolicy(
982
- trustedTypes,
983
- originalDocument
984
- );
985
- var emptyHTML =
986
- trustedTypesPolicy && RETURN_TRUSTED_TYPE
987
- ? trustedTypesPolicy.createHTML('')
988
- : '';
989
-
990
- var _document = document,
991
- implementation = _document.implementation,
992
- createNodeIterator = _document.createNodeIterator,
993
- getElementsByTagName = _document.getElementsByTagName,
994
- createDocumentFragment = _document.createDocumentFragment;
995
- var importNode = originalDocument.importNode;
996
-
997
- var documentMode = {};
998
- try {
999
- documentMode = clone(document).documentMode
1000
- ? document.documentMode
1001
- : {};
1002
- } catch (_) {}
1003
-
1004
- var hooks = {};
985
+ let trustedTypesPolicy;
986
+ let emptyHTML = '';
987
+ const {
988
+ implementation,
989
+ createNodeIterator,
990
+ createDocumentFragment,
991
+ getElementsByTagName,
992
+ } = document;
993
+ const { importNode } = originalDocument;
994
+ let hooks = {};
1005
995
 
1006
996
  /**
1007
997
  * Expose whether this browser supports running the full DOMPurify.
1008
998
  */
1009
999
  DOMPurify.isSupported =
1000
+ typeof entries === 'function' &&
1001
+ typeof getParentNode === 'function' &&
1010
1002
  implementation &&
1011
- typeof implementation.createHTMLDocument !== 'undefined' &&
1012
- documentMode !== 9;
1013
-
1014
- var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
1015
- ERB_EXPR$$1 = ERB_EXPR,
1016
- DATA_ATTR$$1 = DATA_ATTR,
1017
- ARIA_ATTR$$1 = ARIA_ATTR,
1018
- IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
1019
- ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
1020
- var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;
1003
+ implementation.createHTMLDocument !== undefined;
1004
+ const {
1005
+ MUSTACHE_EXPR,
1006
+ ERB_EXPR,
1007
+ TMPLIT_EXPR,
1008
+ DATA_ATTR,
1009
+ ARIA_ATTR,
1010
+ IS_SCRIPT_OR_DATA,
1011
+ ATTR_WHITESPACE,
1012
+ CUSTOM_ELEMENT,
1013
+ } = EXPRESSIONS;
1014
+ let { IS_ALLOWED_URI: IS_ALLOWED_URI$1 } = EXPRESSIONS;
1021
1015
 
1022
1016
  /**
1023
1017
  * We consider the elements and attributes below to be safe. Ideally
@@ -1025,101 +1019,135 @@ function createDOMPurify() {
1025
1019
  */
1026
1020
 
1027
1021
  /* allowed element names */
1028
-
1029
- var ALLOWED_TAGS = null;
1030
- var DEFAULT_ALLOWED_TAGS = addToSet(
1031
- {},
1032
- [].concat(
1033
- _toConsumableArray$1(html),
1034
- _toConsumableArray$1(svg),
1035
- _toConsumableArray$1(svgFilters),
1036
- _toConsumableArray$1(mathMl),
1037
- _toConsumableArray$1(text)
1038
- )
1039
- );
1022
+ let ALLOWED_TAGS = null;
1023
+ const DEFAULT_ALLOWED_TAGS = addToSet({}, [
1024
+ ...html$1,
1025
+ ...svg$1,
1026
+ ...svgFilters,
1027
+ ...mathMl$1,
1028
+ ...text,
1029
+ ]);
1040
1030
 
1041
1031
  /* Allowed attribute names */
1042
- var ALLOWED_ATTR = null;
1043
- var DEFAULT_ALLOWED_ATTR = addToSet(
1044
- {},
1045
- [].concat(
1046
- _toConsumableArray$1(html$1),
1047
- _toConsumableArray$1(svg$1),
1048
- _toConsumableArray$1(mathMl$1),
1049
- _toConsumableArray$1(xml)
1050
- )
1032
+ let ALLOWED_ATTR = null;
1033
+ const DEFAULT_ALLOWED_ATTR = addToSet({}, [
1034
+ ...html,
1035
+ ...svg,
1036
+ ...mathMl,
1037
+ ...xml,
1038
+ ]);
1039
+
1040
+ /*
1041
+ * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
1042
+ * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
1043
+ * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
1044
+ * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
1045
+ */
1046
+ let CUSTOM_ELEMENT_HANDLING = Object.seal(
1047
+ create(null, {
1048
+ tagNameCheck: {
1049
+ writable: true,
1050
+ configurable: false,
1051
+ enumerable: true,
1052
+ value: null,
1053
+ },
1054
+ attributeNameCheck: {
1055
+ writable: true,
1056
+ configurable: false,
1057
+ enumerable: true,
1058
+ value: null,
1059
+ },
1060
+ allowCustomizedBuiltInElements: {
1061
+ writable: true,
1062
+ configurable: false,
1063
+ enumerable: true,
1064
+ value: false,
1065
+ },
1066
+ })
1051
1067
  );
1052
1068
 
1053
1069
  /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
1054
- var FORBID_TAGS = null;
1070
+ let FORBID_TAGS = null;
1055
1071
 
1056
1072
  /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
1057
- var FORBID_ATTR = null;
1073
+ let FORBID_ATTR = null;
1058
1074
 
1059
1075
  /* Decide if ARIA attributes are okay */
1060
- var ALLOW_ARIA_ATTR = true;
1076
+ let ALLOW_ARIA_ATTR = true;
1061
1077
 
1062
1078
  /* Decide if custom data attributes are okay */
1063
- var ALLOW_DATA_ATTR = true;
1079
+ let ALLOW_DATA_ATTR = true;
1064
1080
 
1065
1081
  /* Decide if unknown protocols are okay */
1066
- var ALLOW_UNKNOWN_PROTOCOLS = false;
1082
+ let ALLOW_UNKNOWN_PROTOCOLS = false;
1083
+
1084
+ /* Decide if self-closing tags in attributes are allowed.
1085
+ * Usually removed due to a mXSS issue in jQuery 3.0 */
1086
+ let ALLOW_SELF_CLOSE_IN_ATTR = true;
1067
1087
 
1068
1088
  /* Output should be safe for common template engines.
1069
1089
  * This means, DOMPurify removes data attributes, mustaches and ERB
1070
1090
  */
1071
- var SAFE_FOR_TEMPLATES = false;
1091
+ let SAFE_FOR_TEMPLATES = false;
1072
1092
 
1073
1093
  /* Decide if document with <html>... should be returned */
1074
- var WHOLE_DOCUMENT = false;
1094
+ let WHOLE_DOCUMENT = false;
1075
1095
 
1076
1096
  /* Track whether config is already set on this instance of DOMPurify. */
1077
- var SET_CONFIG = false;
1097
+ let SET_CONFIG = false;
1078
1098
 
1079
1099
  /* Decide if all elements (e.g. style, script) must be children of
1080
1100
  * document.body. By default, browsers might move them to document.head */
1081
- var FORCE_BODY = false;
1101
+ let FORCE_BODY = false;
1082
1102
 
1083
1103
  /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
1084
1104
  * string (or a TrustedHTML object if Trusted Types are supported).
1085
1105
  * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
1086
1106
  */
1087
- var RETURN_DOM = false;
1107
+ let RETURN_DOM = false;
1088
1108
 
1089
1109
  /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
1090
1110
  * string (or a TrustedHTML object if Trusted Types are supported) */
1091
- var RETURN_DOM_FRAGMENT = false;
1092
-
1093
- /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM
1094
- * `Node` is imported into the current `Document`. If this flag is not enabled the
1095
- * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by
1096
- * DOMPurify.
1097
- *
1098
- * This defaults to `true` starting DOMPurify 2.2.0. Note that setting it to `false`
1099
- * might cause XSS from attacks hidden in closed shadowroots in case the browser
1100
- * supports Declarative Shadow: DOM https://web.dev/declarative-shadow-dom/
1101
- */
1102
- var RETURN_DOM_IMPORT = true;
1111
+ let RETURN_DOM_FRAGMENT = false;
1103
1112
 
1104
1113
  /* Try to return a Trusted Type object instead of a string, return a string in
1105
1114
  * case Trusted Types are not supported */
1106
- var RETURN_TRUSTED_TYPE = false;
1115
+ let RETURN_TRUSTED_TYPE = false;
1107
1116
 
1108
- /* Output should be free from DOM clobbering attacks? */
1109
- var SANITIZE_DOM = true;
1117
+ /* Output should be free from DOM clobbering attacks?
1118
+ * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
1119
+ */
1120
+ let SANITIZE_DOM = true;
1121
+
1122
+ /* Achieve full DOM Clobbering protection by isolating the namespace of named
1123
+ * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
1124
+ *
1125
+ * HTML/DOM spec rules that enable DOM Clobbering:
1126
+ * - Named Access on Window (§7.3.3)
1127
+ * - DOM Tree Accessors (§3.1.5)
1128
+ * - Form Element Parent-Child Relations (§4.10.3)
1129
+ * - Iframe srcdoc / Nested WindowProxies (§4.8.5)
1130
+ * - HTMLCollection (§4.2.10.2)
1131
+ *
1132
+ * Namespace isolation is implemented by prefixing `id` and `name` attributes
1133
+ * with a constant string, i.e., `user-content-`
1134
+ */
1135
+ let SANITIZE_NAMED_PROPS = false;
1136
+ const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
1110
1137
 
1111
1138
  /* Keep element content when removing element? */
1112
- var KEEP_CONTENT = true;
1139
+ let KEEP_CONTENT = true;
1113
1140
 
1114
1141
  /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
1115
1142
  * of importing it into a new Document and returning a sanitized copy */
1116
- var IN_PLACE = false;
1143
+ let IN_PLACE = false;
1117
1144
 
1118
1145
  /* Allow usage of profiles like html, svg and mathMl */
1119
- var USE_PROFILES = {};
1146
+ let USE_PROFILES = {};
1120
1147
 
1121
1148
  /* Tags to ignore content of when KEEP_CONTENT is true */
1122
- var FORBID_CONTENTS = addToSet({}, [
1149
+ let FORBID_CONTENTS = null;
1150
+ const DEFAULT_FORBID_CONTENTS = addToSet({}, [
1123
1151
  'annotation-xml',
1124
1152
  'audio',
1125
1153
  'colgroup',
@@ -1148,8 +1176,8 @@ function createDOMPurify() {
1148
1176
  ]);
1149
1177
 
1150
1178
  /* Tags that are safe for data: URIs */
1151
- var DATA_URI_TAGS = null;
1152
- var DEFAULT_DATA_URI_TAGS = addToSet({}, [
1179
+ let DATA_URI_TAGS = null;
1180
+ const DEFAULT_DATA_URI_TAGS = addToSet({}, [
1153
1181
  'audio',
1154
1182
  'video',
1155
1183
  'img',
@@ -1159,8 +1187,8 @@ function createDOMPurify() {
1159
1187
  ]);
1160
1188
 
1161
1189
  /* Attributes safe for values like "javascript:" */
1162
- var URI_SAFE_ATTRIBUTES = null;
1163
- var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [
1190
+ let URI_SAFE_ATTRIBUTES = null;
1191
+ const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [
1164
1192
  'alt',
1165
1193
  'class',
1166
1194
  'for',
@@ -1169,20 +1197,44 @@ function createDOMPurify() {
1169
1197
  'name',
1170
1198
  'pattern',
1171
1199
  'placeholder',
1200
+ 'role',
1172
1201
  'summary',
1173
1202
  'title',
1174
1203
  'value',
1175
1204
  'style',
1176
1205
  'xmlns',
1177
1206
  ]);
1207
+ const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
1208
+ const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
1209
+ const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
1210
+ /* Document namespace */
1211
+ let NAMESPACE = HTML_NAMESPACE;
1212
+ let IS_EMPTY_INPUT = false;
1213
+
1214
+ /* Allowed XHTML+XML namespaces */
1215
+ let ALLOWED_NAMESPACES = null;
1216
+ const DEFAULT_ALLOWED_NAMESPACES = addToSet(
1217
+ {},
1218
+ [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE],
1219
+ stringToString
1220
+ );
1221
+
1222
+ /* Parsing of strict XHTML documents */
1223
+ let PARSER_MEDIA_TYPE = null;
1224
+ const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
1225
+ const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
1226
+ let transformCaseFunc = null;
1178
1227
 
1179
1228
  /* Keep a reference to config to pass to hooks */
1180
- var CONFIG = null;
1229
+ let CONFIG = null;
1181
1230
 
1182
1231
  /* Ideally, do not touch anything below this line */
1183
1232
  /* ______________________________________________ */
1184
1233
 
1185
- var formElement = document.createElement('form');
1234
+ const formElement = document.createElement('form');
1235
+ const isRegexOrFunction = function isRegexOrFunction(testValue) {
1236
+ return testValue instanceof RegExp || testValue instanceof Function;
1237
+ };
1186
1238
 
1187
1239
  /**
1188
1240
  * _parseConfig
@@ -1190,92 +1242,141 @@ function createDOMPurify() {
1190
1242
  * @param {Object} cfg optional config literal
1191
1243
  */
1192
1244
  // eslint-disable-next-line complexity
1193
- var _parseConfig = function _parseConfig(cfg) {
1245
+ const _parseConfig = function _parseConfig() {
1246
+ let cfg =
1247
+ arguments.length > 0 && arguments[0] !== undefined
1248
+ ? arguments[0]
1249
+ : {};
1194
1250
  if (CONFIG && CONFIG === cfg) {
1195
1251
  return;
1196
1252
  }
1197
1253
 
1198
1254
  /* Shield configuration object from tampering */
1199
- if (
1200
- !cfg ||
1201
- (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !==
1202
- 'object'
1203
- ) {
1255
+ if (!cfg || typeof cfg !== 'object') {
1204
1256
  cfg = {};
1205
1257
  }
1206
1258
 
1207
1259
  /* Shield configuration object from prototype pollution */
1208
1260
  cfg = clone(cfg);
1261
+ PARSER_MEDIA_TYPE =
1262
+ // eslint-disable-next-line unicorn/prefer-includes
1263
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1
1264
+ ? DEFAULT_PARSER_MEDIA_TYPE
1265
+ : cfg.PARSER_MEDIA_TYPE;
1266
+
1267
+ // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
1268
+ transformCaseFunc =
1269
+ PARSER_MEDIA_TYPE === 'application/xhtml+xml'
1270
+ ? stringToString
1271
+ : stringToLowerCase;
1209
1272
 
1210
1273
  /* Set configuration parameters */
1211
- ALLOWED_TAGS =
1212
- 'ALLOWED_TAGS' in cfg
1213
- ? addToSet({}, cfg.ALLOWED_TAGS)
1214
- : DEFAULT_ALLOWED_TAGS;
1215
- ALLOWED_ATTR =
1216
- 'ALLOWED_ATTR' in cfg
1217
- ? addToSet({}, cfg.ALLOWED_ATTR)
1218
- : DEFAULT_ALLOWED_ATTR;
1219
- URI_SAFE_ATTRIBUTES =
1220
- 'ADD_URI_SAFE_ATTR' in cfg
1221
- ? addToSet(
1222
- clone(DEFAULT_URI_SAFE_ATTRIBUTES),
1223
- cfg.ADD_URI_SAFE_ATTR
1224
- )
1225
- : DEFAULT_URI_SAFE_ATTRIBUTES;
1226
- DATA_URI_TAGS =
1227
- 'ADD_DATA_URI_TAGS' in cfg
1228
- ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS)
1229
- : DEFAULT_DATA_URI_TAGS;
1230
- FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
1231
- FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
1232
- USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
1274
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS')
1275
+ ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc)
1276
+ : DEFAULT_ALLOWED_TAGS;
1277
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR')
1278
+ ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc)
1279
+ : DEFAULT_ALLOWED_ATTR;
1280
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES')
1281
+ ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString)
1282
+ : DEFAULT_ALLOWED_NAMESPACES;
1283
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR')
1284
+ ? addToSet(
1285
+ clone(DEFAULT_URI_SAFE_ATTRIBUTES),
1286
+ // eslint-disable-line indent
1287
+ cfg.ADD_URI_SAFE_ATTR,
1288
+ // eslint-disable-line indent
1289
+ transformCaseFunc // eslint-disable-line indent
1290
+ ) // eslint-disable-line indent
1291
+ : DEFAULT_URI_SAFE_ATTRIBUTES;
1292
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS')
1293
+ ? addToSet(
1294
+ clone(DEFAULT_DATA_URI_TAGS),
1295
+ // eslint-disable-line indent
1296
+ cfg.ADD_DATA_URI_TAGS,
1297
+ // eslint-disable-line indent
1298
+ transformCaseFunc // eslint-disable-line indent
1299
+ ) // eslint-disable-line indent
1300
+ : DEFAULT_DATA_URI_TAGS;
1301
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS')
1302
+ ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc)
1303
+ : DEFAULT_FORBID_CONTENTS;
1304
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS')
1305
+ ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc)
1306
+ : {};
1307
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR')
1308
+ ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc)
1309
+ : {};
1310
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES')
1311
+ ? cfg.USE_PROFILES
1312
+ : false;
1233
1313
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
1234
1314
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
1235
1315
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
1316
+ ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
1236
1317
  SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
1237
1318
  WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
1238
1319
  RETURN_DOM = cfg.RETURN_DOM || false; // Default false
1239
1320
  RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
1240
- RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true
1241
1321
  RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
1242
1322
  FORCE_BODY = cfg.FORCE_BODY || false; // Default false
1243
1323
  SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
1324
+ SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
1244
1325
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
1245
1326
  IN_PLACE = cfg.IN_PLACE || false; // Default false
1246
- IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
1327
+ IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
1328
+ NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
1329
+ CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
1330
+ if (
1331
+ cfg.CUSTOM_ELEMENT_HANDLING &&
1332
+ isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)
1333
+ ) {
1334
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck =
1335
+ cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
1336
+ }
1337
+ if (
1338
+ cfg.CUSTOM_ELEMENT_HANDLING &&
1339
+ isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)
1340
+ ) {
1341
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck =
1342
+ cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
1343
+ }
1344
+ if (
1345
+ cfg.CUSTOM_ELEMENT_HANDLING &&
1346
+ typeof cfg.CUSTOM_ELEMENT_HANDLING
1347
+ .allowCustomizedBuiltInElements === 'boolean'
1348
+ ) {
1349
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements =
1350
+ cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
1351
+ }
1247
1352
  if (SAFE_FOR_TEMPLATES) {
1248
1353
  ALLOW_DATA_ATTR = false;
1249
1354
  }
1250
-
1251
1355
  if (RETURN_DOM_FRAGMENT) {
1252
1356
  RETURN_DOM = true;
1253
1357
  }
1254
1358
 
1255
1359
  /* Parse profile info */
1256
1360
  if (USE_PROFILES) {
1257
- ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text)));
1361
+ ALLOWED_TAGS = addToSet({}, text);
1258
1362
  ALLOWED_ATTR = [];
1259
1363
  if (USE_PROFILES.html === true) {
1260
- addToSet(ALLOWED_TAGS, html);
1261
- addToSet(ALLOWED_ATTR, html$1);
1364
+ addToSet(ALLOWED_TAGS, html$1);
1365
+ addToSet(ALLOWED_ATTR, html);
1262
1366
  }
1263
-
1264
1367
  if (USE_PROFILES.svg === true) {
1265
- addToSet(ALLOWED_TAGS, svg);
1266
- addToSet(ALLOWED_ATTR, svg$1);
1368
+ addToSet(ALLOWED_TAGS, svg$1);
1369
+ addToSet(ALLOWED_ATTR, svg);
1267
1370
  addToSet(ALLOWED_ATTR, xml);
1268
1371
  }
1269
-
1270
1372
  if (USE_PROFILES.svgFilters === true) {
1271
1373
  addToSet(ALLOWED_TAGS, svgFilters);
1272
- addToSet(ALLOWED_ATTR, svg$1);
1374
+ addToSet(ALLOWED_ATTR, svg);
1273
1375
  addToSet(ALLOWED_ATTR, xml);
1274
1376
  }
1275
-
1276
1377
  if (USE_PROFILES.mathMl === true) {
1277
- addToSet(ALLOWED_TAGS, mathMl);
1278
- addToSet(ALLOWED_ATTR, mathMl$1);
1378
+ addToSet(ALLOWED_TAGS, mathMl$1);
1379
+ addToSet(ALLOWED_ATTR, mathMl);
1279
1380
  addToSet(ALLOWED_ATTR, xml);
1280
1381
  }
1281
1382
  }
@@ -1285,20 +1386,26 @@ function createDOMPurify() {
1285
1386
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
1286
1387
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
1287
1388
  }
1288
-
1289
- addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
1389
+ addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
1290
1390
  }
1291
-
1292
1391
  if (cfg.ADD_ATTR) {
1293
1392
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
1294
1393
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
1295
1394
  }
1296
-
1297
- addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
1395
+ addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
1298
1396
  }
1299
-
1300
1397
  if (cfg.ADD_URI_SAFE_ATTR) {
1301
- addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
1398
+ addToSet(
1399
+ URI_SAFE_ATTRIBUTES,
1400
+ cfg.ADD_URI_SAFE_ATTR,
1401
+ transformCaseFunc
1402
+ );
1403
+ }
1404
+ if (cfg.FORBID_CONTENTS) {
1405
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
1406
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
1407
+ }
1408
+ addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
1302
1409
  }
1303
1410
 
1304
1411
  /* Add #text in case KEEP_CONTENT is set to true */
@@ -1316,68 +1423,105 @@ function createDOMPurify() {
1316
1423
  addToSet(ALLOWED_TAGS, ['tbody']);
1317
1424
  delete FORBID_TAGS.tbody;
1318
1425
  }
1426
+ if (cfg.TRUSTED_TYPES_POLICY) {
1427
+ if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
1428
+ throw typeErrorCreate(
1429
+ 'TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.'
1430
+ );
1431
+ }
1432
+ if (
1433
+ typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function'
1434
+ ) {
1435
+ throw typeErrorCreate(
1436
+ 'TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.'
1437
+ );
1438
+ }
1439
+
1440
+ // Overwrite existing TrustedTypes policy.
1441
+ trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
1442
+
1443
+ // Sign local variables required by `sanitize`.
1444
+ emptyHTML = trustedTypesPolicy.createHTML('');
1445
+ } else {
1446
+ // Uninitialized policy, attempt to initialize the internal dompurify policy.
1447
+ if (trustedTypesPolicy === undefined) {
1448
+ trustedTypesPolicy = _createTrustedTypesPolicy(
1449
+ trustedTypes,
1450
+ currentScript
1451
+ );
1452
+ }
1453
+
1454
+ // If creating the internal policy succeeded sign internal variables.
1455
+ if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
1456
+ emptyHTML = trustedTypesPolicy.createHTML('');
1457
+ }
1458
+ }
1319
1459
 
1320
1460
  // Prevent further manipulation of configuration.
1321
1461
  // Not available in IE8, Safari 5, etc.
1322
1462
  if (freeze) {
1323
1463
  freeze(cfg);
1324
1464
  }
1325
-
1326
1465
  CONFIG = cfg;
1327
1466
  };
1328
-
1329
- var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [
1467
+ const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, [
1330
1468
  'mi',
1331
1469
  'mo',
1332
1470
  'mn',
1333
1471
  'ms',
1334
1472
  'mtext',
1335
1473
  ]);
1336
-
1337
- var HTML_INTEGRATION_POINTS = addToSet({}, [
1474
+ const HTML_INTEGRATION_POINTS = addToSet({}, [
1338
1475
  'foreignobject',
1339
1476
  'desc',
1340
1477
  'title',
1341
1478
  'annotation-xml',
1342
1479
  ]);
1343
1480
 
1481
+ // Certain elements are allowed in both SVG and HTML
1482
+ // namespace. We need to specify them explicitly
1483
+ // so that they don't get erroneously deleted from
1484
+ // HTML namespace.
1485
+ const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [
1486
+ 'title',
1487
+ 'style',
1488
+ 'font',
1489
+ 'a',
1490
+ 'script',
1491
+ ]);
1492
+
1344
1493
  /* Keep track of all possible SVG and MathML tags
1345
1494
  * so that we can perform the namespace checks
1346
1495
  * correctly. */
1347
- var ALL_SVG_TAGS = addToSet({}, svg);
1348
- addToSet(ALL_SVG_TAGS, svgFilters);
1349
- addToSet(ALL_SVG_TAGS, svgDisallowed);
1350
-
1351
- var ALL_MATHML_TAGS = addToSet({}, mathMl);
1352
- addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
1353
-
1354
- var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
1355
- var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
1356
- var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
1496
+ const ALL_SVG_TAGS = addToSet({}, [
1497
+ ...svg$1,
1498
+ ...svgFilters,
1499
+ ...svgDisallowed,
1500
+ ]);
1501
+ const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
1357
1502
 
1358
1503
  /**
1359
- *
1360
- *
1361
1504
  * @param {Element} element a DOM element whose namespace is being checked
1362
1505
  * @returns {boolean} Return false if the element has a
1363
1506
  * namespace that a spec-compliant parser would never
1364
1507
  * return. Return true otherwise.
1365
1508
  */
1366
- var _checkValidNamespace = function _checkValidNamespace(element) {
1367
- var parent = getParentNode(element);
1509
+ const _checkValidNamespace = function _checkValidNamespace(element) {
1510
+ let parent = getParentNode(element);
1368
1511
 
1369
1512
  // In JSDOM, if we're inside shadow DOM, then parentNode
1370
1513
  // can be null. We just simulate parent in this case.
1371
1514
  if (!parent || !parent.tagName) {
1372
1515
  parent = {
1373
- namespaceURI: HTML_NAMESPACE,
1516
+ namespaceURI: NAMESPACE,
1374
1517
  tagName: 'template',
1375
1518
  };
1376
1519
  }
1377
-
1378
- var tagName = stringToLowerCase(element.tagName);
1379
- var parentTagName = stringToLowerCase(parent.tagName);
1380
-
1520
+ const tagName = stringToLowerCase(element.tagName);
1521
+ const parentTagName = stringToLowerCase(parent.tagName);
1522
+ if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
1523
+ return false;
1524
+ }
1381
1525
  if (element.namespaceURI === SVG_NAMESPACE) {
1382
1526
  // The only way to switch from HTML namespace to SVG
1383
1527
  // is via <svg>. If it happens via any other tag, then
@@ -1386,7 +1530,7 @@ function createDOMPurify() {
1386
1530
  return tagName === 'svg';
1387
1531
  }
1388
1532
 
1389
- // The only way to switch from MathML to SVG is via
1533
+ // The only way to switch from MathML to SVG is via`
1390
1534
  // svg if parent is either <annotation-xml> or MathML
1391
1535
  // text integration points.
1392
1536
  if (parent.namespaceURI === MATHML_NAMESPACE) {
@@ -1401,7 +1545,6 @@ function createDOMPurify() {
1401
1545
  // spec. All others are disallowed in SVG namespace.
1402
1546
  return Boolean(ALL_SVG_TAGS[tagName]);
1403
1547
  }
1404
-
1405
1548
  if (element.namespaceURI === MATHML_NAMESPACE) {
1406
1549
  // The only way to switch from HTML namespace to MathML
1407
1550
  // is via <math>. If it happens via any other tag, then
@@ -1422,7 +1565,6 @@ function createDOMPurify() {
1422
1565
  // spec. All others are disallowed in MathML namespace.
1423
1566
  return Boolean(ALL_MATHML_TAGS[tagName]);
1424
1567
  }
1425
-
1426
1568
  if (element.namespaceURI === HTML_NAMESPACE) {
1427
1569
  // The only way to switch from SVG to HTML is via
1428
1570
  // HTML integration points, and from MathML to HTML
@@ -1433,7 +1575,6 @@ function createDOMPurify() {
1433
1575
  ) {
1434
1576
  return false;
1435
1577
  }
1436
-
1437
1578
  if (
1438
1579
  parent.namespaceURI === MATHML_NAMESPACE &&
1439
1580
  !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]
@@ -1441,29 +1582,27 @@ function createDOMPurify() {
1441
1582
  return false;
1442
1583
  }
1443
1584
 
1444
- // Certain elements are allowed in both SVG and HTML
1445
- // namespace. We need to specify them explicitly
1446
- // so that they don't get erronously deleted from
1447
- // HTML namespace.
1448
- var commonSvgAndHTMLElements = addToSet({}, [
1449
- 'title',
1450
- 'style',
1451
- 'font',
1452
- 'a',
1453
- 'script',
1454
- ]);
1455
-
1456
1585
  // We disallow tags that are specific for MathML
1457
1586
  // or SVG and should never appear in HTML namespace
1458
1587
  return (
1459
1588
  !ALL_MATHML_TAGS[tagName] &&
1460
- (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName])
1589
+ (COMMON_SVG_AND_HTML_ELEMENTS[tagName] ||
1590
+ !ALL_SVG_TAGS[tagName])
1461
1591
  );
1462
1592
  }
1463
1593
 
1594
+ // For XHTML and XML documents that support custom namespaces
1595
+ if (
1596
+ PARSER_MEDIA_TYPE === 'application/xhtml+xml' &&
1597
+ ALLOWED_NAMESPACES[element.namespaceURI]
1598
+ ) {
1599
+ return true;
1600
+ }
1601
+
1464
1602
  // The code should never reach this place (this means
1465
1603
  // that the element somehow got namespace that is not
1466
- // HTML, SVG or MathML). Return false just in case.
1604
+ // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
1605
+ // Return false just in case.
1467
1606
  return false;
1468
1607
  };
1469
1608
 
@@ -1472,16 +1611,15 @@ function createDOMPurify() {
1472
1611
  *
1473
1612
  * @param {Node} node a DOM node
1474
1613
  */
1475
- var _forceRemove = function _forceRemove(node) {
1476
- arrayPush(DOMPurify.removed, { element: node });
1614
+ const _forceRemove = function _forceRemove(node) {
1615
+ arrayPush(DOMPurify.removed, {
1616
+ element: node,
1617
+ });
1477
1618
  try {
1619
+ // eslint-disable-next-line unicorn/prefer-dom-node-remove
1478
1620
  node.parentNode.removeChild(node);
1479
1621
  } catch (_) {
1480
- try {
1481
- node.outerHTML = emptyHTML;
1482
- } catch (_) {
1483
- node.remove();
1484
- }
1622
+ node.remove();
1485
1623
  }
1486
1624
  };
1487
1625
 
@@ -1491,7 +1629,7 @@ function createDOMPurify() {
1491
1629
  * @param {String} name an Attribute name
1492
1630
  * @param {Node} node a DOM node
1493
1631
  */
1494
- var _removeAttribute = function _removeAttribute(name, node) {
1632
+ const _removeAttribute = function _removeAttribute(name, node) {
1495
1633
  try {
1496
1634
  arrayPush(DOMPurify.removed, {
1497
1635
  attribute: node.getAttributeNode(name),
@@ -1503,8 +1641,20 @@ function createDOMPurify() {
1503
1641
  from: node,
1504
1642
  });
1505
1643
  }
1506
-
1507
1644
  node.removeAttribute(name);
1645
+
1646
+ // We void attribute values for unremovable "is"" attributes
1647
+ if (name === 'is' && !ALLOWED_ATTR[name]) {
1648
+ if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
1649
+ try {
1650
+ _forceRemove(node);
1651
+ } catch (_) {}
1652
+ } else {
1653
+ try {
1654
+ node.setAttribute(name, '');
1655
+ } catch (_) {}
1656
+ }
1657
+ }
1508
1658
  };
1509
1659
 
1510
1660
  /**
@@ -1513,68 +1663,89 @@ function createDOMPurify() {
1513
1663
  * @param {String} dirty a string of dirty markup
1514
1664
  * @return {Document} a DOM, filled with the dirty markup
1515
1665
  */
1516
- var _initDocument = function _initDocument(dirty) {
1666
+ const _initDocument = function _initDocument(dirty) {
1517
1667
  /* Create a HTML document */
1518
- var doc = void 0;
1519
- var leadingWhitespace = void 0;
1520
-
1668
+ let doc = null;
1669
+ let leadingWhitespace = null;
1521
1670
  if (FORCE_BODY) {
1522
1671
  dirty = '<remove></remove>' + dirty;
1523
1672
  } else {
1524
1673
  /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
1525
- var matches = stringMatch(dirty, /^[\r\n\t ]+/);
1674
+ const matches = stringMatch(dirty, /^[\r\n\t ]+/);
1526
1675
  leadingWhitespace = matches && matches[0];
1527
1676
  }
1528
-
1529
- var dirtyPayload = trustedTypesPolicy
1677
+ if (
1678
+ PARSER_MEDIA_TYPE === 'application/xhtml+xml' &&
1679
+ NAMESPACE === HTML_NAMESPACE
1680
+ ) {
1681
+ // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
1682
+ dirty =
1683
+ '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' +
1684
+ dirty +
1685
+ '</body></html>';
1686
+ }
1687
+ const dirtyPayload = trustedTypesPolicy
1530
1688
  ? trustedTypesPolicy.createHTML(dirty)
1531
1689
  : dirty;
1532
- /* Use the DOMParser API by default, fallback later if needs be */
1533
- try {
1534
- doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
1535
- } catch (_) {}
1690
+ /*
1691
+ * Use the DOMParser API by default, fallback later if needs be
1692
+ * DOMParser not work for svg when has multiple root element.
1693
+ */
1694
+ if (NAMESPACE === HTML_NAMESPACE) {
1695
+ try {
1696
+ doc = new DOMParser().parseFromString(
1697
+ dirtyPayload,
1698
+ PARSER_MEDIA_TYPE
1699
+ );
1700
+ } catch (_) {}
1701
+ }
1536
1702
 
1537
1703
  /* Use createHTMLDocument in case DOMParser is not available */
1538
1704
  if (!doc || !doc.documentElement) {
1539
- doc = implementation.createHTMLDocument('');
1540
- var _doc = doc,
1541
- body = _doc.body;
1542
-
1543
- body.parentNode.removeChild(body.parentNode.firstElementChild);
1544
- body.outerHTML = dirtyPayload;
1705
+ doc = implementation.createDocument(NAMESPACE, 'template', null);
1706
+ try {
1707
+ doc.documentElement.innerHTML = IS_EMPTY_INPUT
1708
+ ? emptyHTML
1709
+ : dirtyPayload;
1710
+ } catch (_) {
1711
+ // Syntax error if dirtyPayload is invalid xml
1712
+ }
1545
1713
  }
1546
-
1714
+ const body = doc.body || doc.documentElement;
1547
1715
  if (dirty && leadingWhitespace) {
1548
- doc.body.insertBefore(
1716
+ body.insertBefore(
1549
1717
  document.createTextNode(leadingWhitespace),
1550
- doc.body.childNodes[0] || null
1718
+ body.childNodes[0] || null
1551
1719
  );
1552
1720
  }
1553
1721
 
1554
1722
  /* Work on whole document or just its body */
1555
- return getElementsByTagName.call(
1556
- doc,
1557
- WHOLE_DOCUMENT ? 'html' : 'body'
1558
- )[0];
1723
+ if (NAMESPACE === HTML_NAMESPACE) {
1724
+ return getElementsByTagName.call(
1725
+ doc,
1726
+ WHOLE_DOCUMENT ? 'html' : 'body'
1727
+ )[0];
1728
+ }
1729
+ return WHOLE_DOCUMENT ? doc.documentElement : body;
1559
1730
  };
1560
1731
 
1561
1732
  /**
1562
- * _createIterator
1733
+ * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
1563
1734
  *
1564
- * @param {Document} root document/fragment to create iterator for
1565
- * @return {Iterator} iterator instance
1735
+ * @param {Node} root The root element or node to start traversing on.
1736
+ * @return {NodeIterator} The created NodeIterator
1566
1737
  */
1567
- var _createIterator = function _createIterator(root) {
1738
+ const _createNodeIterator = function _createNodeIterator(root) {
1568
1739
  return createNodeIterator.call(
1569
1740
  root.ownerDocument || root,
1570
1741
  root,
1742
+ // eslint-disable-next-line no-bitwise
1571
1743
  NodeFilter.SHOW_ELEMENT |
1572
1744
  NodeFilter.SHOW_COMMENT |
1573
- NodeFilter.SHOW_TEXT,
1574
- function () {
1575
- return NodeFilter.FILTER_ACCEPT;
1576
- },
1577
- false
1745
+ NodeFilter.SHOW_TEXT |
1746
+ NodeFilter.SHOW_PROCESSING_INSTRUCTION |
1747
+ NodeFilter.SHOW_CDATA_SECTION,
1748
+ null
1578
1749
  );
1579
1750
  };
1580
1751
 
@@ -1584,43 +1755,29 @@ function createDOMPurify() {
1584
1755
  * @param {Node} elm element to check for clobbering attacks
1585
1756
  * @return {Boolean} true if clobbered, false if safe
1586
1757
  */
1587
- var _isClobbered = function _isClobbered(elm) {
1588
- if (elm instanceof Text || elm instanceof Comment) {
1589
- return false;
1590
- }
1591
-
1592
- if (
1593
- typeof elm.nodeName !== 'string' ||
1594
- typeof elm.textContent !== 'string' ||
1595
- typeof elm.removeChild !== 'function' ||
1596
- !(elm.attributes instanceof NamedNodeMap) ||
1597
- typeof elm.removeAttribute !== 'function' ||
1598
- typeof elm.setAttribute !== 'function' ||
1599
- typeof elm.namespaceURI !== 'string' ||
1600
- typeof elm.insertBefore !== 'function'
1601
- ) {
1602
- return true;
1603
- }
1604
-
1605
- return false;
1758
+ const _isClobbered = function _isClobbered(elm) {
1759
+ return (
1760
+ elm instanceof HTMLFormElement &&
1761
+ (typeof elm.nodeName !== 'string' ||
1762
+ typeof elm.textContent !== 'string' ||
1763
+ typeof elm.removeChild !== 'function' ||
1764
+ !(elm.attributes instanceof NamedNodeMap) ||
1765
+ typeof elm.removeAttribute !== 'function' ||
1766
+ typeof elm.setAttribute !== 'function' ||
1767
+ typeof elm.namespaceURI !== 'string' ||
1768
+ typeof elm.insertBefore !== 'function' ||
1769
+ typeof elm.hasChildNodes !== 'function')
1770
+ );
1606
1771
  };
1607
1772
 
1608
1773
  /**
1609
- * _isNode
1774
+ * Checks whether the given object is a DOM node.
1610
1775
  *
1611
- * @param {Node} obj object to check whether it's a DOM node
1776
+ * @param {Node} object object to check whether it's a DOM node
1612
1777
  * @return {Boolean} true is object is a DOM node
1613
1778
  */
1614
- var _isNode = function _isNode(object) {
1615
- return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) ===
1616
- 'object'
1617
- ? object instanceof Node
1618
- : object &&
1619
- (typeof object === 'undefined'
1620
- ? 'undefined'
1621
- : _typeof(object)) === 'object' &&
1622
- typeof object.nodeType === 'number' &&
1623
- typeof object.nodeName === 'string';
1779
+ const _isNode = function _isNode(object) {
1780
+ return typeof Node === 'function' && object instanceof Node;
1624
1781
  };
1625
1782
 
1626
1783
  /**
@@ -1631,12 +1788,11 @@ function createDOMPurify() {
1631
1788
  * @param {Node} currentNode node to work on with the hook
1632
1789
  * @param {Object} data additional hook parameters
1633
1790
  */
1634
- var _executeHook = function _executeHook(entryPoint, currentNode, data) {
1791
+ const _executeHook = function _executeHook(entryPoint, currentNode, data) {
1635
1792
  if (!hooks[entryPoint]) {
1636
1793
  return;
1637
1794
  }
1638
-
1639
- arrayForEach(hooks[entryPoint], function (hook) {
1795
+ arrayForEach(hooks[entryPoint], (hook) => {
1640
1796
  hook.call(DOMPurify, currentNode, data, CONFIG);
1641
1797
  });
1642
1798
  };
@@ -1651,8 +1807,8 @@ function createDOMPurify() {
1651
1807
  * @param {Node} currentNode to check for permission to exist
1652
1808
  * @return {Boolean} true if node was killed, false if left alive
1653
1809
  */
1654
- var _sanitizeElements = function _sanitizeElements(currentNode) {
1655
- var content = void 0;
1810
+ const _sanitizeElements = function _sanitizeElements(currentNode) {
1811
+ let content = null;
1656
1812
 
1657
1813
  /* Execute a hook if present */
1658
1814
  _executeHook('beforeSanitizeElements', currentNode, null);
@@ -1663,26 +1819,19 @@ function createDOMPurify() {
1663
1819
  return true;
1664
1820
  }
1665
1821
 
1666
- /* Check if tagname contains Unicode */
1667
- if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) {
1668
- _forceRemove(currentNode);
1669
- return true;
1670
- }
1671
-
1672
1822
  /* Now let's check the element's type and name */
1673
- var tagName = stringToLowerCase(currentNode.nodeName);
1823
+ const tagName = transformCaseFunc(currentNode.nodeName);
1674
1824
 
1675
1825
  /* Execute a hook if present */
1676
1826
  _executeHook('uponSanitizeElement', currentNode, {
1677
- tagName: tagName,
1827
+ tagName,
1678
1828
  allowedTags: ALLOWED_TAGS,
1679
1829
  });
1680
1830
 
1681
1831
  /* Detect mXSS attempts abusing namespace confusion */
1682
1832
  if (
1833
+ currentNode.hasChildNodes() &&
1683
1834
  !_isNode(currentNode.firstElementChild) &&
1684
- (!_isNode(currentNode.content) ||
1685
- !_isNode(currentNode.content.firstElementChild)) &&
1686
1835
  regExpTest(/<[/\w]/g, currentNode.innerHTML) &&
1687
1836
  regExpTest(/<[/\w]/g, currentNode.textContent)
1688
1837
  ) {
@@ -1690,21 +1839,46 @@ function createDOMPurify() {
1690
1839
  return true;
1691
1840
  }
1692
1841
 
1842
+ /* Remove any ocurrence of processing instructions */
1843
+ if (currentNode.nodeType === 7) {
1844
+ _forceRemove(currentNode);
1845
+ return true;
1846
+ }
1847
+
1693
1848
  /* Remove element if anything forbids its presence */
1694
1849
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1850
+ /* Check if we have a custom element to handle */
1851
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
1852
+ if (
1853
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp &&
1854
+ regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)
1855
+ ) {
1856
+ return false;
1857
+ }
1858
+ if (
1859
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function &&
1860
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)
1861
+ ) {
1862
+ return false;
1863
+ }
1864
+ }
1865
+
1695
1866
  /* Keep content except for bad-listed elements */
1696
1867
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1697
- var parentNode = getParentNode(currentNode);
1698
- var childNodes = getChildNodes(currentNode);
1699
- var childCount = childNodes.length;
1700
- for (var i = childCount - 1; i >= 0; --i) {
1701
- parentNode.insertBefore(
1702
- cloneNode(childNodes[i], true),
1703
- getNextSibling(currentNode)
1704
- );
1868
+ const parentNode =
1869
+ getParentNode(currentNode) || currentNode.parentNode;
1870
+ const childNodes =
1871
+ getChildNodes(currentNode) || currentNode.childNodes;
1872
+ if (childNodes && parentNode) {
1873
+ const childCount = childNodes.length;
1874
+ for (let i = childCount - 1; i >= 0; --i) {
1875
+ parentNode.insertBefore(
1876
+ cloneNode(childNodes[i], true),
1877
+ getNextSibling(currentNode)
1878
+ );
1879
+ }
1705
1880
  }
1706
1881
  }
1707
-
1708
1882
  _forceRemove(currentNode);
1709
1883
  return true;
1710
1884
  }
@@ -1718,9 +1892,12 @@ function createDOMPurify() {
1718
1892
  return true;
1719
1893
  }
1720
1894
 
1895
+ /* Make sure that older browsers don't get fallback-tag mXSS */
1721
1896
  if (
1722
- (tagName === 'noscript' || tagName === 'noembed') &&
1723
- regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)
1897
+ (tagName === 'noscript' ||
1898
+ tagName === 'noembed' ||
1899
+ tagName === 'noframes') &&
1900
+ regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)
1724
1901
  ) {
1725
1902
  _forceRemove(currentNode);
1726
1903
  return true;
@@ -1730,8 +1907,9 @@ function createDOMPurify() {
1730
1907
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
1731
1908
  /* Get the element's text content */
1732
1909
  content = currentNode.textContent;
1733
- content = stringReplace(content, MUSTACHE_EXPR$$1, ' ');
1734
- content = stringReplace(content, ERB_EXPR$$1, ' ');
1910
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], (expr) => {
1911
+ content = stringReplace(content, expr, ' ');
1912
+ });
1735
1913
  if (currentNode.textContent !== content) {
1736
1914
  arrayPush(DOMPurify.removed, {
1737
1915
  element: currentNode.cloneNode(),
@@ -1742,7 +1920,6 @@ function createDOMPurify() {
1742
1920
 
1743
1921
  /* Execute a hook if present */
1744
1922
  _executeHook('afterSanitizeElements', currentNode, null);
1745
-
1746
1923
  return false;
1747
1924
  };
1748
1925
 
@@ -1755,7 +1932,7 @@ function createDOMPurify() {
1755
1932
  * @return {Boolean} Returns true if `value` is valid, otherwise false.
1756
1933
  */
1757
1934
  // eslint-disable-next-line complexity
1758
- var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1935
+ const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1759
1936
  /* Make sure attribute cannot clobber */
1760
1937
  if (
1761
1938
  SANITIZE_DOM &&
@@ -1769,17 +1946,59 @@ function createDOMPurify() {
1769
1946
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1770
1947
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1771
1948
  We don't need to check the value; it's always URI safe. */
1772
- if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName));
1773
- else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName));
1949
+ if (
1950
+ ALLOW_DATA_ATTR &&
1951
+ !FORBID_ATTR[lcName] &&
1952
+ regExpTest(DATA_ATTR, lcName)
1953
+ );
1954
+ else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName));
1774
1955
  else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
1775
- return false;
1776
-
1956
+ if (
1957
+ // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1958
+ // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1959
+ // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1960
+ (_isBasicCustomElement(lcTag) &&
1961
+ ((CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp &&
1962
+ regExpTest(
1963
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck,
1964
+ lcTag
1965
+ )) ||
1966
+ (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof
1967
+ Function &&
1968
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag))) &&
1969
+ ((CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof
1970
+ RegExp &&
1971
+ regExpTest(
1972
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck,
1973
+ lcName
1974
+ )) ||
1975
+ (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof
1976
+ Function &&
1977
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck(
1978
+ lcName
1979
+ )))) ||
1980
+ // Alternative, second condition checks if it's an `is`-attribute, AND
1981
+ // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1982
+ (lcName === 'is' &&
1983
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements &&
1984
+ ((CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp &&
1985
+ regExpTest(
1986
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck,
1987
+ value
1988
+ )) ||
1989
+ (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof
1990
+ Function &&
1991
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))))
1992
+ );
1993
+ else {
1994
+ return false;
1995
+ }
1777
1996
  /* Check value is safe. First, is attr inert? If so, is safe */
1778
1997
  } else if (URI_SAFE_ATTRIBUTES[lcName]);
1779
1998
  else if (
1780
1999
  regExpTest(
1781
- IS_ALLOWED_URI$$1,
1782
- stringReplace(value, ATTR_WHITESPACE$$1, '')
2000
+ IS_ALLOWED_URI$1,
2001
+ stringReplace(value, ATTR_WHITESPACE, '')
1783
2002
  )
1784
2003
  );
1785
2004
  else if (
@@ -1793,18 +2012,30 @@ function createDOMPurify() {
1793
2012
  else if (
1794
2013
  ALLOW_UNKNOWN_PROTOCOLS &&
1795
2014
  !regExpTest(
1796
- IS_SCRIPT_OR_DATA$$1,
1797
- stringReplace(value, ATTR_WHITESPACE$$1, '')
2015
+ IS_SCRIPT_OR_DATA,
2016
+ stringReplace(value, ATTR_WHITESPACE, '')
1798
2017
  )
1799
2018
  );
1800
- else if (!value);
1801
- else {
2019
+ else if (value) {
1802
2020
  return false;
1803
- }
1804
-
2021
+ } else;
1805
2022
  return true;
1806
2023
  };
1807
2024
 
2025
+ /**
2026
+ * _isBasicCustomElement
2027
+ * checks if at least one dash is included in tagName, and it's not the first char
2028
+ * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
2029
+ *
2030
+ * @param {string} tagName name of the tag of the node to sanitize
2031
+ * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
2032
+ */
2033
+ const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
2034
+ return (
2035
+ tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT)
2036
+ );
2037
+ };
2038
+
1808
2039
  /**
1809
2040
  * _sanitizeAttributes
1810
2041
  *
@@ -1815,39 +2046,29 @@ function createDOMPurify() {
1815
2046
  *
1816
2047
  * @param {Node} currentNode to sanitize
1817
2048
  */
1818
- var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1819
- var attr = void 0;
1820
- var value = void 0;
1821
- var lcName = void 0;
1822
- var l = void 0;
2049
+ const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1823
2050
  /* Execute a hook if present */
1824
2051
  _executeHook('beforeSanitizeAttributes', currentNode, null);
1825
-
1826
- var attributes = currentNode.attributes;
2052
+ const { attributes } = currentNode;
1827
2053
 
1828
2054
  /* Check if we have attributes; if not we might have a text node */
1829
-
1830
2055
  if (!attributes) {
1831
2056
  return;
1832
2057
  }
1833
-
1834
- var hookEvent = {
2058
+ const hookEvent = {
1835
2059
  attrName: '',
1836
2060
  attrValue: '',
1837
2061
  keepAttr: true,
1838
2062
  allowedAttributes: ALLOWED_ATTR,
1839
2063
  };
1840
- l = attributes.length;
2064
+ let l = attributes.length;
1841
2065
 
1842
2066
  /* Go backwards over all attributes; safely remove bad ones */
1843
2067
  while (l--) {
1844
- attr = attributes[l];
1845
- var _attr = attr,
1846
- name = _attr.name,
1847
- namespaceURI = _attr.namespaceURI;
1848
-
1849
- value = stringTrim(attr.value);
1850
- lcName = stringToLowerCase(name);
2068
+ const attr = attributes[l];
2069
+ const { name, namespaceURI, value: attrValue } = attr;
2070
+ const lcName = transformCaseFunc(name);
2071
+ let value = name === 'value' ? attrValue : stringTrim(attrValue);
1851
2072
 
1852
2073
  /* Execute a hook if present */
1853
2074
  hookEvent.attrName = lcName;
@@ -1870,23 +2091,59 @@ function createDOMPurify() {
1870
2091
  }
1871
2092
 
1872
2093
  /* Work around a security issue in jQuery 3.0 */
1873
- if (regExpTest(/\/>/i, value)) {
2094
+ if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1874
2095
  _removeAttribute(name, currentNode);
1875
2096
  continue;
1876
2097
  }
1877
2098
 
1878
2099
  /* Sanitize attribute content to be template-safe */
1879
2100
  if (SAFE_FOR_TEMPLATES) {
1880
- value = stringReplace(value, MUSTACHE_EXPR$$1, ' ');
1881
- value = stringReplace(value, ERB_EXPR$$1, ' ');
2101
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], (expr) => {
2102
+ value = stringReplace(value, expr, ' ');
2103
+ });
1882
2104
  }
1883
2105
 
1884
2106
  /* Is `value` valid for this attribute? */
1885
- var lcTag = currentNode.nodeName.toLowerCase();
2107
+ const lcTag = transformCaseFunc(currentNode.nodeName);
1886
2108
  if (!_isValidAttribute(lcTag, lcName, value)) {
1887
2109
  continue;
1888
2110
  }
1889
2111
 
2112
+ /* Full DOM Clobbering protection via namespace isolation,
2113
+ * Prefix id and name attributes with `user-content-`
2114
+ */
2115
+ if (
2116
+ SANITIZE_NAMED_PROPS &&
2117
+ (lcName === 'id' || lcName === 'name')
2118
+ ) {
2119
+ // Remove the attribute with this value
2120
+ _removeAttribute(name, currentNode);
2121
+
2122
+ // Prefix the value and later re-create the attribute with the sanitized value
2123
+ value = SANITIZE_NAMED_PROPS_PREFIX + value;
2124
+ }
2125
+
2126
+ /* Handle attributes that require Trusted Types */
2127
+ if (
2128
+ trustedTypesPolicy &&
2129
+ typeof trustedTypes === 'object' &&
2130
+ typeof trustedTypes.getAttributeType === 'function'
2131
+ ) {
2132
+ if (namespaceURI);
2133
+ else {
2134
+ switch (trustedTypes.getAttributeType(lcTag, lcName)) {
2135
+ case 'TrustedHTML': {
2136
+ value = trustedTypesPolicy.createHTML(value);
2137
+ break;
2138
+ }
2139
+ case 'TrustedScriptURL': {
2140
+ value = trustedTypesPolicy.createScriptURL(value);
2141
+ break;
2142
+ }
2143
+ }
2144
+ }
2145
+ }
2146
+
1890
2147
  /* Handle invalid data-* attribute set by try-catching it */
1891
2148
  try {
1892
2149
  if (namespaceURI) {
@@ -1895,7 +2152,6 @@ function createDOMPurify() {
1895
2152
  /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1896
2153
  currentNode.setAttribute(name, value);
1897
2154
  }
1898
-
1899
2155
  arrayPop(DOMPurify.removed);
1900
2156
  } catch (_) {}
1901
2157
  }
@@ -1909,13 +2165,12 @@ function createDOMPurify() {
1909
2165
  *
1910
2166
  * @param {DocumentFragment} fragment to iterate over recursively
1911
2167
  */
1912
- var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1913
- var shadowNode = void 0;
1914
- var shadowIterator = _createIterator(fragment);
2168
+ const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
2169
+ let shadowNode = null;
2170
+ const shadowIterator = _createNodeIterator(fragment);
1915
2171
 
1916
2172
  /* Execute a hook if present */
1917
2173
  _executeHook('beforeSanitizeShadowDOM', fragment, null);
1918
-
1919
2174
  while ((shadowNode = shadowIterator.nextNode())) {
1920
2175
  /* Execute a hook if present */
1921
2176
  _executeHook('uponSanitizeShadowNode', shadowNode, null);
@@ -1943,50 +2198,40 @@ function createDOMPurify() {
1943
2198
  * Public method providing core sanitation functionality
1944
2199
  *
1945
2200
  * @param {String|Node} dirty string or DOM node
1946
- * @param {Object} configuration object
2201
+ * @param {Object} cfg object
1947
2202
  */
1948
2203
  // eslint-disable-next-line complexity
1949
- DOMPurify.sanitize = function (dirty, cfg) {
1950
- var body = void 0;
1951
- var importedNode = void 0;
1952
- var currentNode = void 0;
1953
- var oldNode = void 0;
1954
- var returnNode = void 0;
2204
+ DOMPurify.sanitize = function (dirty) {
2205
+ let cfg =
2206
+ arguments.length > 1 && arguments[1] !== undefined
2207
+ ? arguments[1]
2208
+ : {};
2209
+ let body = null;
2210
+ let importedNode = null;
2211
+ let currentNode = null;
2212
+ let returnNode = null;
1955
2213
  /* Make sure we have a string to sanitize.
1956
2214
  DO NOT return early, as this will return the wrong type if
1957
2215
  the user has requested a DOM object rather than a string */
1958
- if (!dirty) {
2216
+ IS_EMPTY_INPUT = !dirty;
2217
+ if (IS_EMPTY_INPUT) {
1959
2218
  dirty = '<!-->';
1960
2219
  }
1961
2220
 
1962
2221
  /* Stringify, in case dirty is an object */
1963
2222
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
1964
- // eslint-disable-next-line no-negated-condition
1965
- if (typeof dirty.toString !== 'function') {
1966
- throw typeErrorCreate('toString is not a function');
1967
- } else {
2223
+ if (typeof dirty.toString === 'function') {
1968
2224
  dirty = dirty.toString();
1969
2225
  if (typeof dirty !== 'string') {
1970
2226
  throw typeErrorCreate('dirty is not a string, aborting');
1971
2227
  }
2228
+ } else {
2229
+ throw typeErrorCreate('toString is not a function');
1972
2230
  }
1973
2231
  }
1974
2232
 
1975
- /* Check we can run. Otherwise fall back or ignore */
2233
+ /* Return dirty HTML if DOMPurify cannot run */
1976
2234
  if (!DOMPurify.isSupported) {
1977
- if (
1978
- _typeof(window.toStaticHTML) === 'object' ||
1979
- typeof window.toStaticHTML === 'function'
1980
- ) {
1981
- if (typeof dirty === 'string') {
1982
- return window.toStaticHTML(dirty);
1983
- }
1984
-
1985
- if (_isNode(dirty)) {
1986
- return window.toStaticHTML(dirty.outerHTML);
1987
- }
1988
- }
1989
-
1990
2235
  return dirty;
1991
2236
  }
1992
2237
 
@@ -2002,11 +2247,19 @@ function createDOMPurify() {
2002
2247
  if (typeof dirty === 'string') {
2003
2248
  IN_PLACE = false;
2004
2249
  }
2005
-
2006
- if (IN_PLACE);
2007
- else if (dirty instanceof Node) {
2250
+ if (IN_PLACE) {
2251
+ /* Do some early pre-sanitization to avoid unsafe root nodes */
2252
+ if (dirty.nodeName) {
2253
+ const tagName = transformCaseFunc(dirty.nodeName);
2254
+ if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
2255
+ throw typeErrorCreate(
2256
+ 'root node is forbidden and cannot be sanitized in-place'
2257
+ );
2258
+ }
2259
+ }
2260
+ } else if (dirty instanceof Node) {
2008
2261
  /* If dirty is a DOM element, append to an empty document to avoid
2009
- elements being stripped by the parser */
2262
+ elements being stripped by the parser */
2010
2263
  body = _initDocument('<!---->');
2011
2264
  importedNode = body.ownerDocument.importNode(dirty, true);
2012
2265
  if (
@@ -2018,7 +2271,7 @@ function createDOMPurify() {
2018
2271
  } else if (importedNode.nodeName === 'HTML') {
2019
2272
  body = importedNode;
2020
2273
  } else {
2021
- // eslint-disable-next-line unicorn/prefer-node-append
2274
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
2022
2275
  body.appendChild(importedNode);
2023
2276
  }
2024
2277
  } else {
@@ -2040,7 +2293,7 @@ function createDOMPurify() {
2040
2293
 
2041
2294
  /* Check we have a DOM node from the data */
2042
2295
  if (!body) {
2043
- return RETURN_DOM ? null : emptyHTML;
2296
+ return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
2044
2297
  }
2045
2298
  }
2046
2299
 
@@ -2050,15 +2303,10 @@ function createDOMPurify() {
2050
2303
  }
2051
2304
 
2052
2305
  /* Get node iterator */
2053
- var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
2306
+ const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
2054
2307
 
2055
2308
  /* Now start iterating over the created document */
2056
2309
  while ((currentNode = nodeIterator.nextNode())) {
2057
- /* Fix IE's strange behavior with manipulated textNodes #89 */
2058
- if (currentNode.nodeType === 3 && currentNode === oldNode) {
2059
- continue;
2060
- }
2061
-
2062
2310
  /* Sanitize tags and elements */
2063
2311
  if (_sanitizeElements(currentNode)) {
2064
2312
  continue;
@@ -2071,12 +2319,8 @@ function createDOMPurify() {
2071
2319
 
2072
2320
  /* Check attributes, sanitize if necessary */
2073
2321
  _sanitizeAttributes(currentNode);
2074
-
2075
- oldNode = currentNode;
2076
2322
  }
2077
2323
 
2078
- oldNode = null;
2079
-
2080
2324
  /* If we sanitized `dirty` in-place, return it. */
2081
2325
  if (IN_PLACE) {
2082
2326
  return dirty;
@@ -2086,45 +2330,53 @@ function createDOMPurify() {
2086
2330
  if (RETURN_DOM) {
2087
2331
  if (RETURN_DOM_FRAGMENT) {
2088
2332
  returnNode = createDocumentFragment.call(body.ownerDocument);
2089
-
2090
2333
  while (body.firstChild) {
2091
- // eslint-disable-next-line unicorn/prefer-node-append
2334
+ // eslint-disable-next-line unicorn/prefer-dom-node-append
2092
2335
  returnNode.appendChild(body.firstChild);
2093
2336
  }
2094
2337
  } else {
2095
2338
  returnNode = body;
2096
2339
  }
2097
-
2098
- if (RETURN_DOM_IMPORT) {
2340
+ if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
2099
2341
  /*
2100
- AdoptNode() is not used because internal state is not reset
2101
- (e.g. the past names map of a HTMLFormElement), this is safe
2102
- in theory but we would rather not risk another attack vector.
2103
- The state that is cloned by importNode() is explicitly defined
2104
- by the specs.
2105
- */
2342
+ AdoptNode() is not used because internal state is not reset
2343
+ (e.g. the past names map of a HTMLFormElement), this is safe
2344
+ in theory but we would rather not risk another attack vector.
2345
+ The state that is cloned by importNode() is explicitly defined
2346
+ by the specs.
2347
+ */
2106
2348
  returnNode = importNode.call(
2107
2349
  originalDocument,
2108
2350
  returnNode,
2109
2351
  true
2110
2352
  );
2111
2353
  }
2112
-
2113
2354
  return returnNode;
2114
2355
  }
2356
+ let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
2115
2357
 
2116
- var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
2358
+ /* Serialize doctype if allowed */
2359
+ if (
2360
+ WHOLE_DOCUMENT &&
2361
+ ALLOWED_TAGS['!doctype'] &&
2362
+ body.ownerDocument &&
2363
+ body.ownerDocument.doctype &&
2364
+ body.ownerDocument.doctype.name &&
2365
+ regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)
2366
+ ) {
2367
+ serializedHTML =
2368
+ '<!DOCTYPE ' +
2369
+ body.ownerDocument.doctype.name +
2370
+ '>\n' +
2371
+ serializedHTML;
2372
+ }
2117
2373
 
2118
2374
  /* Sanitize final string template-safe */
2119
2375
  if (SAFE_FOR_TEMPLATES) {
2120
- serializedHTML = stringReplace(
2121
- serializedHTML,
2122
- MUSTACHE_EXPR$$1,
2123
- ' '
2124
- );
2125
- serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');
2376
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], (expr) => {
2377
+ serializedHTML = stringReplace(serializedHTML, expr, ' ');
2378
+ });
2126
2379
  }
2127
-
2128
2380
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE
2129
2381
  ? trustedTypesPolicy.createHTML(serializedHTML)
2130
2382
  : serializedHTML;
@@ -2136,7 +2388,11 @@ function createDOMPurify() {
2136
2388
  *
2137
2389
  * @param {Object} cfg configuration object
2138
2390
  */
2139
- DOMPurify.setConfig = function (cfg) {
2391
+ DOMPurify.setConfig = function () {
2392
+ let cfg =
2393
+ arguments.length > 0 && arguments[0] !== undefined
2394
+ ? arguments[0]
2395
+ : {};
2140
2396
  _parseConfig(cfg);
2141
2397
  SET_CONFIG = true;
2142
2398
  };
@@ -2156,9 +2412,9 @@ function createDOMPurify() {
2156
2412
  * Uses last set config, if any. Otherwise, uses config defaults.
2157
2413
  * isValidAttribute
2158
2414
  *
2159
- * @param {string} tag Tag name of containing element.
2160
- * @param {string} attr Attribute name.
2161
- * @param {string} value Attribute value.
2415
+ * @param {String} tag Tag name of containing element.
2416
+ * @param {String} attr Attribute name.
2417
+ * @param {String} value Attribute value.
2162
2418
  * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
2163
2419
  */
2164
2420
  DOMPurify.isValidAttribute = function (tag, attr, value) {
@@ -2166,9 +2422,8 @@ function createDOMPurify() {
2166
2422
  if (!CONFIG) {
2167
2423
  _parseConfig({});
2168
2424
  }
2169
-
2170
- var lcTag = stringToLowerCase(tag);
2171
- var lcName = stringToLowerCase(attr);
2425
+ const lcTag = transformCaseFunc(tag);
2426
+ const lcName = transformCaseFunc(attr);
2172
2427
  return _isValidAttribute(lcTag, lcName, value);
2173
2428
  };
2174
2429
 
@@ -2183,7 +2438,6 @@ function createDOMPurify() {
2183
2438
  if (typeof hookFunction !== 'function') {
2184
2439
  return;
2185
2440
  }
2186
-
2187
2441
  hooks[entryPoint] = hooks[entryPoint] || [];
2188
2442
  arrayPush(hooks[entryPoint], hookFunction);
2189
2443
  };
@@ -2194,10 +2448,11 @@ function createDOMPurify() {
2194
2448
  * (pops it from the stack of hooks if more are present)
2195
2449
  *
2196
2450
  * @param {String} entryPoint entry point for the hook to remove
2451
+ * @return {Function} removed(popped) hook
2197
2452
  */
2198
2453
  DOMPurify.removeHook = function (entryPoint) {
2199
2454
  if (hooks[entryPoint]) {
2200
- arrayPop(hooks[entryPoint]);
2455
+ return arrayPop(hooks[entryPoint]);
2201
2456
  }
2202
2457
  };
2203
2458
 
@@ -2216,15 +2471,12 @@ function createDOMPurify() {
2216
2471
  /**
2217
2472
  * RemoveAllHooks
2218
2473
  * Public method to remove all DOMPurify hooks
2219
- *
2220
2474
  */
2221
2475
  DOMPurify.removeAllHooks = function () {
2222
2476
  hooks = {};
2223
2477
  };
2224
-
2225
2478
  return DOMPurify;
2226
2479
  }
2227
-
2228
2480
  var purify = createDOMPurify();
2229
2481
 
2230
2482
  export default purify;