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