From 04add5a094558d8b6ef5cbb5b989337ccc7535a2 Mon Sep 17 00:00:00 2001 From: Dobrin Dimchev Date: Wed, 9 Oct 2024 11:37:37 +0300 Subject: [PATCH] [INTERNAL] sap.m.Image: Update DOMPurify version Change-Id: Ic48f24af6f831f49386591be0ef2ddaa628d55b1 JIRA: BGSOFUIPIRIN-6675 --- THIRDPARTY.txt | 2 +- src/sap.m/src/sap/m/.library | 2 +- src/sap.m/src/sap/m/_thirdparty/purify.js | 1329 ++++++++++----------- 3 files changed, 634 insertions(+), 699 deletions(-) diff --git a/THIRDPARTY.txt b/THIRDPARTY.txt index b4db4f09e591..f5188cb33a6d 100644 --- a/THIRDPARTY.txt +++ b/THIRDPARTY.txt @@ -6,7 +6,7 @@ The full text of all referenced licenses is appended at the end of this file. Library: sap.m: -Component: purify.js, version: 2.3.8 +Component: purify.js, version: 3.1.7 Copyright: Mario Heiderich License: Apache-2.0 License Text: https://github.com/SAP/openui5/blob/master/LICENSES/Apache-2.0.txt diff --git a/src/sap.m/src/sap/m/.library b/src/sap.m/src/sap/m/.library index ded43fc9b464..1c7265ca2cc4 100644 --- a/src/sap.m/src/sap/m/.library +++ b/src/sap.m/src/sap/m/.library @@ -43,7 +43,7 @@ - + Mario Heiderich sap/m/_thirdparty/purify.js diff --git a/src/sap.m/src/sap/m/_thirdparty/purify.js b/src/sap.m/src/sap/m/_thirdparty/purify.js index cfd79c31e85b..c95605135894 100644 --- a/src/sap.m/src/sap/m/_thirdparty/purify.js +++ b/src/sap.m/src/sap/m/_thirdparty/purify.js @@ -1,301 +1,286 @@ -/*! @license DOMPurify 2.3.8 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.8/LICENSE */ +/*! @license DOMPurify 3.1.7 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.1.7/LICENSE */ sap.ui.define([], function () { 'use strict'; - function _typeof(obj) { - "@babel/helpers - typeof"; - - return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }, _typeof(obj); - } - - function _setPrototypeOf(o, p) { - _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { - o.__proto__ = p; - return o; - }; - - return _setPrototypeOf(o, p); - } - - function _isNativeReflectConstruct() { - if (typeof Reflect === "undefined" || !Reflect.construct) return false; - if (Reflect.construct.sham) return false; - if (typeof Proxy === "function") return true; - - try { - Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); - return true; - } catch (e) { - return false; - } - } - - function _construct(Parent, args, Class) { - if (_isNativeReflectConstruct()) { - _construct = Reflect.construct; - } else { - _construct = function _construct(Parent, args, Class) { - var a = [null]; - a.push.apply(a, args); - var Constructor = Function.bind.apply(Parent, a); - var instance = new Constructor(); - if (Class) _setPrototypeOf(instance, Class.prototype); - return instance; - }; - } - - return _construct.apply(null, arguments); - } - - function _toConsumableArray(arr) { - return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); - } - - function _arrayWithoutHoles(arr) { - if (Array.isArray(arr)) return _arrayLikeToArray(arr); - } - - function _iterableToArray(iter) { - if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); - } - - function _unsupportedIterableToArray(o, minLen) { - if (!o) return; - if (typeof o === "string") return _arrayLikeToArray(o, minLen); - var n = Object.prototype.toString.call(o).slice(8, -1); - if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(o); - if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); - } - - function _arrayLikeToArray(arr, len) { - if (len == null || len > arr.length) len = arr.length; - - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - - return arr2; - } - - function _nonIterableSpread() { - throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); - } - - var hasOwnProperty = Object.hasOwnProperty, - setPrototypeOf = Object.setPrototypeOf, - isFrozen = Object.isFrozen, - getPrototypeOf = Object.getPrototypeOf, - getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; - var freeze = Object.freeze, - seal = Object.seal, - create = Object.create; // eslint-disable-line import/no-mutable-exports - - var _ref = typeof Reflect !== 'undefined' && Reflect, - apply = _ref.apply, - construct = _ref.construct; - - if (!apply) { - apply = function apply(fun, thisValue, args) { - return fun.apply(thisValue, args); - }; - } - + const { + entries, + setPrototypeOf, + isFrozen, + getPrototypeOf, + getOwnPropertyDescriptor + } = Object; + let { + freeze, + seal, + create + } = Object; // eslint-disable-line import/no-mutable-exports + let { + apply, + construct + } = typeof Reflect !== 'undefined' && Reflect; if (!freeze) { freeze = function freeze(x) { return x; }; } - if (!seal) { seal = function seal(x) { return x; }; } - + if (!apply) { + apply = function apply(fun, thisValue, args) { + return fun.apply(thisValue, args); + }; + } if (!construct) { construct = function construct(Func, args) { - return _construct(Func, _toConsumableArray(args)); + return new Func(...args); }; } + const arrayForEach = unapply(Array.prototype.forEach); + const arrayPop = unapply(Array.prototype.pop); + const arrayPush = unapply(Array.prototype.push); + const stringToLowerCase = unapply(String.prototype.toLowerCase); + const stringToString = unapply(String.prototype.toString); + const stringMatch = unapply(String.prototype.match); + const stringReplace = unapply(String.prototype.replace); + const stringIndexOf = unapply(String.prototype.indexOf); + const stringTrim = unapply(String.prototype.trim); + const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty); + const regExpTest = unapply(RegExp.prototype.test); + const typeErrorCreate = unconstruct(TypeError); - var arrayForEach = unapply(Array.prototype.forEach); - var arrayPop = unapply(Array.prototype.pop); - var arrayPush = unapply(Array.prototype.push); - var stringToLowerCase = unapply(String.prototype.toLowerCase); - var stringMatch = unapply(String.prototype.match); - var stringReplace = unapply(String.prototype.replace); - var stringIndexOf = unapply(String.prototype.indexOf); - var stringTrim = unapply(String.prototype.trim); - var regExpTest = unapply(RegExp.prototype.test); - var typeErrorCreate = unconstruct(TypeError); + /** + * Creates a new function that calls the given function with a specified thisArg and arguments. + * + * @param {Function} func - The function to be wrapped and called. + * @returns {Function} A new function that calls the given function with a specified thisArg and arguments. + */ function unapply(func) { return function (thisArg) { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } - return apply(func, thisArg, args); }; } + + /** + * Creates a new function that constructs an instance of the given constructor function with the provided arguments. + * + * @param {Function} func - The constructor function to be wrapped and called. + * @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments. + */ function unconstruct(func) { return function () { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } - return construct(func, args); }; } - /* Add properties to a lookup table */ - - function addToSet(set, array, transformCaseFunc) { - transformCaseFunc = transformCaseFunc ? transformCaseFunc : stringToLowerCase; + /** + * Add properties to a lookup table + * + * @param {Object} set - The set to which elements will be added. + * @param {Array} array - The array containing elements to be added to the set. + * @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set. + * @returns {Object} The modified set with added elements. + */ + function addToSet(set, array) { + let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase; if (setPrototypeOf) { // Make 'in' and truthy checks like Boolean(set.constructor) // independent of any properties defined on Object.prototype. // Prevent prototype setters from intercepting set as a this value. setPrototypeOf(set, null); } - - var l = array.length; - + let l = array.length; while (l--) { - var element = array[l]; - + let element = array[l]; if (typeof element === 'string') { - var lcElement = transformCaseFunc(element); - + const lcElement = transformCaseFunc(element); if (lcElement !== element) { // Config presets (e.g. tags.js, attrs.js) are immutable. if (!isFrozen(array)) { array[l] = lcElement; } - element = lcElement; } } - set[element] = true; } - return set; } - /* Shallow clone an object */ - - function clone(object) { - var newObject = create(null); - var property; - for (property in object) { - if (apply(hasOwnProperty, object, [property])) { - newObject[property] = object[property]; + /** + * Clean up an array to harden against CSPP + * + * @param {Array} array - The array to be cleaned. + * @returns {Array} The cleaned version of the array + */ + function cleanArray(array) { + for (let index = 0; index < array.length; index++) { + const isPropertyExist = objectHasOwnProperty(array, index); + if (!isPropertyExist) { + array[index] = null; } } + return array; + } + /** + * Shallow clone an object + * + * @param {Object} object - The object to be cloned. + * @returns {Object} A new object that copies the original. + */ + function clone(object) { + const newObject = create(null); + for (const [property, value] of entries(object)) { + const isPropertyExist = objectHasOwnProperty(object, property); + if (isPropertyExist) { + if (Array.isArray(value)) { + newObject[property] = cleanArray(value); + } else if (value && typeof value === 'object' && value.constructor === Object) { + newObject[property] = clone(value); + } else { + newObject[property] = value; + } + } + } return newObject; } - /* IE10 doesn't support __lookupGetter__ so lets' - * simulate it. It also automatically checks - * if the prop is function or getter and behaves - * accordingly. */ + /** + * This method automatically checks if the prop is function or getter and behaves accordingly. + * + * @param {Object} object - The object to look up the getter function in its prototype chain. + * @param {String} prop - The property name for which to find the getter function. + * @returns {Function} The getter function found in the prototype chain or a fallback function. + */ function lookupGetter(object, prop) { while (object !== null) { - var desc = getOwnPropertyDescriptor(object, prop); - + const desc = getOwnPropertyDescriptor(object, prop); if (desc) { if (desc.get) { return unapply(desc.get); } - if (typeof desc.value === 'function') { return unapply(desc.value); } } - object = getPrototypeOf(object); } - - function fallbackValue(element) { - console.warn('fallback value for', element); + function fallbackValue() { return null; } - return fallbackValue; } - var html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); // SVG + 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']); - var 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']); - var 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']); // List of SVG elements that are disallowed by default. + // SVG + 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']); + const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']); + + // List of SVG elements that are disallowed by default. // We still need to know them so that we can do namespace // checks properly in case one wants to add them to // allow-list. + 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']); + 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']); - var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', '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']); - var 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']); // Similarly to SVG, we want to know all MathML elements, + // Similarly to SVG, we want to know all MathML elements, // even those that we disallow by default. - - var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']); - var text = freeze(['#text']); - - var 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']); - var 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']); - var 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']); - var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']); - - var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode - - var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm); - var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape - - var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape - - var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape + const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']); + const text = freeze(['#text']); + + const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']); + const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']); + 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']); + const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']); + + // eslint-disable-next-line unicorn/better-regex + const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode + const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm); + const TMPLIT_EXPR = seal(/\${[\w\W]*}/gm); + const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape + const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape + 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 ); - var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); - var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex + const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); + const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex ); - var DOCTYPE_NAME = seal(/^html$/i); - - var getGlobal = function getGlobal() { + const DOCTYPE_NAME = seal(/^html$/i); + const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i); + + var EXPRESSIONS = /*#__PURE__*/Object.freeze({ + __proto__: null, + MUSTACHE_EXPR: MUSTACHE_EXPR, + ERB_EXPR: ERB_EXPR, + TMPLIT_EXPR: TMPLIT_EXPR, + DATA_ATTR: DATA_ATTR, + ARIA_ATTR: ARIA_ATTR, + IS_ALLOWED_URI: IS_ALLOWED_URI, + IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA, + ATTR_WHITESPACE: ATTR_WHITESPACE, + DOCTYPE_NAME: DOCTYPE_NAME, + CUSTOM_ELEMENT: CUSTOM_ELEMENT + }); + + // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType + const NODE_TYPE = { + element: 1, + attribute: 2, + text: 3, + cdataSection: 4, + entityReference: 5, + // Deprecated + entityNode: 6, + // Deprecated + progressingInstruction: 7, + comment: 8, + document: 9, + documentType: 10, + documentFragment: 11, + notation: 12 // Deprecated + }; + const getGlobal = function getGlobal() { return typeof window === 'undefined' ? null : window; }; + /** * Creates a no-op policy for internal use only. * Don't export this function outside this module! - * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory. - * @param {Document} document The document object (to determine policy name suffix) - * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types - * are not supported). + * @param {TrustedTypePolicyFactory} trustedTypes The policy factory. + * @param {HTMLScriptElement} purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix). + * @return {TrustedTypePolicy} The policy created (or null, if Trusted Types + * are not supported or creating the policy failed). */ - - - var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) { - if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') { + const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) { + if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') { return null; - } // Allow the callers to control the unique policy name + } + + // Allow the callers to control the unique policy name // by adding a data-tt-policy-suffix to the script element with the DOMPurify. // Policy creation with duplicate names throws in Trusted Types. - - - var suffix = null; - var ATTR_NAME = 'data-tt-policy-suffix'; - - if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) { - suffix = document.currentScript.getAttribute(ATTR_NAME); + let suffix = null; + const ATTR_NAME = 'data-tt-policy-suffix'; + if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) { + suffix = purifyHostElement.getAttribute(ATTR_NAME); } - - var policyName = 'dompurify' + (suffix ? '#' + suffix : ''); - + const policyName = 'dompurify' + (suffix ? '#' + suffix : ''); try { return trustedTypes.createPolicy(policyName, { - createHTML: function createHTML(html) { + createHTML(html) { return html; + }, + createScriptURL(scriptUrl) { + return scriptUrl; } }); } catch (_) { @@ -306,114 +291,113 @@ sap.ui.define([], function () { 'use strict'; return null; } }; - function createDOMPurify() { - var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal(); + let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal(); + const DOMPurify = root => createDOMPurify(root); - var DOMPurify = function DOMPurify(root) { - return createDOMPurify(root); - }; /** * Version label, exposed for easier checks * if DOMPurify is up to date or not */ + DOMPurify.version = '3.1.7'; - - DOMPurify.version = '2.3.8'; /** * Array of elements that DOMPurify removed during sanitation. * Empty if nothing was removed. */ - DOMPurify.removed = []; - - if (!window || !window.document || window.document.nodeType !== 9) { + if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) { // Not running in a browser, provide a factory function // so that you can pass your own Window DOMPurify.isSupported = false; return DOMPurify; } - - var originalDocument = window.document; - var document = window.document; - var DocumentFragment = window.DocumentFragment, - HTMLTemplateElement = window.HTMLTemplateElement, - Node = window.Node, - Element = window.Element, - NodeFilter = window.NodeFilter, - _window$NamedNodeMap = window.NamedNodeMap, - NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, - HTMLFormElement = window.HTMLFormElement, - DOMParser = window.DOMParser, - trustedTypes = window.trustedTypes; - var ElementPrototype = Element.prototype; - var cloneNode = lookupGetter(ElementPrototype, 'cloneNode'); - var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling'); - var getChildNodes = lookupGetter(ElementPrototype, 'childNodes'); - var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a + let { + document + } = window; + const originalDocument = document; + const currentScript = originalDocument.currentScript; + const { + DocumentFragment, + HTMLTemplateElement, + Node, + Element, + NodeFilter, + NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap, + HTMLFormElement, + DOMParser, + trustedTypes + } = window; + const ElementPrototype = Element.prototype; + const cloneNode = lookupGetter(ElementPrototype, 'cloneNode'); + const remove = lookupGetter(ElementPrototype, 'remove'); + const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling'); + const getChildNodes = lookupGetter(ElementPrototype, 'childNodes'); + const getParentNode = lookupGetter(ElementPrototype, 'parentNode'); + + // As per issue #47, the web-components registry is inherited by a // new document created via createHTMLDocument. As per the spec // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries) // a new empty registry is used when creating a template contents owner // document, so we use that as our parent document to ensure nothing // is inherited. - if (typeof HTMLTemplateElement === 'function') { - var template = document.createElement('template'); - + const template = document.createElement('template'); if (template.content && template.content.ownerDocument) { document = template.content.ownerDocument; } } + let trustedTypesPolicy; + let emptyHTML = ''; + const { + implementation, + createNodeIterator, + createDocumentFragment, + getElementsByTagName + } = document; + const { + importNode + } = originalDocument; + let hooks = {}; - var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument); - - var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : ''; - var _document = document, - implementation = _document.implementation, - createNodeIterator = _document.createNodeIterator, - createDocumentFragment = _document.createDocumentFragment, - getElementsByTagName = _document.getElementsByTagName; - var importNode = originalDocument.importNode; - var documentMode = {}; - - try { - documentMode = clone(document).documentMode ? document.documentMode : {}; - } catch (_) {} - - var hooks = {}; /** * Expose whether this browser supports running the full DOMPurify. */ + DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined; + const { + MUSTACHE_EXPR, + ERB_EXPR, + TMPLIT_EXPR, + DATA_ATTR, + ARIA_ATTR, + IS_SCRIPT_OR_DATA, + ATTR_WHITESPACE, + CUSTOM_ELEMENT + } = EXPRESSIONS; + let { + IS_ALLOWED_URI: IS_ALLOWED_URI$1 + } = EXPRESSIONS; - DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9; - var MUSTACHE_EXPR$1 = MUSTACHE_EXPR, - ERB_EXPR$1 = ERB_EXPR, - DATA_ATTR$1 = DATA_ATTR, - ARIA_ATTR$1 = ARIA_ATTR, - IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA, - ATTR_WHITESPACE$1 = ATTR_WHITESPACE; - var IS_ALLOWED_URI$1 = IS_ALLOWED_URI; /** * We consider the elements and attributes below to be safe. Ideally * don't add any new ones but feel free to remove unwanted ones. */ /* allowed element names */ + let ALLOWED_TAGS = null; + const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]); - var ALLOWED_TAGS = null; - var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text))); /* Allowed attribute names */ + let ALLOWED_ATTR = null; + const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]); - var ALLOWED_ATTR = null; - var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml))); /* * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements. * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements) * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list) * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`. */ - - var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, { + let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, { tagNameCheck: { writable: true, configurable: false, @@ -433,359 +417,381 @@ sap.ui.define([], function () { 'use strict'; value: false } })); + /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */ + let FORBID_TAGS = null; - var FORBID_TAGS = null; /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */ + let FORBID_ATTR = null; - var FORBID_ATTR = null; /* Decide if ARIA attributes are okay */ + let ALLOW_ARIA_ATTR = true; - var ALLOW_ARIA_ATTR = true; /* Decide if custom data attributes are okay */ + let ALLOW_DATA_ATTR = true; - var ALLOW_DATA_ATTR = true; /* Decide if unknown protocols are okay */ + let ALLOW_UNKNOWN_PROTOCOLS = false; + + /* Decide if self-closing tags in attributes are allowed. + * Usually removed due to a mXSS issue in jQuery 3.0 */ + let ALLOW_SELF_CLOSE_IN_ATTR = true; - var ALLOW_UNKNOWN_PROTOCOLS = false; /* Output should be safe for common template engines. * This means, DOMPurify removes data attributes, mustaches and ERB */ + let SAFE_FOR_TEMPLATES = false; + + /* Output should be safe even for XML used within HTML and alike. + * This means, DOMPurify removes comments when containing risky content. + */ + let SAFE_FOR_XML = true; - var SAFE_FOR_TEMPLATES = false; /* Decide if document with ... should be returned */ + let WHOLE_DOCUMENT = false; - var WHOLE_DOCUMENT = false; /* Track whether config is already set on this instance of DOMPurify. */ + let SET_CONFIG = false; - var SET_CONFIG = false; /* Decide if all elements (e.g. style, script) must be children of * document.body. By default, browsers might move them to document.head */ + let FORCE_BODY = false; - var FORCE_BODY = false; /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html * string (or a TrustedHTML object if Trusted Types are supported). * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead */ + let RETURN_DOM = false; - var RETURN_DOM = false; /* Decide if a DOM `DocumentFragment` should be returned, instead of a html * string (or a TrustedHTML object if Trusted Types are supported) */ + let RETURN_DOM_FRAGMENT = false; - var RETURN_DOM_FRAGMENT = false; /* Try to return a Trusted Type object instead of a string, return a string in * case Trusted Types are not supported */ + let RETURN_TRUSTED_TYPE = false; - var RETURN_TRUSTED_TYPE = false; - /* Output should be free from DOM clobbering attacks? */ + /* Output should be free from DOM clobbering attacks? + * This sanitizes markups named with colliding, clobberable built-in DOM APIs. + */ + let SANITIZE_DOM = true; + + /* Achieve full DOM Clobbering protection by isolating the namespace of named + * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules. + * + * HTML/DOM spec rules that enable DOM Clobbering: + * - Named Access on Window (�7.3.3) + * - DOM Tree Accessors (�3.1.5) + * - Form Element Parent-Child Relations (�4.10.3) + * - Iframe srcdoc / Nested WindowProxies (�4.8.5) + * - HTMLCollection (�4.2.10.2) + * + * Namespace isolation is implemented by prefixing `id` and `name` attributes + * with a constant string, i.e., `user-content-` + */ + let SANITIZE_NAMED_PROPS = false; + const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-'; - var SANITIZE_DOM = true; /* Keep element content when removing element? */ + let KEEP_CONTENT = true; - var KEEP_CONTENT = true; /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead * of importing it into a new Document and returning a sanitized copy */ + let IN_PLACE = false; - var IN_PLACE = false; /* Allow usage of profiles like html, svg and mathMl */ + let USE_PROFILES = {}; - var USE_PROFILES = {}; /* Tags to ignore content of when KEEP_CONTENT is true */ + let FORBID_CONTENTS = null; + 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']); - var FORBID_CONTENTS = null; - var 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']); /* Tags that are safe for data: URIs */ + let DATA_URI_TAGS = null; + const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']); - var DATA_URI_TAGS = null; - var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']); /* Attributes safe for values like "javascript:" */ - - var URI_SAFE_ATTRIBUTES = null; - var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']); - var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML'; - var SVG_NAMESPACE = 'http://www.w3.org/2000/svg'; - var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; + let URI_SAFE_ATTRIBUTES = null; + const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']); + const MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML'; + const SVG_NAMESPACE = 'http://www.w3.org/2000/svg'; + const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; /* Document namespace */ + let NAMESPACE = HTML_NAMESPACE; + let IS_EMPTY_INPUT = false; + + /* Allowed XHTML+XML namespaces */ + let ALLOWED_NAMESPACES = null; + const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString); - var NAMESPACE = HTML_NAMESPACE; - var IS_EMPTY_INPUT = false; /* Parsing of strict XHTML documents */ + let PARSER_MEDIA_TYPE = null; + const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html']; + const DEFAULT_PARSER_MEDIA_TYPE = 'text/html'; + let transformCaseFunc = null; - var PARSER_MEDIA_TYPE; - var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html']; - var DEFAULT_PARSER_MEDIA_TYPE = 'text/html'; - var transformCaseFunc; /* Keep a reference to config to pass to hooks */ + let CONFIG = null; - var CONFIG = null; /* Ideally, do not touch anything below this line */ - /* ______________________________________________ */ - var formElement = document.createElement('form'); - - var isRegexOrFunction = function isRegexOrFunction(testValue) { + const formElement = document.createElement('form'); + const isRegexOrFunction = function isRegexOrFunction(testValue) { return testValue instanceof RegExp || testValue instanceof Function; }; + /** * _parseConfig * * @param {Object} cfg optional config literal */ // eslint-disable-next-line complexity - - - var _parseConfig = function _parseConfig(cfg) { + const _parseConfig = function _parseConfig() { + let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (CONFIG && CONFIG === cfg) { return; } - /* Shield configuration object from tampering */ - - if (!cfg || _typeof(cfg) !== 'object') { + /* Shield configuration object from tampering */ + if (!cfg || typeof cfg !== 'object') { cfg = {}; } - /* Shield configuration object from prototype pollution */ - + /* Shield configuration object from prototype pollution */ cfg = clone(cfg); - PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes - SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE; // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is. + PARSER_MEDIA_TYPE = + // eslint-disable-next-line unicorn/prefer-includes + SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE; - transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) { - return x; - } : stringToLowerCase; - /* Set configuration parameters */ + // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is. + transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase; - ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS; - ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR; - URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent - cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent + /* Set configuration parameters */ + ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS; + ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR; + ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES; + URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), + // eslint-disable-line indent + cfg.ADD_URI_SAFE_ATTR, + // eslint-disable-line indent transformCaseFunc // eslint-disable-line indent ) // eslint-disable-line indent : DEFAULT_URI_SAFE_ATTRIBUTES; - DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent - cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent + DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), + // eslint-disable-line indent + cfg.ADD_DATA_URI_TAGS, + // eslint-disable-line indent transformCaseFunc // eslint-disable-line indent ) // eslint-disable-line indent : DEFAULT_DATA_URI_TAGS; - FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS; - FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {}; - FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {}; - USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false; + FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS; + FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {}; + FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {}; + USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false; ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true - ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true - ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false - + ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false - + SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false - RETURN_DOM = cfg.RETURN_DOM || false; // Default false - RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false - RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false - FORCE_BODY = cfg.FORCE_BODY || false; // Default false - SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true - + SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true - IN_PLACE = cfg.IN_PLACE || false; // Default false - - IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1; + IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI; NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE; - + CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {}; if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) { CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck; } - if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) { CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck; } - if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') { CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements; } - if (SAFE_FOR_TEMPLATES) { ALLOW_DATA_ATTR = false; } - if (RETURN_DOM_FRAGMENT) { RETURN_DOM = true; } - /* Parse profile info */ - + /* Parse profile info */ if (USE_PROFILES) { - ALLOWED_TAGS = addToSet({}, _toConsumableArray(text)); + ALLOWED_TAGS = addToSet({}, text); ALLOWED_ATTR = []; - if (USE_PROFILES.html === true) { addToSet(ALLOWED_TAGS, html$1); addToSet(ALLOWED_ATTR, html); } - if (USE_PROFILES.svg === true) { addToSet(ALLOWED_TAGS, svg$1); addToSet(ALLOWED_ATTR, svg); addToSet(ALLOWED_ATTR, xml); } - if (USE_PROFILES.svgFilters === true) { addToSet(ALLOWED_TAGS, svgFilters); addToSet(ALLOWED_ATTR, svg); addToSet(ALLOWED_ATTR, xml); } - if (USE_PROFILES.mathMl === true) { addToSet(ALLOWED_TAGS, mathMl$1); addToSet(ALLOWED_ATTR, mathMl); addToSet(ALLOWED_ATTR, xml); } } - /* Merge configuration parameters */ - + /* Merge configuration parameters */ if (cfg.ADD_TAGS) { if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) { ALLOWED_TAGS = clone(ALLOWED_TAGS); } - addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc); } - if (cfg.ADD_ATTR) { if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) { ALLOWED_ATTR = clone(ALLOWED_ATTR); } - addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc); } - if (cfg.ADD_URI_SAFE_ATTR) { addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc); } - if (cfg.FORBID_CONTENTS) { if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) { FORBID_CONTENTS = clone(FORBID_CONTENTS); } - addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc); } - /* Add #text in case KEEP_CONTENT is set to true */ - + /* Add #text in case KEEP_CONTENT is set to true */ if (KEEP_CONTENT) { ALLOWED_TAGS['#text'] = true; } - /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */ - + /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */ if (WHOLE_DOCUMENT) { addToSet(ALLOWED_TAGS, ['html', 'head', 'body']); } - /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */ - + /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */ if (ALLOWED_TAGS.table) { addToSet(ALLOWED_TAGS, ['tbody']); delete FORBID_TAGS.tbody; - } // Prevent further manipulation of configuration. - // Not available in IE8, Safari 5, etc. + } + if (cfg.TRUSTED_TYPES_POLICY) { + if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') { + throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.'); + } + if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') { + throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.'); + } + + // Overwrite existing TrustedTypes policy. + trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; + + // Sign local variables required by `sanitize`. + emptyHTML = trustedTypesPolicy.createHTML(''); + } else { + // Uninitialized policy, attempt to initialize the internal dompurify policy. + if (trustedTypesPolicy === undefined) { + trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript); + } + // If creating the internal policy succeeded sign internal variables. + if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') { + emptyHTML = trustedTypesPolicy.createHTML(''); + } + } + // Prevent further manipulation of configuration. + // Not available in IE8, Safari 5, etc. if (freeze) { freeze(cfg); } - CONFIG = cfg; }; + const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); + const HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']); - var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); - var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML + // Certain elements are allowed in both SVG and HTML // namespace. We need to specify them explicitly // so that they don't get erroneously deleted from // HTML namespace. + const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']); - var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']); /* Keep track of all possible SVG and MathML tags * so that we can perform the namespace checks * correctly. */ + const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]); + const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]); - var ALL_SVG_TAGS = addToSet({}, svg$1); - addToSet(ALL_SVG_TAGS, svgFilters); - addToSet(ALL_SVG_TAGS, svgDisallowed); - var ALL_MATHML_TAGS = addToSet({}, mathMl$1); - addToSet(ALL_MATHML_TAGS, mathMlDisallowed); /** - * - * * @param {Element} element a DOM element whose namespace is being checked * @returns {boolean} Return false if the element has a * namespace that a spec-compliant parser would never * return. Return true otherwise. */ + const _checkValidNamespace = function _checkValidNamespace(element) { + let parent = getParentNode(element); - var _checkValidNamespace = function _checkValidNamespace(element) { - var parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode + // In JSDOM, if we're inside shadow DOM, then parentNode // can be null. We just simulate parent in this case. - if (!parent || !parent.tagName) { parent = { - namespaceURI: HTML_NAMESPACE, + namespaceURI: NAMESPACE, tagName: 'template' }; } - - var tagName = stringToLowerCase(element.tagName); - var parentTagName = stringToLowerCase(parent.tagName); - + const tagName = stringToLowerCase(element.tagName); + const parentTagName = stringToLowerCase(parent.tagName); + if (!ALLOWED_NAMESPACES[element.namespaceURI]) { + return false; + } if (element.namespaceURI === SVG_NAMESPACE) { // The only way to switch from HTML namespace to SVG // is via . If it happens via any other tag, then // it should be killed. if (parent.namespaceURI === HTML_NAMESPACE) { return tagName === 'svg'; - } // The only way to switch from MathML to SVG is via + } + + // The only way to switch from MathML to SVG is via` // svg if parent is either or MathML // text integration points. - - if (parent.namespaceURI === MATHML_NAMESPACE) { return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]); - } // We only allow elements that are defined in SVG - // spec. All others are disallowed in SVG namespace. - + } + // We only allow elements that are defined in SVG + // spec. All others are disallowed in SVG namespace. return Boolean(ALL_SVG_TAGS[tagName]); } - if (element.namespaceURI === MATHML_NAMESPACE) { // The only way to switch from HTML namespace to MathML // is via . If it happens via any other tag, then // it should be killed. if (parent.namespaceURI === HTML_NAMESPACE) { return tagName === 'math'; - } // The only way to switch from SVG to MathML is via - // and HTML integration points - + } + // The only way to switch from SVG to MathML is via + // and HTML integration points if (parent.namespaceURI === SVG_NAMESPACE) { return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName]; - } // We only allow elements that are defined in MathML - // spec. All others are disallowed in MathML namespace. - + } + // We only allow elements that are defined in MathML + // spec. All others are disallowed in MathML namespace. return Boolean(ALL_MATHML_TAGS[tagName]); } - if (element.namespaceURI === HTML_NAMESPACE) { // The only way to switch from SVG to HTML is via // HTML integration points, and from MathML to HTML @@ -793,53 +799,51 @@ sap.ui.define([], function () { 'use strict'; if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) { return false; } - if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) { return false; - } // We disallow tags that are specific for MathML - // or SVG and should never appear in HTML namespace - + } + // We disallow tags that are specific for MathML + // or SVG and should never appear in HTML namespace return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]); - } // The code should never reach this place (this means - // that the element somehow got namespace that is not - // HTML, SVG or MathML). Return false just in case. + } + // For XHTML and XML documents that support custom namespaces + if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) { + return true; + } + // The code should never reach this place (this means + // that the element somehow got namespace that is not + // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES). + // Return false just in case. return false; }; + /** * _forceRemove * * @param {Node} node a DOM node */ - - - var _forceRemove = function _forceRemove(node) { + const _forceRemove = function _forceRemove(node) { arrayPush(DOMPurify.removed, { element: node }); - try { // eslint-disable-next-line unicorn/prefer-dom-node-remove - node.parentNode.removeChild(node); + getParentNode(node).removeChild(node); } catch (_) { - try { - node.outerHTML = emptyHTML; - } catch (_) { - node.remove(); - } + remove(node); } }; + /** * _removeAttribute * * @param {String} name an Attribute name * @param {Node} node a DOM node */ - - - var _removeAttribute = function _removeAttribute(name, node) { + const _removeAttribute = function _removeAttribute(name, node) { try { arrayPush(DOMPurify.removed, { attribute: node.getAttributeNode(name), @@ -851,9 +855,9 @@ sap.ui.define([], function () { 'use strict'; from: node }); } + node.removeAttribute(name); - node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes - + // We void attribute values for unremovable "is"" attributes if (name === 'is' && !ALLOWED_ATTR[name]) { if (RETURN_DOM || RETURN_DOM_FRAGMENT) { try { @@ -866,103 +870,92 @@ sap.ui.define([], function () { 'use strict'; } } }; + /** * _initDocument * * @param {String} dirty a string of dirty markup * @return {Document} a DOM, filled with the dirty markup */ - - - var _initDocument = function _initDocument(dirty) { + const _initDocument = function _initDocument(dirty) { /* Create a HTML document */ - var doc; - var leadingWhitespace; - + let doc = null; + let leadingWhitespace = null; if (FORCE_BODY) { dirty = '' + dirty; } else { /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */ - var matches = stringMatch(dirty, /^[\r\n\t ]+/); + const matches = stringMatch(dirty, /^[\r\n\t ]+/); leadingWhitespace = matches && matches[0]; } - - if (PARSER_MEDIA_TYPE === 'application/xhtml+xml') { + if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) { // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict) dirty = '' + dirty + ''; } - - var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; + const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; /* * Use the DOMParser API by default, fallback later if needs be * DOMParser not work for svg when has multiple root element. */ - if (NAMESPACE === HTML_NAMESPACE) { try { doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE); } catch (_) {} } - /* Use createHTMLDocument in case DOMParser is not available */ - + /* Use createHTMLDocument in case DOMParser is not available */ if (!doc || !doc.documentElement) { doc = implementation.createDocument(NAMESPACE, 'template', null); - try { - doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload; - } catch (_) {// Syntax error if dirtyPayload is invalid xml + doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload; + } catch (_) { + // Syntax error if dirtyPayload is invalid xml } } - - var body = doc.body || doc.documentElement; - + const body = doc.body || doc.documentElement; if (dirty && leadingWhitespace) { body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null); } - /* Work on whole document or just its body */ - + /* Work on whole document or just its body */ if (NAMESPACE === HTML_NAMESPACE) { return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0]; } - return WHOLE_DOCUMENT ? doc.documentElement : body; }; + /** - * _createIterator + * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document. * - * @param {Document} root document/fragment to create iterator for - * @return {Iterator} iterator instance + * @param {Node} root The root element or node to start traversing on. + * @return {NodeIterator} The created NodeIterator */ - - - var _createIterator = function _createIterator(root) { - return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise - NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false); + const _createNodeIterator = function _createNodeIterator(root) { + return createNodeIterator.call(root.ownerDocument || root, root, + // eslint-disable-next-line no-bitwise + NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null); }; + /** * _isClobbered * * @param {Node} elm element to check for clobbering attacks * @return {Boolean} true if clobbered, false if safe */ - - - var _isClobbered = function _isClobbered(elm) { - return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function'); + const _isClobbered = function _isClobbered(elm) { + return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); }; + /** - * _isNode + * Checks whether the given object is a DOM node. * - * @param {Node} obj object to check whether it's a DOM node + * @param {Node} object object to check whether it's a DOM node * @return {Boolean} true is object is a DOM node */ - - - var _isNode = function _isNode(object) { - return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'; + const _isNode = function _isNode(object) { + return typeof Node === 'function' && object instanceof Node; }; + /** * _executeHook * Execute user configurable hooks @@ -971,17 +964,15 @@ sap.ui.define([], function () { 'use strict'; * @param {Node} currentNode node to work on with the hook * @param {Object} data additional hook parameters */ - - - var _executeHook = function _executeHook(entryPoint, currentNode, data) { + const _executeHook = function _executeHook(entryPoint, currentNode, data) { if (!hooks[entryPoint]) { return; } - - arrayForEach(hooks[entryPoint], function (hook) { + arrayForEach(hooks[entryPoint], hook => { hook.call(DOMPurify, currentNode, data, CONFIG); }); }; + /** * _sanitizeElements * @@ -992,107 +983,93 @@ sap.ui.define([], function () { 'use strict'; * @param {Node} currentNode to check for permission to exist * @return {Boolean} true if node was killed, false if left alive */ + const _sanitizeElements = function _sanitizeElements(currentNode) { + let content = null; - - var _sanitizeElements = function _sanitizeElements(currentNode) { - var content; /* Execute a hook if present */ - _executeHook('beforeSanitizeElements', currentNode, null); - /* Check if element is clobbered or can clobber */ - + /* Check if element is clobbered or can clobber */ if (_isClobbered(currentNode)) { _forceRemove(currentNode); - return true; } - /* Check if tagname contains Unicode */ - - - if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) { - _forceRemove(currentNode); - return true; - } /* Now let's check the element's type and name */ + const tagName = transformCaseFunc(currentNode.nodeName); - - var tagName = transformCaseFunc(currentNode.nodeName); /* Execute a hook if present */ - _executeHook('uponSanitizeElement', currentNode, { - tagName: tagName, + tagName, allowedTags: ALLOWED_TAGS }); - /* Detect mXSS attempts abusing namespace confusion */ - - if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) { + /* Detect mXSS attempts abusing namespace confusion */ + if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) { _forceRemove(currentNode); - return true; } - /* Mitigate a problem with templates inside select */ - - if (tagName === 'select' && regExpTest(/