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