@bigbinary/neeto-payments-frontend 2.5.3 → 2.5.4
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/dist/Dashboard.js +1 -1
- package/dist/{ExportModal-3a383682.js → ExportModal-b6c3a531.js} +2 -2
- package/dist/ExportModal-b6c3a531.js.map +1 -0
- package/dist/PayoutsPage.js +1 -1
- package/dist/RazorpayDashboard.js +1 -1
- package/dist/StripeConnect.js +481 -467
- package/dist/StripeConnect.js.map +1 -1
- package/dist/cjs/Dashboard.js +1 -1
- package/dist/cjs/{ExportModal-ca5effed.js → ExportModal-54b915d4.js} +2 -2
- package/dist/cjs/ExportModal-54b915d4.js.map +1 -0
- package/dist/cjs/PayoutsPage.js +1 -1
- package/dist/cjs/RazorpayDashboard.js +1 -1
- package/dist/cjs/StripeConnect.js +481 -467
- package/dist/cjs/StripeConnect.js.map +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +5 -5
- package/dist/ExportModal-3a383682.js.map +0 -1
- package/dist/cjs/ExportModal-ca5effed.js.map +0 -1
|
@@ -410,7 +410,7 @@ var Configure = function Configure(_ref) {
|
|
|
410
410
|
});
|
|
411
411
|
};
|
|
412
412
|
|
|
413
|
-
/*! @license DOMPurify 3.
|
|
413
|
+
/*! @license DOMPurify 3.1.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.1.3/LICENSE */
|
|
414
414
|
|
|
415
415
|
const {
|
|
416
416
|
entries,
|
|
@@ -424,36 +424,30 @@ let {
|
|
|
424
424
|
seal,
|
|
425
425
|
create
|
|
426
426
|
} = Object; // eslint-disable-line import/no-mutable-exports
|
|
427
|
-
|
|
428
427
|
let {
|
|
429
428
|
apply,
|
|
430
429
|
construct
|
|
431
430
|
} = typeof Reflect !== 'undefined' && Reflect;
|
|
432
|
-
|
|
433
|
-
if (!apply) {
|
|
434
|
-
apply = function apply(fun, thisValue, args) {
|
|
435
|
-
return fun.apply(thisValue, args);
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
|
|
439
431
|
if (!freeze) {
|
|
440
432
|
freeze = function freeze(x) {
|
|
441
433
|
return x;
|
|
442
434
|
};
|
|
443
435
|
}
|
|
444
|
-
|
|
445
436
|
if (!seal) {
|
|
446
437
|
seal = function seal(x) {
|
|
447
438
|
return x;
|
|
448
439
|
};
|
|
449
440
|
}
|
|
450
|
-
|
|
441
|
+
if (!apply) {
|
|
442
|
+
apply = function apply(fun, thisValue, args) {
|
|
443
|
+
return fun.apply(thisValue, args);
|
|
444
|
+
};
|
|
445
|
+
}
|
|
451
446
|
if (!construct) {
|
|
452
447
|
construct = function construct(Func, args) {
|
|
453
448
|
return new Func(...args);
|
|
454
449
|
};
|
|
455
450
|
}
|
|
456
|
-
|
|
457
451
|
const arrayForEach = unapply(Array.prototype.forEach);
|
|
458
452
|
const arrayPop = unapply(Array.prototype.pop);
|
|
459
453
|
const arrayPush = unapply(Array.prototype.push);
|
|
@@ -463,134 +457,178 @@ const stringMatch = unapply(String.prototype.match);
|
|
|
463
457
|
const stringReplace = unapply(String.prototype.replace);
|
|
464
458
|
const stringIndexOf = unapply(String.prototype.indexOf);
|
|
465
459
|
const stringTrim = unapply(String.prototype.trim);
|
|
460
|
+
const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
|
|
466
461
|
const regExpTest = unapply(RegExp.prototype.test);
|
|
467
462
|
const typeErrorCreate = unconstruct(TypeError);
|
|
463
|
+
const numberIsNaN = unapply(Number.isNaN);
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Creates a new function that calls the given function with a specified thisArg and arguments.
|
|
467
|
+
*
|
|
468
|
+
* @param {Function} func - The function to be wrapped and called.
|
|
469
|
+
* @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
|
|
470
|
+
*/
|
|
468
471
|
function unapply(func) {
|
|
469
472
|
return function (thisArg) {
|
|
470
473
|
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
471
474
|
args[_key - 1] = arguments[_key];
|
|
472
475
|
}
|
|
473
|
-
|
|
474
476
|
return apply(func, thisArg, args);
|
|
475
477
|
};
|
|
476
478
|
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Creates a new function that constructs an instance of the given constructor function with the provided arguments.
|
|
482
|
+
*
|
|
483
|
+
* @param {Function} func - The constructor function to be wrapped and called.
|
|
484
|
+
* @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
|
|
485
|
+
*/
|
|
477
486
|
function unconstruct(func) {
|
|
478
487
|
return function () {
|
|
479
488
|
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
480
489
|
args[_key2] = arguments[_key2];
|
|
481
490
|
}
|
|
482
|
-
|
|
483
491
|
return construct(func, args);
|
|
484
492
|
};
|
|
485
493
|
}
|
|
486
|
-
/* Add properties to a lookup table */
|
|
487
|
-
|
|
488
|
-
function addToSet(set, array, transformCaseFunc) {
|
|
489
|
-
transformCaseFunc = transformCaseFunc ? transformCaseFunc : stringToLowerCase;
|
|
490
494
|
|
|
495
|
+
/**
|
|
496
|
+
* Add properties to a lookup table
|
|
497
|
+
*
|
|
498
|
+
* @param {Object} set - The set to which elements will be added.
|
|
499
|
+
* @param {Array} array - The array containing elements to be added to the set.
|
|
500
|
+
* @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
|
|
501
|
+
* @returns {Object} The modified set with added elements.
|
|
502
|
+
*/
|
|
503
|
+
function addToSet(set, array) {
|
|
504
|
+
let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
|
|
491
505
|
if (setPrototypeOf) {
|
|
492
506
|
// Make 'in' and truthy checks like Boolean(set.constructor)
|
|
493
507
|
// independent of any properties defined on Object.prototype.
|
|
494
508
|
// Prevent prototype setters from intercepting set as a this value.
|
|
495
509
|
setPrototypeOf(set, null);
|
|
496
510
|
}
|
|
497
|
-
|
|
498
511
|
let l = array.length;
|
|
499
|
-
|
|
500
512
|
while (l--) {
|
|
501
513
|
let element = array[l];
|
|
502
|
-
|
|
503
514
|
if (typeof element === 'string') {
|
|
504
515
|
const lcElement = transformCaseFunc(element);
|
|
505
|
-
|
|
506
516
|
if (lcElement !== element) {
|
|
507
517
|
// Config presets (e.g. tags.js, attrs.js) are immutable.
|
|
508
518
|
if (!isFrozen(array)) {
|
|
509
519
|
array[l] = lcElement;
|
|
510
520
|
}
|
|
511
|
-
|
|
512
521
|
element = lcElement;
|
|
513
522
|
}
|
|
514
523
|
}
|
|
515
|
-
|
|
516
524
|
set[element] = true;
|
|
517
525
|
}
|
|
518
|
-
|
|
519
526
|
return set;
|
|
520
527
|
}
|
|
521
|
-
/* Shallow clone an object */
|
|
522
528
|
|
|
529
|
+
/**
|
|
530
|
+
* Clean up an array to harden against CSPP
|
|
531
|
+
*
|
|
532
|
+
* @param {Array} array - The array to be cleaned.
|
|
533
|
+
* @returns {Array} The cleaned version of the array
|
|
534
|
+
*/
|
|
535
|
+
function cleanArray(array) {
|
|
536
|
+
for (let index = 0; index < array.length; index++) {
|
|
537
|
+
const isPropertyExist = objectHasOwnProperty(array, index);
|
|
538
|
+
if (!isPropertyExist) {
|
|
539
|
+
array[index] = null;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return array;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Shallow clone an object
|
|
547
|
+
*
|
|
548
|
+
* @param {Object} object - The object to be cloned.
|
|
549
|
+
* @returns {Object} A new object that copies the original.
|
|
550
|
+
*/
|
|
523
551
|
function clone(object) {
|
|
524
552
|
const newObject = create(null);
|
|
525
|
-
|
|
526
553
|
for (const [property, value] of entries(object)) {
|
|
527
|
-
|
|
554
|
+
const isPropertyExist = objectHasOwnProperty(object, property);
|
|
555
|
+
if (isPropertyExist) {
|
|
556
|
+
if (Array.isArray(value)) {
|
|
557
|
+
newObject[property] = cleanArray(value);
|
|
558
|
+
} else if (value && typeof value === 'object' && value.constructor === Object) {
|
|
559
|
+
newObject[property] = clone(value);
|
|
560
|
+
} else {
|
|
561
|
+
newObject[property] = value;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
528
564
|
}
|
|
529
|
-
|
|
530
565
|
return newObject;
|
|
531
566
|
}
|
|
532
|
-
/* This method automatically checks if the prop is function
|
|
533
|
-
* or getter and behaves accordingly. */
|
|
534
567
|
|
|
568
|
+
/**
|
|
569
|
+
* This method automatically checks if the prop is function or getter and behaves accordingly.
|
|
570
|
+
*
|
|
571
|
+
* @param {Object} object - The object to look up the getter function in its prototype chain.
|
|
572
|
+
* @param {String} prop - The property name for which to find the getter function.
|
|
573
|
+
* @returns {Function} The getter function found in the prototype chain or a fallback function.
|
|
574
|
+
*/
|
|
535
575
|
function lookupGetter(object, prop) {
|
|
536
576
|
while (object !== null) {
|
|
537
577
|
const desc = getOwnPropertyDescriptor(object, prop);
|
|
538
|
-
|
|
539
578
|
if (desc) {
|
|
540
579
|
if (desc.get) {
|
|
541
580
|
return unapply(desc.get);
|
|
542
581
|
}
|
|
543
|
-
|
|
544
582
|
if (typeof desc.value === 'function') {
|
|
545
583
|
return unapply(desc.value);
|
|
546
584
|
}
|
|
547
585
|
}
|
|
548
|
-
|
|
549
586
|
object = getPrototypeOf(object);
|
|
550
587
|
}
|
|
551
|
-
|
|
552
|
-
function fallbackValue(element) {
|
|
553
|
-
console.warn('fallback value for', element);
|
|
588
|
+
function fallbackValue() {
|
|
554
589
|
return null;
|
|
555
590
|
}
|
|
556
|
-
|
|
557
591
|
return fallbackValue;
|
|
558
592
|
}
|
|
559
593
|
|
|
560
|
-
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']);
|
|
594
|
+
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']);
|
|
561
595
|
|
|
596
|
+
// SVG
|
|
562
597
|
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']);
|
|
563
|
-
const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
|
|
598
|
+
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']);
|
|
599
|
+
|
|
600
|
+
// List of SVG elements that are disallowed by default.
|
|
564
601
|
// We still need to know them so that we can do namespace
|
|
565
602
|
// checks properly in case one wants to add them to
|
|
566
603
|
// allow-list.
|
|
604
|
+
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']);
|
|
605
|
+
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']);
|
|
567
606
|
|
|
568
|
-
|
|
569
|
-
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,
|
|
607
|
+
// Similarly to SVG, we want to know all MathML elements,
|
|
570
608
|
// even those that we disallow by default.
|
|
571
|
-
|
|
572
609
|
const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
|
|
573
610
|
const text = freeze(['#text']);
|
|
574
611
|
|
|
575
|
-
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']);
|
|
612
|
+
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', 'wrap', 'xmlns', 'slot']);
|
|
576
613
|
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']);
|
|
577
614
|
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']);
|
|
578
615
|
const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
579
616
|
|
|
617
|
+
// eslint-disable-next-line unicorn/better-regex
|
|
580
618
|
const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
|
|
581
|
-
|
|
582
619
|
const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
|
|
583
620
|
const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
|
|
584
621
|
const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
|
|
585
|
-
|
|
586
622
|
const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
587
|
-
|
|
588
623
|
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
|
|
589
624
|
);
|
|
625
|
+
|
|
590
626
|
const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
|
591
627
|
const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
|
|
592
628
|
);
|
|
629
|
+
|
|
593
630
|
const DOCTYPE_NAME = seal(/^html$/i);
|
|
631
|
+
const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
|
|
594
632
|
|
|
595
633
|
var EXPRESSIONS = /*#__PURE__*/Object.freeze({
|
|
596
634
|
__proto__: null,
|
|
@@ -602,47 +640,62 @@ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
|
|
|
602
640
|
IS_ALLOWED_URI: IS_ALLOWED_URI,
|
|
603
641
|
IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
|
|
604
642
|
ATTR_WHITESPACE: ATTR_WHITESPACE,
|
|
605
|
-
DOCTYPE_NAME: DOCTYPE_NAME
|
|
643
|
+
DOCTYPE_NAME: DOCTYPE_NAME,
|
|
644
|
+
CUSTOM_ELEMENT: CUSTOM_ELEMENT
|
|
606
645
|
});
|
|
607
646
|
|
|
608
|
-
|
|
647
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
|
|
648
|
+
const NODE_TYPE = {
|
|
649
|
+
element: 1,
|
|
650
|
+
attribute: 2,
|
|
651
|
+
text: 3,
|
|
652
|
+
cdataSection: 4,
|
|
653
|
+
entityReference: 5,
|
|
654
|
+
// Deprecated
|
|
655
|
+
entityNode: 6,
|
|
656
|
+
// Deprecated
|
|
657
|
+
progressingInstruction: 7,
|
|
658
|
+
comment: 8,
|
|
659
|
+
document: 9,
|
|
660
|
+
documentType: 10,
|
|
661
|
+
documentFragment: 11,
|
|
662
|
+
notation: 12 // Deprecated
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
const getGlobal = function getGlobal() {
|
|
666
|
+
return typeof window === 'undefined' ? null : window;
|
|
667
|
+
};
|
|
668
|
+
|
|
609
669
|
/**
|
|
610
670
|
* Creates a no-op policy for internal use only.
|
|
611
671
|
* Don't export this function outside this module!
|
|
612
|
-
* @param {
|
|
613
|
-
* @param {
|
|
614
|
-
* @return {
|
|
615
|
-
* are not supported).
|
|
672
|
+
* @param {TrustedTypePolicyFactory} trustedTypes The policy factory.
|
|
673
|
+
* @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
|
|
674
|
+
* @return {TrustedTypePolicy} The policy created (or null, if Trusted Types
|
|
675
|
+
* are not supported or creating the policy failed).
|
|
616
676
|
*/
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
|
|
677
|
+
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
|
|
620
678
|
if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
|
|
621
679
|
return null;
|
|
622
|
-
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Allow the callers to control the unique policy name
|
|
623
683
|
// by adding a data-tt-policy-suffix to the script element with the DOMPurify.
|
|
624
684
|
// Policy creation with duplicate names throws in Trusted Types.
|
|
625
|
-
|
|
626
|
-
|
|
627
685
|
let suffix = null;
|
|
628
686
|
const ATTR_NAME = 'data-tt-policy-suffix';
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
suffix = document.currentScript.getAttribute(ATTR_NAME);
|
|
687
|
+
if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) {
|
|
688
|
+
suffix = purifyHostElement.getAttribute(ATTR_NAME);
|
|
632
689
|
}
|
|
633
|
-
|
|
634
690
|
const policyName = 'dompurify' + (suffix ? '#' + suffix : '');
|
|
635
|
-
|
|
636
691
|
try {
|
|
637
692
|
return trustedTypes.createPolicy(policyName, {
|
|
638
693
|
createHTML(html) {
|
|
639
694
|
return html;
|
|
640
695
|
},
|
|
641
|
-
|
|
642
696
|
createScriptURL(scriptUrl) {
|
|
643
697
|
return scriptUrl;
|
|
644
698
|
}
|
|
645
|
-
|
|
646
699
|
});
|
|
647
700
|
} catch (_) {
|
|
648
701
|
// Policy creation failed (most likely another DOMPurify script has
|
|
@@ -652,36 +705,32 @@ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedType
|
|
|
652
705
|
return null;
|
|
653
706
|
}
|
|
654
707
|
};
|
|
655
|
-
|
|
656
708
|
function createDOMPurify() {
|
|
657
709
|
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
658
|
-
|
|
659
710
|
const DOMPurify = root => createDOMPurify(root);
|
|
711
|
+
|
|
660
712
|
/**
|
|
661
713
|
* Version label, exposed for easier checks
|
|
662
714
|
* if DOMPurify is up to date or not
|
|
663
715
|
*/
|
|
716
|
+
DOMPurify.version = '3.1.3';
|
|
664
717
|
|
|
665
|
-
|
|
666
|
-
DOMPurify.version = '3.0.2';
|
|
667
718
|
/**
|
|
668
719
|
* Array of elements that DOMPurify removed during sanitation.
|
|
669
720
|
* Empty if nothing was removed.
|
|
670
721
|
*/
|
|
671
|
-
|
|
672
722
|
DOMPurify.removed = [];
|
|
673
|
-
|
|
674
|
-
if (!window || !window.document || window.document.nodeType !== 9) {
|
|
723
|
+
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
|
|
675
724
|
// Not running in a browser, provide a factory function
|
|
676
725
|
// so that you can pass your own Window
|
|
677
726
|
DOMPurify.isSupported = false;
|
|
678
727
|
return DOMPurify;
|
|
679
728
|
}
|
|
680
|
-
|
|
681
|
-
const originalDocument = window.document;
|
|
682
729
|
let {
|
|
683
730
|
document
|
|
684
731
|
} = window;
|
|
732
|
+
const originalDocument = document;
|
|
733
|
+
const currentScript = originalDocument.currentScript;
|
|
685
734
|
const {
|
|
686
735
|
DocumentFragment,
|
|
687
736
|
HTMLTemplateElement,
|
|
@@ -697,24 +746,22 @@ function createDOMPurify() {
|
|
|
697
746
|
const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
698
747
|
const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
699
748
|
const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
700
|
-
const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
749
|
+
const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
750
|
+
|
|
751
|
+
// As per issue #47, the web-components registry is inherited by a
|
|
701
752
|
// new document created via createHTMLDocument. As per the spec
|
|
702
753
|
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
|
|
703
754
|
// a new empty registry is used when creating a template contents owner
|
|
704
755
|
// document, so we use that as our parent document to ensure nothing
|
|
705
756
|
// is inherited.
|
|
706
|
-
|
|
707
757
|
if (typeof HTMLTemplateElement === 'function') {
|
|
708
758
|
const template = document.createElement('template');
|
|
709
|
-
|
|
710
759
|
if (template.content && template.content.ownerDocument) {
|
|
711
760
|
document = template.content.ownerDocument;
|
|
712
761
|
}
|
|
713
762
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
const emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
|
|
763
|
+
let trustedTypesPolicy;
|
|
764
|
+
let emptyHTML = '';
|
|
718
765
|
const {
|
|
719
766
|
implementation,
|
|
720
767
|
createNodeIterator,
|
|
@@ -725,11 +772,11 @@ function createDOMPurify() {
|
|
|
725
772
|
importNode
|
|
726
773
|
} = originalDocument;
|
|
727
774
|
let hooks = {};
|
|
775
|
+
|
|
728
776
|
/**
|
|
729
777
|
* Expose whether this browser supports running the full DOMPurify.
|
|
730
778
|
*/
|
|
731
|
-
|
|
732
|
-
DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined';
|
|
779
|
+
DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
|
|
733
780
|
const {
|
|
734
781
|
MUSTACHE_EXPR,
|
|
735
782
|
ERB_EXPR,
|
|
@@ -737,32 +784,33 @@ function createDOMPurify() {
|
|
|
737
784
|
DATA_ATTR,
|
|
738
785
|
ARIA_ATTR,
|
|
739
786
|
IS_SCRIPT_OR_DATA,
|
|
740
|
-
ATTR_WHITESPACE
|
|
787
|
+
ATTR_WHITESPACE,
|
|
788
|
+
CUSTOM_ELEMENT
|
|
741
789
|
} = EXPRESSIONS;
|
|
742
790
|
let {
|
|
743
791
|
IS_ALLOWED_URI: IS_ALLOWED_URI$1
|
|
744
792
|
} = EXPRESSIONS;
|
|
793
|
+
|
|
745
794
|
/**
|
|
746
795
|
* We consider the elements and attributes below to be safe. Ideally
|
|
747
796
|
* don't add any new ones but feel free to remove unwanted ones.
|
|
748
797
|
*/
|
|
749
798
|
|
|
750
799
|
/* allowed element names */
|
|
751
|
-
|
|
752
800
|
let ALLOWED_TAGS = null;
|
|
753
801
|
const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
|
|
754
|
-
/* Allowed attribute names */
|
|
755
802
|
|
|
803
|
+
/* Allowed attribute names */
|
|
756
804
|
let ALLOWED_ATTR = null;
|
|
757
805
|
const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
|
|
806
|
+
|
|
758
807
|
/*
|
|
759
808
|
* Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
|
|
760
809
|
* @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
|
|
761
810
|
* @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
|
|
762
811
|
* @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
|
|
763
812
|
*/
|
|
764
|
-
|
|
765
|
-
let CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
|
|
813
|
+
let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
|
|
766
814
|
tagNameCheck: {
|
|
767
815
|
writable: true,
|
|
768
816
|
configurable: false,
|
|
@@ -782,59 +830,65 @@ function createDOMPurify() {
|
|
|
782
830
|
value: false
|
|
783
831
|
}
|
|
784
832
|
}));
|
|
785
|
-
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
|
|
786
833
|
|
|
834
|
+
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
|
|
787
835
|
let FORBID_TAGS = null;
|
|
788
|
-
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
|
|
789
836
|
|
|
837
|
+
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
|
|
790
838
|
let FORBID_ATTR = null;
|
|
791
|
-
/* Decide if ARIA attributes are okay */
|
|
792
839
|
|
|
840
|
+
/* Decide if ARIA attributes are okay */
|
|
793
841
|
let ALLOW_ARIA_ATTR = true;
|
|
794
|
-
/* Decide if custom data attributes are okay */
|
|
795
842
|
|
|
843
|
+
/* Decide if custom data attributes are okay */
|
|
796
844
|
let ALLOW_DATA_ATTR = true;
|
|
797
|
-
/* Decide if unknown protocols are okay */
|
|
798
845
|
|
|
846
|
+
/* Decide if unknown protocols are okay */
|
|
799
847
|
let ALLOW_UNKNOWN_PROTOCOLS = false;
|
|
848
|
+
|
|
800
849
|
/* Decide if self-closing tags in attributes are allowed.
|
|
801
850
|
* Usually removed due to a mXSS issue in jQuery 3.0 */
|
|
802
|
-
|
|
803
851
|
let ALLOW_SELF_CLOSE_IN_ATTR = true;
|
|
852
|
+
|
|
804
853
|
/* Output should be safe for common template engines.
|
|
805
854
|
* This means, DOMPurify removes data attributes, mustaches and ERB
|
|
806
855
|
*/
|
|
807
|
-
|
|
808
856
|
let SAFE_FOR_TEMPLATES = false;
|
|
809
|
-
/* Decide if document with <html>... should be returned */
|
|
810
857
|
|
|
858
|
+
/* Output should be safe even for XML used within HTML and alike.
|
|
859
|
+
* This means, DOMPurify removes comments when containing risky content.
|
|
860
|
+
*/
|
|
861
|
+
let SAFE_FOR_XML = true;
|
|
862
|
+
|
|
863
|
+
/* Decide if document with <html>... should be returned */
|
|
811
864
|
let WHOLE_DOCUMENT = false;
|
|
812
|
-
/* Track whether config is already set on this instance of DOMPurify. */
|
|
813
865
|
|
|
866
|
+
/* Track whether config is already set on this instance of DOMPurify. */
|
|
814
867
|
let SET_CONFIG = false;
|
|
868
|
+
|
|
815
869
|
/* Decide if all elements (e.g. style, script) must be children of
|
|
816
870
|
* document.body. By default, browsers might move them to document.head */
|
|
817
|
-
|
|
818
871
|
let FORCE_BODY = false;
|
|
872
|
+
|
|
819
873
|
/* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
|
|
820
874
|
* string (or a TrustedHTML object if Trusted Types are supported).
|
|
821
875
|
* If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
|
|
822
876
|
*/
|
|
823
|
-
|
|
824
877
|
let RETURN_DOM = false;
|
|
878
|
+
|
|
825
879
|
/* Decide if a DOM `DocumentFragment` should be returned, instead of a html
|
|
826
880
|
* string (or a TrustedHTML object if Trusted Types are supported) */
|
|
827
|
-
|
|
828
881
|
let RETURN_DOM_FRAGMENT = false;
|
|
882
|
+
|
|
829
883
|
/* Try to return a Trusted Type object instead of a string, return a string in
|
|
830
884
|
* case Trusted Types are not supported */
|
|
831
|
-
|
|
832
885
|
let RETURN_TRUSTED_TYPE = false;
|
|
886
|
+
|
|
833
887
|
/* Output should be free from DOM clobbering attacks?
|
|
834
888
|
* This sanitizes markups named with colliding, clobberable built-in DOM APIs.
|
|
835
889
|
*/
|
|
836
|
-
|
|
837
890
|
let SANITIZE_DOM = true;
|
|
891
|
+
|
|
838
892
|
/* Achieve full DOM Clobbering protection by isolating the namespace of named
|
|
839
893
|
* properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
|
|
840
894
|
*
|
|
@@ -848,327 +902,312 @@ function createDOMPurify() {
|
|
|
848
902
|
* Namespace isolation is implemented by prefixing `id` and `name` attributes
|
|
849
903
|
* with a constant string, i.e., `user-content-`
|
|
850
904
|
*/
|
|
851
|
-
|
|
852
905
|
let SANITIZE_NAMED_PROPS = false;
|
|
853
906
|
const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
|
|
854
|
-
/* Keep element content when removing element? */
|
|
855
907
|
|
|
908
|
+
/* Keep element content when removing element? */
|
|
856
909
|
let KEEP_CONTENT = true;
|
|
910
|
+
|
|
857
911
|
/* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
|
|
858
912
|
* of importing it into a new Document and returning a sanitized copy */
|
|
859
|
-
|
|
860
913
|
let IN_PLACE = false;
|
|
861
|
-
/* Allow usage of profiles like html, svg and mathMl */
|
|
862
914
|
|
|
915
|
+
/* Allow usage of profiles like html, svg and mathMl */
|
|
863
916
|
let USE_PROFILES = {};
|
|
864
|
-
/* Tags to ignore content of when KEEP_CONTENT is true */
|
|
865
917
|
|
|
918
|
+
/* Tags to ignore content of when KEEP_CONTENT is true */
|
|
866
919
|
let FORBID_CONTENTS = null;
|
|
867
920
|
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']);
|
|
868
|
-
/* Tags that are safe for data: URIs */
|
|
869
921
|
|
|
922
|
+
/* Tags that are safe for data: URIs */
|
|
870
923
|
let DATA_URI_TAGS = null;
|
|
871
924
|
const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
|
|
872
|
-
/* Attributes safe for values like "javascript:" */
|
|
873
925
|
|
|
926
|
+
/* Attributes safe for values like "javascript:" */
|
|
874
927
|
let URI_SAFE_ATTRIBUTES = null;
|
|
875
928
|
const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
|
|
876
929
|
const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
|
|
877
930
|
const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
878
931
|
const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
879
932
|
/* Document namespace */
|
|
880
|
-
|
|
881
933
|
let NAMESPACE = HTML_NAMESPACE;
|
|
882
934
|
let IS_EMPTY_INPUT = false;
|
|
883
|
-
/* Allowed XHTML+XML namespaces */
|
|
884
935
|
|
|
936
|
+
/* Allowed XHTML+XML namespaces */
|
|
885
937
|
let ALLOWED_NAMESPACES = null;
|
|
886
938
|
const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
|
|
887
|
-
/* Parsing of strict XHTML documents */
|
|
888
939
|
|
|
889
|
-
|
|
940
|
+
/* Parsing of strict XHTML documents */
|
|
941
|
+
let PARSER_MEDIA_TYPE = null;
|
|
890
942
|
const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
|
|
891
943
|
const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
|
|
892
|
-
let transformCaseFunc;
|
|
893
|
-
/* Keep a reference to config to pass to hooks */
|
|
944
|
+
let transformCaseFunc = null;
|
|
894
945
|
|
|
946
|
+
/* Keep a reference to config to pass to hooks */
|
|
895
947
|
let CONFIG = null;
|
|
896
|
-
/* Ideally, do not touch anything below this line */
|
|
897
948
|
|
|
949
|
+
/* Specify the maximum element nesting depth to prevent mXSS */
|
|
950
|
+
const MAX_NESTING_DEPTH = 255;
|
|
951
|
+
|
|
952
|
+
/* Ideally, do not touch anything below this line */
|
|
898
953
|
/* ______________________________________________ */
|
|
899
954
|
|
|
900
955
|
const formElement = document.createElement('form');
|
|
901
|
-
|
|
902
956
|
const isRegexOrFunction = function isRegexOrFunction(testValue) {
|
|
903
957
|
return testValue instanceof RegExp || testValue instanceof Function;
|
|
904
958
|
};
|
|
959
|
+
|
|
905
960
|
/**
|
|
906
961
|
* _parseConfig
|
|
907
962
|
*
|
|
908
963
|
* @param {Object} cfg optional config literal
|
|
909
964
|
*/
|
|
910
965
|
// eslint-disable-next-line complexity
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
const _parseConfig = function _parseConfig(cfg) {
|
|
966
|
+
const _parseConfig = function _parseConfig() {
|
|
967
|
+
let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
914
968
|
if (CONFIG && CONFIG === cfg) {
|
|
915
969
|
return;
|
|
916
970
|
}
|
|
917
|
-
/* Shield configuration object from tampering */
|
|
918
|
-
|
|
919
971
|
|
|
972
|
+
/* Shield configuration object from tampering */
|
|
920
973
|
if (!cfg || typeof cfg !== 'object') {
|
|
921
974
|
cfg = {};
|
|
922
975
|
}
|
|
923
|
-
/* Shield configuration object from prototype pollution */
|
|
924
|
-
|
|
925
976
|
|
|
977
|
+
/* Shield configuration object from prototype pollution */
|
|
926
978
|
cfg = clone(cfg);
|
|
927
|
-
PARSER_MEDIA_TYPE =
|
|
928
|
-
|
|
979
|
+
PARSER_MEDIA_TYPE =
|
|
980
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
981
|
+
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
|
|
929
982
|
|
|
983
|
+
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
930
984
|
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
|
|
931
|
-
/* Set configuration parameters */
|
|
932
985
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
cfg
|
|
986
|
+
/* Set configuration parameters */
|
|
987
|
+
ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
988
|
+
ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
989
|
+
ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
|
|
990
|
+
URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
|
|
991
|
+
// eslint-disable-line indent
|
|
992
|
+
cfg.ADD_URI_SAFE_ATTR,
|
|
993
|
+
// eslint-disable-line indent
|
|
938
994
|
transformCaseFunc // eslint-disable-line indent
|
|
939
995
|
) // eslint-disable-line indent
|
|
940
996
|
: DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
941
|
-
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS'
|
|
942
|
-
|
|
997
|
+
DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
|
|
998
|
+
// eslint-disable-line indent
|
|
999
|
+
cfg.ADD_DATA_URI_TAGS,
|
|
1000
|
+
// eslint-disable-line indent
|
|
943
1001
|
transformCaseFunc // eslint-disable-line indent
|
|
944
1002
|
) // eslint-disable-line indent
|
|
945
1003
|
: DEFAULT_DATA_URI_TAGS;
|
|
946
|
-
FORBID_CONTENTS = 'FORBID_CONTENTS'
|
|
947
|
-
FORBID_TAGS = 'FORBID_TAGS'
|
|
948
|
-
FORBID_ATTR = 'FORBID_ATTR'
|
|
949
|
-
USE_PROFILES = 'USE_PROFILES'
|
|
1004
|
+
FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
|
|
1005
|
+
FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
|
|
1006
|
+
FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
|
|
1007
|
+
USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
|
|
950
1008
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
951
|
-
|
|
952
1009
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
953
|
-
|
|
954
1010
|
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
|
|
955
|
-
|
|
956
1011
|
ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
|
|
957
|
-
|
|
958
1012
|
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
|
|
959
|
-
|
|
1013
|
+
SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
|
|
960
1014
|
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
|
|
961
|
-
|
|
962
1015
|
RETURN_DOM = cfg.RETURN_DOM || false; // Default false
|
|
963
|
-
|
|
964
1016
|
RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
|
|
965
|
-
|
|
966
1017
|
RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
|
|
967
|
-
|
|
968
1018
|
FORCE_BODY = cfg.FORCE_BODY || false; // Default false
|
|
969
|
-
|
|
970
1019
|
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
|
|
971
|
-
|
|
972
1020
|
SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
|
|
973
|
-
|
|
974
1021
|
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
|
|
975
|
-
|
|
976
1022
|
IN_PLACE = cfg.IN_PLACE || false; // Default false
|
|
977
|
-
|
|
978
1023
|
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
|
|
979
1024
|
NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
|
|
980
1025
|
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
|
|
981
|
-
|
|
982
1026
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
|
|
983
1027
|
CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
|
|
984
1028
|
}
|
|
985
|
-
|
|
986
1029
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
|
|
987
1030
|
CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
|
|
988
1031
|
}
|
|
989
|
-
|
|
990
1032
|
if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
|
|
991
1033
|
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
|
|
992
1034
|
}
|
|
993
|
-
|
|
994
1035
|
if (SAFE_FOR_TEMPLATES) {
|
|
995
1036
|
ALLOW_DATA_ATTR = false;
|
|
996
1037
|
}
|
|
997
|
-
|
|
998
1038
|
if (RETURN_DOM_FRAGMENT) {
|
|
999
1039
|
RETURN_DOM = true;
|
|
1000
1040
|
}
|
|
1001
|
-
/* Parse profile info */
|
|
1002
|
-
|
|
1003
1041
|
|
|
1042
|
+
/* Parse profile info */
|
|
1004
1043
|
if (USE_PROFILES) {
|
|
1005
|
-
ALLOWED_TAGS = addToSet({},
|
|
1044
|
+
ALLOWED_TAGS = addToSet({}, text);
|
|
1006
1045
|
ALLOWED_ATTR = [];
|
|
1007
|
-
|
|
1008
1046
|
if (USE_PROFILES.html === true) {
|
|
1009
1047
|
addToSet(ALLOWED_TAGS, html$1);
|
|
1010
1048
|
addToSet(ALLOWED_ATTR, html);
|
|
1011
1049
|
}
|
|
1012
|
-
|
|
1013
1050
|
if (USE_PROFILES.svg === true) {
|
|
1014
1051
|
addToSet(ALLOWED_TAGS, svg$1);
|
|
1015
1052
|
addToSet(ALLOWED_ATTR, svg);
|
|
1016
1053
|
addToSet(ALLOWED_ATTR, xml);
|
|
1017
1054
|
}
|
|
1018
|
-
|
|
1019
1055
|
if (USE_PROFILES.svgFilters === true) {
|
|
1020
1056
|
addToSet(ALLOWED_TAGS, svgFilters);
|
|
1021
1057
|
addToSet(ALLOWED_ATTR, svg);
|
|
1022
1058
|
addToSet(ALLOWED_ATTR, xml);
|
|
1023
1059
|
}
|
|
1024
|
-
|
|
1025
1060
|
if (USE_PROFILES.mathMl === true) {
|
|
1026
1061
|
addToSet(ALLOWED_TAGS, mathMl$1);
|
|
1027
1062
|
addToSet(ALLOWED_ATTR, mathMl);
|
|
1028
1063
|
addToSet(ALLOWED_ATTR, xml);
|
|
1029
1064
|
}
|
|
1030
1065
|
}
|
|
1031
|
-
/* Merge configuration parameters */
|
|
1032
|
-
|
|
1033
1066
|
|
|
1067
|
+
/* Merge configuration parameters */
|
|
1034
1068
|
if (cfg.ADD_TAGS) {
|
|
1035
1069
|
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
1036
1070
|
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
1037
1071
|
}
|
|
1038
|
-
|
|
1039
1072
|
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
1040
1073
|
}
|
|
1041
|
-
|
|
1042
1074
|
if (cfg.ADD_ATTR) {
|
|
1043
1075
|
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
1044
1076
|
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
1045
1077
|
}
|
|
1046
|
-
|
|
1047
1078
|
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
1048
1079
|
}
|
|
1049
|
-
|
|
1050
1080
|
if (cfg.ADD_URI_SAFE_ATTR) {
|
|
1051
1081
|
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
1052
1082
|
}
|
|
1053
|
-
|
|
1054
1083
|
if (cfg.FORBID_CONTENTS) {
|
|
1055
1084
|
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
|
|
1056
1085
|
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
1057
1086
|
}
|
|
1058
|
-
|
|
1059
1087
|
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
1060
1088
|
}
|
|
1061
|
-
/* Add #text in case KEEP_CONTENT is set to true */
|
|
1062
|
-
|
|
1063
1089
|
|
|
1090
|
+
/* Add #text in case KEEP_CONTENT is set to true */
|
|
1064
1091
|
if (KEEP_CONTENT) {
|
|
1065
1092
|
ALLOWED_TAGS['#text'] = true;
|
|
1066
1093
|
}
|
|
1067
|
-
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
1068
|
-
|
|
1069
1094
|
|
|
1095
|
+
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
1070
1096
|
if (WHOLE_DOCUMENT) {
|
|
1071
1097
|
addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
|
|
1072
1098
|
}
|
|
1073
|
-
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
1074
|
-
|
|
1075
1099
|
|
|
1100
|
+
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
1076
1101
|
if (ALLOWED_TAGS.table) {
|
|
1077
1102
|
addToSet(ALLOWED_TAGS, ['tbody']);
|
|
1078
1103
|
delete FORBID_TAGS.tbody;
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1104
|
+
}
|
|
1105
|
+
if (cfg.TRUSTED_TYPES_POLICY) {
|
|
1106
|
+
if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
|
|
1107
|
+
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
|
|
1108
|
+
}
|
|
1109
|
+
if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
|
|
1110
|
+
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// Overwrite existing TrustedTypes policy.
|
|
1114
|
+
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
|
|
1115
|
+
|
|
1116
|
+
// Sign local variables required by `sanitize`.
|
|
1117
|
+
emptyHTML = trustedTypesPolicy.createHTML('');
|
|
1118
|
+
} else {
|
|
1119
|
+
// Uninitialized policy, attempt to initialize the internal dompurify policy.
|
|
1120
|
+
if (trustedTypesPolicy === undefined) {
|
|
1121
|
+
trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
|
|
1122
|
+
}
|
|
1081
1123
|
|
|
1124
|
+
// If creating the internal policy succeeded sign internal variables.
|
|
1125
|
+
if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
|
|
1126
|
+
emptyHTML = trustedTypesPolicy.createHTML('');
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1082
1129
|
|
|
1130
|
+
// Prevent further manipulation of configuration.
|
|
1131
|
+
// Not available in IE8, Safari 5, etc.
|
|
1083
1132
|
if (freeze) {
|
|
1084
1133
|
freeze(cfg);
|
|
1085
1134
|
}
|
|
1086
|
-
|
|
1087
1135
|
CONFIG = cfg;
|
|
1088
1136
|
};
|
|
1089
|
-
|
|
1090
1137
|
const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
1091
|
-
const HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', '
|
|
1138
|
+
const HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'annotation-xml']);
|
|
1139
|
+
|
|
1140
|
+
// Certain elements are allowed in both SVG and HTML
|
|
1092
1141
|
// namespace. We need to specify them explicitly
|
|
1093
1142
|
// so that they don't get erroneously deleted from
|
|
1094
1143
|
// HTML namespace.
|
|
1095
|
-
|
|
1096
1144
|
const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
|
|
1145
|
+
|
|
1097
1146
|
/* Keep track of all possible SVG and MathML tags
|
|
1098
1147
|
* so that we can perform the namespace checks
|
|
1099
1148
|
* correctly. */
|
|
1149
|
+
const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
|
|
1150
|
+
const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
|
|
1100
1151
|
|
|
1101
|
-
const ALL_SVG_TAGS = addToSet({}, svg$1);
|
|
1102
|
-
addToSet(ALL_SVG_TAGS, svgFilters);
|
|
1103
|
-
addToSet(ALL_SVG_TAGS, svgDisallowed);
|
|
1104
|
-
const ALL_MATHML_TAGS = addToSet({}, mathMl$1);
|
|
1105
|
-
addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
|
|
1106
1152
|
/**
|
|
1107
|
-
*
|
|
1108
|
-
*
|
|
1109
1153
|
* @param {Element} element a DOM element whose namespace is being checked
|
|
1110
1154
|
* @returns {boolean} Return false if the element has a
|
|
1111
1155
|
* namespace that a spec-compliant parser would never
|
|
1112
1156
|
* return. Return true otherwise.
|
|
1113
1157
|
*/
|
|
1114
|
-
|
|
1115
1158
|
const _checkValidNamespace = function _checkValidNamespace(element) {
|
|
1116
|
-
let parent = getParentNode(element);
|
|
1117
|
-
// can be null. We just simulate parent in this case.
|
|
1159
|
+
let parent = getParentNode(element);
|
|
1118
1160
|
|
|
1161
|
+
// In JSDOM, if we're inside shadow DOM, then parentNode
|
|
1162
|
+
// can be null. We just simulate parent in this case.
|
|
1119
1163
|
if (!parent || !parent.tagName) {
|
|
1120
1164
|
parent = {
|
|
1121
1165
|
namespaceURI: NAMESPACE,
|
|
1122
1166
|
tagName: 'template'
|
|
1123
1167
|
};
|
|
1124
1168
|
}
|
|
1125
|
-
|
|
1126
1169
|
const tagName = stringToLowerCase(element.tagName);
|
|
1127
1170
|
const parentTagName = stringToLowerCase(parent.tagName);
|
|
1128
|
-
|
|
1129
1171
|
if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
1130
1172
|
return false;
|
|
1131
1173
|
}
|
|
1132
|
-
|
|
1133
1174
|
if (element.namespaceURI === SVG_NAMESPACE) {
|
|
1134
1175
|
// The only way to switch from HTML namespace to SVG
|
|
1135
1176
|
// is via <svg>. If it happens via any other tag, then
|
|
1136
1177
|
// it should be killed.
|
|
1137
1178
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
1138
1179
|
return tagName === 'svg';
|
|
1139
|
-
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// The only way to switch from MathML to SVG is via`
|
|
1140
1183
|
// svg if parent is either <annotation-xml> or MathML
|
|
1141
1184
|
// text integration points.
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
1185
|
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
1145
1186
|
return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
|
|
1146
|
-
}
|
|
1147
|
-
// spec. All others are disallowed in SVG namespace.
|
|
1148
|
-
|
|
1187
|
+
}
|
|
1149
1188
|
|
|
1189
|
+
// We only allow elements that are defined in SVG
|
|
1190
|
+
// spec. All others are disallowed in SVG namespace.
|
|
1150
1191
|
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
1151
1192
|
}
|
|
1152
|
-
|
|
1153
1193
|
if (element.namespaceURI === MATHML_NAMESPACE) {
|
|
1154
1194
|
// The only way to switch from HTML namespace to MathML
|
|
1155
1195
|
// is via <math>. If it happens via any other tag, then
|
|
1156
1196
|
// it should be killed.
|
|
1157
1197
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
1158
1198
|
return tagName === 'math';
|
|
1159
|
-
}
|
|
1160
|
-
// <math> and HTML integration points
|
|
1161
|
-
|
|
1199
|
+
}
|
|
1162
1200
|
|
|
1201
|
+
// The only way to switch from SVG to MathML is via
|
|
1202
|
+
// <math> and HTML integration points
|
|
1163
1203
|
if (parent.namespaceURI === SVG_NAMESPACE) {
|
|
1164
1204
|
return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
|
|
1165
|
-
}
|
|
1166
|
-
// spec. All others are disallowed in MathML namespace.
|
|
1167
|
-
|
|
1205
|
+
}
|
|
1168
1206
|
|
|
1207
|
+
// We only allow elements that are defined in MathML
|
|
1208
|
+
// spec. All others are disallowed in MathML namespace.
|
|
1169
1209
|
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
1170
1210
|
}
|
|
1171
|
-
|
|
1172
1211
|
if (element.namespaceURI === HTML_NAMESPACE) {
|
|
1173
1212
|
// The only way to switch from SVG to HTML is via
|
|
1174
1213
|
// HTML integration points, and from MathML to HTML
|
|
@@ -1176,39 +1215,36 @@ function createDOMPurify() {
|
|
|
1176
1215
|
if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
|
|
1177
1216
|
return false;
|
|
1178
1217
|
}
|
|
1179
|
-
|
|
1180
1218
|
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
|
|
1181
1219
|
return false;
|
|
1182
|
-
}
|
|
1183
|
-
// or SVG and should never appear in HTML namespace
|
|
1184
|
-
|
|
1220
|
+
}
|
|
1185
1221
|
|
|
1222
|
+
// We disallow tags that are specific for MathML
|
|
1223
|
+
// or SVG and should never appear in HTML namespace
|
|
1186
1224
|
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1225
|
+
}
|
|
1189
1226
|
|
|
1227
|
+
// For XHTML and XML documents that support custom namespaces
|
|
1190
1228
|
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
1191
1229
|
return true;
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// The code should never reach this place (this means
|
|
1233
|
+
// that the element somehow got namespace that is not
|
|
1194
1234
|
// HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
|
|
1195
1235
|
// Return false just in case.
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
1236
|
return false;
|
|
1199
1237
|
};
|
|
1238
|
+
|
|
1200
1239
|
/**
|
|
1201
1240
|
* _forceRemove
|
|
1202
1241
|
*
|
|
1203
1242
|
* @param {Node} node a DOM node
|
|
1204
1243
|
*/
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
1244
|
const _forceRemove = function _forceRemove(node) {
|
|
1208
1245
|
arrayPush(DOMPurify.removed, {
|
|
1209
1246
|
element: node
|
|
1210
1247
|
});
|
|
1211
|
-
|
|
1212
1248
|
try {
|
|
1213
1249
|
// eslint-disable-next-line unicorn/prefer-dom-node-remove
|
|
1214
1250
|
node.parentNode.removeChild(node);
|
|
@@ -1216,14 +1252,13 @@ function createDOMPurify() {
|
|
|
1216
1252
|
node.remove();
|
|
1217
1253
|
}
|
|
1218
1254
|
};
|
|
1255
|
+
|
|
1219
1256
|
/**
|
|
1220
1257
|
* _removeAttribute
|
|
1221
1258
|
*
|
|
1222
1259
|
* @param {String} name an Attribute name
|
|
1223
1260
|
* @param {Node} node a DOM node
|
|
1224
1261
|
*/
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
1262
|
const _removeAttribute = function _removeAttribute(name, node) {
|
|
1228
1263
|
try {
|
|
1229
1264
|
arrayPush(DOMPurify.removed, {
|
|
@@ -1236,9 +1271,9 @@ function createDOMPurify() {
|
|
|
1236
1271
|
from: node
|
|
1237
1272
|
});
|
|
1238
1273
|
}
|
|
1274
|
+
node.removeAttribute(name);
|
|
1239
1275
|
|
|
1240
|
-
|
|
1241
|
-
|
|
1276
|
+
// We void attribute values for unremovable "is"" attributes
|
|
1242
1277
|
if (name === 'is' && !ALLOWED_ATTR[name]) {
|
|
1243
1278
|
if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
|
|
1244
1279
|
try {
|
|
@@ -1251,19 +1286,17 @@ function createDOMPurify() {
|
|
|
1251
1286
|
}
|
|
1252
1287
|
}
|
|
1253
1288
|
};
|
|
1289
|
+
|
|
1254
1290
|
/**
|
|
1255
1291
|
* _initDocument
|
|
1256
1292
|
*
|
|
1257
1293
|
* @param {String} dirty a string of dirty markup
|
|
1258
1294
|
* @return {Document} a DOM, filled with the dirty markup
|
|
1259
1295
|
*/
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
1296
|
const _initDocument = function _initDocument(dirty) {
|
|
1263
1297
|
/* Create a HTML document */
|
|
1264
|
-
let doc;
|
|
1265
|
-
let leadingWhitespace;
|
|
1266
|
-
|
|
1298
|
+
let doc = null;
|
|
1299
|
+
let leadingWhitespace = null;
|
|
1267
1300
|
if (FORCE_BODY) {
|
|
1268
1301
|
dirty = '<remove></remove>' + dirty;
|
|
1269
1302
|
} else {
|
|
@@ -1271,83 +1304,78 @@ function createDOMPurify() {
|
|
|
1271
1304
|
const matches = stringMatch(dirty, /^[\r\n\t ]+/);
|
|
1272
1305
|
leadingWhitespace = matches && matches[0];
|
|
1273
1306
|
}
|
|
1274
|
-
|
|
1275
1307
|
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
|
|
1276
1308
|
// Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
|
|
1277
1309
|
dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
|
|
1278
1310
|
}
|
|
1279
|
-
|
|
1280
1311
|
const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
1281
1312
|
/*
|
|
1282
1313
|
* Use the DOMParser API by default, fallback later if needs be
|
|
1283
1314
|
* DOMParser not work for svg when has multiple root element.
|
|
1284
1315
|
*/
|
|
1285
|
-
|
|
1286
1316
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
1287
1317
|
try {
|
|
1288
1318
|
doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
|
|
1289
1319
|
} catch (_) {}
|
|
1290
1320
|
}
|
|
1291
|
-
/* Use createHTMLDocument in case DOMParser is not available */
|
|
1292
|
-
|
|
1293
1321
|
|
|
1322
|
+
/* Use createHTMLDocument in case DOMParser is not available */
|
|
1294
1323
|
if (!doc || !doc.documentElement) {
|
|
1295
1324
|
doc = implementation.createDocument(NAMESPACE, 'template', null);
|
|
1296
|
-
|
|
1297
1325
|
try {
|
|
1298
1326
|
doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
|
|
1299
|
-
} catch (_) {
|
|
1327
|
+
} catch (_) {
|
|
1328
|
+
// Syntax error if dirtyPayload is invalid xml
|
|
1300
1329
|
}
|
|
1301
1330
|
}
|
|
1302
|
-
|
|
1303
1331
|
const body = doc.body || doc.documentElement;
|
|
1304
|
-
|
|
1305
1332
|
if (dirty && leadingWhitespace) {
|
|
1306
1333
|
body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
|
|
1307
1334
|
}
|
|
1308
|
-
/* Work on whole document or just its body */
|
|
1309
|
-
|
|
1310
1335
|
|
|
1336
|
+
/* Work on whole document or just its body */
|
|
1311
1337
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
1312
1338
|
return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
|
|
1313
1339
|
}
|
|
1314
|
-
|
|
1315
1340
|
return WHOLE_DOCUMENT ? doc.documentElement : body;
|
|
1316
1341
|
};
|
|
1342
|
+
|
|
1317
1343
|
/**
|
|
1318
|
-
*
|
|
1344
|
+
* Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
|
|
1319
1345
|
*
|
|
1320
|
-
* @param {
|
|
1321
|
-
* @return {
|
|
1346
|
+
* @param {Node} root The root element or node to start traversing on.
|
|
1347
|
+
* @return {NodeIterator} The created NodeIterator
|
|
1322
1348
|
*/
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
|
|
1349
|
+
const _createNodeIterator = function _createNodeIterator(root) {
|
|
1350
|
+
return createNodeIterator.call(root.ownerDocument || root, root,
|
|
1351
|
+
// eslint-disable-next-line no-bitwise
|
|
1352
|
+
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
|
|
1328
1353
|
};
|
|
1354
|
+
|
|
1329
1355
|
/**
|
|
1330
1356
|
* _isClobbered
|
|
1331
1357
|
*
|
|
1332
1358
|
* @param {Node} elm element to check for clobbering attacks
|
|
1333
1359
|
* @return {Boolean} true if clobbered, false if safe
|
|
1334
1360
|
*/
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
1361
|
const _isClobbered = function _isClobbered(elm) {
|
|
1338
|
-
return elm instanceof HTMLFormElement && (
|
|
1362
|
+
return elm instanceof HTMLFormElement && (
|
|
1363
|
+
// eslint-disable-next-line unicorn/no-typeof-undefined
|
|
1364
|
+
typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' ||
|
|
1365
|
+
// eslint-disable-next-line unicorn/no-typeof-undefined
|
|
1366
|
+
typeof elm.__removalCount !== 'undefined' && typeof elm.__removalCount !== 'number' || 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');
|
|
1339
1367
|
};
|
|
1368
|
+
|
|
1340
1369
|
/**
|
|
1341
|
-
*
|
|
1370
|
+
* Checks whether the given object is a DOM node.
|
|
1342
1371
|
*
|
|
1343
|
-
* @param {Node}
|
|
1372
|
+
* @param {Node} object object to check whether it's a DOM node
|
|
1344
1373
|
* @return {Boolean} true is object is a DOM node
|
|
1345
1374
|
*/
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
1375
|
const _isNode = function _isNode(object) {
|
|
1349
|
-
return typeof Node === '
|
|
1376
|
+
return typeof Node === 'function' && object instanceof Node;
|
|
1350
1377
|
};
|
|
1378
|
+
|
|
1351
1379
|
/**
|
|
1352
1380
|
* _executeHook
|
|
1353
1381
|
* Execute user configurable hooks
|
|
@@ -1356,17 +1384,15 @@ function createDOMPurify() {
|
|
|
1356
1384
|
* @param {Node} currentNode node to work on with the hook
|
|
1357
1385
|
* @param {Object} data additional hook parameters
|
|
1358
1386
|
*/
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
1387
|
const _executeHook = function _executeHook(entryPoint, currentNode, data) {
|
|
1362
1388
|
if (!hooks[entryPoint]) {
|
|
1363
1389
|
return;
|
|
1364
1390
|
}
|
|
1365
|
-
|
|
1366
1391
|
arrayForEach(hooks[entryPoint], hook => {
|
|
1367
1392
|
hook.call(DOMPurify, currentNode, data, CONFIG);
|
|
1368
1393
|
});
|
|
1369
1394
|
};
|
|
1395
|
+
|
|
1370
1396
|
/**
|
|
1371
1397
|
* _sanitizeElements
|
|
1372
1398
|
*
|
|
@@ -1377,94 +1403,93 @@ function createDOMPurify() {
|
|
|
1377
1403
|
* @param {Node} currentNode to check for permission to exist
|
|
1378
1404
|
* @return {Boolean} true if node was killed, false if left alive
|
|
1379
1405
|
*/
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
1406
|
const _sanitizeElements = function _sanitizeElements(currentNode) {
|
|
1383
|
-
let content;
|
|
1384
|
-
/* Execute a hook if present */
|
|
1407
|
+
let content = null;
|
|
1385
1408
|
|
|
1409
|
+
/* Execute a hook if present */
|
|
1386
1410
|
_executeHook('beforeSanitizeElements', currentNode, null);
|
|
1387
|
-
/* Check if element is clobbered or can clobber */
|
|
1388
|
-
|
|
1389
1411
|
|
|
1412
|
+
/* Check if element is clobbered or can clobber */
|
|
1390
1413
|
if (_isClobbered(currentNode)) {
|
|
1391
1414
|
_forceRemove(currentNode);
|
|
1392
|
-
|
|
1393
1415
|
return true;
|
|
1394
1416
|
}
|
|
1395
|
-
/* Now let's check the element's type and name */
|
|
1396
|
-
|
|
1397
1417
|
|
|
1418
|
+
/* Now let's check the element's type and name */
|
|
1398
1419
|
const tagName = transformCaseFunc(currentNode.nodeName);
|
|
1399
|
-
/* Execute a hook if present */
|
|
1400
1420
|
|
|
1421
|
+
/* Execute a hook if present */
|
|
1401
1422
|
_executeHook('uponSanitizeElement', currentNode, {
|
|
1402
1423
|
tagName,
|
|
1403
1424
|
allowedTags: ALLOWED_TAGS
|
|
1404
1425
|
});
|
|
1405
|
-
/* Detect mXSS attempts abusing namespace confusion */
|
|
1406
|
-
|
|
1407
1426
|
|
|
1408
|
-
|
|
1427
|
+
/* Detect mXSS attempts abusing namespace confusion */
|
|
1428
|
+
if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
|
|
1409
1429
|
_forceRemove(currentNode);
|
|
1430
|
+
return true;
|
|
1431
|
+
}
|
|
1410
1432
|
|
|
1433
|
+
/* Remove any ocurrence of processing instructions */
|
|
1434
|
+
if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
|
|
1435
|
+
_forceRemove(currentNode);
|
|
1411
1436
|
return true;
|
|
1412
1437
|
}
|
|
1413
|
-
/* Remove element if anything forbids its presence */
|
|
1414
1438
|
|
|
1439
|
+
/* Remove any kind of possibly harmful comments */
|
|
1440
|
+
if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
|
|
1441
|
+
_forceRemove(currentNode);
|
|
1442
|
+
return true;
|
|
1443
|
+
}
|
|
1415
1444
|
|
|
1445
|
+
/* Remove element if anything forbids its presence */
|
|
1416
1446
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1417
1447
|
/* Check if we have a custom element to handle */
|
|
1418
|
-
if (!FORBID_TAGS[tagName] &&
|
|
1419
|
-
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName))
|
|
1420
|
-
|
|
1448
|
+
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
|
|
1449
|
+
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
|
|
1450
|
+
return false;
|
|
1451
|
+
}
|
|
1452
|
+
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
|
|
1453
|
+
return false;
|
|
1454
|
+
}
|
|
1421
1455
|
}
|
|
1422
|
-
/* Keep content except for bad-listed elements */
|
|
1423
|
-
|
|
1424
1456
|
|
|
1457
|
+
/* Keep content except for bad-listed elements */
|
|
1425
1458
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
1426
1459
|
const parentNode = getParentNode(currentNode) || currentNode.parentNode;
|
|
1427
1460
|
const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
|
|
1428
|
-
|
|
1429
1461
|
if (childNodes && parentNode) {
|
|
1430
1462
|
const childCount = childNodes.length;
|
|
1431
|
-
|
|
1432
1463
|
for (let i = childCount - 1; i >= 0; --i) {
|
|
1433
|
-
|
|
1464
|
+
const childClone = cloneNode(childNodes[i], true);
|
|
1465
|
+
childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
|
|
1466
|
+
parentNode.insertBefore(childClone, getNextSibling(currentNode));
|
|
1434
1467
|
}
|
|
1435
1468
|
}
|
|
1436
1469
|
}
|
|
1437
|
-
|
|
1438
1470
|
_forceRemove(currentNode);
|
|
1439
|
-
|
|
1440
1471
|
return true;
|
|
1441
1472
|
}
|
|
1442
|
-
/* Check whether element has a valid namespace */
|
|
1443
|
-
|
|
1444
1473
|
|
|
1474
|
+
/* Check whether element has a valid namespace */
|
|
1445
1475
|
if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
|
|
1446
1476
|
_forceRemove(currentNode);
|
|
1447
|
-
|
|
1448
1477
|
return true;
|
|
1449
1478
|
}
|
|
1450
|
-
/* Make sure that older browsers don't get noscript mXSS */
|
|
1451
|
-
|
|
1452
1479
|
|
|
1453
|
-
|
|
1480
|
+
/* Make sure that older browsers don't get fallback-tag mXSS */
|
|
1481
|
+
if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
|
|
1454
1482
|
_forceRemove(currentNode);
|
|
1455
|
-
|
|
1456
1483
|
return true;
|
|
1457
1484
|
}
|
|
1458
|
-
/* Sanitize element content to be template-safe */
|
|
1459
1485
|
|
|
1460
|
-
|
|
1461
|
-
if (SAFE_FOR_TEMPLATES && currentNode.nodeType ===
|
|
1486
|
+
/* Sanitize element content to be template-safe */
|
|
1487
|
+
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
|
|
1462
1488
|
/* Get the element's text content */
|
|
1463
1489
|
content = currentNode.textContent;
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1490
|
+
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
1491
|
+
content = stringReplace(content, expr, ' ');
|
|
1492
|
+
});
|
|
1468
1493
|
if (currentNode.textContent !== content) {
|
|
1469
1494
|
arrayPush(DOMPurify.removed, {
|
|
1470
1495
|
element: currentNode.cloneNode()
|
|
@@ -1472,13 +1497,12 @@ function createDOMPurify() {
|
|
|
1472
1497
|
currentNode.textContent = content;
|
|
1473
1498
|
}
|
|
1474
1499
|
}
|
|
1475
|
-
/* Execute a hook if present */
|
|
1476
|
-
|
|
1477
1500
|
|
|
1501
|
+
/* Execute a hook if present */
|
|
1478
1502
|
_executeHook('afterSanitizeElements', currentNode, null);
|
|
1479
|
-
|
|
1480
1503
|
return false;
|
|
1481
1504
|
};
|
|
1505
|
+
|
|
1482
1506
|
/**
|
|
1483
1507
|
* _isValidAttribute
|
|
1484
1508
|
*
|
|
@@ -1488,47 +1512,46 @@ function createDOMPurify() {
|
|
|
1488
1512
|
* @return {Boolean} Returns true if `value` is valid, otherwise false.
|
|
1489
1513
|
*/
|
|
1490
1514
|
// eslint-disable-next-line complexity
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
1515
|
const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
1494
1516
|
/* Make sure attribute cannot clobber */
|
|
1495
|
-
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
1517
|
+
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement || value === '__depth' || value === '__removalCount')) {
|
|
1496
1518
|
return false;
|
|
1497
1519
|
}
|
|
1520
|
+
|
|
1498
1521
|
/* Allow valid data-* attributes: At least one character after "-"
|
|
1499
1522
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
1500
1523
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
1501
1524
|
We don't need to check the value; it's always URI safe. */
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
1525
|
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]) {
|
|
1505
|
-
if (
|
|
1526
|
+
if (
|
|
1527
|
+
// First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
1506
1528
|
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1507
1529
|
// and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
|
|
1508
|
-
|
|
1530
|
+
_isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) ||
|
|
1531
|
+
// Alternative, second condition checks if it's an `is`-attribute, AND
|
|
1509
1532
|
// the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1510
1533
|
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 {
|
|
1511
1534
|
return false;
|
|
1512
1535
|
}
|
|
1513
1536
|
/* Check value is safe. First, is attr inert? If so, is safe */
|
|
1514
|
-
|
|
1515
|
-
} 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) ; else {
|
|
1537
|
+
} 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) {
|
|
1516
1538
|
return false;
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1539
|
+
} else ;
|
|
1519
1540
|
return true;
|
|
1520
1541
|
};
|
|
1542
|
+
|
|
1521
1543
|
/**
|
|
1522
|
-
*
|
|
1544
|
+
* _isBasicCustomElement
|
|
1523
1545
|
* checks if at least one dash is included in tagName, and it's not the first char
|
|
1524
1546
|
* for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
|
|
1547
|
+
*
|
|
1525
1548
|
* @param {string} tagName name of the tag of the node to sanitize
|
|
1549
|
+
* @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
|
|
1526
1550
|
*/
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
const _basicCustomElementTest = function _basicCustomElementTest(tagName) {
|
|
1530
|
-
return tagName.indexOf('-') > 0;
|
|
1551
|
+
const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
|
|
1552
|
+
return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
|
|
1531
1553
|
};
|
|
1554
|
+
|
|
1532
1555
|
/**
|
|
1533
1556
|
* _sanitizeAttributes
|
|
1534
1557
|
*
|
|
@@ -1539,123 +1562,111 @@ function createDOMPurify() {
|
|
|
1539
1562
|
*
|
|
1540
1563
|
* @param {Node} currentNode to sanitize
|
|
1541
1564
|
*/
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
1565
|
const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
1545
|
-
let attr;
|
|
1546
|
-
let value;
|
|
1547
|
-
let lcName;
|
|
1548
|
-
let l;
|
|
1549
1566
|
/* Execute a hook if present */
|
|
1550
|
-
|
|
1551
1567
|
_executeHook('beforeSanitizeAttributes', currentNode, null);
|
|
1552
|
-
|
|
1553
1568
|
const {
|
|
1554
1569
|
attributes
|
|
1555
1570
|
} = currentNode;
|
|
1556
|
-
/* Check if we have attributes; if not we might have a text node */
|
|
1557
1571
|
|
|
1572
|
+
/* Check if we have attributes; if not we might have a text node */
|
|
1558
1573
|
if (!attributes) {
|
|
1559
1574
|
return;
|
|
1560
1575
|
}
|
|
1561
|
-
|
|
1562
1576
|
const hookEvent = {
|
|
1563
1577
|
attrName: '',
|
|
1564
1578
|
attrValue: '',
|
|
1565
1579
|
keepAttr: true,
|
|
1566
1580
|
allowedAttributes: ALLOWED_ATTR
|
|
1567
1581
|
};
|
|
1568
|
-
l = attributes.length;
|
|
1569
|
-
/* Go backwards over all attributes; safely remove bad ones */
|
|
1582
|
+
let l = attributes.length;
|
|
1570
1583
|
|
|
1584
|
+
/* Go backwards over all attributes; safely remove bad ones */
|
|
1571
1585
|
while (l--) {
|
|
1572
|
-
attr = attributes[l];
|
|
1586
|
+
const attr = attributes[l];
|
|
1573
1587
|
const {
|
|
1574
1588
|
name,
|
|
1575
|
-
namespaceURI
|
|
1589
|
+
namespaceURI,
|
|
1590
|
+
value: attrValue
|
|
1576
1591
|
} = attr;
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
/* Execute a hook if present */
|
|
1592
|
+
const lcName = transformCaseFunc(name);
|
|
1593
|
+
let value = name === 'value' ? attrValue : stringTrim(attrValue);
|
|
1580
1594
|
|
|
1595
|
+
/* Execute a hook if present */
|
|
1581
1596
|
hookEvent.attrName = lcName;
|
|
1582
1597
|
hookEvent.attrValue = value;
|
|
1583
1598
|
hookEvent.keepAttr = true;
|
|
1584
1599
|
hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
|
|
1585
|
-
|
|
1586
1600
|
_executeHook('uponSanitizeAttribute', currentNode, hookEvent);
|
|
1587
|
-
|
|
1588
1601
|
value = hookEvent.attrValue;
|
|
1589
1602
|
/* Did the hooks approve of the attribute? */
|
|
1590
|
-
|
|
1591
1603
|
if (hookEvent.forceKeepAttr) {
|
|
1592
1604
|
continue;
|
|
1593
1605
|
}
|
|
1594
|
-
/* Remove attribute */
|
|
1595
|
-
|
|
1596
1606
|
|
|
1607
|
+
/* Remove attribute */
|
|
1597
1608
|
_removeAttribute(name, currentNode);
|
|
1598
|
-
/* Did the hooks approve of the attribute? */
|
|
1599
|
-
|
|
1600
1609
|
|
|
1610
|
+
/* Did the hooks approve of the attribute? */
|
|
1601
1611
|
if (!hookEvent.keepAttr) {
|
|
1602
1612
|
continue;
|
|
1603
1613
|
}
|
|
1604
|
-
/* Work around a security issue in jQuery 3.0 */
|
|
1605
|
-
|
|
1606
1614
|
|
|
1615
|
+
/* Work around a security issue in jQuery 3.0 */
|
|
1607
1616
|
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
|
|
1608
1617
|
_removeAttribute(name, currentNode);
|
|
1609
|
-
|
|
1610
1618
|
continue;
|
|
1611
1619
|
}
|
|
1612
|
-
/* Sanitize attribute content to be template-safe */
|
|
1613
1620
|
|
|
1621
|
+
/* Work around a security issue with comments inside attributes */
|
|
1622
|
+
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
|
|
1623
|
+
_removeAttribute(name, currentNode);
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1614
1626
|
|
|
1627
|
+
/* Sanitize attribute content to be template-safe */
|
|
1615
1628
|
if (SAFE_FOR_TEMPLATES) {
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1629
|
+
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
1630
|
+
value = stringReplace(value, expr, ' ');
|
|
1631
|
+
});
|
|
1619
1632
|
}
|
|
1620
|
-
/* Is `value` valid for this attribute? */
|
|
1621
|
-
|
|
1622
1633
|
|
|
1634
|
+
/* Is `value` valid for this attribute? */
|
|
1623
1635
|
const lcTag = transformCaseFunc(currentNode.nodeName);
|
|
1624
|
-
|
|
1625
1636
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
1626
1637
|
continue;
|
|
1627
1638
|
}
|
|
1639
|
+
|
|
1628
1640
|
/* Full DOM Clobbering protection via namespace isolation,
|
|
1629
1641
|
* Prefix id and name attributes with `user-content-`
|
|
1630
1642
|
*/
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
1643
|
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
|
|
1634
1644
|
// Remove the attribute with this value
|
|
1635
|
-
_removeAttribute(name, currentNode);
|
|
1636
|
-
|
|
1645
|
+
_removeAttribute(name, currentNode);
|
|
1637
1646
|
|
|
1647
|
+
// Prefix the value and later re-create the attribute with the sanitized value
|
|
1638
1648
|
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
1639
1649
|
}
|
|
1640
|
-
/* Handle attributes that require Trusted Types */
|
|
1641
|
-
|
|
1642
1650
|
|
|
1651
|
+
/* Handle attributes that require Trusted Types */
|
|
1643
1652
|
if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
|
|
1644
1653
|
if (namespaceURI) ; else {
|
|
1645
1654
|
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
|
|
1646
1655
|
case 'TrustedHTML':
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1656
|
+
{
|
|
1657
|
+
value = trustedTypesPolicy.createHTML(value);
|
|
1658
|
+
break;
|
|
1659
|
+
}
|
|
1650
1660
|
case 'TrustedScriptURL':
|
|
1651
|
-
|
|
1652
|
-
|
|
1661
|
+
{
|
|
1662
|
+
value = trustedTypesPolicy.createScriptURL(value);
|
|
1663
|
+
break;
|
|
1664
|
+
}
|
|
1653
1665
|
}
|
|
1654
1666
|
}
|
|
1655
1667
|
}
|
|
1656
|
-
/* Handle invalid data-* attribute set by try-catching it */
|
|
1657
|
-
|
|
1658
1668
|
|
|
1669
|
+
/* Handle invalid data-* attribute set by try-catching it */
|
|
1659
1670
|
try {
|
|
1660
1671
|
if (namespaceURI) {
|
|
1661
1672
|
currentNode.setAttributeNS(namespaceURI, name, value);
|
|
@@ -1663,123 +1674,129 @@ function createDOMPurify() {
|
|
|
1663
1674
|
/* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
|
|
1664
1675
|
currentNode.setAttribute(name, value);
|
|
1665
1676
|
}
|
|
1666
|
-
|
|
1667
|
-
|
|
1677
|
+
if (_isClobbered(currentNode)) {
|
|
1678
|
+
_forceRemove(currentNode);
|
|
1679
|
+
} else {
|
|
1680
|
+
arrayPop(DOMPurify.removed);
|
|
1681
|
+
}
|
|
1668
1682
|
} catch (_) {}
|
|
1669
1683
|
}
|
|
1670
|
-
/* Execute a hook if present */
|
|
1671
|
-
|
|
1672
1684
|
|
|
1685
|
+
/* Execute a hook if present */
|
|
1673
1686
|
_executeHook('afterSanitizeAttributes', currentNode, null);
|
|
1674
1687
|
};
|
|
1688
|
+
|
|
1675
1689
|
/**
|
|
1676
1690
|
* _sanitizeShadowDOM
|
|
1677
1691
|
*
|
|
1678
1692
|
* @param {DocumentFragment} fragment to iterate over recursively
|
|
1679
1693
|
*/
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
1694
|
const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
|
|
1683
|
-
let shadowNode;
|
|
1695
|
+
let shadowNode = null;
|
|
1696
|
+
const shadowIterator = _createNodeIterator(fragment);
|
|
1684
1697
|
|
|
1685
|
-
const shadowIterator = _createIterator(fragment);
|
|
1686
1698
|
/* Execute a hook if present */
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
1699
|
_executeHook('beforeSanitizeShadowDOM', fragment, null);
|
|
1690
|
-
|
|
1691
1700
|
while (shadowNode = shadowIterator.nextNode()) {
|
|
1692
1701
|
/* Execute a hook if present */
|
|
1693
1702
|
_executeHook('uponSanitizeShadowNode', shadowNode, null);
|
|
1694
|
-
/* Sanitize tags and elements */
|
|
1695
|
-
|
|
1696
1703
|
|
|
1704
|
+
/* Sanitize tags and elements */
|
|
1697
1705
|
if (_sanitizeElements(shadowNode)) {
|
|
1698
1706
|
continue;
|
|
1699
1707
|
}
|
|
1700
|
-
|
|
1708
|
+
const parentNode = getParentNode(shadowNode);
|
|
1709
|
+
|
|
1710
|
+
/* Set the nesting depth of an element */
|
|
1711
|
+
if (shadowNode.nodeType === NODE_TYPE.element) {
|
|
1712
|
+
if (parentNode && parentNode.__depth) {
|
|
1713
|
+
/*
|
|
1714
|
+
We want the depth of the node in the original tree, which can
|
|
1715
|
+
change when it's removed from its parent.
|
|
1716
|
+
*/
|
|
1717
|
+
shadowNode.__depth = (shadowNode.__removalCount || 0) + parentNode.__depth + 1;
|
|
1718
|
+
} else {
|
|
1719
|
+
shadowNode.__depth = 1;
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1701
1722
|
|
|
1723
|
+
/*
|
|
1724
|
+
* Remove an element if nested too deeply to avoid mXSS
|
|
1725
|
+
* or if the __depth might have been tampered with
|
|
1726
|
+
*/
|
|
1727
|
+
if (shadowNode.__depth >= MAX_NESTING_DEPTH || shadowNode.__depth < 0 || numberIsNaN(shadowNode.__depth)) {
|
|
1728
|
+
_forceRemove(shadowNode);
|
|
1729
|
+
}
|
|
1702
1730
|
|
|
1731
|
+
/* Deep shadow DOM detected */
|
|
1703
1732
|
if (shadowNode.content instanceof DocumentFragment) {
|
|
1733
|
+
shadowNode.content.__depth = shadowNode.__depth;
|
|
1704
1734
|
_sanitizeShadowDOM(shadowNode.content);
|
|
1705
1735
|
}
|
|
1706
|
-
/* Check attributes, sanitize if necessary */
|
|
1707
|
-
|
|
1708
1736
|
|
|
1737
|
+
/* Check attributes, sanitize if necessary */
|
|
1709
1738
|
_sanitizeAttributes(shadowNode);
|
|
1710
1739
|
}
|
|
1711
|
-
/* Execute a hook if present */
|
|
1712
|
-
|
|
1713
1740
|
|
|
1741
|
+
/* Execute a hook if present */
|
|
1714
1742
|
_executeHook('afterSanitizeShadowDOM', fragment, null);
|
|
1715
1743
|
};
|
|
1744
|
+
|
|
1716
1745
|
/**
|
|
1717
1746
|
* Sanitize
|
|
1718
1747
|
* Public method providing core sanitation functionality
|
|
1719
1748
|
*
|
|
1720
1749
|
* @param {String|Node} dirty string or DOM node
|
|
1721
|
-
* @param {Object}
|
|
1750
|
+
* @param {Object} cfg object
|
|
1722
1751
|
*/
|
|
1723
1752
|
// eslint-disable-next-line complexity
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
1753
|
DOMPurify.sanitize = function (dirty) {
|
|
1727
1754
|
let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1728
|
-
let body;
|
|
1729
|
-
let importedNode;
|
|
1730
|
-
let currentNode;
|
|
1731
|
-
let returnNode;
|
|
1755
|
+
let body = null;
|
|
1756
|
+
let importedNode = null;
|
|
1757
|
+
let currentNode = null;
|
|
1758
|
+
let returnNode = null;
|
|
1732
1759
|
/* Make sure we have a string to sanitize.
|
|
1733
1760
|
DO NOT return early, as this will return the wrong type if
|
|
1734
1761
|
the user has requested a DOM object rather than a string */
|
|
1735
|
-
|
|
1736
1762
|
IS_EMPTY_INPUT = !dirty;
|
|
1737
|
-
|
|
1738
1763
|
if (IS_EMPTY_INPUT) {
|
|
1739
1764
|
dirty = '<!-->';
|
|
1740
1765
|
}
|
|
1741
|
-
/* Stringify, in case dirty is an object */
|
|
1742
|
-
|
|
1743
1766
|
|
|
1767
|
+
/* Stringify, in case dirty is an object */
|
|
1744
1768
|
if (typeof dirty !== 'string' && !_isNode(dirty)) {
|
|
1745
|
-
|
|
1746
|
-
if (typeof dirty.toString !== 'function') {
|
|
1747
|
-
throw typeErrorCreate('toString is not a function');
|
|
1748
|
-
} else {
|
|
1769
|
+
if (typeof dirty.toString === 'function') {
|
|
1749
1770
|
dirty = dirty.toString();
|
|
1750
|
-
|
|
1751
1771
|
if (typeof dirty !== 'string') {
|
|
1752
1772
|
throw typeErrorCreate('dirty is not a string, aborting');
|
|
1753
1773
|
}
|
|
1774
|
+
} else {
|
|
1775
|
+
throw typeErrorCreate('toString is not a function');
|
|
1754
1776
|
}
|
|
1755
1777
|
}
|
|
1756
|
-
/* Return dirty HTML if DOMPurify cannot run */
|
|
1757
|
-
|
|
1758
1778
|
|
|
1779
|
+
/* Return dirty HTML if DOMPurify cannot run */
|
|
1759
1780
|
if (!DOMPurify.isSupported) {
|
|
1760
1781
|
return dirty;
|
|
1761
1782
|
}
|
|
1762
|
-
/* Assign config vars */
|
|
1763
|
-
|
|
1764
1783
|
|
|
1784
|
+
/* Assign config vars */
|
|
1765
1785
|
if (!SET_CONFIG) {
|
|
1766
1786
|
_parseConfig(cfg);
|
|
1767
1787
|
}
|
|
1768
|
-
/* Clean up removed elements */
|
|
1769
|
-
|
|
1770
1788
|
|
|
1789
|
+
/* Clean up removed elements */
|
|
1771
1790
|
DOMPurify.removed = [];
|
|
1772
|
-
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1773
1791
|
|
|
1792
|
+
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1774
1793
|
if (typeof dirty === 'string') {
|
|
1775
1794
|
IN_PLACE = false;
|
|
1776
1795
|
}
|
|
1777
|
-
|
|
1778
1796
|
if (IN_PLACE) {
|
|
1779
1797
|
/* Do some early pre-sanitization to avoid unsafe root nodes */
|
|
1780
1798
|
if (dirty.nodeName) {
|
|
1781
1799
|
const tagName = transformCaseFunc(dirty.nodeName);
|
|
1782
|
-
|
|
1783
1800
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1784
1801
|
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
1785
1802
|
}
|
|
@@ -1789,8 +1806,7 @@ function createDOMPurify() {
|
|
|
1789
1806
|
elements being stripped by the parser */
|
|
1790
1807
|
body = _initDocument('<!---->');
|
|
1791
1808
|
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
1792
|
-
|
|
1793
|
-
if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
|
|
1809
|
+
if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
|
|
1794
1810
|
/* Node is already a body, use as is */
|
|
1795
1811
|
body = importedNode;
|
|
1796
1812
|
} else if (importedNode.nodeName === 'HTML') {
|
|
@@ -1801,62 +1817,77 @@ function createDOMPurify() {
|
|
|
1801
1817
|
}
|
|
1802
1818
|
} else {
|
|
1803
1819
|
/* Exit directly if we have nothing to do */
|
|
1804
|
-
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
1820
|
+
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
1821
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
1805
1822
|
dirty.indexOf('<') === -1) {
|
|
1806
1823
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
1807
1824
|
}
|
|
1808
|
-
/* Initialize the document to work on */
|
|
1809
|
-
|
|
1810
1825
|
|
|
1826
|
+
/* Initialize the document to work on */
|
|
1811
1827
|
body = _initDocument(dirty);
|
|
1812
|
-
/* Check we have a DOM node from the data */
|
|
1813
1828
|
|
|
1829
|
+
/* Check we have a DOM node from the data */
|
|
1814
1830
|
if (!body) {
|
|
1815
1831
|
return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
|
|
1816
1832
|
}
|
|
1817
1833
|
}
|
|
1818
|
-
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1819
|
-
|
|
1820
1834
|
|
|
1835
|
+
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1821
1836
|
if (body && FORCE_BODY) {
|
|
1822
1837
|
_forceRemove(body.firstChild);
|
|
1823
1838
|
}
|
|
1824
|
-
/* Get node iterator */
|
|
1825
1839
|
|
|
1840
|
+
/* Get node iterator */
|
|
1841
|
+
const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
|
|
1826
1842
|
|
|
1827
|
-
const nodeIterator = _createIterator(IN_PLACE ? dirty : body);
|
|
1828
1843
|
/* Now start iterating over the created document */
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
1844
|
while (currentNode = nodeIterator.nextNode()) {
|
|
1832
1845
|
/* Sanitize tags and elements */
|
|
1833
1846
|
if (_sanitizeElements(currentNode)) {
|
|
1834
1847
|
continue;
|
|
1835
1848
|
}
|
|
1836
|
-
|
|
1849
|
+
const parentNode = getParentNode(currentNode);
|
|
1850
|
+
|
|
1851
|
+
/* Set the nesting depth of an element */
|
|
1852
|
+
if (currentNode.nodeType === NODE_TYPE.element) {
|
|
1853
|
+
if (parentNode && parentNode.__depth) {
|
|
1854
|
+
/*
|
|
1855
|
+
We want the depth of the node in the original tree, which can
|
|
1856
|
+
change when it's removed from its parent.
|
|
1857
|
+
*/
|
|
1858
|
+
currentNode.__depth = (currentNode.__removalCount || 0) + parentNode.__depth + 1;
|
|
1859
|
+
} else {
|
|
1860
|
+
currentNode.__depth = 1;
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1837
1863
|
|
|
1864
|
+
/*
|
|
1865
|
+
* Remove an element if nested too deeply to avoid mXSS
|
|
1866
|
+
* or if the __depth might have been tampered with
|
|
1867
|
+
*/
|
|
1868
|
+
if (currentNode.__depth >= MAX_NESTING_DEPTH || currentNode.__depth < 0 || numberIsNaN(currentNode.__depth)) {
|
|
1869
|
+
_forceRemove(currentNode);
|
|
1870
|
+
}
|
|
1838
1871
|
|
|
1872
|
+
/* Shadow DOM detected, sanitize it */
|
|
1839
1873
|
if (currentNode.content instanceof DocumentFragment) {
|
|
1874
|
+
currentNode.content.__depth = currentNode.__depth;
|
|
1840
1875
|
_sanitizeShadowDOM(currentNode.content);
|
|
1841
1876
|
}
|
|
1842
|
-
/* Check attributes, sanitize if necessary */
|
|
1843
|
-
|
|
1844
1877
|
|
|
1878
|
+
/* Check attributes, sanitize if necessary */
|
|
1845
1879
|
_sanitizeAttributes(currentNode);
|
|
1846
1880
|
}
|
|
1847
|
-
/* If we sanitized `dirty` in-place, return it. */
|
|
1848
|
-
|
|
1849
1881
|
|
|
1882
|
+
/* If we sanitized `dirty` in-place, return it. */
|
|
1850
1883
|
if (IN_PLACE) {
|
|
1851
1884
|
return dirty;
|
|
1852
1885
|
}
|
|
1853
|
-
/* Return sanitized string or DOM */
|
|
1854
|
-
|
|
1855
1886
|
|
|
1887
|
+
/* Return sanitized string or DOM */
|
|
1856
1888
|
if (RETURN_DOM) {
|
|
1857
1889
|
if (RETURN_DOM_FRAGMENT) {
|
|
1858
1890
|
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
1859
|
-
|
|
1860
1891
|
while (body.firstChild) {
|
|
1861
1892
|
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
1862
1893
|
returnNode.appendChild(body.firstChild);
|
|
@@ -1864,8 +1895,7 @@ function createDOMPurify() {
|
|
|
1864
1895
|
} else {
|
|
1865
1896
|
returnNode = body;
|
|
1866
1897
|
}
|
|
1867
|
-
|
|
1868
|
-
if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
|
|
1898
|
+
if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
|
|
1869
1899
|
/*
|
|
1870
1900
|
AdoptNode() is not used because internal state is not reset
|
|
1871
1901
|
(e.g. the past names map of a HTMLFormElement), this is safe
|
|
@@ -1875,73 +1905,66 @@ function createDOMPurify() {
|
|
|
1875
1905
|
*/
|
|
1876
1906
|
returnNode = importNode.call(originalDocument, returnNode, true);
|
|
1877
1907
|
}
|
|
1878
|
-
|
|
1879
1908
|
return returnNode;
|
|
1880
1909
|
}
|
|
1881
|
-
|
|
1882
1910
|
let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
|
|
1883
|
-
/* Serialize doctype if allowed */
|
|
1884
1911
|
|
|
1912
|
+
/* Serialize doctype if allowed */
|
|
1885
1913
|
if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
|
|
1886
1914
|
serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
|
|
1887
1915
|
}
|
|
1888
|
-
/* Sanitize final string template-safe */
|
|
1889
|
-
|
|
1890
1916
|
|
|
1917
|
+
/* Sanitize final string template-safe */
|
|
1891
1918
|
if (SAFE_FOR_TEMPLATES) {
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1919
|
+
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
1920
|
+
serializedHTML = stringReplace(serializedHTML, expr, ' ');
|
|
1921
|
+
});
|
|
1895
1922
|
}
|
|
1896
|
-
|
|
1897
1923
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
|
|
1898
1924
|
};
|
|
1925
|
+
|
|
1899
1926
|
/**
|
|
1900
1927
|
* Public method to set the configuration once
|
|
1901
1928
|
* setConfig
|
|
1902
1929
|
*
|
|
1903
1930
|
* @param {Object} cfg configuration object
|
|
1904
1931
|
*/
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
DOMPurify.setConfig = function (cfg) {
|
|
1932
|
+
DOMPurify.setConfig = function () {
|
|
1933
|
+
let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
1908
1934
|
_parseConfig(cfg);
|
|
1909
|
-
|
|
1910
1935
|
SET_CONFIG = true;
|
|
1911
1936
|
};
|
|
1937
|
+
|
|
1912
1938
|
/**
|
|
1913
1939
|
* Public method to remove the configuration
|
|
1914
1940
|
* clearConfig
|
|
1915
1941
|
*
|
|
1916
1942
|
*/
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
1943
|
DOMPurify.clearConfig = function () {
|
|
1920
1944
|
CONFIG = null;
|
|
1921
1945
|
SET_CONFIG = false;
|
|
1922
1946
|
};
|
|
1947
|
+
|
|
1923
1948
|
/**
|
|
1924
1949
|
* Public method to check if an attribute value is valid.
|
|
1925
1950
|
* Uses last set config, if any. Otherwise, uses config defaults.
|
|
1926
1951
|
* isValidAttribute
|
|
1927
1952
|
*
|
|
1928
|
-
* @param {
|
|
1929
|
-
* @param {
|
|
1930
|
-
* @param {
|
|
1953
|
+
* @param {String} tag Tag name of containing element.
|
|
1954
|
+
* @param {String} attr Attribute name.
|
|
1955
|
+
* @param {String} value Attribute value.
|
|
1931
1956
|
* @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
|
|
1932
1957
|
*/
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
1958
|
DOMPurify.isValidAttribute = function (tag, attr, value) {
|
|
1936
1959
|
/* Initialize shared config vars if necessary. */
|
|
1937
1960
|
if (!CONFIG) {
|
|
1938
1961
|
_parseConfig({});
|
|
1939
1962
|
}
|
|
1940
|
-
|
|
1941
1963
|
const lcTag = transformCaseFunc(tag);
|
|
1942
1964
|
const lcName = transformCaseFunc(attr);
|
|
1943
1965
|
return _isValidAttribute(lcTag, lcName, value);
|
|
1944
1966
|
};
|
|
1967
|
+
|
|
1945
1968
|
/**
|
|
1946
1969
|
* AddHook
|
|
1947
1970
|
* Public method to add DOMPurify hooks
|
|
@@ -1949,16 +1972,14 @@ function createDOMPurify() {
|
|
|
1949
1972
|
* @param {String} entryPoint entry point for the hook to add
|
|
1950
1973
|
* @param {Function} hookFunction function to execute
|
|
1951
1974
|
*/
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
1975
|
DOMPurify.addHook = function (entryPoint, hookFunction) {
|
|
1955
1976
|
if (typeof hookFunction !== 'function') {
|
|
1956
1977
|
return;
|
|
1957
1978
|
}
|
|
1958
|
-
|
|
1959
1979
|
hooks[entryPoint] = hooks[entryPoint] || [];
|
|
1960
1980
|
arrayPush(hooks[entryPoint], hookFunction);
|
|
1961
1981
|
};
|
|
1982
|
+
|
|
1962
1983
|
/**
|
|
1963
1984
|
* RemoveHook
|
|
1964
1985
|
* Public method to remove a DOMPurify hook at a given entryPoint
|
|
@@ -1967,40 +1988,33 @@ function createDOMPurify() {
|
|
|
1967
1988
|
* @param {String} entryPoint entry point for the hook to remove
|
|
1968
1989
|
* @return {Function} removed(popped) hook
|
|
1969
1990
|
*/
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
1991
|
DOMPurify.removeHook = function (entryPoint) {
|
|
1973
1992
|
if (hooks[entryPoint]) {
|
|
1974
1993
|
return arrayPop(hooks[entryPoint]);
|
|
1975
1994
|
}
|
|
1976
1995
|
};
|
|
1996
|
+
|
|
1977
1997
|
/**
|
|
1978
1998
|
* RemoveHooks
|
|
1979
1999
|
* Public method to remove all DOMPurify hooks at a given entryPoint
|
|
1980
2000
|
*
|
|
1981
2001
|
* @param {String} entryPoint entry point for the hooks to remove
|
|
1982
2002
|
*/
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
2003
|
DOMPurify.removeHooks = function (entryPoint) {
|
|
1986
2004
|
if (hooks[entryPoint]) {
|
|
1987
2005
|
hooks[entryPoint] = [];
|
|
1988
2006
|
}
|
|
1989
2007
|
};
|
|
2008
|
+
|
|
1990
2009
|
/**
|
|
1991
2010
|
* RemoveAllHooks
|
|
1992
2011
|
* Public method to remove all DOMPurify hooks
|
|
1993
|
-
*
|
|
1994
2012
|
*/
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
2013
|
DOMPurify.removeAllHooks = function () {
|
|
1998
2014
|
hooks = {};
|
|
1999
2015
|
};
|
|
2000
|
-
|
|
2001
2016
|
return DOMPurify;
|
|
2002
2017
|
}
|
|
2003
|
-
|
|
2004
2018
|
var purify = createDOMPurify();
|
|
2005
2019
|
|
|
2006
2020
|
var getInitialTab = function getInitialTab(stripeAccount) {
|