@bigbinary/neeto-commons-frontend 2.1.35 → 2.1.36
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/initializers.cjs.js +1762 -1796
- package/initializers.cjs.js.map +1 -1
- package/initializers.js +1763 -1797
- package/initializers.js.map +1 -1
- package/package.json +1 -1
- package/utils.cjs.js.map +1 -1
- package/utils.js.map +1 -1
package/initializers.js
CHANGED
|
@@ -5,7 +5,7 @@ import i18n from 'i18next';
|
|
|
5
5
|
import { serializeKeysToSnakeCase, keysToCamelCase, matches, deepFreezeObject } from '@bigbinary/neeto-commons-frontend/pure';
|
|
6
6
|
import { resetAuthTokens } from '@bigbinary/neeto-commons-frontend/utils';
|
|
7
7
|
import { Toastr } from '@bigbinary/neetoui';
|
|
8
|
-
import { evolve, omit, values,
|
|
8
|
+
import { evolve, omit, values, mergeDeepLeft } from 'ramda';
|
|
9
9
|
import { initReactI18next } from 'react-i18next';
|
|
10
10
|
import Logger from 'js-logger';
|
|
11
11
|
|
|
@@ -55,7 +55,8 @@ var HEADERS_KEYS = {
|
|
|
55
55
|
contentType: "Content-Type",
|
|
56
56
|
accept: "Accept"
|
|
57
57
|
};
|
|
58
|
-
var
|
|
58
|
+
var LOWERCASED = "__LOWERCASED__";
|
|
59
|
+
var ANY_CASE = "anyCase";
|
|
59
60
|
|
|
60
61
|
var shouldNot = function shouldNot(skip) {
|
|
61
62
|
return _typeof$1(skip) === "object" || !skip;
|
|
@@ -243,2192 +244,2167 @@ function initializeGlobalProps() {
|
|
|
243
244
|
deepFreezeObject(window.globalProps);
|
|
244
245
|
}
|
|
245
246
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
isFrozen,
|
|
252
|
-
getPrototypeOf,
|
|
253
|
-
getOwnPropertyDescriptor
|
|
254
|
-
} = Object;
|
|
255
|
-
let {
|
|
256
|
-
freeze,
|
|
257
|
-
seal,
|
|
258
|
-
create
|
|
259
|
-
} = Object; // eslint-disable-line import/no-mutable-exports
|
|
247
|
+
function _classCallCheck(instance, Constructor) {
|
|
248
|
+
if (!(instance instanceof Constructor)) {
|
|
249
|
+
throw new TypeError("Cannot call a class as a function");
|
|
250
|
+
}
|
|
251
|
+
}
|
|
260
252
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
construct
|
|
264
|
-
} = typeof Reflect !== 'undefined' && Reflect;
|
|
253
|
+
function _typeof(obj) {
|
|
254
|
+
"@babel/helpers - typeof";
|
|
265
255
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
256
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
|
|
257
|
+
return typeof obj;
|
|
258
|
+
} : function (obj) {
|
|
259
|
+
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
260
|
+
}, _typeof(obj);
|
|
270
261
|
}
|
|
271
262
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
263
|
+
function _toPrimitive(input, hint) {
|
|
264
|
+
if (_typeof(input) !== "object" || input === null) return input;
|
|
265
|
+
var prim = input[Symbol.toPrimitive];
|
|
266
|
+
if (prim !== undefined) {
|
|
267
|
+
var res = prim.call(input, hint || "default");
|
|
268
|
+
if (_typeof(res) !== "object") return res;
|
|
269
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
270
|
+
}
|
|
271
|
+
return (hint === "string" ? String : Number)(input);
|
|
276
272
|
}
|
|
277
273
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
};
|
|
274
|
+
function _toPropertyKey(arg) {
|
|
275
|
+
var key = _toPrimitive(arg, "string");
|
|
276
|
+
return _typeof(key) === "symbol" ? key : String(key);
|
|
282
277
|
}
|
|
283
278
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
279
|
+
function _defineProperties(target, props) {
|
|
280
|
+
for (var i = 0; i < props.length; i++) {
|
|
281
|
+
var descriptor = props[i];
|
|
282
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
283
|
+
descriptor.configurable = true;
|
|
284
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
285
|
+
Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
|
|
286
|
+
}
|
|
288
287
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const stringReplace = unapply(String.prototype.replace);
|
|
297
|
-
const stringIndexOf = unapply(String.prototype.indexOf);
|
|
298
|
-
const stringTrim = unapply(String.prototype.trim);
|
|
299
|
-
const regExpTest = unapply(RegExp.prototype.test);
|
|
300
|
-
const typeErrorCreate = unconstruct(TypeError);
|
|
301
|
-
function unapply(func) {
|
|
302
|
-
return function (thisArg) {
|
|
303
|
-
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
304
|
-
args[_key - 1] = arguments[_key];
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return apply(func, thisArg, args);
|
|
308
|
-
};
|
|
288
|
+
function _createClass(Constructor, protoProps, staticProps) {
|
|
289
|
+
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
290
|
+
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
291
|
+
Object.defineProperty(Constructor, "prototype", {
|
|
292
|
+
writable: false
|
|
293
|
+
});
|
|
294
|
+
return Constructor;
|
|
309
295
|
}
|
|
310
|
-
function unconstruct(func) {
|
|
311
|
-
return function () {
|
|
312
|
-
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
313
|
-
args[_key2] = arguments[_key2];
|
|
314
|
-
}
|
|
315
296
|
|
|
316
|
-
|
|
317
|
-
|
|
297
|
+
var arr = [];
|
|
298
|
+
var each = arr.forEach;
|
|
299
|
+
var slice = arr.slice;
|
|
300
|
+
function defaults(obj) {
|
|
301
|
+
each.call(slice.call(arguments, 1), function (source) {
|
|
302
|
+
if (source) {
|
|
303
|
+
for (var prop in source) {
|
|
304
|
+
if (obj[prop] === undefined) obj[prop] = source[prop];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
return obj;
|
|
318
309
|
}
|
|
319
|
-
/* Add properties to a lookup table */
|
|
320
|
-
|
|
321
|
-
function addToSet(set, array, transformCaseFunc) {
|
|
322
|
-
var _transformCaseFunc;
|
|
323
310
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
311
|
+
// eslint-disable-next-line no-control-regex
|
|
312
|
+
var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
|
|
313
|
+
var serializeCookie = function serializeCookie(name, val, options) {
|
|
314
|
+
var opt = options || {};
|
|
315
|
+
opt.path = opt.path || '/';
|
|
316
|
+
var value = encodeURIComponent(val);
|
|
317
|
+
var str = "".concat(name, "=").concat(value);
|
|
318
|
+
if (opt.maxAge > 0) {
|
|
319
|
+
var maxAge = opt.maxAge - 0;
|
|
320
|
+
if (Number.isNaN(maxAge)) throw new Error('maxAge should be a Number');
|
|
321
|
+
str += "; Max-Age=".concat(Math.floor(maxAge));
|
|
331
322
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
while (l--) {
|
|
336
|
-
let element = array[l];
|
|
337
|
-
|
|
338
|
-
if (typeof element === 'string') {
|
|
339
|
-
const lcElement = transformCaseFunc(element);
|
|
340
|
-
|
|
341
|
-
if (lcElement !== element) {
|
|
342
|
-
// Config presets (e.g. tags.js, attrs.js) are immutable.
|
|
343
|
-
if (!isFrozen(array)) {
|
|
344
|
-
array[l] = lcElement;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
element = lcElement;
|
|
348
|
-
}
|
|
323
|
+
if (opt.domain) {
|
|
324
|
+
if (!fieldContentRegExp.test(opt.domain)) {
|
|
325
|
+
throw new TypeError('option domain is invalid');
|
|
349
326
|
}
|
|
350
|
-
|
|
351
|
-
set[element] = true;
|
|
327
|
+
str += "; Domain=".concat(opt.domain);
|
|
352
328
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
function clone(object) {
|
|
359
|
-
const newObject = create(null);
|
|
360
|
-
|
|
361
|
-
for (const [property, value] of entries(object)) {
|
|
362
|
-
newObject[property] = value;
|
|
329
|
+
if (opt.path) {
|
|
330
|
+
if (!fieldContentRegExp.test(opt.path)) {
|
|
331
|
+
throw new TypeError('option path is invalid');
|
|
332
|
+
}
|
|
333
|
+
str += "; Path=".concat(opt.path);
|
|
363
334
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
/* This method automatically checks if the prop is function
|
|
368
|
-
* or getter and behaves accordingly. */
|
|
369
|
-
|
|
370
|
-
function lookupGetter(object, prop) {
|
|
371
|
-
while (object !== null) {
|
|
372
|
-
const desc = getOwnPropertyDescriptor(object, prop);
|
|
373
|
-
|
|
374
|
-
if (desc) {
|
|
375
|
-
if (desc.get) {
|
|
376
|
-
return unapply(desc.get);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if (typeof desc.value === 'function') {
|
|
380
|
-
return unapply(desc.value);
|
|
381
|
-
}
|
|
335
|
+
if (opt.expires) {
|
|
336
|
+
if (typeof opt.expires.toUTCString !== 'function') {
|
|
337
|
+
throw new TypeError('option expires is invalid');
|
|
382
338
|
}
|
|
383
|
-
|
|
384
|
-
object = getPrototypeOf(object);
|
|
339
|
+
str += "; Expires=".concat(opt.expires.toUTCString());
|
|
385
340
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
341
|
+
if (opt.httpOnly) str += '; HttpOnly';
|
|
342
|
+
if (opt.secure) str += '; Secure';
|
|
343
|
+
if (opt.sameSite) {
|
|
344
|
+
var sameSite = typeof opt.sameSite === 'string' ? opt.sameSite.toLowerCase() : opt.sameSite;
|
|
345
|
+
switch (sameSite) {
|
|
346
|
+
case true:
|
|
347
|
+
str += '; SameSite=Strict';
|
|
348
|
+
break;
|
|
349
|
+
case 'lax':
|
|
350
|
+
str += '; SameSite=Lax';
|
|
351
|
+
break;
|
|
352
|
+
case 'strict':
|
|
353
|
+
str += '; SameSite=Strict';
|
|
354
|
+
break;
|
|
355
|
+
case 'none':
|
|
356
|
+
str += '; SameSite=None';
|
|
357
|
+
break;
|
|
358
|
+
default:
|
|
359
|
+
throw new TypeError('option sameSite is invalid');
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return str;
|
|
363
|
+
};
|
|
364
|
+
var cookie = {
|
|
365
|
+
create: function create(name, value, minutes, domain) {
|
|
366
|
+
var cookieOptions = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {
|
|
367
|
+
path: '/',
|
|
368
|
+
sameSite: 'strict'
|
|
369
|
+
};
|
|
370
|
+
if (minutes) {
|
|
371
|
+
cookieOptions.expires = new Date();
|
|
372
|
+
cookieOptions.expires.setTime(cookieOptions.expires.getTime() + minutes * 60 * 1000);
|
|
373
|
+
}
|
|
374
|
+
if (domain) cookieOptions.domain = domain;
|
|
375
|
+
document.cookie = serializeCookie(name, encodeURIComponent(value), cookieOptions);
|
|
376
|
+
},
|
|
377
|
+
read: function read(name) {
|
|
378
|
+
var nameEQ = "".concat(name, "=");
|
|
379
|
+
var ca = document.cookie.split(';');
|
|
380
|
+
for (var i = 0; i < ca.length; i++) {
|
|
381
|
+
var c = ca[i];
|
|
382
|
+
while (c.charAt(0) === ' ') {
|
|
383
|
+
c = c.substring(1, c.length);
|
|
384
|
+
}
|
|
385
|
+
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
|
|
386
|
+
}
|
|
389
387
|
return null;
|
|
388
|
+
},
|
|
389
|
+
remove: function remove(name) {
|
|
390
|
+
this.create(name, '', -1);
|
|
390
391
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
|
|
408
|
-
const text = freeze(['#text']);
|
|
409
|
-
|
|
410
|
-
const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']);
|
|
411
|
-
const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', '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', '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', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', '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', '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']);
|
|
412
|
-
const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
|
|
413
|
-
const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
414
|
-
|
|
415
|
-
const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
|
|
416
|
-
|
|
417
|
-
const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
|
|
418
|
-
const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
|
|
419
|
-
const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
|
|
420
|
-
|
|
421
|
-
const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
422
|
-
|
|
423
|
-
const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
|
424
|
-
);
|
|
425
|
-
const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
|
426
|
-
const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
|
|
427
|
-
);
|
|
428
|
-
const DOCTYPE_NAME = seal(/^html$/i);
|
|
429
|
-
|
|
430
|
-
var EXPRESSIONS = /*#__PURE__*/Object.freeze({
|
|
431
|
-
__proto__: null,
|
|
432
|
-
MUSTACHE_EXPR: MUSTACHE_EXPR,
|
|
433
|
-
ERB_EXPR: ERB_EXPR,
|
|
434
|
-
TMPLIT_EXPR: TMPLIT_EXPR,
|
|
435
|
-
DATA_ATTR: DATA_ATTR,
|
|
436
|
-
ARIA_ATTR: ARIA_ATTR,
|
|
437
|
-
IS_ALLOWED_URI: IS_ALLOWED_URI,
|
|
438
|
-
IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
|
|
439
|
-
ATTR_WHITESPACE: ATTR_WHITESPACE,
|
|
440
|
-
DOCTYPE_NAME: DOCTYPE_NAME
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
const getGlobal = () => typeof window === 'undefined' ? null : window;
|
|
444
|
-
/**
|
|
445
|
-
* Creates a no-op policy for internal use only.
|
|
446
|
-
* Don't export this function outside this module!
|
|
447
|
-
* @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
|
|
448
|
-
* @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
|
|
449
|
-
* @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
|
|
450
|
-
* are not supported or creating the policy failed).
|
|
451
|
-
*/
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
|
|
455
|
-
if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
|
|
456
|
-
return null;
|
|
457
|
-
} // Allow the callers to control the unique policy name
|
|
458
|
-
// by adding a data-tt-policy-suffix to the script element with the DOMPurify.
|
|
459
|
-
// Policy creation with duplicate names throws in Trusted Types.
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
let suffix = null;
|
|
463
|
-
const ATTR_NAME = 'data-tt-policy-suffix';
|
|
464
|
-
|
|
465
|
-
if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
|
|
466
|
-
suffix = purifyHostElement.getAttribute(ATTR_NAME);
|
|
392
|
+
};
|
|
393
|
+
var cookie$1 = {
|
|
394
|
+
name: 'cookie',
|
|
395
|
+
lookup: function lookup(options) {
|
|
396
|
+
var found;
|
|
397
|
+
if (options.lookupCookie && typeof document !== 'undefined') {
|
|
398
|
+
var c = cookie.read(options.lookupCookie);
|
|
399
|
+
if (c) found = c;
|
|
400
|
+
}
|
|
401
|
+
return found;
|
|
402
|
+
},
|
|
403
|
+
cacheUserLanguage: function cacheUserLanguage(lng, options) {
|
|
404
|
+
if (options.lookupCookie && typeof document !== 'undefined') {
|
|
405
|
+
cookie.create(options.lookupCookie, lng, options.cookieMinutes, options.cookieDomain, options.cookieOptions);
|
|
406
|
+
}
|
|
467
407
|
}
|
|
408
|
+
};
|
|
468
409
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
createScriptURL(scriptUrl) {
|
|
478
|
-
return scriptUrl;
|
|
410
|
+
var querystring = {
|
|
411
|
+
name: 'querystring',
|
|
412
|
+
lookup: function lookup(options) {
|
|
413
|
+
var found;
|
|
414
|
+
if (typeof window !== 'undefined') {
|
|
415
|
+
var search = window.location.search;
|
|
416
|
+
if (!window.location.search && window.location.hash && window.location.hash.indexOf('?') > -1) {
|
|
417
|
+
search = window.location.hash.substring(window.location.hash.indexOf('?'));
|
|
479
418
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
419
|
+
var query = search.substring(1);
|
|
420
|
+
var params = query.split('&');
|
|
421
|
+
for (var i = 0; i < params.length; i++) {
|
|
422
|
+
var pos = params[i].indexOf('=');
|
|
423
|
+
if (pos > 0) {
|
|
424
|
+
var key = params[i].substring(0, pos);
|
|
425
|
+
if (key === options.lookupQuerystring) {
|
|
426
|
+
found = params[i].substring(pos + 1);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return found;
|
|
488
432
|
}
|
|
489
433
|
};
|
|
490
434
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
DOMPurify.version = '3.0.3';
|
|
502
|
-
/**
|
|
503
|
-
* Array of elements that DOMPurify removed during sanitation.
|
|
504
|
-
* Empty if nothing was removed.
|
|
505
|
-
*/
|
|
506
|
-
|
|
507
|
-
DOMPurify.removed = [];
|
|
508
|
-
|
|
509
|
-
if (!window || !window.document || window.document.nodeType !== 9) {
|
|
510
|
-
// Not running in a browser, provide a factory function
|
|
511
|
-
// so that you can pass your own Window
|
|
512
|
-
DOMPurify.isSupported = false;
|
|
513
|
-
return DOMPurify;
|
|
435
|
+
var hasLocalStorageSupport = null;
|
|
436
|
+
var localStorageAvailable = function localStorageAvailable() {
|
|
437
|
+
if (hasLocalStorageSupport !== null) return hasLocalStorageSupport;
|
|
438
|
+
try {
|
|
439
|
+
hasLocalStorageSupport = window !== 'undefined' && window.localStorage !== null;
|
|
440
|
+
var testKey = 'i18next.translate.boo';
|
|
441
|
+
window.localStorage.setItem(testKey, 'foo');
|
|
442
|
+
window.localStorage.removeItem(testKey);
|
|
443
|
+
} catch (e) {
|
|
444
|
+
hasLocalStorageSupport = false;
|
|
514
445
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
trustedTypes
|
|
531
|
-
} = window;
|
|
532
|
-
const ElementPrototype = Element.prototype;
|
|
533
|
-
const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
534
|
-
const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
535
|
-
const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
536
|
-
const getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a
|
|
537
|
-
// new document created via createHTMLDocument. As per the spec
|
|
538
|
-
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
|
|
539
|
-
// a new empty registry is used when creating a template contents owner
|
|
540
|
-
// document, so we use that as our parent document to ensure nothing
|
|
541
|
-
// is inherited.
|
|
542
|
-
|
|
543
|
-
if (typeof HTMLTemplateElement === 'function') {
|
|
544
|
-
const template = document.createElement('template');
|
|
545
|
-
|
|
546
|
-
if (template.content && template.content.ownerDocument) {
|
|
547
|
-
document = template.content.ownerDocument;
|
|
446
|
+
return hasLocalStorageSupport;
|
|
447
|
+
};
|
|
448
|
+
var localStorage = {
|
|
449
|
+
name: 'localStorage',
|
|
450
|
+
lookup: function lookup(options) {
|
|
451
|
+
var found;
|
|
452
|
+
if (options.lookupLocalStorage && localStorageAvailable()) {
|
|
453
|
+
var lng = window.localStorage.getItem(options.lookupLocalStorage);
|
|
454
|
+
if (lng) found = lng;
|
|
455
|
+
}
|
|
456
|
+
return found;
|
|
457
|
+
},
|
|
458
|
+
cacheUserLanguage: function cacheUserLanguage(lng, options) {
|
|
459
|
+
if (options.lookupLocalStorage && localStorageAvailable()) {
|
|
460
|
+
window.localStorage.setItem(options.lookupLocalStorage, lng);
|
|
548
461
|
}
|
|
549
462
|
}
|
|
463
|
+
};
|
|
550
464
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
TMPLIT_EXPR,
|
|
572
|
-
DATA_ATTR,
|
|
573
|
-
ARIA_ATTR,
|
|
574
|
-
IS_SCRIPT_OR_DATA,
|
|
575
|
-
ATTR_WHITESPACE
|
|
576
|
-
} = EXPRESSIONS;
|
|
577
|
-
let {
|
|
578
|
-
IS_ALLOWED_URI: IS_ALLOWED_URI$1
|
|
579
|
-
} = EXPRESSIONS;
|
|
580
|
-
/**
|
|
581
|
-
* We consider the elements and attributes below to be safe. Ideally
|
|
582
|
-
* don't add any new ones but feel free to remove unwanted ones.
|
|
583
|
-
*/
|
|
584
|
-
|
|
585
|
-
/* allowed element names */
|
|
586
|
-
|
|
587
|
-
let ALLOWED_TAGS = null;
|
|
588
|
-
const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
|
|
589
|
-
/* Allowed attribute names */
|
|
590
|
-
|
|
591
|
-
let ALLOWED_ATTR = null;
|
|
592
|
-
const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
|
|
593
|
-
/*
|
|
594
|
-
* Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
|
|
595
|
-
* @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
|
|
596
|
-
* @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
|
|
597
|
-
* @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
|
|
598
|
-
*/
|
|
599
|
-
|
|
600
|
-
let CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
|
|
601
|
-
tagNameCheck: {
|
|
602
|
-
writable: true,
|
|
603
|
-
configurable: false,
|
|
604
|
-
enumerable: true,
|
|
605
|
-
value: null
|
|
606
|
-
},
|
|
607
|
-
attributeNameCheck: {
|
|
608
|
-
writable: true,
|
|
609
|
-
configurable: false,
|
|
610
|
-
enumerable: true,
|
|
611
|
-
value: null
|
|
612
|
-
},
|
|
613
|
-
allowCustomizedBuiltInElements: {
|
|
614
|
-
writable: true,
|
|
615
|
-
configurable: false,
|
|
616
|
-
enumerable: true,
|
|
617
|
-
value: false
|
|
465
|
+
var hasSessionStorageSupport = null;
|
|
466
|
+
var sessionStorageAvailable = function sessionStorageAvailable() {
|
|
467
|
+
if (hasSessionStorageSupport !== null) return hasSessionStorageSupport;
|
|
468
|
+
try {
|
|
469
|
+
hasSessionStorageSupport = window !== 'undefined' && window.sessionStorage !== null;
|
|
470
|
+
var testKey = 'i18next.translate.boo';
|
|
471
|
+
window.sessionStorage.setItem(testKey, 'foo');
|
|
472
|
+
window.sessionStorage.removeItem(testKey);
|
|
473
|
+
} catch (e) {
|
|
474
|
+
hasSessionStorageSupport = false;
|
|
475
|
+
}
|
|
476
|
+
return hasSessionStorageSupport;
|
|
477
|
+
};
|
|
478
|
+
var sessionStorage = {
|
|
479
|
+
name: 'sessionStorage',
|
|
480
|
+
lookup: function lookup(options) {
|
|
481
|
+
var found;
|
|
482
|
+
if (options.lookupSessionStorage && sessionStorageAvailable()) {
|
|
483
|
+
var lng = window.sessionStorage.getItem(options.lookupSessionStorage);
|
|
484
|
+
if (lng) found = lng;
|
|
618
485
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
let ALLOW_ARIA_ATTR = true;
|
|
629
|
-
/* Decide if custom data attributes are okay */
|
|
630
|
-
|
|
631
|
-
let ALLOW_DATA_ATTR = true;
|
|
632
|
-
/* Decide if unknown protocols are okay */
|
|
633
|
-
|
|
634
|
-
let ALLOW_UNKNOWN_PROTOCOLS = false;
|
|
635
|
-
/* Decide if self-closing tags in attributes are allowed.
|
|
636
|
-
* Usually removed due to a mXSS issue in jQuery 3.0 */
|
|
637
|
-
|
|
638
|
-
let ALLOW_SELF_CLOSE_IN_ATTR = true;
|
|
639
|
-
/* Output should be safe for common template engines.
|
|
640
|
-
* This means, DOMPurify removes data attributes, mustaches and ERB
|
|
641
|
-
*/
|
|
642
|
-
|
|
643
|
-
let SAFE_FOR_TEMPLATES = false;
|
|
644
|
-
/* Decide if document with <html>... should be returned */
|
|
486
|
+
return found;
|
|
487
|
+
},
|
|
488
|
+
cacheUserLanguage: function cacheUserLanguage(lng, options) {
|
|
489
|
+
if (options.lookupSessionStorage && sessionStorageAvailable()) {
|
|
490
|
+
window.sessionStorage.setItem(options.lookupSessionStorage, lng);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
};
|
|
645
494
|
|
|
646
|
-
|
|
647
|
-
|
|
495
|
+
var navigator$1 = {
|
|
496
|
+
name: 'navigator',
|
|
497
|
+
lookup: function lookup(options) {
|
|
498
|
+
var found = [];
|
|
499
|
+
if (typeof navigator !== 'undefined') {
|
|
500
|
+
if (navigator.languages) {
|
|
501
|
+
// chrome only; not an array, so can't use .push.apply instead of iterating
|
|
502
|
+
for (var i = 0; i < navigator.languages.length; i++) {
|
|
503
|
+
found.push(navigator.languages[i]);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (navigator.userLanguage) {
|
|
507
|
+
found.push(navigator.userLanguage);
|
|
508
|
+
}
|
|
509
|
+
if (navigator.language) {
|
|
510
|
+
found.push(navigator.language);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return found.length > 0 ? found : undefined;
|
|
514
|
+
}
|
|
515
|
+
};
|
|
648
516
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
517
|
+
var htmlTag = {
|
|
518
|
+
name: 'htmlTag',
|
|
519
|
+
lookup: function lookup(options) {
|
|
520
|
+
var found;
|
|
521
|
+
var htmlTag = options.htmlTag || (typeof document !== 'undefined' ? document.documentElement : null);
|
|
522
|
+
if (htmlTag && typeof htmlTag.getAttribute === 'function') {
|
|
523
|
+
found = htmlTag.getAttribute('lang');
|
|
524
|
+
}
|
|
525
|
+
return found;
|
|
526
|
+
}
|
|
527
|
+
};
|
|
652
528
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
529
|
+
var path = {
|
|
530
|
+
name: 'path',
|
|
531
|
+
lookup: function lookup(options) {
|
|
532
|
+
var found;
|
|
533
|
+
if (typeof window !== 'undefined') {
|
|
534
|
+
var language = window.location.pathname.match(/\/([a-zA-Z-]*)/g);
|
|
535
|
+
if (language instanceof Array) {
|
|
536
|
+
if (typeof options.lookupFromPathIndex === 'number') {
|
|
537
|
+
if (typeof language[options.lookupFromPathIndex] !== 'string') {
|
|
538
|
+
return undefined;
|
|
539
|
+
}
|
|
540
|
+
found = language[options.lookupFromPathIndex].replace('/', '');
|
|
541
|
+
} else {
|
|
542
|
+
found = language[0].replace('/', '');
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return found;
|
|
547
|
+
}
|
|
548
|
+
};
|
|
658
549
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
550
|
+
var subdomain = {
|
|
551
|
+
name: 'subdomain',
|
|
552
|
+
lookup: function lookup(options) {
|
|
553
|
+
// If given get the subdomain index else 1
|
|
554
|
+
var lookupFromSubdomainIndex = typeof options.lookupFromSubdomainIndex === 'number' ? options.lookupFromSubdomainIndex + 1 : 1;
|
|
555
|
+
// get all matches if window.location. is existing
|
|
556
|
+
// first item of match is the match itself and the second is the first group macht which sould be the first subdomain match
|
|
557
|
+
// is the hostname no public domain get the or option of localhost
|
|
558
|
+
var language = typeof window !== 'undefined' && window.location && window.location.hostname && window.location.hostname.match(/^(\w{2,5})\.(([a-z0-9-]{1,63}\.[a-z]{2,6})|localhost)/i);
|
|
662
559
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
560
|
+
// if there is no match (null) return undefined
|
|
561
|
+
if (!language) return undefined;
|
|
562
|
+
// return the given group match
|
|
563
|
+
return language[lookupFromSubdomainIndex];
|
|
564
|
+
}
|
|
565
|
+
};
|
|
666
566
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
567
|
+
function getDefaults() {
|
|
568
|
+
return {
|
|
569
|
+
order: ['querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag'],
|
|
570
|
+
lookupQuerystring: 'lng',
|
|
571
|
+
lookupCookie: 'i18next',
|
|
572
|
+
lookupLocalStorage: 'i18nextLng',
|
|
573
|
+
lookupSessionStorage: 'i18nextLng',
|
|
574
|
+
// cache user language
|
|
575
|
+
caches: ['localStorage'],
|
|
576
|
+
excludeCacheFor: ['cimode'],
|
|
577
|
+
// cookieMinutes: 10,
|
|
578
|
+
// cookieDomain: 'myDomain'
|
|
671
579
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
580
|
+
convertDetectedLanguage: function convertDetectedLanguage(l) {
|
|
581
|
+
return l;
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
var Browser = /*#__PURE__*/function () {
|
|
586
|
+
function Browser(services) {
|
|
587
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
588
|
+
_classCallCheck(this, Browser);
|
|
589
|
+
this.type = 'languageDetector';
|
|
590
|
+
this.detectors = {};
|
|
591
|
+
this.init(services, options);
|
|
592
|
+
}
|
|
593
|
+
_createClass(Browser, [{
|
|
594
|
+
key: "init",
|
|
595
|
+
value: function init(services) {
|
|
596
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
597
|
+
var i18nOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
598
|
+
this.services = services || {
|
|
599
|
+
languageUtils: {}
|
|
600
|
+
}; // this way the language detector can be used without i18next
|
|
601
|
+
this.options = defaults(options, this.options || {}, getDefaults());
|
|
602
|
+
if (typeof this.options.convertDetectedLanguage === 'string' && this.options.convertDetectedLanguage.indexOf('15897') > -1) {
|
|
603
|
+
this.options.convertDetectedLanguage = function (l) {
|
|
604
|
+
return l.replace('-', '_');
|
|
605
|
+
};
|
|
606
|
+
}
|
|
686
607
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
608
|
+
// backwards compatibility
|
|
609
|
+
if (this.options.lookupFromUrlIndex) this.options.lookupFromPathIndex = this.options.lookupFromUrlIndex;
|
|
610
|
+
this.i18nOptions = i18nOptions;
|
|
611
|
+
this.addDetector(cookie$1);
|
|
612
|
+
this.addDetector(querystring);
|
|
613
|
+
this.addDetector(localStorage);
|
|
614
|
+
this.addDetector(sessionStorage);
|
|
615
|
+
this.addDetector(navigator$1);
|
|
616
|
+
this.addDetector(htmlTag);
|
|
617
|
+
this.addDetector(path);
|
|
618
|
+
this.addDetector(subdomain);
|
|
619
|
+
}
|
|
620
|
+
}, {
|
|
621
|
+
key: "addDetector",
|
|
622
|
+
value: function addDetector(detector) {
|
|
623
|
+
this.detectors[detector.name] = detector;
|
|
624
|
+
}
|
|
625
|
+
}, {
|
|
626
|
+
key: "detect",
|
|
627
|
+
value: function detect(detectionOrder) {
|
|
628
|
+
var _this = this;
|
|
629
|
+
if (!detectionOrder) detectionOrder = this.options.order;
|
|
630
|
+
var detected = [];
|
|
631
|
+
detectionOrder.forEach(function (detectorName) {
|
|
632
|
+
if (_this.detectors[detectorName]) {
|
|
633
|
+
var lookup = _this.detectors[detectorName].lookup(_this.options);
|
|
634
|
+
if (lookup && typeof lookup === 'string') lookup = [lookup];
|
|
635
|
+
if (lookup) detected = detected.concat(lookup);
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
detected = detected.map(function (d) {
|
|
639
|
+
return _this.options.convertDetectedLanguage(d);
|
|
640
|
+
});
|
|
641
|
+
if (this.services.languageUtils.getBestMatchFromCodes) return detected; // new i18next v19.5.0
|
|
642
|
+
return detected.length > 0 ? detected[0] : null; // a little backward compatibility
|
|
643
|
+
}
|
|
644
|
+
}, {
|
|
645
|
+
key: "cacheUserLanguage",
|
|
646
|
+
value: function cacheUserLanguage(lng, caches) {
|
|
647
|
+
var _this2 = this;
|
|
648
|
+
if (!caches) caches = this.options.caches;
|
|
649
|
+
if (!caches) return;
|
|
650
|
+
if (this.options.excludeCacheFor && this.options.excludeCacheFor.indexOf(lng) > -1) return;
|
|
651
|
+
caches.forEach(function (cacheName) {
|
|
652
|
+
if (_this2.detectors[cacheName]) _this2.detectors[cacheName].cacheUserLanguage(lng, _this2.options);
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
}]);
|
|
656
|
+
return Browser;
|
|
657
|
+
}();
|
|
658
|
+
Browser.type = 'languageDetector';
|
|
690
659
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
660
|
+
function _arrayWithHoles(arr) {
|
|
661
|
+
if (Array.isArray(arr)) return arr;
|
|
662
|
+
}
|
|
694
663
|
|
|
695
|
-
|
|
696
|
-
|
|
664
|
+
function _iterableToArrayLimit(arr, i) {
|
|
665
|
+
var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"];
|
|
666
|
+
if (null != _i) {
|
|
667
|
+
var _s,
|
|
668
|
+
_e,
|
|
669
|
+
_x,
|
|
670
|
+
_r,
|
|
671
|
+
_arr = [],
|
|
672
|
+
_n = !0,
|
|
673
|
+
_d = !1;
|
|
674
|
+
try {
|
|
675
|
+
if (_x = (_i = _i.call(arr)).next, 0 === i) {
|
|
676
|
+
if (Object(_i) !== _i) return;
|
|
677
|
+
_n = !1;
|
|
678
|
+
} else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0);
|
|
679
|
+
} catch (err) {
|
|
680
|
+
_d = !0, _e = err;
|
|
681
|
+
} finally {
|
|
682
|
+
try {
|
|
683
|
+
if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return;
|
|
684
|
+
} finally {
|
|
685
|
+
if (_d) throw _e;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return _arr;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
697
691
|
|
|
698
|
-
|
|
699
|
-
|
|
692
|
+
function _arrayLikeToArray(arr, len) {
|
|
693
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
694
|
+
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
|
695
|
+
return arr2;
|
|
696
|
+
}
|
|
700
697
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
698
|
+
function _unsupportedIterableToArray(o, minLen) {
|
|
699
|
+
if (!o) return;
|
|
700
|
+
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
701
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
702
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
703
|
+
if (n === "Map" || n === "Set") return Array.from(o);
|
|
704
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
|
705
|
+
}
|
|
704
706
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
707
|
+
function _nonIterableRest() {
|
|
708
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
709
|
+
}
|
|
708
710
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
713
|
-
const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
714
|
-
/* Document namespace */
|
|
711
|
+
function _slicedToArray(arr, i) {
|
|
712
|
+
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
|
|
713
|
+
}
|
|
715
714
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
715
|
+
var getter = function getter(key) {
|
|
716
|
+
return function () {
|
|
717
|
+
return i18n.t("taxonomyDefaultLabels.".concat(key));
|
|
718
|
+
};
|
|
719
|
+
};
|
|
720
|
+
var replaceNullValuesWithGetter = function replaceNullValuesWithGetter(inputObject) {
|
|
721
|
+
var parentKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
|
|
722
|
+
var result = {};
|
|
723
|
+
for (var _i = 0, _Object$entries = Object.entries(inputObject); _i < _Object$entries.length; _i++) {
|
|
724
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
725
|
+
key = _Object$entries$_i[0],
|
|
726
|
+
value = _Object$entries$_i[1];
|
|
727
|
+
var transKey = parentKey ? "".concat(parentKey, ".").concat(key) : key;
|
|
728
|
+
if (value === null) {
|
|
729
|
+
Object.defineProperty(result, key, {
|
|
730
|
+
get: getter(transKey)
|
|
731
|
+
});
|
|
732
|
+
} else if (_typeof$1(value) === "object") {
|
|
733
|
+
result[key] = replaceNullValuesWithGetter(value, transKey);
|
|
734
|
+
} else {
|
|
735
|
+
result[key] = value;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return result;
|
|
739
|
+
};
|
|
719
740
|
|
|
720
|
-
|
|
721
|
-
const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
|
|
722
|
-
/* Parsing of strict XHTML documents */
|
|
741
|
+
/*! @license DOMPurify 3.0.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.3/LICENSE */
|
|
723
742
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
743
|
+
const {
|
|
744
|
+
entries,
|
|
745
|
+
setPrototypeOf,
|
|
746
|
+
isFrozen,
|
|
747
|
+
getPrototypeOf,
|
|
748
|
+
getOwnPropertyDescriptor
|
|
749
|
+
} = Object;
|
|
750
|
+
let {
|
|
751
|
+
freeze,
|
|
752
|
+
seal,
|
|
753
|
+
create
|
|
754
|
+
} = Object; // eslint-disable-line import/no-mutable-exports
|
|
729
755
|
|
|
730
|
-
|
|
731
|
-
|
|
756
|
+
let {
|
|
757
|
+
apply,
|
|
758
|
+
construct
|
|
759
|
+
} = typeof Reflect !== 'undefined' && Reflect;
|
|
732
760
|
|
|
733
|
-
|
|
761
|
+
if (!apply) {
|
|
762
|
+
apply = function apply(fun, thisValue, args) {
|
|
763
|
+
return fun.apply(thisValue, args);
|
|
764
|
+
};
|
|
765
|
+
}
|
|
734
766
|
|
|
735
|
-
|
|
767
|
+
if (!freeze) {
|
|
768
|
+
freeze = function freeze(x) {
|
|
769
|
+
return x;
|
|
770
|
+
};
|
|
771
|
+
}
|
|
736
772
|
|
|
737
|
-
|
|
738
|
-
|
|
773
|
+
if (!seal) {
|
|
774
|
+
seal = function seal(x) {
|
|
775
|
+
return x;
|
|
739
776
|
};
|
|
740
|
-
|
|
741
|
-
* _parseConfig
|
|
742
|
-
*
|
|
743
|
-
* @param {Object} cfg optional config literal
|
|
744
|
-
*/
|
|
745
|
-
// eslint-disable-next-line complexity
|
|
777
|
+
}
|
|
746
778
|
|
|
779
|
+
if (!construct) {
|
|
780
|
+
construct = function construct(Func, args) {
|
|
781
|
+
return new Func(...args);
|
|
782
|
+
};
|
|
783
|
+
}
|
|
747
784
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
785
|
+
const arrayForEach = unapply(Array.prototype.forEach);
|
|
786
|
+
const arrayPop = unapply(Array.prototype.pop);
|
|
787
|
+
const arrayPush = unapply(Array.prototype.push);
|
|
788
|
+
const stringToLowerCase = unapply(String.prototype.toLowerCase);
|
|
789
|
+
const stringToString = unapply(String.prototype.toString);
|
|
790
|
+
const stringMatch = unapply(String.prototype.match);
|
|
791
|
+
const stringReplace = unapply(String.prototype.replace);
|
|
792
|
+
const stringIndexOf = unapply(String.prototype.indexOf);
|
|
793
|
+
const stringTrim = unapply(String.prototype.trim);
|
|
794
|
+
const regExpTest = unapply(RegExp.prototype.test);
|
|
795
|
+
const typeErrorCreate = unconstruct(TypeError);
|
|
796
|
+
function unapply(func) {
|
|
797
|
+
return function (thisArg) {
|
|
798
|
+
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
799
|
+
args[_key - 1] = arguments[_key];
|
|
751
800
|
}
|
|
752
|
-
/* Shield configuration object from tampering */
|
|
753
801
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
802
|
+
return apply(func, thisArg, args);
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
function unconstruct(func) {
|
|
806
|
+
return function () {
|
|
807
|
+
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
808
|
+
args[_key2] = arguments[_key2];
|
|
757
809
|
}
|
|
758
|
-
/* Shield configuration object from prototype pollution */
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
cfg = clone(cfg);
|
|
762
|
-
PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
|
|
763
|
-
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE; // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
764
|
-
|
|
765
|
-
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
|
|
766
|
-
/* Set configuration parameters */
|
|
767
|
-
|
|
768
|
-
ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
769
|
-
ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
770
|
-
ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
|
|
771
|
-
URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent
|
|
772
|
-
cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent
|
|
773
|
-
transformCaseFunc // eslint-disable-line indent
|
|
774
|
-
) // eslint-disable-line indent
|
|
775
|
-
: DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
776
|
-
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent
|
|
777
|
-
cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent
|
|
778
|
-
transformCaseFunc // eslint-disable-line indent
|
|
779
|
-
) // eslint-disable-line indent
|
|
780
|
-
: DEFAULT_DATA_URI_TAGS;
|
|
781
|
-
FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
|
|
782
|
-
FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
|
|
783
|
-
FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
|
|
784
|
-
USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
|
|
785
|
-
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
786
|
-
|
|
787
|
-
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
788
810
|
|
|
789
|
-
|
|
811
|
+
return construct(func, args);
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
/* Add properties to a lookup table */
|
|
790
815
|
|
|
791
|
-
|
|
816
|
+
function addToSet(set, array, transformCaseFunc) {
|
|
817
|
+
var _transformCaseFunc;
|
|
792
818
|
|
|
793
|
-
|
|
819
|
+
transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase;
|
|
794
820
|
|
|
795
|
-
|
|
821
|
+
if (setPrototypeOf) {
|
|
822
|
+
// Make 'in' and truthy checks like Boolean(set.constructor)
|
|
823
|
+
// independent of any properties defined on Object.prototype.
|
|
824
|
+
// Prevent prototype setters from intercepting set as a this value.
|
|
825
|
+
setPrototypeOf(set, null);
|
|
826
|
+
}
|
|
796
827
|
|
|
797
|
-
|
|
828
|
+
let l = array.length;
|
|
798
829
|
|
|
799
|
-
|
|
830
|
+
while (l--) {
|
|
831
|
+
let element = array[l];
|
|
800
832
|
|
|
801
|
-
|
|
833
|
+
if (typeof element === 'string') {
|
|
834
|
+
const lcElement = transformCaseFunc(element);
|
|
802
835
|
|
|
803
|
-
|
|
836
|
+
if (lcElement !== element) {
|
|
837
|
+
// Config presets (e.g. tags.js, attrs.js) are immutable.
|
|
838
|
+
if (!isFrozen(array)) {
|
|
839
|
+
array[l] = lcElement;
|
|
840
|
+
}
|
|
804
841
|
|
|
805
|
-
|
|
842
|
+
element = lcElement;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
806
845
|
|
|
807
|
-
|
|
846
|
+
set[element] = true;
|
|
847
|
+
}
|
|
808
848
|
|
|
809
|
-
|
|
849
|
+
return set;
|
|
850
|
+
}
|
|
851
|
+
/* Shallow clone an object */
|
|
810
852
|
|
|
811
|
-
|
|
853
|
+
function clone(object) {
|
|
854
|
+
const newObject = create(null);
|
|
812
855
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
856
|
+
for (const [property, value] of entries(object)) {
|
|
857
|
+
newObject[property] = value;
|
|
858
|
+
}
|
|
816
859
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
860
|
+
return newObject;
|
|
861
|
+
}
|
|
862
|
+
/* This method automatically checks if the prop is function
|
|
863
|
+
* or getter and behaves accordingly. */
|
|
820
864
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
865
|
+
function lookupGetter(object, prop) {
|
|
866
|
+
while (object !== null) {
|
|
867
|
+
const desc = getOwnPropertyDescriptor(object, prop);
|
|
824
868
|
|
|
825
|
-
if (
|
|
826
|
-
|
|
827
|
-
|
|
869
|
+
if (desc) {
|
|
870
|
+
if (desc.get) {
|
|
871
|
+
return unapply(desc.get);
|
|
872
|
+
}
|
|
828
873
|
|
|
829
|
-
|
|
830
|
-
|
|
874
|
+
if (typeof desc.value === 'function') {
|
|
875
|
+
return unapply(desc.value);
|
|
876
|
+
}
|
|
831
877
|
}
|
|
832
878
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
}
|
|
836
|
-
/* Parse profile info */
|
|
879
|
+
object = getPrototypeOf(object);
|
|
880
|
+
}
|
|
837
881
|
|
|
882
|
+
function fallbackValue(element) {
|
|
883
|
+
console.warn('fallback value for', element);
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
838
886
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
ALLOWED_ATTR = [];
|
|
887
|
+
return fallbackValue;
|
|
888
|
+
}
|
|
842
889
|
|
|
843
|
-
|
|
844
|
-
addToSet(ALLOWED_TAGS, html$1);
|
|
845
|
-
addToSet(ALLOWED_ATTR, html);
|
|
846
|
-
}
|
|
890
|
+
const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); // SVG
|
|
847
891
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
892
|
+
const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
|
|
893
|
+
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']); // List of SVG elements that are disallowed by default.
|
|
894
|
+
// We still need to know them so that we can do namespace
|
|
895
|
+
// checks properly in case one wants to add them to
|
|
896
|
+
// allow-list.
|
|
853
897
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
addToSet(ALLOWED_ATTR, xml);
|
|
858
|
-
}
|
|
898
|
+
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']);
|
|
899
|
+
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']); // Similarly to SVG, we want to know all MathML elements,
|
|
900
|
+
// even those that we disallow by default.
|
|
859
901
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
addToSet(ALLOWED_ATTR, mathMl);
|
|
863
|
-
addToSet(ALLOWED_ATTR, xml);
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
/* Merge configuration parameters */
|
|
902
|
+
const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
|
|
903
|
+
const text = freeze(['#text']);
|
|
867
904
|
|
|
905
|
+
const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']);
|
|
906
|
+
const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', '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', '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', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', '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', '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']);
|
|
907
|
+
const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
|
|
908
|
+
const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
868
909
|
|
|
869
|
-
|
|
870
|
-
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
871
|
-
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
872
|
-
}
|
|
910
|
+
const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
|
|
873
911
|
|
|
874
|
-
|
|
875
|
-
|
|
912
|
+
const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
|
|
913
|
+
const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
|
|
914
|
+
const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
|
|
876
915
|
|
|
877
|
-
|
|
878
|
-
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
879
|
-
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
880
|
-
}
|
|
916
|
+
const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
881
917
|
|
|
882
|
-
|
|
883
|
-
|
|
918
|
+
const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
|
919
|
+
);
|
|
920
|
+
const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
|
921
|
+
const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
|
|
922
|
+
);
|
|
923
|
+
const DOCTYPE_NAME = seal(/^html$/i);
|
|
884
924
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
925
|
+
var EXPRESSIONS = /*#__PURE__*/Object.freeze({
|
|
926
|
+
__proto__: null,
|
|
927
|
+
MUSTACHE_EXPR: MUSTACHE_EXPR,
|
|
928
|
+
ERB_EXPR: ERB_EXPR,
|
|
929
|
+
TMPLIT_EXPR: TMPLIT_EXPR,
|
|
930
|
+
DATA_ATTR: DATA_ATTR,
|
|
931
|
+
ARIA_ATTR: ARIA_ATTR,
|
|
932
|
+
IS_ALLOWED_URI: IS_ALLOWED_URI,
|
|
933
|
+
IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
|
|
934
|
+
ATTR_WHITESPACE: ATTR_WHITESPACE,
|
|
935
|
+
DOCTYPE_NAME: DOCTYPE_NAME
|
|
936
|
+
});
|
|
888
937
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
938
|
+
const getGlobal = () => typeof window === 'undefined' ? null : window;
|
|
939
|
+
/**
|
|
940
|
+
* Creates a no-op policy for internal use only.
|
|
941
|
+
* Don't export this function outside this module!
|
|
942
|
+
* @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
|
|
943
|
+
* @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
|
|
944
|
+
* @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
|
|
945
|
+
* are not supported or creating the policy failed).
|
|
946
|
+
*/
|
|
893
947
|
|
|
894
|
-
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
895
|
-
}
|
|
896
|
-
/* Add #text in case KEEP_CONTENT is set to true */
|
|
897
948
|
|
|
949
|
+
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
|
|
950
|
+
if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
|
|
951
|
+
return null;
|
|
952
|
+
} // Allow the callers to control the unique policy name
|
|
953
|
+
// by adding a data-tt-policy-suffix to the script element with the DOMPurify.
|
|
954
|
+
// Policy creation with duplicate names throws in Trusted Types.
|
|
898
955
|
|
|
899
|
-
if (KEEP_CONTENT) {
|
|
900
|
-
ALLOWED_TAGS['#text'] = true;
|
|
901
|
-
}
|
|
902
|
-
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
903
956
|
|
|
957
|
+
let suffix = null;
|
|
958
|
+
const ATTR_NAME = 'data-tt-policy-suffix';
|
|
904
959
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
960
|
+
if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
|
|
961
|
+
suffix = purifyHostElement.getAttribute(ATTR_NAME);
|
|
962
|
+
}
|
|
909
963
|
|
|
964
|
+
const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
|
|
910
965
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
966
|
+
try {
|
|
967
|
+
return trustedTypes.createPolicy(policyName, {
|
|
968
|
+
createHTML(html) {
|
|
969
|
+
return html;
|
|
970
|
+
},
|
|
915
971
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
|
|
972
|
+
createScriptURL(scriptUrl) {
|
|
973
|
+
return scriptUrl;
|
|
919
974
|
}
|
|
920
975
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
976
|
+
});
|
|
977
|
+
} catch (_) {
|
|
978
|
+
// Policy creation failed (most likely another DOMPurify script has
|
|
979
|
+
// already run). Skip creating the policy, as this will only cause errors
|
|
980
|
+
// if TT are enforced.
|
|
981
|
+
console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
|
|
982
|
+
return null;
|
|
983
|
+
}
|
|
984
|
+
};
|
|
924
985
|
|
|
986
|
+
function createDOMPurify() {
|
|
987
|
+
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
925
988
|
|
|
926
|
-
|
|
989
|
+
const DOMPurify = root => createDOMPurify(root);
|
|
990
|
+
/**
|
|
991
|
+
* Version label, exposed for easier checks
|
|
992
|
+
* if DOMPurify is up to date or not
|
|
993
|
+
*/
|
|
927
994
|
|
|
928
|
-
emptyHTML = trustedTypesPolicy.createHTML('');
|
|
929
|
-
} else {
|
|
930
|
-
// Uninitialized policy, attempt to initialize the internal dompurify policy.
|
|
931
|
-
if (trustedTypesPolicy === undefined) {
|
|
932
|
-
trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
|
|
933
|
-
} // If creating the internal policy succeeded sign internal variables.
|
|
934
995
|
|
|
996
|
+
DOMPurify.version = '3.0.3';
|
|
997
|
+
/**
|
|
998
|
+
* Array of elements that DOMPurify removed during sanitation.
|
|
999
|
+
* Empty if nothing was removed.
|
|
1000
|
+
*/
|
|
935
1001
|
|
|
936
|
-
|
|
937
|
-
emptyHTML = trustedTypesPolicy.createHTML('');
|
|
938
|
-
}
|
|
939
|
-
} // Prevent further manipulation of configuration.
|
|
940
|
-
// Not available in IE8, Safari 5, etc.
|
|
1002
|
+
DOMPurify.removed = [];
|
|
941
1003
|
|
|
1004
|
+
if (!window || !window.document || window.document.nodeType !== 9) {
|
|
1005
|
+
// Not running in a browser, provide a factory function
|
|
1006
|
+
// so that you can pass your own Window
|
|
1007
|
+
DOMPurify.isSupported = false;
|
|
1008
|
+
return DOMPurify;
|
|
1009
|
+
}
|
|
942
1010
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1011
|
+
const originalDocument = window.document;
|
|
1012
|
+
const currentScript = originalDocument.currentScript;
|
|
1013
|
+
let {
|
|
1014
|
+
document
|
|
1015
|
+
} = window;
|
|
1016
|
+
const {
|
|
1017
|
+
DocumentFragment,
|
|
1018
|
+
HTMLTemplateElement,
|
|
1019
|
+
Node,
|
|
1020
|
+
Element,
|
|
1021
|
+
NodeFilter,
|
|
1022
|
+
NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
|
|
1023
|
+
HTMLFormElement,
|
|
1024
|
+
DOMParser,
|
|
1025
|
+
trustedTypes
|
|
1026
|
+
} = window;
|
|
1027
|
+
const ElementPrototype = Element.prototype;
|
|
1028
|
+
const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
1029
|
+
const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
1030
|
+
const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
1031
|
+
const getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a
|
|
1032
|
+
// new document created via createHTMLDocument. As per the spec
|
|
1033
|
+
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
|
|
1034
|
+
// a new empty registry is used when creating a template contents owner
|
|
1035
|
+
// document, so we use that as our parent document to ensure nothing
|
|
1036
|
+
// is inherited.
|
|
946
1037
|
|
|
947
|
-
|
|
948
|
-
|
|
1038
|
+
if (typeof HTMLTemplateElement === 'function') {
|
|
1039
|
+
const template = document.createElement('template');
|
|
949
1040
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
// HTML namespace.
|
|
1041
|
+
if (template.content && template.content.ownerDocument) {
|
|
1042
|
+
document = template.content.ownerDocument;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
955
1045
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1046
|
+
let trustedTypesPolicy;
|
|
1047
|
+
let emptyHTML = '';
|
|
1048
|
+
const {
|
|
1049
|
+
implementation,
|
|
1050
|
+
createNodeIterator,
|
|
1051
|
+
createDocumentFragment,
|
|
1052
|
+
getElementsByTagName
|
|
1053
|
+
} = document;
|
|
1054
|
+
const {
|
|
1055
|
+
importNode
|
|
1056
|
+
} = originalDocument;
|
|
1057
|
+
let hooks = {};
|
|
1058
|
+
/**
|
|
1059
|
+
* Expose whether this browser supports running the full DOMPurify.
|
|
1060
|
+
*/
|
|
960
1061
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1062
|
+
DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
|
|
1063
|
+
const {
|
|
1064
|
+
MUSTACHE_EXPR,
|
|
1065
|
+
ERB_EXPR,
|
|
1066
|
+
TMPLIT_EXPR,
|
|
1067
|
+
DATA_ATTR,
|
|
1068
|
+
ARIA_ATTR,
|
|
1069
|
+
IS_SCRIPT_OR_DATA,
|
|
1070
|
+
ATTR_WHITESPACE
|
|
1071
|
+
} = EXPRESSIONS;
|
|
1072
|
+
let {
|
|
1073
|
+
IS_ALLOWED_URI: IS_ALLOWED_URI$1
|
|
1074
|
+
} = EXPRESSIONS;
|
|
966
1075
|
/**
|
|
967
|
-
*
|
|
968
|
-
*
|
|
969
|
-
* @param {Element} element a DOM element whose namespace is being checked
|
|
970
|
-
* @returns {boolean} Return false if the element has a
|
|
971
|
-
* namespace that a spec-compliant parser would never
|
|
972
|
-
* return. Return true otherwise.
|
|
1076
|
+
* We consider the elements and attributes below to be safe. Ideally
|
|
1077
|
+
* don't add any new ones but feel free to remove unwanted ones.
|
|
973
1078
|
*/
|
|
974
1079
|
|
|
975
|
-
|
|
976
|
-
let parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode
|
|
977
|
-
// can be null. We just simulate parent in this case.
|
|
1080
|
+
/* allowed element names */
|
|
978
1081
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
tagName: 'template'
|
|
983
|
-
};
|
|
984
|
-
}
|
|
1082
|
+
let ALLOWED_TAGS = null;
|
|
1083
|
+
const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
|
|
1084
|
+
/* Allowed attribute names */
|
|
985
1085
|
|
|
986
|
-
|
|
987
|
-
|
|
1086
|
+
let ALLOWED_ATTR = null;
|
|
1087
|
+
const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
|
|
1088
|
+
/*
|
|
1089
|
+
* Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
|
|
1090
|
+
* @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
|
|
1091
|
+
* @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
|
|
1092
|
+
* @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
|
|
1093
|
+
*/
|
|
988
1094
|
|
|
989
|
-
|
|
990
|
-
|
|
1095
|
+
let CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
|
|
1096
|
+
tagNameCheck: {
|
|
1097
|
+
writable: true,
|
|
1098
|
+
configurable: false,
|
|
1099
|
+
enumerable: true,
|
|
1100
|
+
value: null
|
|
1101
|
+
},
|
|
1102
|
+
attributeNameCheck: {
|
|
1103
|
+
writable: true,
|
|
1104
|
+
configurable: false,
|
|
1105
|
+
enumerable: true,
|
|
1106
|
+
value: null
|
|
1107
|
+
},
|
|
1108
|
+
allowCustomizedBuiltInElements: {
|
|
1109
|
+
writable: true,
|
|
1110
|
+
configurable: false,
|
|
1111
|
+
enumerable: true,
|
|
1112
|
+
value: false
|
|
991
1113
|
}
|
|
1114
|
+
}));
|
|
1115
|
+
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
|
|
992
1116
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
return tagName === 'svg';
|
|
999
|
-
} // The only way to switch from MathML to SVG is via`
|
|
1000
|
-
// svg if parent is either <annotation-xml> or MathML
|
|
1001
|
-
// text integration points.
|
|
1117
|
+
let FORBID_TAGS = null;
|
|
1118
|
+
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
|
|
1119
|
+
|
|
1120
|
+
let FORBID_ATTR = null;
|
|
1121
|
+
/* Decide if ARIA attributes are okay */
|
|
1002
1122
|
|
|
1123
|
+
let ALLOW_ARIA_ATTR = true;
|
|
1124
|
+
/* Decide if custom data attributes are okay */
|
|
1125
|
+
|
|
1126
|
+
let ALLOW_DATA_ATTR = true;
|
|
1127
|
+
/* Decide if unknown protocols are okay */
|
|
1128
|
+
|
|
1129
|
+
let ALLOW_UNKNOWN_PROTOCOLS = false;
|
|
1130
|
+
/* Decide if self-closing tags in attributes are allowed.
|
|
1131
|
+
* Usually removed due to a mXSS issue in jQuery 3.0 */
|
|
1003
1132
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1133
|
+
let ALLOW_SELF_CLOSE_IN_ATTR = true;
|
|
1134
|
+
/* Output should be safe for common template engines.
|
|
1135
|
+
* This means, DOMPurify removes data attributes, mustaches and ERB
|
|
1136
|
+
*/
|
|
1008
1137
|
|
|
1138
|
+
let SAFE_FOR_TEMPLATES = false;
|
|
1139
|
+
/* Decide if document with <html>... should be returned */
|
|
1009
1140
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1141
|
+
let WHOLE_DOCUMENT = false;
|
|
1142
|
+
/* Track whether config is already set on this instance of DOMPurify. */
|
|
1012
1143
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
// it should be killed.
|
|
1017
|
-
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
1018
|
-
return tagName === 'math';
|
|
1019
|
-
} // The only way to switch from SVG to MathML is via
|
|
1020
|
-
// <math> and HTML integration points
|
|
1144
|
+
let SET_CONFIG = false;
|
|
1145
|
+
/* Decide if all elements (e.g. style, script) must be children of
|
|
1146
|
+
* document.body. By default, browsers might move them to document.head */
|
|
1021
1147
|
|
|
1148
|
+
let FORCE_BODY = false;
|
|
1149
|
+
/* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
|
|
1150
|
+
* string (or a TrustedHTML object if Trusted Types are supported).
|
|
1151
|
+
* If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
|
|
1152
|
+
*/
|
|
1022
1153
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
// spec. All others are disallowed in MathML namespace.
|
|
1154
|
+
let RETURN_DOM = false;
|
|
1155
|
+
/* Decide if a DOM `DocumentFragment` should be returned, instead of a html
|
|
1156
|
+
* string (or a TrustedHTML object if Trusted Types are supported) */
|
|
1027
1157
|
|
|
1158
|
+
let RETURN_DOM_FRAGMENT = false;
|
|
1159
|
+
/* Try to return a Trusted Type object instead of a string, return a string in
|
|
1160
|
+
* case Trusted Types are not supported */
|
|
1028
1161
|
|
|
1029
|
-
|
|
1030
|
-
|
|
1162
|
+
let RETURN_TRUSTED_TYPE = false;
|
|
1163
|
+
/* Output should be free from DOM clobbering attacks?
|
|
1164
|
+
* This sanitizes markups named with colliding, clobberable built-in DOM APIs.
|
|
1165
|
+
*/
|
|
1031
1166
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1167
|
+
let SANITIZE_DOM = true;
|
|
1168
|
+
/* Achieve full DOM Clobbering protection by isolating the namespace of named
|
|
1169
|
+
* properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
|
|
1170
|
+
*
|
|
1171
|
+
* HTML/DOM spec rules that enable DOM Clobbering:
|
|
1172
|
+
* - Named Access on Window (§7.3.3)
|
|
1173
|
+
* - DOM Tree Accessors (§3.1.5)
|
|
1174
|
+
* - Form Element Parent-Child Relations (§4.10.3)
|
|
1175
|
+
* - Iframe srcdoc / Nested WindowProxies (§4.8.5)
|
|
1176
|
+
* - HTMLCollection (§4.2.10.2)
|
|
1177
|
+
*
|
|
1178
|
+
* Namespace isolation is implemented by prefixing `id` and `name` attributes
|
|
1179
|
+
* with a constant string, i.e., `user-content-`
|
|
1180
|
+
*/
|
|
1039
1181
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
// or SVG and should never appear in HTML namespace
|
|
1182
|
+
let SANITIZE_NAMED_PROPS = false;
|
|
1183
|
+
const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
|
|
1184
|
+
/* Keep element content when removing element? */
|
|
1044
1185
|
|
|
1186
|
+
let KEEP_CONTENT = true;
|
|
1187
|
+
/* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
|
|
1188
|
+
* of importing it into a new Document and returning a sanitized copy */
|
|
1045
1189
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1190
|
+
let IN_PLACE = false;
|
|
1191
|
+
/* Allow usage of profiles like html, svg and mathMl */
|
|
1048
1192
|
|
|
1193
|
+
let USE_PROFILES = {};
|
|
1194
|
+
/* Tags to ignore content of when KEEP_CONTENT is true */
|
|
1049
1195
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
// that the element somehow got namespace that is not
|
|
1054
|
-
// HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
|
|
1055
|
-
// Return false just in case.
|
|
1196
|
+
let FORBID_CONTENTS = null;
|
|
1197
|
+
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']);
|
|
1198
|
+
/* Tags that are safe for data: URIs */
|
|
1056
1199
|
|
|
1200
|
+
let DATA_URI_TAGS = null;
|
|
1201
|
+
const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
|
|
1202
|
+
/* Attributes safe for values like "javascript:" */
|
|
1057
1203
|
|
|
1058
|
-
|
|
1059
|
-
};
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
*/
|
|
1204
|
+
let URI_SAFE_ATTRIBUTES = null;
|
|
1205
|
+
const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
|
|
1206
|
+
const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
|
|
1207
|
+
const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
1208
|
+
const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
1209
|
+
/* Document namespace */
|
|
1065
1210
|
|
|
1211
|
+
let NAMESPACE = HTML_NAMESPACE;
|
|
1212
|
+
let IS_EMPTY_INPUT = false;
|
|
1213
|
+
/* Allowed XHTML+XML namespaces */
|
|
1066
1214
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
});
|
|
1215
|
+
let ALLOWED_NAMESPACES = null;
|
|
1216
|
+
const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
|
|
1217
|
+
/* Parsing of strict XHTML documents */
|
|
1071
1218
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
}
|
|
1078
|
-
};
|
|
1079
|
-
/**
|
|
1080
|
-
* _removeAttribute
|
|
1081
|
-
*
|
|
1082
|
-
* @param {String} name an Attribute name
|
|
1083
|
-
* @param {Node} node a DOM node
|
|
1084
|
-
*/
|
|
1219
|
+
let PARSER_MEDIA_TYPE;
|
|
1220
|
+
const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
|
|
1221
|
+
const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
|
|
1222
|
+
let transformCaseFunc;
|
|
1223
|
+
/* Keep a reference to config to pass to hooks */
|
|
1085
1224
|
|
|
1225
|
+
let CONFIG = null;
|
|
1226
|
+
/* Ideally, do not touch anything below this line */
|
|
1086
1227
|
|
|
1087
|
-
|
|
1088
|
-
try {
|
|
1089
|
-
arrayPush(DOMPurify.removed, {
|
|
1090
|
-
attribute: node.getAttributeNode(name),
|
|
1091
|
-
from: node
|
|
1092
|
-
});
|
|
1093
|
-
} catch (_) {
|
|
1094
|
-
arrayPush(DOMPurify.removed, {
|
|
1095
|
-
attribute: null,
|
|
1096
|
-
from: node
|
|
1097
|
-
});
|
|
1098
|
-
}
|
|
1228
|
+
/* ______________________________________________ */
|
|
1099
1229
|
|
|
1100
|
-
|
|
1230
|
+
const formElement = document.createElement('form');
|
|
1101
1231
|
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
try {
|
|
1105
|
-
_forceRemove(node);
|
|
1106
|
-
} catch (_) {}
|
|
1107
|
-
} else {
|
|
1108
|
-
try {
|
|
1109
|
-
node.setAttribute(name, '');
|
|
1110
|
-
} catch (_) {}
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1232
|
+
const isRegexOrFunction = function isRegexOrFunction(testValue) {
|
|
1233
|
+
return testValue instanceof RegExp || testValue instanceof Function;
|
|
1113
1234
|
};
|
|
1114
1235
|
/**
|
|
1115
|
-
*
|
|
1236
|
+
* _parseConfig
|
|
1116
1237
|
*
|
|
1117
|
-
* @param {
|
|
1118
|
-
* @return {Document} a DOM, filled with the dirty markup
|
|
1238
|
+
* @param {Object} cfg optional config literal
|
|
1119
1239
|
*/
|
|
1240
|
+
// eslint-disable-next-line complexity
|
|
1120
1241
|
|
|
1121
1242
|
|
|
1122
|
-
const
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
let leadingWhitespace;
|
|
1126
|
-
|
|
1127
|
-
if (FORCE_BODY) {
|
|
1128
|
-
dirty = '<remove></remove>' + dirty;
|
|
1129
|
-
} else {
|
|
1130
|
-
/* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
|
|
1131
|
-
const matches = stringMatch(dirty, /^[\r\n\t ]+/);
|
|
1132
|
-
leadingWhitespace = matches && matches[0];
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
|
|
1136
|
-
// Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
|
|
1137
|
-
dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
1141
|
-
/*
|
|
1142
|
-
* Use the DOMParser API by default, fallback later if needs be
|
|
1143
|
-
* DOMParser not work for svg when has multiple root element.
|
|
1144
|
-
*/
|
|
1145
|
-
|
|
1146
|
-
if (NAMESPACE === HTML_NAMESPACE) {
|
|
1147
|
-
try {
|
|
1148
|
-
doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
|
|
1149
|
-
} catch (_) {}
|
|
1243
|
+
const _parseConfig = function _parseConfig(cfg) {
|
|
1244
|
+
if (CONFIG && CONFIG === cfg) {
|
|
1245
|
+
return;
|
|
1150
1246
|
}
|
|
1151
|
-
/*
|
|
1152
|
-
|
|
1247
|
+
/* Shield configuration object from tampering */
|
|
1153
1248
|
|
|
1154
|
-
if (!doc || !doc.documentElement) {
|
|
1155
|
-
doc = implementation.createDocument(NAMESPACE, 'template', null);
|
|
1156
1249
|
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
} catch (_) {// Syntax error if dirtyPayload is invalid xml
|
|
1160
|
-
}
|
|
1250
|
+
if (!cfg || typeof cfg !== 'object') {
|
|
1251
|
+
cfg = {};
|
|
1161
1252
|
}
|
|
1253
|
+
/* Shield configuration object from prototype pollution */
|
|
1162
1254
|
|
|
1163
|
-
const body = doc.body || doc.documentElement;
|
|
1164
1255
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
/* Work on whole document or just its body */
|
|
1256
|
+
cfg = clone(cfg);
|
|
1257
|
+
PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
|
|
1258
|
+
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE; // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
1169
1259
|
|
|
1260
|
+
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
|
|
1261
|
+
/* Set configuration parameters */
|
|
1170
1262
|
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
}
|
|
1263
|
+
ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
1264
|
+
ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
1265
|
+
ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
|
|
1266
|
+
URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent
|
|
1267
|
+
cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent
|
|
1268
|
+
transformCaseFunc // eslint-disable-line indent
|
|
1269
|
+
) // eslint-disable-line indent
|
|
1270
|
+
: DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
1271
|
+
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent
|
|
1272
|
+
cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent
|
|
1273
|
+
transformCaseFunc // eslint-disable-line indent
|
|
1274
|
+
) // eslint-disable-line indent
|
|
1275
|
+
: DEFAULT_DATA_URI_TAGS;
|
|
1276
|
+
FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
|
|
1277
|
+
FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
|
|
1278
|
+
FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
|
|
1279
|
+
USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
|
|
1280
|
+
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
1174
1281
|
|
|
1175
|
-
|
|
1176
|
-
};
|
|
1177
|
-
/**
|
|
1178
|
-
* _createIterator
|
|
1179
|
-
*
|
|
1180
|
-
* @param {Document} root document/fragment to create iterator for
|
|
1181
|
-
* @return {Iterator} iterator instance
|
|
1182
|
-
*/
|
|
1282
|
+
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
1183
1283
|
|
|
1284
|
+
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
|
|
1184
1285
|
|
|
1185
|
-
|
|
1186
|
-
return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
|
|
1187
|
-
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
|
|
1188
|
-
};
|
|
1189
|
-
/**
|
|
1190
|
-
* _isClobbered
|
|
1191
|
-
*
|
|
1192
|
-
* @param {Node} elm element to check for clobbering attacks
|
|
1193
|
-
* @return {Boolean} true if clobbered, false if safe
|
|
1194
|
-
*/
|
|
1286
|
+
ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
|
|
1195
1287
|
|
|
1288
|
+
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
|
|
1196
1289
|
|
|
1197
|
-
|
|
1198
|
-
return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function');
|
|
1199
|
-
};
|
|
1200
|
-
/**
|
|
1201
|
-
* _isNode
|
|
1202
|
-
*
|
|
1203
|
-
* @param {Node} obj object to check whether it's a DOM node
|
|
1204
|
-
* @return {Boolean} true is object is a DOM node
|
|
1205
|
-
*/
|
|
1290
|
+
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
|
|
1206
1291
|
|
|
1292
|
+
RETURN_DOM = cfg.RETURN_DOM || false; // Default false
|
|
1207
1293
|
|
|
1208
|
-
|
|
1209
|
-
return typeof Node === 'object' ? object instanceof Node : object && typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
|
|
1210
|
-
};
|
|
1211
|
-
/**
|
|
1212
|
-
* _executeHook
|
|
1213
|
-
* Execute user configurable hooks
|
|
1214
|
-
*
|
|
1215
|
-
* @param {String} entryPoint Name of the hook's entry point
|
|
1216
|
-
* @param {Node} currentNode node to work on with the hook
|
|
1217
|
-
* @param {Object} data additional hook parameters
|
|
1218
|
-
*/
|
|
1294
|
+
RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
|
|
1219
1295
|
|
|
1296
|
+
RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
|
|
1220
1297
|
|
|
1221
|
-
|
|
1222
|
-
if (!hooks[entryPoint]) {
|
|
1223
|
-
return;
|
|
1224
|
-
}
|
|
1298
|
+
FORCE_BODY = cfg.FORCE_BODY || false; // Default false
|
|
1225
1299
|
|
|
1226
|
-
|
|
1227
|
-
hook.call(DOMPurify, currentNode, data, CONFIG);
|
|
1228
|
-
});
|
|
1229
|
-
};
|
|
1230
|
-
/**
|
|
1231
|
-
* _sanitizeElements
|
|
1232
|
-
*
|
|
1233
|
-
* @protect nodeName
|
|
1234
|
-
* @protect textContent
|
|
1235
|
-
* @protect removeChild
|
|
1236
|
-
*
|
|
1237
|
-
* @param {Node} currentNode to check for permission to exist
|
|
1238
|
-
* @return {Boolean} true if node was killed, false if left alive
|
|
1239
|
-
*/
|
|
1300
|
+
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
|
|
1240
1301
|
|
|
1302
|
+
SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
|
|
1241
1303
|
|
|
1242
|
-
|
|
1243
|
-
let content;
|
|
1244
|
-
/* Execute a hook if present */
|
|
1304
|
+
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
|
|
1245
1305
|
|
|
1246
|
-
|
|
1247
|
-
/* Check if element is clobbered or can clobber */
|
|
1306
|
+
IN_PLACE = cfg.IN_PLACE || false; // Default false
|
|
1248
1307
|
|
|
1308
|
+
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
|
|
1309
|
+
NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
|
|
1310
|
+
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
|
|
1249
1311
|
|
|
1250
|
-
if (
|
|
1251
|
-
|
|
1312
|
+
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
|
|
1313
|
+
CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
|
|
1314
|
+
}
|
|
1252
1315
|
|
|
1253
|
-
|
|
1316
|
+
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
|
|
1317
|
+
CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
|
|
1254
1318
|
}
|
|
1255
|
-
/* Now let's check the element's type and name */
|
|
1256
1319
|
|
|
1320
|
+
if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
|
|
1321
|
+
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
|
|
1322
|
+
}
|
|
1257
1323
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1324
|
+
if (SAFE_FOR_TEMPLATES) {
|
|
1325
|
+
ALLOW_DATA_ATTR = false;
|
|
1326
|
+
}
|
|
1260
1327
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
/* Detect mXSS attempts abusing namespace confusion */
|
|
1328
|
+
if (RETURN_DOM_FRAGMENT) {
|
|
1329
|
+
RETURN_DOM = true;
|
|
1330
|
+
}
|
|
1331
|
+
/* Parse profile info */
|
|
1266
1332
|
|
|
1267
1333
|
|
|
1268
|
-
if (
|
|
1269
|
-
|
|
1334
|
+
if (USE_PROFILES) {
|
|
1335
|
+
ALLOWED_TAGS = addToSet({}, [...text]);
|
|
1336
|
+
ALLOWED_ATTR = [];
|
|
1270
1337
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1338
|
+
if (USE_PROFILES.html === true) {
|
|
1339
|
+
addToSet(ALLOWED_TAGS, html$1);
|
|
1340
|
+
addToSet(ALLOWED_ATTR, html);
|
|
1341
|
+
}
|
|
1274
1342
|
|
|
1343
|
+
if (USE_PROFILES.svg === true) {
|
|
1344
|
+
addToSet(ALLOWED_TAGS, svg$1);
|
|
1345
|
+
addToSet(ALLOWED_ATTR, svg);
|
|
1346
|
+
addToSet(ALLOWED_ATTR, xml);
|
|
1347
|
+
}
|
|
1275
1348
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
|
|
1349
|
+
if (USE_PROFILES.svgFilters === true) {
|
|
1350
|
+
addToSet(ALLOWED_TAGS, svgFilters);
|
|
1351
|
+
addToSet(ALLOWED_ATTR, svg);
|
|
1352
|
+
addToSet(ALLOWED_ATTR, xml);
|
|
1281
1353
|
}
|
|
1282
|
-
|
|
1354
|
+
|
|
1355
|
+
if (USE_PROFILES.mathMl === true) {
|
|
1356
|
+
addToSet(ALLOWED_TAGS, mathMl$1);
|
|
1357
|
+
addToSet(ALLOWED_ATTR, mathMl);
|
|
1358
|
+
addToSet(ALLOWED_ATTR, xml);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
/* Merge configuration parameters */
|
|
1283
1362
|
|
|
1284
1363
|
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1364
|
+
if (cfg.ADD_TAGS) {
|
|
1365
|
+
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
1366
|
+
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
1367
|
+
}
|
|
1288
1368
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1369
|
+
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
1370
|
+
}
|
|
1291
1371
|
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
}
|
|
1372
|
+
if (cfg.ADD_ATTR) {
|
|
1373
|
+
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
1374
|
+
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
1296
1375
|
}
|
|
1297
1376
|
|
|
1298
|
-
|
|
1377
|
+
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
1378
|
+
}
|
|
1299
1379
|
|
|
1300
|
-
|
|
1380
|
+
if (cfg.ADD_URI_SAFE_ATTR) {
|
|
1381
|
+
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
1301
1382
|
}
|
|
1302
|
-
/* Check whether element has a valid namespace */
|
|
1303
1383
|
|
|
1384
|
+
if (cfg.FORBID_CONTENTS) {
|
|
1385
|
+
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
|
|
1386
|
+
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
1387
|
+
}
|
|
1304
1388
|
|
|
1305
|
-
|
|
1306
|
-
|
|
1389
|
+
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
1390
|
+
}
|
|
1391
|
+
/* Add #text in case KEEP_CONTENT is set to true */
|
|
1307
1392
|
|
|
1308
|
-
|
|
1393
|
+
|
|
1394
|
+
if (KEEP_CONTENT) {
|
|
1395
|
+
ALLOWED_TAGS['#text'] = true;
|
|
1309
1396
|
}
|
|
1310
|
-
/*
|
|
1397
|
+
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
1311
1398
|
|
|
1312
1399
|
|
|
1313
|
-
if (
|
|
1314
|
-
|
|
1400
|
+
if (WHOLE_DOCUMENT) {
|
|
1401
|
+
addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
|
|
1402
|
+
}
|
|
1403
|
+
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
1315
1404
|
|
|
1316
|
-
|
|
1405
|
+
|
|
1406
|
+
if (ALLOWED_TAGS.table) {
|
|
1407
|
+
addToSet(ALLOWED_TAGS, ['tbody']);
|
|
1408
|
+
delete FORBID_TAGS.tbody;
|
|
1317
1409
|
}
|
|
1318
|
-
/* Sanitize element content to be template-safe */
|
|
1319
1410
|
|
|
1411
|
+
if (cfg.TRUSTED_TYPES_POLICY) {
|
|
1412
|
+
if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
|
|
1413
|
+
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
|
|
1414
|
+
}
|
|
1320
1415
|
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
content = stringReplace(content, MUSTACHE_EXPR, ' ');
|
|
1325
|
-
content = stringReplace(content, ERB_EXPR, ' ');
|
|
1326
|
-
content = stringReplace(content, TMPLIT_EXPR, ' ');
|
|
1416
|
+
if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
|
|
1417
|
+
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
|
|
1418
|
+
} // Overwrite existing TrustedTypes policy.
|
|
1327
1419
|
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1420
|
+
|
|
1421
|
+
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; // Sign local variables required by `sanitize`.
|
|
1422
|
+
|
|
1423
|
+
emptyHTML = trustedTypesPolicy.createHTML('');
|
|
1424
|
+
} else {
|
|
1425
|
+
// Uninitialized policy, attempt to initialize the internal dompurify policy.
|
|
1426
|
+
if (trustedTypesPolicy === undefined) {
|
|
1427
|
+
trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
|
|
1428
|
+
} // If creating the internal policy succeeded sign internal variables.
|
|
1429
|
+
|
|
1430
|
+
|
|
1431
|
+
if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
|
|
1432
|
+
emptyHTML = trustedTypesPolicy.createHTML('');
|
|
1333
1433
|
}
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1434
|
+
} // Prevent further manipulation of configuration.
|
|
1435
|
+
// Not available in IE8, Safari 5, etc.
|
|
1336
1436
|
|
|
1337
1437
|
|
|
1338
|
-
|
|
1438
|
+
if (freeze) {
|
|
1439
|
+
freeze(cfg);
|
|
1440
|
+
}
|
|
1339
1441
|
|
|
1340
|
-
|
|
1442
|
+
CONFIG = cfg;
|
|
1341
1443
|
};
|
|
1444
|
+
|
|
1445
|
+
const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
1446
|
+
const HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML
|
|
1447
|
+
// namespace. We need to specify them explicitly
|
|
1448
|
+
// so that they don't get erroneously deleted from
|
|
1449
|
+
// HTML namespace.
|
|
1450
|
+
|
|
1451
|
+
const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
|
|
1452
|
+
/* Keep track of all possible SVG and MathML tags
|
|
1453
|
+
* so that we can perform the namespace checks
|
|
1454
|
+
* correctly. */
|
|
1455
|
+
|
|
1456
|
+
const ALL_SVG_TAGS = addToSet({}, svg$1);
|
|
1457
|
+
addToSet(ALL_SVG_TAGS, svgFilters);
|
|
1458
|
+
addToSet(ALL_SVG_TAGS, svgDisallowed);
|
|
1459
|
+
const ALL_MATHML_TAGS = addToSet({}, mathMl$1);
|
|
1460
|
+
addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
|
|
1342
1461
|
/**
|
|
1343
|
-
* _isValidAttribute
|
|
1344
1462
|
*
|
|
1345
|
-
*
|
|
1346
|
-
* @param {
|
|
1347
|
-
* @
|
|
1348
|
-
*
|
|
1463
|
+
*
|
|
1464
|
+
* @param {Element} element a DOM element whose namespace is being checked
|
|
1465
|
+
* @returns {boolean} Return false if the element has a
|
|
1466
|
+
* namespace that a spec-compliant parser would never
|
|
1467
|
+
* return. Return true otherwise.
|
|
1349
1468
|
*/
|
|
1350
|
-
// eslint-disable-next-line complexity
|
|
1351
1469
|
|
|
1470
|
+
const _checkValidNamespace = function _checkValidNamespace(element) {
|
|
1471
|
+
let parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode
|
|
1472
|
+
// can be null. We just simulate parent in this case.
|
|
1473
|
+
|
|
1474
|
+
if (!parent || !parent.tagName) {
|
|
1475
|
+
parent = {
|
|
1476
|
+
namespaceURI: NAMESPACE,
|
|
1477
|
+
tagName: 'template'
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
const tagName = stringToLowerCase(element.tagName);
|
|
1482
|
+
const parentTagName = stringToLowerCase(parent.tagName);
|
|
1352
1483
|
|
|
1353
|
-
|
|
1354
|
-
/* Make sure attribute cannot clobber */
|
|
1355
|
-
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
1484
|
+
if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
1356
1485
|
return false;
|
|
1357
1486
|
}
|
|
1358
|
-
/* Allow valid data-* attributes: At least one character after "-"
|
|
1359
|
-
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
1360
|
-
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
1361
|
-
We don't need to check the value; it's always URI safe. */
|
|
1362
|
-
|
|
1363
1487
|
|
|
1364
|
-
if (
|
|
1365
|
-
|
|
1366
|
-
//
|
|
1367
|
-
//
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
/* Check value is safe. First, is attr inert? If so, is safe */
|
|
1488
|
+
if (element.namespaceURI === SVG_NAMESPACE) {
|
|
1489
|
+
// The only way to switch from HTML namespace to SVG
|
|
1490
|
+
// is via <svg>. If it happens via any other tag, then
|
|
1491
|
+
// it should be killed.
|
|
1492
|
+
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
1493
|
+
return tagName === 'svg';
|
|
1494
|
+
} // The only way to switch from MathML to SVG is via`
|
|
1495
|
+
// svg if parent is either <annotation-xml> or MathML
|
|
1496
|
+
// text integration points.
|
|
1374
1497
|
|
|
1375
|
-
} else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
|
|
1376
|
-
return false;
|
|
1377
|
-
} else ;
|
|
1378
1498
|
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
* checks if at least one dash is included in tagName, and it's not the first char
|
|
1384
|
-
* for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
|
|
1385
|
-
* @param {string} tagName name of the tag of the node to sanitize
|
|
1386
|
-
*/
|
|
1499
|
+
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
1500
|
+
return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
|
|
1501
|
+
} // We only allow elements that are defined in SVG
|
|
1502
|
+
// spec. All others are disallowed in SVG namespace.
|
|
1387
1503
|
|
|
1388
1504
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
};
|
|
1392
|
-
/**
|
|
1393
|
-
* _sanitizeAttributes
|
|
1394
|
-
*
|
|
1395
|
-
* @protect attributes
|
|
1396
|
-
* @protect nodeName
|
|
1397
|
-
* @protect removeAttribute
|
|
1398
|
-
* @protect setAttribute
|
|
1399
|
-
*
|
|
1400
|
-
* @param {Node} currentNode to sanitize
|
|
1401
|
-
*/
|
|
1505
|
+
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
1506
|
+
}
|
|
1402
1507
|
|
|
1508
|
+
if (element.namespaceURI === MATHML_NAMESPACE) {
|
|
1509
|
+
// The only way to switch from HTML namespace to MathML
|
|
1510
|
+
// is via <math>. If it happens via any other tag, then
|
|
1511
|
+
// it should be killed.
|
|
1512
|
+
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
1513
|
+
return tagName === 'math';
|
|
1514
|
+
} // The only way to switch from SVG to MathML is via
|
|
1515
|
+
// <math> and HTML integration points
|
|
1403
1516
|
|
|
1404
|
-
const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
1405
|
-
let attr;
|
|
1406
|
-
let value;
|
|
1407
|
-
let lcName;
|
|
1408
|
-
let l;
|
|
1409
|
-
/* Execute a hook if present */
|
|
1410
1517
|
|
|
1411
|
-
|
|
1518
|
+
if (parent.namespaceURI === SVG_NAMESPACE) {
|
|
1519
|
+
return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
|
|
1520
|
+
} // We only allow elements that are defined in MathML
|
|
1521
|
+
// spec. All others are disallowed in MathML namespace.
|
|
1412
1522
|
|
|
1413
|
-
const {
|
|
1414
|
-
attributes
|
|
1415
|
-
} = currentNode;
|
|
1416
|
-
/* Check if we have attributes; if not we might have a text node */
|
|
1417
1523
|
|
|
1418
|
-
|
|
1419
|
-
return;
|
|
1524
|
+
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
1420
1525
|
}
|
|
1421
1526
|
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
/* Go backwards over all attributes; safely remove bad ones */
|
|
1527
|
+
if (element.namespaceURI === HTML_NAMESPACE) {
|
|
1528
|
+
// The only way to switch from SVG to HTML is via
|
|
1529
|
+
// HTML integration points, and from MathML to HTML
|
|
1530
|
+
// is via MathML text integration points
|
|
1531
|
+
if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
|
|
1532
|
+
return false;
|
|
1533
|
+
}
|
|
1430
1534
|
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
namespaceURI
|
|
1436
|
-
} = attr;
|
|
1437
|
-
value = name === 'value' ? attr.value : stringTrim(attr.value);
|
|
1438
|
-
lcName = transformCaseFunc(name);
|
|
1439
|
-
/* Execute a hook if present */
|
|
1535
|
+
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
|
|
1536
|
+
return false;
|
|
1537
|
+
} // We disallow tags that are specific for MathML
|
|
1538
|
+
// or SVG and should never appear in HTML namespace
|
|
1440
1539
|
|
|
1441
|
-
hookEvent.attrName = lcName;
|
|
1442
|
-
hookEvent.attrValue = value;
|
|
1443
|
-
hookEvent.keepAttr = true;
|
|
1444
|
-
hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
|
|
1445
1540
|
|
|
1446
|
-
|
|
1541
|
+
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
1542
|
+
} // For XHTML and XML documents that support custom namespaces
|
|
1447
1543
|
|
|
1448
|
-
value = hookEvent.attrValue;
|
|
1449
|
-
/* Did the hooks approve of the attribute? */
|
|
1450
1544
|
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1545
|
+
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
1546
|
+
return true;
|
|
1547
|
+
} // The code should never reach this place (this means
|
|
1548
|
+
// that the element somehow got namespace that is not
|
|
1549
|
+
// HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
|
|
1550
|
+
// Return false just in case.
|
|
1455
1551
|
|
|
1456
1552
|
|
|
1457
|
-
|
|
1458
|
-
|
|
1553
|
+
return false;
|
|
1554
|
+
};
|
|
1555
|
+
/**
|
|
1556
|
+
* _forceRemove
|
|
1557
|
+
*
|
|
1558
|
+
* @param {Node} node a DOM node
|
|
1559
|
+
*/
|
|
1459
1560
|
|
|
1460
1561
|
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1562
|
+
const _forceRemove = function _forceRemove(node) {
|
|
1563
|
+
arrayPush(DOMPurify.removed, {
|
|
1564
|
+
element: node
|
|
1565
|
+
});
|
|
1465
1566
|
|
|
1567
|
+
try {
|
|
1568
|
+
// eslint-disable-next-line unicorn/prefer-dom-node-remove
|
|
1569
|
+
node.parentNode.removeChild(node);
|
|
1570
|
+
} catch (_) {
|
|
1571
|
+
node.remove();
|
|
1572
|
+
}
|
|
1573
|
+
};
|
|
1574
|
+
/**
|
|
1575
|
+
* _removeAttribute
|
|
1576
|
+
*
|
|
1577
|
+
* @param {String} name an Attribute name
|
|
1578
|
+
* @param {Node} node a DOM node
|
|
1579
|
+
*/
|
|
1466
1580
|
|
|
1467
|
-
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
|
|
1468
|
-
_removeAttribute(name, currentNode);
|
|
1469
1581
|
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1582
|
+
const _removeAttribute = function _removeAttribute(name, node) {
|
|
1583
|
+
try {
|
|
1584
|
+
arrayPush(DOMPurify.removed, {
|
|
1585
|
+
attribute: node.getAttributeNode(name),
|
|
1586
|
+
from: node
|
|
1587
|
+
});
|
|
1588
|
+
} catch (_) {
|
|
1589
|
+
arrayPush(DOMPurify.removed, {
|
|
1590
|
+
attribute: null,
|
|
1591
|
+
from: node
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1473
1594
|
|
|
1595
|
+
node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes
|
|
1474
1596
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1597
|
+
if (name === 'is' && !ALLOWED_ATTR[name]) {
|
|
1598
|
+
if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
|
|
1599
|
+
try {
|
|
1600
|
+
_forceRemove(node);
|
|
1601
|
+
} catch (_) {}
|
|
1602
|
+
} else {
|
|
1603
|
+
try {
|
|
1604
|
+
node.setAttribute(name, '');
|
|
1605
|
+
} catch (_) {}
|
|
1479
1606
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1607
|
+
}
|
|
1608
|
+
};
|
|
1609
|
+
/**
|
|
1610
|
+
* _initDocument
|
|
1611
|
+
*
|
|
1612
|
+
* @param {String} dirty a string of dirty markup
|
|
1613
|
+
* @return {Document} a DOM, filled with the dirty markup
|
|
1614
|
+
*/
|
|
1482
1615
|
|
|
1483
|
-
const lcTag = transformCaseFunc(currentNode.nodeName);
|
|
1484
1616
|
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
* Prefix id and name attributes with `user-content-`
|
|
1490
|
-
*/
|
|
1617
|
+
const _initDocument = function _initDocument(dirty) {
|
|
1618
|
+
/* Create a HTML document */
|
|
1619
|
+
let doc;
|
|
1620
|
+
let leadingWhitespace;
|
|
1491
1621
|
|
|
1622
|
+
if (FORCE_BODY) {
|
|
1623
|
+
dirty = '<remove></remove>' + dirty;
|
|
1624
|
+
} else {
|
|
1625
|
+
/* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
|
|
1626
|
+
const matches = stringMatch(dirty, /^[\r\n\t ]+/);
|
|
1627
|
+
leadingWhitespace = matches && matches[0];
|
|
1628
|
+
}
|
|
1492
1629
|
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1630
|
+
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
|
|
1631
|
+
// Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
|
|
1632
|
+
dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
|
|
1633
|
+
}
|
|
1496
1634
|
|
|
1635
|
+
const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
1636
|
+
/*
|
|
1637
|
+
* Use the DOMParser API by default, fallback later if needs be
|
|
1638
|
+
* DOMParser not work for svg when has multiple root element.
|
|
1639
|
+
*/
|
|
1497
1640
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1641
|
+
if (NAMESPACE === HTML_NAMESPACE) {
|
|
1642
|
+
try {
|
|
1643
|
+
doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
|
|
1644
|
+
} catch (_) {}
|
|
1645
|
+
}
|
|
1646
|
+
/* Use createHTMLDocument in case DOMParser is not available */
|
|
1501
1647
|
|
|
1502
1648
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
|
|
1506
|
-
case 'TrustedHTML':
|
|
1507
|
-
{
|
|
1508
|
-
value = trustedTypesPolicy.createHTML(value);
|
|
1509
|
-
break;
|
|
1510
|
-
}
|
|
1649
|
+
if (!doc || !doc.documentElement) {
|
|
1650
|
+
doc = implementation.createDocument(NAMESPACE, 'template', null);
|
|
1511
1651
|
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
break;
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1652
|
+
try {
|
|
1653
|
+
doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
|
|
1654
|
+
} catch (_) {// Syntax error if dirtyPayload is invalid xml
|
|
1519
1655
|
}
|
|
1520
|
-
|
|
1656
|
+
}
|
|
1521
1657
|
|
|
1658
|
+
const body = doc.body || doc.documentElement;
|
|
1522
1659
|
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
/* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
|
|
1528
|
-
currentNode.setAttribute(name, value);
|
|
1529
|
-
}
|
|
1660
|
+
if (dirty && leadingWhitespace) {
|
|
1661
|
+
body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
|
|
1662
|
+
}
|
|
1663
|
+
/* Work on whole document or just its body */
|
|
1530
1664
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1665
|
+
|
|
1666
|
+
if (NAMESPACE === HTML_NAMESPACE) {
|
|
1667
|
+
return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
|
|
1533
1668
|
}
|
|
1534
|
-
/* Execute a hook if present */
|
|
1535
1669
|
|
|
1670
|
+
return WHOLE_DOCUMENT ? doc.documentElement : body;
|
|
1671
|
+
};
|
|
1672
|
+
/**
|
|
1673
|
+
* _createIterator
|
|
1674
|
+
*
|
|
1675
|
+
* @param {Document} root document/fragment to create iterator for
|
|
1676
|
+
* @return {Iterator} iterator instance
|
|
1677
|
+
*/
|
|
1536
1678
|
|
|
1537
|
-
|
|
1679
|
+
|
|
1680
|
+
const _createIterator = function _createIterator(root) {
|
|
1681
|
+
return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
|
|
1682
|
+
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
|
|
1538
1683
|
};
|
|
1539
1684
|
/**
|
|
1540
|
-
*
|
|
1685
|
+
* _isClobbered
|
|
1541
1686
|
*
|
|
1542
|
-
* @param {
|
|
1687
|
+
* @param {Node} elm element to check for clobbering attacks
|
|
1688
|
+
* @return {Boolean} true if clobbered, false if safe
|
|
1543
1689
|
*/
|
|
1544
1690
|
|
|
1545
1691
|
|
|
1546
|
-
const
|
|
1547
|
-
|
|
1692
|
+
const _isClobbered = function _isClobbered(elm) {
|
|
1693
|
+
return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function');
|
|
1694
|
+
};
|
|
1695
|
+
/**
|
|
1696
|
+
* _isNode
|
|
1697
|
+
*
|
|
1698
|
+
* @param {Node} obj object to check whether it's a DOM node
|
|
1699
|
+
* @return {Boolean} true is object is a DOM node
|
|
1700
|
+
*/
|
|
1548
1701
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1702
|
+
|
|
1703
|
+
const _isNode = function _isNode(object) {
|
|
1704
|
+
return typeof Node === 'object' ? object instanceof Node : object && typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
|
|
1705
|
+
};
|
|
1706
|
+
/**
|
|
1707
|
+
* _executeHook
|
|
1708
|
+
* Execute user configurable hooks
|
|
1709
|
+
*
|
|
1710
|
+
* @param {String} entryPoint Name of the hook's entry point
|
|
1711
|
+
* @param {Node} currentNode node to work on with the hook
|
|
1712
|
+
* @param {Object} data additional hook parameters
|
|
1713
|
+
*/
|
|
1551
1714
|
|
|
1552
1715
|
|
|
1553
|
-
|
|
1716
|
+
const _executeHook = function _executeHook(entryPoint, currentNode, data) {
|
|
1717
|
+
if (!hooks[entryPoint]) {
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1554
1720
|
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1721
|
+
arrayForEach(hooks[entryPoint], hook => {
|
|
1722
|
+
hook.call(DOMPurify, currentNode, data, CONFIG);
|
|
1723
|
+
});
|
|
1724
|
+
};
|
|
1725
|
+
/**
|
|
1726
|
+
* _sanitizeElements
|
|
1727
|
+
*
|
|
1728
|
+
* @protect nodeName
|
|
1729
|
+
* @protect textContent
|
|
1730
|
+
* @protect removeChild
|
|
1731
|
+
*
|
|
1732
|
+
* @param {Node} currentNode to check for permission to exist
|
|
1733
|
+
* @return {Boolean} true if node was killed, false if left alive
|
|
1734
|
+
*/
|
|
1559
1735
|
|
|
1560
1736
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
/* Deep shadow DOM detected */
|
|
1737
|
+
const _sanitizeElements = function _sanitizeElements(currentNode) {
|
|
1738
|
+
let content;
|
|
1739
|
+
/* Execute a hook if present */
|
|
1565
1740
|
|
|
1741
|
+
_executeHook('beforeSanitizeElements', currentNode, null);
|
|
1742
|
+
/* Check if element is clobbered or can clobber */
|
|
1566
1743
|
|
|
1567
|
-
if (shadowNode.content instanceof DocumentFragment) {
|
|
1568
|
-
_sanitizeShadowDOM(shadowNode.content);
|
|
1569
|
-
}
|
|
1570
|
-
/* Check attributes, sanitize if necessary */
|
|
1571
1744
|
|
|
1745
|
+
if (_isClobbered(currentNode)) {
|
|
1746
|
+
_forceRemove(currentNode);
|
|
1572
1747
|
|
|
1573
|
-
|
|
1748
|
+
return true;
|
|
1574
1749
|
}
|
|
1575
|
-
/*
|
|
1750
|
+
/* Now let's check the element's type and name */
|
|
1576
1751
|
|
|
1577
1752
|
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
/**
|
|
1581
|
-
* Sanitize
|
|
1582
|
-
* Public method providing core sanitation functionality
|
|
1583
|
-
*
|
|
1584
|
-
* @param {String|Node} dirty string or DOM node
|
|
1585
|
-
* @param {Object} configuration object
|
|
1586
|
-
*/
|
|
1587
|
-
// eslint-disable-next-line complexity
|
|
1753
|
+
const tagName = transformCaseFunc(currentNode.nodeName);
|
|
1754
|
+
/* Execute a hook if present */
|
|
1588
1755
|
|
|
1756
|
+
_executeHook('uponSanitizeElement', currentNode, {
|
|
1757
|
+
tagName,
|
|
1758
|
+
allowedTags: ALLOWED_TAGS
|
|
1759
|
+
});
|
|
1760
|
+
/* Detect mXSS attempts abusing namespace confusion */
|
|
1589
1761
|
|
|
1590
|
-
DOMPurify.sanitize = function (dirty) {
|
|
1591
|
-
let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1592
|
-
let body;
|
|
1593
|
-
let importedNode;
|
|
1594
|
-
let currentNode;
|
|
1595
|
-
let returnNode;
|
|
1596
|
-
/* Make sure we have a string to sanitize.
|
|
1597
|
-
DO NOT return early, as this will return the wrong type if
|
|
1598
|
-
the user has requested a DOM object rather than a string */
|
|
1599
1762
|
|
|
1600
|
-
|
|
1763
|
+
if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
|
|
1764
|
+
_forceRemove(currentNode);
|
|
1601
1765
|
|
|
1602
|
-
|
|
1603
|
-
dirty = '<!-->';
|
|
1766
|
+
return true;
|
|
1604
1767
|
}
|
|
1605
|
-
/*
|
|
1768
|
+
/* Remove element if anything forbids its presence */
|
|
1606
1769
|
|
|
1607
1770
|
|
|
1608
|
-
if (
|
|
1609
|
-
if
|
|
1610
|
-
|
|
1771
|
+
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1772
|
+
/* Check if we have a custom element to handle */
|
|
1773
|
+
if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
|
|
1774
|
+
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
1775
|
+
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
|
|
1776
|
+
}
|
|
1777
|
+
/* Keep content except for bad-listed elements */
|
|
1611
1778
|
|
|
1612
|
-
|
|
1613
|
-
|
|
1779
|
+
|
|
1780
|
+
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
1781
|
+
const parentNode = getParentNode(currentNode) || currentNode.parentNode;
|
|
1782
|
+
const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
|
|
1783
|
+
|
|
1784
|
+
if (childNodes && parentNode) {
|
|
1785
|
+
const childCount = childNodes.length;
|
|
1786
|
+
|
|
1787
|
+
for (let i = childCount - 1; i >= 0; --i) {
|
|
1788
|
+
parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
|
|
1789
|
+
}
|
|
1614
1790
|
}
|
|
1615
|
-
} else {
|
|
1616
|
-
throw typeErrorCreate('toString is not a function');
|
|
1617
1791
|
}
|
|
1618
|
-
}
|
|
1619
|
-
/* Return dirty HTML if DOMPurify cannot run */
|
|
1620
1792
|
|
|
1793
|
+
_forceRemove(currentNode);
|
|
1621
1794
|
|
|
1622
|
-
|
|
1623
|
-
return dirty;
|
|
1795
|
+
return true;
|
|
1624
1796
|
}
|
|
1625
|
-
/*
|
|
1797
|
+
/* Check whether element has a valid namespace */
|
|
1626
1798
|
|
|
1627
1799
|
|
|
1628
|
-
if (!
|
|
1629
|
-
|
|
1800
|
+
if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
|
|
1801
|
+
_forceRemove(currentNode);
|
|
1802
|
+
|
|
1803
|
+
return true;
|
|
1630
1804
|
}
|
|
1631
|
-
/*
|
|
1805
|
+
/* Make sure that older browsers don't get noscript mXSS */
|
|
1632
1806
|
|
|
1633
1807
|
|
|
1634
|
-
|
|
1635
|
-
|
|
1808
|
+
if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
|
|
1809
|
+
_forceRemove(currentNode);
|
|
1636
1810
|
|
|
1637
|
-
|
|
1638
|
-
IN_PLACE = false;
|
|
1811
|
+
return true;
|
|
1639
1812
|
}
|
|
1813
|
+
/* Sanitize element content to be template-safe */
|
|
1640
1814
|
|
|
1641
|
-
if (IN_PLACE) {
|
|
1642
|
-
/* Do some early pre-sanitization to avoid unsafe root nodes */
|
|
1643
|
-
if (dirty.nodeName) {
|
|
1644
|
-
const tagName = transformCaseFunc(dirty.nodeName);
|
|
1645
1815
|
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
elements being stripped by the parser */
|
|
1653
|
-
body = _initDocument('<!---->');
|
|
1654
|
-
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
1816
|
+
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
|
|
1817
|
+
/* Get the element's text content */
|
|
1818
|
+
content = currentNode.textContent;
|
|
1819
|
+
content = stringReplace(content, MUSTACHE_EXPR, ' ');
|
|
1820
|
+
content = stringReplace(content, ERB_EXPR, ' ');
|
|
1821
|
+
content = stringReplace(content, TMPLIT_EXPR, ' ');
|
|
1655
1822
|
|
|
1656
|
-
if (
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
} else {
|
|
1662
|
-
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
1663
|
-
body.appendChild(importedNode);
|
|
1664
|
-
}
|
|
1665
|
-
} else {
|
|
1666
|
-
/* Exit directly if we have nothing to do */
|
|
1667
|
-
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
|
|
1668
|
-
dirty.indexOf('<') === -1) {
|
|
1669
|
-
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
1823
|
+
if (currentNode.textContent !== content) {
|
|
1824
|
+
arrayPush(DOMPurify.removed, {
|
|
1825
|
+
element: currentNode.cloneNode()
|
|
1826
|
+
});
|
|
1827
|
+
currentNode.textContent = content;
|
|
1670
1828
|
}
|
|
1671
|
-
|
|
1829
|
+
}
|
|
1830
|
+
/* Execute a hook if present */
|
|
1672
1831
|
|
|
1673
1832
|
|
|
1674
|
-
|
|
1675
|
-
|
|
1833
|
+
_executeHook('afterSanitizeElements', currentNode, null);
|
|
1834
|
+
|
|
1835
|
+
return false;
|
|
1836
|
+
};
|
|
1837
|
+
/**
|
|
1838
|
+
* _isValidAttribute
|
|
1839
|
+
*
|
|
1840
|
+
* @param {string} lcTag Lowercase tag name of containing element.
|
|
1841
|
+
* @param {string} lcName Lowercase attribute name.
|
|
1842
|
+
* @param {string} value Attribute value.
|
|
1843
|
+
* @return {Boolean} Returns true if `value` is valid, otherwise false.
|
|
1844
|
+
*/
|
|
1845
|
+
// eslint-disable-next-line complexity
|
|
1676
1846
|
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1847
|
+
|
|
1848
|
+
const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
1849
|
+
/* Make sure attribute cannot clobber */
|
|
1850
|
+
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
1851
|
+
return false;
|
|
1680
1852
|
}
|
|
1681
|
-
/*
|
|
1853
|
+
/* Allow valid data-* attributes: At least one character after "-"
|
|
1854
|
+
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
1855
|
+
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
1856
|
+
We don't need to check the value; it's always URI safe. */
|
|
1682
1857
|
|
|
1683
1858
|
|
|
1684
|
-
if (
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1859
|
+
if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
|
|
1860
|
+
if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
1861
|
+
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1862
|
+
// and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
|
|
1863
|
+
_basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || // Alternative, second condition checks if it's an `is`-attribute, AND
|
|
1864
|
+
// the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1865
|
+
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 {
|
|
1866
|
+
return false;
|
|
1867
|
+
}
|
|
1868
|
+
/* Check value is safe. First, is attr inert? If so, is safe */
|
|
1688
1869
|
|
|
1870
|
+
} else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
|
|
1871
|
+
return false;
|
|
1872
|
+
} else ;
|
|
1689
1873
|
|
|
1690
|
-
|
|
1691
|
-
|
|
1874
|
+
return true;
|
|
1875
|
+
};
|
|
1876
|
+
/**
|
|
1877
|
+
* _basicCustomElementCheck
|
|
1878
|
+
* checks if at least one dash is included in tagName, and it's not the first char
|
|
1879
|
+
* for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
|
|
1880
|
+
* @param {string} tagName name of the tag of the node to sanitize
|
|
1881
|
+
*/
|
|
1692
1882
|
|
|
1693
1883
|
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1884
|
+
const _basicCustomElementTest = function _basicCustomElementTest(tagName) {
|
|
1885
|
+
return tagName.indexOf('-') > 0;
|
|
1886
|
+
};
|
|
1887
|
+
/**
|
|
1888
|
+
* _sanitizeAttributes
|
|
1889
|
+
*
|
|
1890
|
+
* @protect attributes
|
|
1891
|
+
* @protect nodeName
|
|
1892
|
+
* @protect removeAttribute
|
|
1893
|
+
* @protect setAttribute
|
|
1894
|
+
*
|
|
1895
|
+
* @param {Node} currentNode to sanitize
|
|
1896
|
+
*/
|
|
1700
1897
|
|
|
1701
1898
|
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1899
|
+
const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
1900
|
+
let attr;
|
|
1901
|
+
let value;
|
|
1902
|
+
let lcName;
|
|
1903
|
+
let l;
|
|
1904
|
+
/* Execute a hook if present */
|
|
1706
1905
|
|
|
1906
|
+
_executeHook('beforeSanitizeAttributes', currentNode, null);
|
|
1707
1907
|
|
|
1708
|
-
|
|
1908
|
+
const {
|
|
1909
|
+
attributes
|
|
1910
|
+
} = currentNode;
|
|
1911
|
+
/* Check if we have attributes; if not we might have a text node */
|
|
1912
|
+
|
|
1913
|
+
if (!attributes) {
|
|
1914
|
+
return;
|
|
1709
1915
|
}
|
|
1710
|
-
/* If we sanitized `dirty` in-place, return it. */
|
|
1711
1916
|
|
|
1917
|
+
const hookEvent = {
|
|
1918
|
+
attrName: '',
|
|
1919
|
+
attrValue: '',
|
|
1920
|
+
keepAttr: true,
|
|
1921
|
+
allowedAttributes: ALLOWED_ATTR
|
|
1922
|
+
};
|
|
1923
|
+
l = attributes.length;
|
|
1924
|
+
/* Go backwards over all attributes; safely remove bad ones */
|
|
1712
1925
|
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1926
|
+
while (l--) {
|
|
1927
|
+
attr = attributes[l];
|
|
1928
|
+
const {
|
|
1929
|
+
name,
|
|
1930
|
+
namespaceURI
|
|
1931
|
+
} = attr;
|
|
1932
|
+
value = name === 'value' ? attr.value : stringTrim(attr.value);
|
|
1933
|
+
lcName = transformCaseFunc(name);
|
|
1934
|
+
/* Execute a hook if present */
|
|
1717
1935
|
|
|
1936
|
+
hookEvent.attrName = lcName;
|
|
1937
|
+
hookEvent.attrValue = value;
|
|
1938
|
+
hookEvent.keepAttr = true;
|
|
1939
|
+
hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
|
|
1718
1940
|
|
|
1719
|
-
|
|
1720
|
-
if (RETURN_DOM_FRAGMENT) {
|
|
1721
|
-
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
1941
|
+
_executeHook('uponSanitizeAttribute', currentNode, hookEvent);
|
|
1722
1942
|
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
returnNode.appendChild(body.firstChild);
|
|
1726
|
-
}
|
|
1727
|
-
} else {
|
|
1728
|
-
returnNode = body;
|
|
1729
|
-
}
|
|
1943
|
+
value = hookEvent.attrValue;
|
|
1944
|
+
/* Did the hooks approve of the attribute? */
|
|
1730
1945
|
|
|
1731
|
-
if (
|
|
1732
|
-
|
|
1733
|
-
AdoptNode() is not used because internal state is not reset
|
|
1734
|
-
(e.g. the past names map of a HTMLFormElement), this is safe
|
|
1735
|
-
in theory but we would rather not risk another attack vector.
|
|
1736
|
-
The state that is cloned by importNode() is explicitly defined
|
|
1737
|
-
by the specs.
|
|
1738
|
-
*/
|
|
1739
|
-
returnNode = importNode.call(originalDocument, returnNode, true);
|
|
1946
|
+
if (hookEvent.forceKeepAttr) {
|
|
1947
|
+
continue;
|
|
1740
1948
|
}
|
|
1949
|
+
/* Remove attribute */
|
|
1741
1950
|
|
|
1742
|
-
return returnNode;
|
|
1743
|
-
}
|
|
1744
1951
|
|
|
1745
|
-
|
|
1746
|
-
|
|
1952
|
+
_removeAttribute(name, currentNode);
|
|
1953
|
+
/* Did the hooks approve of the attribute? */
|
|
1747
1954
|
|
|
1748
|
-
if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
|
|
1749
|
-
serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
|
|
1750
|
-
}
|
|
1751
|
-
/* Sanitize final string template-safe */
|
|
1752
1955
|
|
|
1956
|
+
if (!hookEvent.keepAttr) {
|
|
1957
|
+
continue;
|
|
1958
|
+
}
|
|
1959
|
+
/* Work around a security issue in jQuery 3.0 */
|
|
1753
1960
|
|
|
1754
|
-
if (SAFE_FOR_TEMPLATES) {
|
|
1755
|
-
serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR, ' ');
|
|
1756
|
-
serializedHTML = stringReplace(serializedHTML, ERB_EXPR, ' ');
|
|
1757
|
-
serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR, ' ');
|
|
1758
|
-
}
|
|
1759
1961
|
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
* @param {Object} cfg configuration object
|
|
1767
|
-
*/
|
|
1962
|
+
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
|
|
1963
|
+
_removeAttribute(name, currentNode);
|
|
1964
|
+
|
|
1965
|
+
continue;
|
|
1966
|
+
}
|
|
1967
|
+
/* Sanitize attribute content to be template-safe */
|
|
1768
1968
|
|
|
1769
1969
|
|
|
1770
|
-
|
|
1771
|
-
|
|
1970
|
+
if (SAFE_FOR_TEMPLATES) {
|
|
1971
|
+
value = stringReplace(value, MUSTACHE_EXPR, ' ');
|
|
1972
|
+
value = stringReplace(value, ERB_EXPR, ' ');
|
|
1973
|
+
value = stringReplace(value, TMPLIT_EXPR, ' ');
|
|
1974
|
+
}
|
|
1975
|
+
/* Is `value` valid for this attribute? */
|
|
1772
1976
|
|
|
1773
|
-
SET_CONFIG = true;
|
|
1774
|
-
};
|
|
1775
|
-
/**
|
|
1776
|
-
* Public method to remove the configuration
|
|
1777
|
-
* clearConfig
|
|
1778
|
-
*
|
|
1779
|
-
*/
|
|
1780
1977
|
|
|
1978
|
+
const lcTag = transformCaseFunc(currentNode.nodeName);
|
|
1781
1979
|
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
* Uses last set config, if any. Otherwise, uses config defaults.
|
|
1789
|
-
* isValidAttribute
|
|
1790
|
-
*
|
|
1791
|
-
* @param {string} tag Tag name of containing element.
|
|
1792
|
-
* @param {string} attr Attribute name.
|
|
1793
|
-
* @param {string} value Attribute value.
|
|
1794
|
-
* @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
|
|
1795
|
-
*/
|
|
1980
|
+
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
1981
|
+
continue;
|
|
1982
|
+
}
|
|
1983
|
+
/* Full DOM Clobbering protection via namespace isolation,
|
|
1984
|
+
* Prefix id and name attributes with `user-content-`
|
|
1985
|
+
*/
|
|
1796
1986
|
|
|
1797
1987
|
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
_parseConfig({});
|
|
1802
|
-
}
|
|
1988
|
+
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
|
|
1989
|
+
// Remove the attribute with this value
|
|
1990
|
+
_removeAttribute(name, currentNode); // Prefix the value and later re-create the attribute with the sanitized value
|
|
1803
1991
|
|
|
1804
|
-
const lcTag = transformCaseFunc(tag);
|
|
1805
|
-
const lcName = transformCaseFunc(attr);
|
|
1806
|
-
return _isValidAttribute(lcTag, lcName, value);
|
|
1807
|
-
};
|
|
1808
|
-
/**
|
|
1809
|
-
* AddHook
|
|
1810
|
-
* Public method to add DOMPurify hooks
|
|
1811
|
-
*
|
|
1812
|
-
* @param {String} entryPoint entry point for the hook to add
|
|
1813
|
-
* @param {Function} hookFunction function to execute
|
|
1814
|
-
*/
|
|
1815
1992
|
|
|
1993
|
+
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
1994
|
+
}
|
|
1995
|
+
/* Handle attributes that require Trusted Types */
|
|
1816
1996
|
|
|
1817
|
-
DOMPurify.addHook = function (entryPoint, hookFunction) {
|
|
1818
|
-
if (typeof hookFunction !== 'function') {
|
|
1819
|
-
return;
|
|
1820
|
-
}
|
|
1821
1997
|
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1998
|
+
if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
|
|
1999
|
+
if (namespaceURI) ; else {
|
|
2000
|
+
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
|
|
2001
|
+
case 'TrustedHTML':
|
|
2002
|
+
{
|
|
2003
|
+
value = trustedTypesPolicy.createHTML(value);
|
|
2004
|
+
break;
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
case 'TrustedScriptURL':
|
|
2008
|
+
{
|
|
2009
|
+
value = trustedTypesPolicy.createScriptURL(value);
|
|
2010
|
+
break;
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
/* Handle invalid data-* attribute set by try-catching it */
|
|
2016
|
+
|
|
1833
2017
|
|
|
2018
|
+
try {
|
|
2019
|
+
if (namespaceURI) {
|
|
2020
|
+
currentNode.setAttributeNS(namespaceURI, name, value);
|
|
2021
|
+
} else {
|
|
2022
|
+
/* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
|
|
2023
|
+
currentNode.setAttribute(name, value);
|
|
2024
|
+
}
|
|
1834
2025
|
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
return arrayPop(hooks[entryPoint]);
|
|
2026
|
+
arrayPop(DOMPurify.removed);
|
|
2027
|
+
} catch (_) {}
|
|
1838
2028
|
}
|
|
1839
|
-
|
|
1840
|
-
/**
|
|
1841
|
-
* RemoveHooks
|
|
1842
|
-
* Public method to remove all DOMPurify hooks at a given entryPoint
|
|
1843
|
-
*
|
|
1844
|
-
* @param {String} entryPoint entry point for the hooks to remove
|
|
1845
|
-
*/
|
|
2029
|
+
/* Execute a hook if present */
|
|
1846
2030
|
|
|
1847
2031
|
|
|
1848
|
-
|
|
1849
|
-
if (hooks[entryPoint]) {
|
|
1850
|
-
hooks[entryPoint] = [];
|
|
1851
|
-
}
|
|
2032
|
+
_executeHook('afterSanitizeAttributes', currentNode, null);
|
|
1852
2033
|
};
|
|
1853
2034
|
/**
|
|
1854
|
-
*
|
|
1855
|
-
* Public method to remove all DOMPurify hooks
|
|
2035
|
+
* _sanitizeShadowDOM
|
|
1856
2036
|
*
|
|
2037
|
+
* @param {DocumentFragment} fragment to iterate over recursively
|
|
1857
2038
|
*/
|
|
1858
2039
|
|
|
1859
2040
|
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
};
|
|
1863
|
-
|
|
1864
|
-
return DOMPurify;
|
|
1865
|
-
}
|
|
2041
|
+
const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
|
|
2042
|
+
let shadowNode;
|
|
1866
2043
|
|
|
1867
|
-
|
|
2044
|
+
const shadowIterator = _createIterator(fragment);
|
|
2045
|
+
/* Execute a hook if present */
|
|
1868
2046
|
|
|
1869
|
-
function _classCallCheck(instance, Constructor) {
|
|
1870
|
-
if (!(instance instanceof Constructor)) {
|
|
1871
|
-
throw new TypeError("Cannot call a class as a function");
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
2047
|
|
|
1875
|
-
|
|
1876
|
-
"@babel/helpers - typeof";
|
|
2048
|
+
_executeHook('beforeSanitizeShadowDOM', fragment, null);
|
|
1877
2049
|
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
}, _typeof(obj);
|
|
1883
|
-
}
|
|
2050
|
+
while (shadowNode = shadowIterator.nextNode()) {
|
|
2051
|
+
/* Execute a hook if present */
|
|
2052
|
+
_executeHook('uponSanitizeShadowNode', shadowNode, null);
|
|
2053
|
+
/* Sanitize tags and elements */
|
|
1884
2054
|
|
|
1885
|
-
function _toPrimitive(input, hint) {
|
|
1886
|
-
if (_typeof(input) !== "object" || input === null) return input;
|
|
1887
|
-
var prim = input[Symbol.toPrimitive];
|
|
1888
|
-
if (prim !== undefined) {
|
|
1889
|
-
var res = prim.call(input, hint || "default");
|
|
1890
|
-
if (_typeof(res) !== "object") return res;
|
|
1891
|
-
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
1892
|
-
}
|
|
1893
|
-
return (hint === "string" ? String : Number)(input);
|
|
1894
|
-
}
|
|
1895
2055
|
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
2056
|
+
if (_sanitizeElements(shadowNode)) {
|
|
2057
|
+
continue;
|
|
2058
|
+
}
|
|
2059
|
+
/* Deep shadow DOM detected */
|
|
1900
2060
|
|
|
1901
|
-
function _defineProperties(target, props) {
|
|
1902
|
-
for (var i = 0; i < props.length; i++) {
|
|
1903
|
-
var descriptor = props[i];
|
|
1904
|
-
descriptor.enumerable = descriptor.enumerable || false;
|
|
1905
|
-
descriptor.configurable = true;
|
|
1906
|
-
if ("value" in descriptor) descriptor.writable = true;
|
|
1907
|
-
Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
|
|
1908
|
-
}
|
|
1909
|
-
}
|
|
1910
|
-
function _createClass(Constructor, protoProps, staticProps) {
|
|
1911
|
-
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
1912
|
-
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
1913
|
-
Object.defineProperty(Constructor, "prototype", {
|
|
1914
|
-
writable: false
|
|
1915
|
-
});
|
|
1916
|
-
return Constructor;
|
|
1917
|
-
}
|
|
1918
2061
|
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
var slice = arr.slice;
|
|
1922
|
-
function defaults(obj) {
|
|
1923
|
-
each.call(slice.call(arguments, 1), function (source) {
|
|
1924
|
-
if (source) {
|
|
1925
|
-
for (var prop in source) {
|
|
1926
|
-
if (obj[prop] === undefined) obj[prop] = source[prop];
|
|
2062
|
+
if (shadowNode.content instanceof DocumentFragment) {
|
|
2063
|
+
_sanitizeShadowDOM(shadowNode.content);
|
|
1927
2064
|
}
|
|
1928
|
-
|
|
1929
|
-
});
|
|
1930
|
-
return obj;
|
|
1931
|
-
}
|
|
2065
|
+
/* Check attributes, sanitize if necessary */
|
|
1932
2066
|
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
var serializeCookie = function serializeCookie(name, val, options) {
|
|
1936
|
-
var opt = options || {};
|
|
1937
|
-
opt.path = opt.path || '/';
|
|
1938
|
-
var value = encodeURIComponent(val);
|
|
1939
|
-
var str = "".concat(name, "=").concat(value);
|
|
1940
|
-
if (opt.maxAge > 0) {
|
|
1941
|
-
var maxAge = opt.maxAge - 0;
|
|
1942
|
-
if (Number.isNaN(maxAge)) throw new Error('maxAge should be a Number');
|
|
1943
|
-
str += "; Max-Age=".concat(Math.floor(maxAge));
|
|
1944
|
-
}
|
|
1945
|
-
if (opt.domain) {
|
|
1946
|
-
if (!fieldContentRegExp.test(opt.domain)) {
|
|
1947
|
-
throw new TypeError('option domain is invalid');
|
|
1948
|
-
}
|
|
1949
|
-
str += "; Domain=".concat(opt.domain);
|
|
1950
|
-
}
|
|
1951
|
-
if (opt.path) {
|
|
1952
|
-
if (!fieldContentRegExp.test(opt.path)) {
|
|
1953
|
-
throw new TypeError('option path is invalid');
|
|
1954
|
-
}
|
|
1955
|
-
str += "; Path=".concat(opt.path);
|
|
1956
|
-
}
|
|
1957
|
-
if (opt.expires) {
|
|
1958
|
-
if (typeof opt.expires.toUTCString !== 'function') {
|
|
1959
|
-
throw new TypeError('option expires is invalid');
|
|
1960
|
-
}
|
|
1961
|
-
str += "; Expires=".concat(opt.expires.toUTCString());
|
|
1962
|
-
}
|
|
1963
|
-
if (opt.httpOnly) str += '; HttpOnly';
|
|
1964
|
-
if (opt.secure) str += '; Secure';
|
|
1965
|
-
if (opt.sameSite) {
|
|
1966
|
-
var sameSite = typeof opt.sameSite === 'string' ? opt.sameSite.toLowerCase() : opt.sameSite;
|
|
1967
|
-
switch (sameSite) {
|
|
1968
|
-
case true:
|
|
1969
|
-
str += '; SameSite=Strict';
|
|
1970
|
-
break;
|
|
1971
|
-
case 'lax':
|
|
1972
|
-
str += '; SameSite=Lax';
|
|
1973
|
-
break;
|
|
1974
|
-
case 'strict':
|
|
1975
|
-
str += '; SameSite=Strict';
|
|
1976
|
-
break;
|
|
1977
|
-
case 'none':
|
|
1978
|
-
str += '; SameSite=None';
|
|
1979
|
-
break;
|
|
1980
|
-
default:
|
|
1981
|
-
throw new TypeError('option sameSite is invalid');
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
return str;
|
|
1985
|
-
};
|
|
1986
|
-
var cookie = {
|
|
1987
|
-
create: function create(name, value, minutes, domain) {
|
|
1988
|
-
var cookieOptions = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {
|
|
1989
|
-
path: '/',
|
|
1990
|
-
sameSite: 'strict'
|
|
1991
|
-
};
|
|
1992
|
-
if (minutes) {
|
|
1993
|
-
cookieOptions.expires = new Date();
|
|
1994
|
-
cookieOptions.expires.setTime(cookieOptions.expires.getTime() + minutes * 60 * 1000);
|
|
1995
|
-
}
|
|
1996
|
-
if (domain) cookieOptions.domain = domain;
|
|
1997
|
-
document.cookie = serializeCookie(name, encodeURIComponent(value), cookieOptions);
|
|
1998
|
-
},
|
|
1999
|
-
read: function read(name) {
|
|
2000
|
-
var nameEQ = "".concat(name, "=");
|
|
2001
|
-
var ca = document.cookie.split(';');
|
|
2002
|
-
for (var i = 0; i < ca.length; i++) {
|
|
2003
|
-
var c = ca[i];
|
|
2004
|
-
while (c.charAt(0) === ' ') {
|
|
2005
|
-
c = c.substring(1, c.length);
|
|
2006
|
-
}
|
|
2007
|
-
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
|
|
2008
|
-
}
|
|
2009
|
-
return null;
|
|
2010
|
-
},
|
|
2011
|
-
remove: function remove(name) {
|
|
2012
|
-
this.create(name, '', -1);
|
|
2013
|
-
}
|
|
2014
|
-
};
|
|
2015
|
-
var cookie$1 = {
|
|
2016
|
-
name: 'cookie',
|
|
2017
|
-
lookup: function lookup(options) {
|
|
2018
|
-
var found;
|
|
2019
|
-
if (options.lookupCookie && typeof document !== 'undefined') {
|
|
2020
|
-
var c = cookie.read(options.lookupCookie);
|
|
2021
|
-
if (c) found = c;
|
|
2067
|
+
|
|
2068
|
+
_sanitizeAttributes(shadowNode);
|
|
2022
2069
|
}
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2070
|
+
/* Execute a hook if present */
|
|
2071
|
+
|
|
2072
|
+
|
|
2073
|
+
_executeHook('afterSanitizeShadowDOM', fragment, null);
|
|
2074
|
+
};
|
|
2075
|
+
/**
|
|
2076
|
+
* Sanitize
|
|
2077
|
+
* Public method providing core sanitation functionality
|
|
2078
|
+
*
|
|
2079
|
+
* @param {String|Node} dirty string or DOM node
|
|
2080
|
+
* @param {Object} configuration object
|
|
2081
|
+
*/
|
|
2082
|
+
// eslint-disable-next-line complexity
|
|
2083
|
+
|
|
2084
|
+
|
|
2085
|
+
DOMPurify.sanitize = function (dirty) {
|
|
2086
|
+
let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
2087
|
+
let body;
|
|
2088
|
+
let importedNode;
|
|
2089
|
+
let currentNode;
|
|
2090
|
+
let returnNode;
|
|
2091
|
+
/* Make sure we have a string to sanitize.
|
|
2092
|
+
DO NOT return early, as this will return the wrong type if
|
|
2093
|
+
the user has requested a DOM object rather than a string */
|
|
2094
|
+
|
|
2095
|
+
IS_EMPTY_INPUT = !dirty;
|
|
2096
|
+
|
|
2097
|
+
if (IS_EMPTY_INPUT) {
|
|
2098
|
+
dirty = '<!-->';
|
|
2028
2099
|
}
|
|
2029
|
-
|
|
2030
|
-
};
|
|
2100
|
+
/* Stringify, in case dirty is an object */
|
|
2031
2101
|
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
search = window.location.hash.substring(window.location.hash.indexOf('?'));
|
|
2040
|
-
}
|
|
2041
|
-
var query = search.substring(1);
|
|
2042
|
-
var params = query.split('&');
|
|
2043
|
-
for (var i = 0; i < params.length; i++) {
|
|
2044
|
-
var pos = params[i].indexOf('=');
|
|
2045
|
-
if (pos > 0) {
|
|
2046
|
-
var key = params[i].substring(0, pos);
|
|
2047
|
-
if (key === options.lookupQuerystring) {
|
|
2048
|
-
found = params[i].substring(pos + 1);
|
|
2049
|
-
}
|
|
2102
|
+
|
|
2103
|
+
if (typeof dirty !== 'string' && !_isNode(dirty)) {
|
|
2104
|
+
if (typeof dirty.toString === 'function') {
|
|
2105
|
+
dirty = dirty.toString();
|
|
2106
|
+
|
|
2107
|
+
if (typeof dirty !== 'string') {
|
|
2108
|
+
throw typeErrorCreate('dirty is not a string, aborting');
|
|
2050
2109
|
}
|
|
2110
|
+
} else {
|
|
2111
|
+
throw typeErrorCreate('toString is not a function');
|
|
2051
2112
|
}
|
|
2052
2113
|
}
|
|
2053
|
-
|
|
2054
|
-
}
|
|
2055
|
-
};
|
|
2114
|
+
/* Return dirty HTML if DOMPurify cannot run */
|
|
2056
2115
|
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
try {
|
|
2061
|
-
hasLocalStorageSupport = window !== 'undefined' && window.localStorage !== null;
|
|
2062
|
-
var testKey = 'i18next.translate.boo';
|
|
2063
|
-
window.localStorage.setItem(testKey, 'foo');
|
|
2064
|
-
window.localStorage.removeItem(testKey);
|
|
2065
|
-
} catch (e) {
|
|
2066
|
-
hasLocalStorageSupport = false;
|
|
2067
|
-
}
|
|
2068
|
-
return hasLocalStorageSupport;
|
|
2069
|
-
};
|
|
2070
|
-
var localStorage = {
|
|
2071
|
-
name: 'localStorage',
|
|
2072
|
-
lookup: function lookup(options) {
|
|
2073
|
-
var found;
|
|
2074
|
-
if (options.lookupLocalStorage && localStorageAvailable()) {
|
|
2075
|
-
var lng = window.localStorage.getItem(options.lookupLocalStorage);
|
|
2076
|
-
if (lng) found = lng;
|
|
2077
|
-
}
|
|
2078
|
-
return found;
|
|
2079
|
-
},
|
|
2080
|
-
cacheUserLanguage: function cacheUserLanguage(lng, options) {
|
|
2081
|
-
if (options.lookupLocalStorage && localStorageAvailable()) {
|
|
2082
|
-
window.localStorage.setItem(options.lookupLocalStorage, lng);
|
|
2116
|
+
|
|
2117
|
+
if (!DOMPurify.isSupported) {
|
|
2118
|
+
return dirty;
|
|
2083
2119
|
}
|
|
2084
|
-
|
|
2085
|
-
};
|
|
2120
|
+
/* Assign config vars */
|
|
2086
2121
|
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
try {
|
|
2091
|
-
hasSessionStorageSupport = window !== 'undefined' && window.sessionStorage !== null;
|
|
2092
|
-
var testKey = 'i18next.translate.boo';
|
|
2093
|
-
window.sessionStorage.setItem(testKey, 'foo');
|
|
2094
|
-
window.sessionStorage.removeItem(testKey);
|
|
2095
|
-
} catch (e) {
|
|
2096
|
-
hasSessionStorageSupport = false;
|
|
2097
|
-
}
|
|
2098
|
-
return hasSessionStorageSupport;
|
|
2099
|
-
};
|
|
2100
|
-
var sessionStorage = {
|
|
2101
|
-
name: 'sessionStorage',
|
|
2102
|
-
lookup: function lookup(options) {
|
|
2103
|
-
var found;
|
|
2104
|
-
if (options.lookupSessionStorage && sessionStorageAvailable()) {
|
|
2105
|
-
var lng = window.sessionStorage.getItem(options.lookupSessionStorage);
|
|
2106
|
-
if (lng) found = lng;
|
|
2122
|
+
|
|
2123
|
+
if (!SET_CONFIG) {
|
|
2124
|
+
_parseConfig(cfg);
|
|
2107
2125
|
}
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2126
|
+
/* Clean up removed elements */
|
|
2127
|
+
|
|
2128
|
+
|
|
2129
|
+
DOMPurify.removed = [];
|
|
2130
|
+
/* Check if dirty is correctly typed for IN_PLACE */
|
|
2131
|
+
|
|
2132
|
+
if (typeof dirty === 'string') {
|
|
2133
|
+
IN_PLACE = false;
|
|
2113
2134
|
}
|
|
2114
|
-
}
|
|
2115
|
-
};
|
|
2116
2135
|
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
for (var i = 0; i < navigator.languages.length; i++) {
|
|
2125
|
-
found.push(navigator.languages[i]);
|
|
2136
|
+
if (IN_PLACE) {
|
|
2137
|
+
/* Do some early pre-sanitization to avoid unsafe root nodes */
|
|
2138
|
+
if (dirty.nodeName) {
|
|
2139
|
+
const tagName = transformCaseFunc(dirty.nodeName);
|
|
2140
|
+
|
|
2141
|
+
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
2142
|
+
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
2126
2143
|
}
|
|
2127
2144
|
}
|
|
2128
|
-
|
|
2129
|
-
|
|
2145
|
+
} else if (dirty instanceof Node) {
|
|
2146
|
+
/* If dirty is a DOM element, append to an empty document to avoid
|
|
2147
|
+
elements being stripped by the parser */
|
|
2148
|
+
body = _initDocument('<!---->');
|
|
2149
|
+
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
2150
|
+
|
|
2151
|
+
if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
|
|
2152
|
+
/* Node is already a body, use as is */
|
|
2153
|
+
body = importedNode;
|
|
2154
|
+
} else if (importedNode.nodeName === 'HTML') {
|
|
2155
|
+
body = importedNode;
|
|
2156
|
+
} else {
|
|
2157
|
+
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
2158
|
+
body.appendChild(importedNode);
|
|
2130
2159
|
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2160
|
+
} else {
|
|
2161
|
+
/* Exit directly if we have nothing to do */
|
|
2162
|
+
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
|
|
2163
|
+
dirty.indexOf('<') === -1) {
|
|
2164
|
+
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
2165
|
+
}
|
|
2166
|
+
/* Initialize the document to work on */
|
|
2167
|
+
|
|
2168
|
+
|
|
2169
|
+
body = _initDocument(dirty);
|
|
2170
|
+
/* Check we have a DOM node from the data */
|
|
2171
|
+
|
|
2172
|
+
if (!body) {
|
|
2173
|
+
return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
|
|
2133
2174
|
}
|
|
2134
2175
|
}
|
|
2135
|
-
|
|
2136
|
-
}
|
|
2137
|
-
};
|
|
2176
|
+
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
2138
2177
|
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
var found;
|
|
2143
|
-
var htmlTag = options.htmlTag || (typeof document !== 'undefined' ? document.documentElement : null);
|
|
2144
|
-
if (htmlTag && typeof htmlTag.getAttribute === 'function') {
|
|
2145
|
-
found = htmlTag.getAttribute('lang');
|
|
2178
|
+
|
|
2179
|
+
if (body && FORCE_BODY) {
|
|
2180
|
+
_forceRemove(body.firstChild);
|
|
2146
2181
|
}
|
|
2147
|
-
|
|
2148
|
-
}
|
|
2149
|
-
};
|
|
2182
|
+
/* Get node iterator */
|
|
2150
2183
|
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2184
|
+
|
|
2185
|
+
const nodeIterator = _createIterator(IN_PLACE ? dirty : body);
|
|
2186
|
+
/* Now start iterating over the created document */
|
|
2187
|
+
|
|
2188
|
+
|
|
2189
|
+
while (currentNode = nodeIterator.nextNode()) {
|
|
2190
|
+
/* Sanitize tags and elements */
|
|
2191
|
+
if (_sanitizeElements(currentNode)) {
|
|
2192
|
+
continue;
|
|
2193
|
+
}
|
|
2194
|
+
/* Shadow DOM detected, sanitize it */
|
|
2195
|
+
|
|
2196
|
+
|
|
2197
|
+
if (currentNode.content instanceof DocumentFragment) {
|
|
2198
|
+
_sanitizeShadowDOM(currentNode.content);
|
|
2199
|
+
}
|
|
2200
|
+
/* Check attributes, sanitize if necessary */
|
|
2201
|
+
|
|
2202
|
+
|
|
2203
|
+
_sanitizeAttributes(currentNode);
|
|
2204
|
+
}
|
|
2205
|
+
/* If we sanitized `dirty` in-place, return it. */
|
|
2206
|
+
|
|
2207
|
+
|
|
2208
|
+
if (IN_PLACE) {
|
|
2209
|
+
return dirty;
|
|
2210
|
+
}
|
|
2211
|
+
/* Return sanitized string or DOM */
|
|
2212
|
+
|
|
2213
|
+
|
|
2214
|
+
if (RETURN_DOM) {
|
|
2215
|
+
if (RETURN_DOM_FRAGMENT) {
|
|
2216
|
+
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
2217
|
+
|
|
2218
|
+
while (body.firstChild) {
|
|
2219
|
+
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
2220
|
+
returnNode.appendChild(body.firstChild);
|
|
2165
2221
|
}
|
|
2222
|
+
} else {
|
|
2223
|
+
returnNode = body;
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
|
|
2227
|
+
/*
|
|
2228
|
+
AdoptNode() is not used because internal state is not reset
|
|
2229
|
+
(e.g. the past names map of a HTMLFormElement), this is safe
|
|
2230
|
+
in theory but we would rather not risk another attack vector.
|
|
2231
|
+
The state that is cloned by importNode() is explicitly defined
|
|
2232
|
+
by the specs.
|
|
2233
|
+
*/
|
|
2234
|
+
returnNode = importNode.call(originalDocument, returnNode, true);
|
|
2166
2235
|
}
|
|
2236
|
+
|
|
2237
|
+
return returnNode;
|
|
2167
2238
|
}
|
|
2168
|
-
return found;
|
|
2169
|
-
}
|
|
2170
|
-
};
|
|
2171
2239
|
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2240
|
+
let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
|
|
2241
|
+
/* Serialize doctype if allowed */
|
|
2242
|
+
|
|
2243
|
+
if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
|
|
2244
|
+
serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
|
|
2245
|
+
}
|
|
2246
|
+
/* Sanitize final string template-safe */
|
|
2247
|
+
|
|
2248
|
+
|
|
2249
|
+
if (SAFE_FOR_TEMPLATES) {
|
|
2250
|
+
serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR, ' ');
|
|
2251
|
+
serializedHTML = stringReplace(serializedHTML, ERB_EXPR, ' ');
|
|
2252
|
+
serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR, ' ');
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
|
|
2256
|
+
};
|
|
2257
|
+
/**
|
|
2258
|
+
* Public method to set the configuration once
|
|
2259
|
+
* setConfig
|
|
2260
|
+
*
|
|
2261
|
+
* @param {Object} cfg configuration object
|
|
2262
|
+
*/
|
|
2263
|
+
|
|
2264
|
+
|
|
2265
|
+
DOMPurify.setConfig = function (cfg) {
|
|
2266
|
+
_parseConfig(cfg);
|
|
2267
|
+
|
|
2268
|
+
SET_CONFIG = true;
|
|
2269
|
+
};
|
|
2270
|
+
/**
|
|
2271
|
+
* Public method to remove the configuration
|
|
2272
|
+
* clearConfig
|
|
2273
|
+
*
|
|
2274
|
+
*/
|
|
2181
2275
|
|
|
2182
|
-
// if there is no match (null) return undefined
|
|
2183
|
-
if (!language) return undefined;
|
|
2184
|
-
// return the given group match
|
|
2185
|
-
return language[lookupFromSubdomainIndex];
|
|
2186
|
-
}
|
|
2187
|
-
};
|
|
2188
2276
|
|
|
2189
|
-
function
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2277
|
+
DOMPurify.clearConfig = function () {
|
|
2278
|
+
CONFIG = null;
|
|
2279
|
+
SET_CONFIG = false;
|
|
2280
|
+
};
|
|
2281
|
+
/**
|
|
2282
|
+
* Public method to check if an attribute value is valid.
|
|
2283
|
+
* Uses last set config, if any. Otherwise, uses config defaults.
|
|
2284
|
+
* isValidAttribute
|
|
2285
|
+
*
|
|
2286
|
+
* @param {string} tag Tag name of containing element.
|
|
2287
|
+
* @param {string} attr Attribute name.
|
|
2288
|
+
* @param {string} value Attribute value.
|
|
2289
|
+
* @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
|
|
2290
|
+
*/
|
|
2201
2291
|
|
|
2202
|
-
|
|
2203
|
-
|
|
2292
|
+
|
|
2293
|
+
DOMPurify.isValidAttribute = function (tag, attr, value) {
|
|
2294
|
+
/* Initialize shared config vars if necessary. */
|
|
2295
|
+
if (!CONFIG) {
|
|
2296
|
+
_parseConfig({});
|
|
2204
2297
|
}
|
|
2298
|
+
|
|
2299
|
+
const lcTag = transformCaseFunc(tag);
|
|
2300
|
+
const lcName = transformCaseFunc(attr);
|
|
2301
|
+
return _isValidAttribute(lcTag, lcName, value);
|
|
2205
2302
|
};
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
this.init(services, options);
|
|
2214
|
-
}
|
|
2215
|
-
_createClass(Browser, [{
|
|
2216
|
-
key: "init",
|
|
2217
|
-
value: function init(services) {
|
|
2218
|
-
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
2219
|
-
var i18nOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
2220
|
-
this.services = services || {
|
|
2221
|
-
languageUtils: {}
|
|
2222
|
-
}; // this way the language detector can be used without i18next
|
|
2223
|
-
this.options = defaults(options, this.options || {}, getDefaults());
|
|
2224
|
-
if (typeof this.options.convertDetectedLanguage === 'string' && this.options.convertDetectedLanguage.indexOf('15897') > -1) {
|
|
2225
|
-
this.options.convertDetectedLanguage = function (l) {
|
|
2226
|
-
return l.replace('-', '_');
|
|
2227
|
-
};
|
|
2228
|
-
}
|
|
2303
|
+
/**
|
|
2304
|
+
* AddHook
|
|
2305
|
+
* Public method to add DOMPurify hooks
|
|
2306
|
+
*
|
|
2307
|
+
* @param {String} entryPoint entry point for the hook to add
|
|
2308
|
+
* @param {Function} hookFunction function to execute
|
|
2309
|
+
*/
|
|
2229
2310
|
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
this.addDetector(querystring);
|
|
2235
|
-
this.addDetector(localStorage);
|
|
2236
|
-
this.addDetector(sessionStorage);
|
|
2237
|
-
this.addDetector(navigator$1);
|
|
2238
|
-
this.addDetector(htmlTag);
|
|
2239
|
-
this.addDetector(path);
|
|
2240
|
-
this.addDetector(subdomain);
|
|
2241
|
-
}
|
|
2242
|
-
}, {
|
|
2243
|
-
key: "addDetector",
|
|
2244
|
-
value: function addDetector(detector) {
|
|
2245
|
-
this.detectors[detector.name] = detector;
|
|
2246
|
-
}
|
|
2247
|
-
}, {
|
|
2248
|
-
key: "detect",
|
|
2249
|
-
value: function detect(detectionOrder) {
|
|
2250
|
-
var _this = this;
|
|
2251
|
-
if (!detectionOrder) detectionOrder = this.options.order;
|
|
2252
|
-
var detected = [];
|
|
2253
|
-
detectionOrder.forEach(function (detectorName) {
|
|
2254
|
-
if (_this.detectors[detectorName]) {
|
|
2255
|
-
var lookup = _this.detectors[detectorName].lookup(_this.options);
|
|
2256
|
-
if (lookup && typeof lookup === 'string') lookup = [lookup];
|
|
2257
|
-
if (lookup) detected = detected.concat(lookup);
|
|
2258
|
-
}
|
|
2259
|
-
});
|
|
2260
|
-
detected = detected.map(function (d) {
|
|
2261
|
-
return _this.options.convertDetectedLanguage(d);
|
|
2262
|
-
});
|
|
2263
|
-
if (this.services.languageUtils.getBestMatchFromCodes) return detected; // new i18next v19.5.0
|
|
2264
|
-
return detected.length > 0 ? detected[0] : null; // a little backward compatibility
|
|
2311
|
+
|
|
2312
|
+
DOMPurify.addHook = function (entryPoint, hookFunction) {
|
|
2313
|
+
if (typeof hookFunction !== 'function') {
|
|
2314
|
+
return;
|
|
2265
2315
|
}
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2316
|
+
|
|
2317
|
+
hooks[entryPoint] = hooks[entryPoint] || [];
|
|
2318
|
+
arrayPush(hooks[entryPoint], hookFunction);
|
|
2319
|
+
};
|
|
2320
|
+
/**
|
|
2321
|
+
* RemoveHook
|
|
2322
|
+
* Public method to remove a DOMPurify hook at a given entryPoint
|
|
2323
|
+
* (pops it from the stack of hooks if more are present)
|
|
2324
|
+
*
|
|
2325
|
+
* @param {String} entryPoint entry point for the hook to remove
|
|
2326
|
+
* @return {Function} removed(popped) hook
|
|
2327
|
+
*/
|
|
2328
|
+
|
|
2329
|
+
|
|
2330
|
+
DOMPurify.removeHook = function (entryPoint) {
|
|
2331
|
+
if (hooks[entryPoint]) {
|
|
2332
|
+
return arrayPop(hooks[entryPoint]);
|
|
2276
2333
|
}
|
|
2277
|
-
}
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2334
|
+
};
|
|
2335
|
+
/**
|
|
2336
|
+
* RemoveHooks
|
|
2337
|
+
* Public method to remove all DOMPurify hooks at a given entryPoint
|
|
2338
|
+
*
|
|
2339
|
+
* @param {String} entryPoint entry point for the hooks to remove
|
|
2340
|
+
*/
|
|
2281
2341
|
|
|
2282
|
-
function _arrayWithHoles(arr) {
|
|
2283
|
-
if (Array.isArray(arr)) return arr;
|
|
2284
|
-
}
|
|
2285
2342
|
|
|
2286
|
-
function
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
var _s,
|
|
2290
|
-
_e,
|
|
2291
|
-
_x,
|
|
2292
|
-
_r,
|
|
2293
|
-
_arr = [],
|
|
2294
|
-
_n = !0,
|
|
2295
|
-
_d = !1;
|
|
2296
|
-
try {
|
|
2297
|
-
if (_x = (_i = _i.call(arr)).next, 0 === i) {
|
|
2298
|
-
if (Object(_i) !== _i) return;
|
|
2299
|
-
_n = !1;
|
|
2300
|
-
} else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0);
|
|
2301
|
-
} catch (err) {
|
|
2302
|
-
_d = !0, _e = err;
|
|
2303
|
-
} finally {
|
|
2304
|
-
try {
|
|
2305
|
-
if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return;
|
|
2306
|
-
} finally {
|
|
2307
|
-
if (_d) throw _e;
|
|
2308
|
-
}
|
|
2343
|
+
DOMPurify.removeHooks = function (entryPoint) {
|
|
2344
|
+
if (hooks[entryPoint]) {
|
|
2345
|
+
hooks[entryPoint] = [];
|
|
2309
2346
|
}
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2347
|
+
};
|
|
2348
|
+
/**
|
|
2349
|
+
* RemoveAllHooks
|
|
2350
|
+
* Public method to remove all DOMPurify hooks
|
|
2351
|
+
*
|
|
2352
|
+
*/
|
|
2313
2353
|
|
|
2314
|
-
function _arrayLikeToArray(arr, len) {
|
|
2315
|
-
if (len == null || len > arr.length) len = arr.length;
|
|
2316
|
-
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
|
2317
|
-
return arr2;
|
|
2318
|
-
}
|
|
2319
2354
|
|
|
2320
|
-
function
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
2324
|
-
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
2325
|
-
if (n === "Map" || n === "Set") return Array.from(o);
|
|
2326
|
-
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
|
2327
|
-
}
|
|
2355
|
+
DOMPurify.removeAllHooks = function () {
|
|
2356
|
+
hooks = {};
|
|
2357
|
+
};
|
|
2328
2358
|
|
|
2329
|
-
|
|
2330
|
-
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
2359
|
+
return DOMPurify;
|
|
2331
2360
|
}
|
|
2332
2361
|
|
|
2333
|
-
|
|
2334
|
-
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
|
|
2335
|
-
}
|
|
2362
|
+
var purify = createDOMPurify();
|
|
2336
2363
|
|
|
2337
|
-
var
|
|
2338
|
-
return
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
};
|
|
2342
|
-
var replaceNullValuesWithGetter = function replaceNullValuesWithGetter(inputObject) {
|
|
2343
|
-
var parentKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
|
|
2344
|
-
var result = {};
|
|
2345
|
-
for (var _i = 0, _Object$entries = Object.entries(inputObject); _i < _Object$entries.length; _i++) {
|
|
2346
|
-
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
2347
|
-
key = _Object$entries$_i[0],
|
|
2348
|
-
value = _Object$entries$_i[1];
|
|
2349
|
-
var transKey = parentKey ? "".concat(parentKey, ".").concat(key) : key;
|
|
2350
|
-
if (value === null) {
|
|
2351
|
-
Object.defineProperty(result, key, {
|
|
2352
|
-
get: getter(transKey)
|
|
2353
|
-
});
|
|
2354
|
-
} else if (_typeof$1(value) === "object") {
|
|
2355
|
-
result[key] = replaceNullValuesWithGetter(value, transKey);
|
|
2356
|
-
} else {
|
|
2357
|
-
result[key] = value;
|
|
2364
|
+
var sanitizer = function sanitizer(array) {
|
|
2365
|
+
return purify.sanitize(array, {
|
|
2366
|
+
USE_PROFILES: {
|
|
2367
|
+
html: true
|
|
2358
2368
|
}
|
|
2359
|
-
}
|
|
2360
|
-
return result;
|
|
2361
|
-
};
|
|
2362
|
-
|
|
2363
|
-
/*
|
|
2364
|
-
For some reason dynamic values available via options param doesn't have formatter 'anyCase' applied.
|
|
2365
|
-
So here we are just re applying.
|
|
2366
|
-
*/
|
|
2367
|
-
var wrapAnyCaseIfApplicable = function wrapAnyCaseIfApplicable(value, dynamic) {
|
|
2368
|
-
return Object.keys(dynamic).map(function (item) {
|
|
2369
|
-
var format = anyCaseFormatter(dynamic[item]);
|
|
2370
|
-
if (value.includes(format)) return format;
|
|
2371
|
-
return dynamic[item];
|
|
2372
2369
|
});
|
|
2373
2370
|
};
|
|
2374
|
-
var
|
|
2375
|
-
|
|
2371
|
+
var cacheStore = new Map();
|
|
2372
|
+
var fetchCachedOrInvoke = function fetchCachedOrInvoke(func, lng, options) {
|
|
2373
|
+
var _cacheStore$get;
|
|
2374
|
+
var cache = (_cacheStore$get = cacheStore.get(lng)) === null || _cacheStore$get === void 0 ? void 0 : _cacheStore$get.get(options);
|
|
2375
|
+
if (cache) return cache;
|
|
2376
|
+
cache = func(lng, options);
|
|
2377
|
+
var lngCache = cacheStore.get(lng);
|
|
2378
|
+
if (lngCache) lngCache.set(options, cache);else cacheStore.set(lng, new Map([[options, cache]]));
|
|
2379
|
+
return cache;
|
|
2376
2380
|
};
|
|
2377
|
-
var
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
var
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
[
|
|
2395
|
-
'Create ', // static
|
|
2396
|
-
'Car', // dynamic
|
|
2397
|
-
', ', // static
|
|
2398
|
-
'__ANY_CASE__BuS__ANY_CASE__', // dynamic
|
|
2399
|
-
' and ', // static
|
|
2400
|
-
'Truck', // dynamic
|
|
2401
|
-
' and apply COLOR' // static
|
|
2402
|
-
]
|
|
2403
|
-
*/
|
|
2404
|
-
var splitArr = value.split(new RegExp(regexPattern)).filter(Boolean);
|
|
2405
|
-
return splitArr.map(function (item, index) {
|
|
2406
|
-
if (dynamicArr.includes(item)) {
|
|
2407
|
-
if (isAnyCaseStr(item)) {
|
|
2408
|
-
return item.replaceAll(ANY_CASE_STR, "");
|
|
2409
|
-
} else if (index === 0) {
|
|
2410
|
-
// Don't lower case if it is first word.
|
|
2411
|
-
return item;
|
|
2412
|
-
}
|
|
2413
|
-
return item.toLocaleLowerCase();
|
|
2414
|
-
}
|
|
2415
|
-
return item;
|
|
2416
|
-
}).join("");
|
|
2381
|
+
var lowerCaseDynamicTextFormatter = function lowerCaseDynamicTextFormatter(value, format) {
|
|
2382
|
+
if (!value || format === ANY_CASE || typeof value !== "string") return value;
|
|
2383
|
+
return LOWERCASED + value.toLocaleLowerCase();
|
|
2384
|
+
};
|
|
2385
|
+
var boldListFormatter = function boldListFormatter(array, lng, options) {
|
|
2386
|
+
var formatter = fetchCachedOrInvoke(function (lng, options) {
|
|
2387
|
+
return new Intl.ListFormat(lng, options);
|
|
2388
|
+
}, lng, options);
|
|
2389
|
+
var boldItems = array.map(function (item) {
|
|
2390
|
+
return "<strong>".concat(item, "</strong>");
|
|
2391
|
+
});
|
|
2392
|
+
var sanitizedItems = sanitizer(boldItems).split(",");
|
|
2393
|
+
return formatter.format(sanitizedItems);
|
|
2394
|
+
};
|
|
2395
|
+
|
|
2396
|
+
var sentenceCase = function sentenceCase(value) {
|
|
2397
|
+
return value.charAt(0).toLocaleUpperCase() + value.slice(1);
|
|
2417
2398
|
};
|
|
2418
2399
|
var sentenceCaseProcessor = {
|
|
2419
2400
|
type: "postProcessor",
|
|
2420
2401
|
name: "sentenceCaseProcessor",
|
|
2421
|
-
process: function process(value
|
|
2422
|
-
var
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
return lowerCaseDynamicText(value, dynamic);
|
|
2402
|
+
process: function process(value) {
|
|
2403
|
+
var shouldSentenceCase = value.startsWith(LOWERCASED);
|
|
2404
|
+
value = value.replaceAll(LOWERCASED, "");
|
|
2405
|
+
return shouldSentenceCase ? sentenceCase(value) : value;
|
|
2426
2406
|
}
|
|
2427
2407
|
};
|
|
2428
|
-
var anyCaseFormatter = function anyCaseFormatter(value) {
|
|
2429
|
-
if (value) return ANY_CASE_STR + value + ANY_CASE_STR;
|
|
2430
|
-
return value;
|
|
2431
|
-
};
|
|
2432
2408
|
|
|
2433
2409
|
var generic = {
|
|
2434
2410
|
error: "Something went wrong. Please try again later."
|
|
@@ -2548,7 +2524,7 @@ var commonsEn = {
|
|
|
2548
2524
|
// eslint-disable-next-line import/no-mutable-exports
|
|
2549
2525
|
var taxonomies = {};
|
|
2550
2526
|
var initializeI18n = function initializeI18n(hostTranslations) {
|
|
2551
|
-
var _window$globalProps
|
|
2527
|
+
var _window$globalProps;
|
|
2552
2528
|
var packageTranslations = preval.require("./configs/scripts/getPkgTranslations.js");
|
|
2553
2529
|
var commonsTranslations = {
|
|
2554
2530
|
en: {
|
|
@@ -2573,9 +2549,17 @@ var initializeI18n = function initializeI18n(hostTranslations) {
|
|
|
2573
2549
|
taxonomies: taxonomies
|
|
2574
2550
|
},
|
|
2575
2551
|
escapeValue: false,
|
|
2576
|
-
skipOnVariables: false
|
|
2552
|
+
skipOnVariables: false,
|
|
2553
|
+
alwaysFormat: true,
|
|
2554
|
+
format: function format(value, _format, lng, options) {
|
|
2555
|
+
var newValue = value;
|
|
2556
|
+
if (_format === "boldList") {
|
|
2557
|
+
newValue = boldListFormatter(newValue, lng, options);
|
|
2558
|
+
}
|
|
2559
|
+
return lowerCaseDynamicTextFormatter(newValue, _format);
|
|
2560
|
+
}
|
|
2577
2561
|
},
|
|
2578
|
-
postProcess: [
|
|
2562
|
+
postProcess: [sentenceCaseProcessor.name],
|
|
2579
2563
|
detection: {
|
|
2580
2564
|
order: ["querystring", "cookie", "navigator", "path"],
|
|
2581
2565
|
caches: ["cookie"],
|
|
@@ -2583,24 +2567,6 @@ var initializeI18n = function initializeI18n(hostTranslations) {
|
|
|
2583
2567
|
lookupCookie: "lang"
|
|
2584
2568
|
}
|
|
2585
2569
|
});
|
|
2586
|
-
(_i18n$services$format = i18n.services.formatter) === null || _i18n$services$format === void 0 ? void 0 : _i18n$services$format.addCached("boldList", function (language, options) {
|
|
2587
|
-
var formatter = new Intl.ListFormat(language, options);
|
|
2588
|
-
var sanitizer = function sanitizer(array) {
|
|
2589
|
-
return purify.sanitize(array, {
|
|
2590
|
-
USE_PROFILES: {
|
|
2591
|
-
html: true
|
|
2592
|
-
}
|
|
2593
|
-
});
|
|
2594
|
-
};
|
|
2595
|
-
return function (array) {
|
|
2596
|
-
var boldItems = array.map(function (item) {
|
|
2597
|
-
return "<strong>".concat(item, "</strong>");
|
|
2598
|
-
});
|
|
2599
|
-
var sanitizedItems = sanitizer(boldItems).split(",");
|
|
2600
|
-
return formatter.format(sanitizedItems);
|
|
2601
|
-
};
|
|
2602
|
-
});
|
|
2603
|
-
(_i18n$services$format2 = i18n.services.formatter) === null || _i18n$services$format2 === void 0 ? void 0 : _i18n$services$format2.add("anyCase", anyCaseFormatter);
|
|
2604
2570
|
};
|
|
2605
2571
|
|
|
2606
2572
|
function initializeLogger() {
|