UNPKG

61.6 kBJavaScriptView Raw
1/*! @license DOMPurify 2.4.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.4.0/LICENSE */
2
3'use strict';
4
5function _typeof(obj) {
6 "@babel/helpers - typeof";
7
8 return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
9 return typeof obj;
10 } : function (obj) {
11 return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
12 }, _typeof(obj);
13}
14
15function _setPrototypeOf(o, p) {
16 _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
17 o.__proto__ = p;
18 return o;
19 };
20
21 return _setPrototypeOf(o, p);
22}
23
24function _isNativeReflectConstruct() {
25 if (typeof Reflect === "undefined" || !Reflect.construct) return false;
26 if (Reflect.construct.sham) return false;
27 if (typeof Proxy === "function") return true;
28
29 try {
30 Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
31 return true;
32 } catch (e) {
33 return false;
34 }
35}
36
37function _construct(Parent, args, Class) {
38 if (_isNativeReflectConstruct()) {
39 _construct = Reflect.construct;
40 } else {
41 _construct = function _construct(Parent, args, Class) {
42 var a = [null];
43 a.push.apply(a, args);
44 var Constructor = Function.bind.apply(Parent, a);
45 var instance = new Constructor();
46 if (Class) _setPrototypeOf(instance, Class.prototype);
47 return instance;
48 };
49 }
50
51 return _construct.apply(null, arguments);
52}
53
54function _toConsumableArray(arr) {
55 return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
56}
57
58function _arrayWithoutHoles(arr) {
59 if (Array.isArray(arr)) return _arrayLikeToArray(arr);
60}
61
62function _iterableToArray(iter) {
63 if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
64}
65
66function _unsupportedIterableToArray(o, minLen) {
67 if (!o) return;
68 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
69 var n = Object.prototype.toString.call(o).slice(8, -1);
70 if (n === "Object" && o.constructor) n = o.constructor.name;
71 if (n === "Map" || n === "Set") return Array.from(o);
72 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
73}
74
75function _arrayLikeToArray(arr, len) {
76 if (len == null || len > arr.length) len = arr.length;
77
78 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
79
80 return arr2;
81}
82
83function _nonIterableSpread() {
84 throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
85}
86
87var hasOwnProperty = Object.hasOwnProperty,
88 setPrototypeOf = Object.setPrototypeOf,
89 isFrozen = Object.isFrozen,
90 getPrototypeOf = Object.getPrototypeOf,
91 getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
92var freeze = Object.freeze,
93 seal = Object.seal,
94 create = Object.create; // eslint-disable-line import/no-mutable-exports
95
96var _ref = typeof Reflect !== 'undefined' && Reflect,
97 apply = _ref.apply,
98 construct = _ref.construct;
99
100if (!apply) {
101 apply = function apply(fun, thisValue, args) {
102 return fun.apply(thisValue, args);
103 };
104}
105
106if (!freeze) {
107 freeze = function freeze(x) {
108 return x;
109 };
110}
111
112if (!seal) {
113 seal = function seal(x) {
114 return x;
115 };
116}
117
118if (!construct) {
119 construct = function construct(Func, args) {
120 return _construct(Func, _toConsumableArray(args));
121 };
122}
123
124var arrayForEach = unapply(Array.prototype.forEach);
125var arrayPop = unapply(Array.prototype.pop);
126var arrayPush = unapply(Array.prototype.push);
127var stringToLowerCase = unapply(String.prototype.toLowerCase);
128var stringMatch = unapply(String.prototype.match);
129var stringReplace = unapply(String.prototype.replace);
130var stringIndexOf = unapply(String.prototype.indexOf);
131var stringTrim = unapply(String.prototype.trim);
132var regExpTest = unapply(RegExp.prototype.test);
133var typeErrorCreate = unconstruct(TypeError);
134function unapply(func) {
135 return function (thisArg) {
136 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
137 args[_key - 1] = arguments[_key];
138 }
139
140 return apply(func, thisArg, args);
141 };
142}
143function unconstruct(func) {
144 return function () {
145 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
146 args[_key2] = arguments[_key2];
147 }
148
149 return construct(func, args);
150 };
151}
152/* Add properties to a lookup table */
153
154function addToSet(set, array, transformCaseFunc) {
155 transformCaseFunc = transformCaseFunc ? transformCaseFunc : stringToLowerCase;
156
157 if (setPrototypeOf) {
158 // Make 'in' and truthy checks like Boolean(set.constructor)
159 // independent of any properties defined on Object.prototype.
160 // Prevent prototype setters from intercepting set as a this value.
161 setPrototypeOf(set, null);
162 }
163
164 var l = array.length;
165
166 while (l--) {
167 var element = array[l];
168
169 if (typeof element === 'string') {
170 var lcElement = transformCaseFunc(element);
171
172 if (lcElement !== element) {
173 // Config presets (e.g. tags.js, attrs.js) are immutable.
174 if (!isFrozen(array)) {
175 array[l] = lcElement;
176 }
177
178 element = lcElement;
179 }
180 }
181
182 set[element] = true;
183 }
184
185 return set;
186}
187/* Shallow clone an object */
188
189function clone(object) {
190 var newObject = create(null);
191 var property;
192
193 for (property in object) {
194 if (apply(hasOwnProperty, object, [property])) {
195 newObject[property] = object[property];
196 }
197 }
198
199 return newObject;
200}
201/* IE10 doesn't support __lookupGetter__ so lets'
202 * simulate it. It also automatically checks
203 * if the prop is function or getter and behaves
204 * accordingly. */
205
206function lookupGetter(object, prop) {
207 while (object !== null) {
208 var desc = getOwnPropertyDescriptor(object, prop);
209
210 if (desc) {
211 if (desc.get) {
212 return unapply(desc.get);
213 }
214
215 if (typeof desc.value === 'function') {
216 return unapply(desc.value);
217 }
218 }
219
220 object = getPrototypeOf(object);
221 }
222
223 function fallbackValue(element) {
224 console.warn('fallback value for', element);
225 return null;
226 }
227
228 return fallbackValue;
229}
230
231var 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
232
233var 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']);
234var 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.
235// We still need to know them so that we can do namespace
236// checks properly in case one wants to add them to
237// allow-list.
238
239var 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']);
240var 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,
241// even those that we disallow by default.
242
243var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
244var text = freeze(['#text']);
245
246var 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']);
247var 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']);
248var 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']);
249var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
250
251var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
252
253var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
254var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
255
256var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
257
258var 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
259);
260var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
261var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
262);
263var DOCTYPE_NAME = seal(/^html$/i);
264
265var getGlobal = function getGlobal() {
266 return typeof window === 'undefined' ? null : window;
267};
268/**
269 * Creates a no-op policy for internal use only.
270 * Don't export this function outside this module!
271 * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
272 * @param {Document} document The document object (to determine policy name suffix)
273 * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
274 * are not supported).
275 */
276
277
278var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
279 if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
280 return null;
281 } // Allow the callers to control the unique policy name
282 // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
283 // Policy creation with duplicate names throws in Trusted Types.
284
285
286 var suffix = null;
287 var ATTR_NAME = 'data-tt-policy-suffix';
288
289 if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
290 suffix = document.currentScript.getAttribute(ATTR_NAME);
291 }
292
293 var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
294
295 try {
296 return trustedTypes.createPolicy(policyName, {
297 createHTML: function createHTML(html) {
298 return html;
299 },
300 createScriptURL: function createScriptURL(scriptUrl) {
301 return scriptUrl;
302 }
303 });
304 } catch (_) {
305 // Policy creation failed (most likely another DOMPurify script has
306 // already run). Skip creating the policy, as this will only cause errors
307 // if TT are enforced.
308 console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
309 return null;
310 }
311};
312
313function createDOMPurify() {
314 var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
315
316 var DOMPurify = function DOMPurify(root) {
317 return createDOMPurify(root);
318 };
319 /**
320 * Version label, exposed for easier checks
321 * if DOMPurify is up to date or not
322 */
323
324
325 DOMPurify.version = '2.4.0';
326 /**
327 * Array of elements that DOMPurify removed during sanitation.
328 * Empty if nothing was removed.
329 */
330
331 DOMPurify.removed = [];
332
333 if (!window || !window.document || window.document.nodeType !== 9) {
334 // Not running in a browser, provide a factory function
335 // so that you can pass your own Window
336 DOMPurify.isSupported = false;
337 return DOMPurify;
338 }
339
340 var originalDocument = window.document;
341 var document = window.document;
342 var DocumentFragment = window.DocumentFragment,
343 HTMLTemplateElement = window.HTMLTemplateElement,
344 Node = window.Node,
345 Element = window.Element,
346 NodeFilter = window.NodeFilter,
347 _window$NamedNodeMap = window.NamedNodeMap,
348 NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
349 HTMLFormElement = window.HTMLFormElement,
350 DOMParser = window.DOMParser,
351 trustedTypes = window.trustedTypes;
352 var ElementPrototype = Element.prototype;
353 var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
354 var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
355 var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
356 var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a
357 // new document created via createHTMLDocument. As per the spec
358 // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
359 // a new empty registry is used when creating a template contents owner
360 // document, so we use that as our parent document to ensure nothing
361 // is inherited.
362
363 if (typeof HTMLTemplateElement === 'function') {
364 var template = document.createElement('template');
365
366 if (template.content && template.content.ownerDocument) {
367 document = template.content.ownerDocument;
368 }
369 }
370
371 var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
372
373 var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
374 var _document = document,
375 implementation = _document.implementation,
376 createNodeIterator = _document.createNodeIterator,
377 createDocumentFragment = _document.createDocumentFragment,
378 getElementsByTagName = _document.getElementsByTagName;
379 var importNode = originalDocument.importNode;
380 var documentMode = {};
381
382 try {
383 documentMode = clone(document).documentMode ? document.documentMode : {};
384 } catch (_) {}
385
386 var hooks = {};
387 /**
388 * Expose whether this browser supports running the full DOMPurify.
389 */
390
391 DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
392 var MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
393 ERB_EXPR$1 = ERB_EXPR,
394 DATA_ATTR$1 = DATA_ATTR,
395 ARIA_ATTR$1 = ARIA_ATTR,
396 IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
397 ATTR_WHITESPACE$1 = ATTR_WHITESPACE;
398 var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
399 /**
400 * We consider the elements and attributes below to be safe. Ideally
401 * don't add any new ones but feel free to remove unwanted ones.
402 */
403
404 /* allowed element names */
405
406 var ALLOWED_TAGS = null;
407 var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));
408 /* Allowed attribute names */
409
410 var ALLOWED_ATTR = null;
411 var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));
412 /*
413 * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
414 * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
415 * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
416 * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
417 */
418
419 var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
420 tagNameCheck: {
421 writable: true,
422 configurable: false,
423 enumerable: true,
424 value: null
425 },
426 attributeNameCheck: {
427 writable: true,
428 configurable: false,
429 enumerable: true,
430 value: null
431 },
432 allowCustomizedBuiltInElements: {
433 writable: true,
434 configurable: false,
435 enumerable: true,
436 value: false
437 }
438 }));
439 /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
440
441 var FORBID_TAGS = null;
442 /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
443
444 var FORBID_ATTR = null;
445 /* Decide if ARIA attributes are okay */
446
447 var ALLOW_ARIA_ATTR = true;
448 /* Decide if custom data attributes are okay */
449
450 var ALLOW_DATA_ATTR = true;
451 /* Decide if unknown protocols are okay */
452
453 var ALLOW_UNKNOWN_PROTOCOLS = false;
454 /* Output should be safe for common template engines.
455 * This means, DOMPurify removes data attributes, mustaches and ERB
456 */
457
458 var SAFE_FOR_TEMPLATES = false;
459 /* Decide if document with <html>... should be returned */
460
461 var WHOLE_DOCUMENT = false;
462 /* Track whether config is already set on this instance of DOMPurify. */
463
464 var SET_CONFIG = false;
465 /* Decide if all elements (e.g. style, script) must be children of
466 * document.body. By default, browsers might move them to document.head */
467
468 var FORCE_BODY = false;
469 /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
470 * string (or a TrustedHTML object if Trusted Types are supported).
471 * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
472 */
473
474 var RETURN_DOM = false;
475 /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
476 * string (or a TrustedHTML object if Trusted Types are supported) */
477
478 var RETURN_DOM_FRAGMENT = false;
479 /* Try to return a Trusted Type object instead of a string, return a string in
480 * case Trusted Types are not supported */
481
482 var RETURN_TRUSTED_TYPE = false;
483 /* Output should be free from DOM clobbering attacks?
484 * This sanitizes markups named with colliding, clobberable built-in DOM APIs.
485 */
486
487 var SANITIZE_DOM = true;
488 /* Achieve full DOM Clobbering protection by isolating the namespace of named
489 * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
490 *
491 * HTML/DOM spec rules that enable DOM Clobbering:
492 * - Named Access on Window (§7.3.3)
493 * - DOM Tree Accessors (§3.1.5)
494 * - Form Element Parent-Child Relations (§4.10.3)
495 * - Iframe srcdoc / Nested WindowProxies (§4.8.5)
496 * - HTMLCollection (§4.2.10.2)
497 *
498 * Namespace isolation is implemented by prefixing `id` and `name` attributes
499 * with a constant string, i.e., `user-content-`
500 */
501
502 var SANITIZE_NAMED_PROPS = false;
503 var SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
504 /* Keep element content when removing element? */
505
506 var KEEP_CONTENT = true;
507 /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
508 * of importing it into a new Document and returning a sanitized copy */
509
510 var IN_PLACE = false;
511 /* Allow usage of profiles like html, svg and mathMl */
512
513 var USE_PROFILES = {};
514 /* Tags to ignore content of when KEEP_CONTENT is true */
515
516 var FORBID_CONTENTS = null;
517 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']);
518 /* Tags that are safe for data: URIs */
519
520 var DATA_URI_TAGS = null;
521 var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
522 /* Attributes safe for values like "javascript:" */
523
524 var URI_SAFE_ATTRIBUTES = null;
525 var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
526 var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
527 var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
528 var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
529 /* Document namespace */
530
531 var NAMESPACE = HTML_NAMESPACE;
532 var IS_EMPTY_INPUT = false;
533 /* Parsing of strict XHTML documents */
534
535 var PARSER_MEDIA_TYPE;
536 var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
537 var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
538 var transformCaseFunc;
539 /* Keep a reference to config to pass to hooks */
540
541 var CONFIG = null;
542 /* Ideally, do not touch anything below this line */
543
544 /* ______________________________________________ */
545
546 var formElement = document.createElement('form');
547
548 var isRegexOrFunction = function isRegexOrFunction(testValue) {
549 return testValue instanceof RegExp || testValue instanceof Function;
550 };
551 /**
552 * _parseConfig
553 *
554 * @param {Object} cfg optional config literal
555 */
556 // eslint-disable-next-line complexity
557
558
559 var _parseConfig = function _parseConfig(cfg) {
560 if (CONFIG && CONFIG === cfg) {
561 return;
562 }
563 /* Shield configuration object from tampering */
564
565
566 if (!cfg || _typeof(cfg) !== 'object') {
567 cfg = {};
568 }
569 /* Shield configuration object from prototype pollution */
570
571
572 cfg = clone(cfg);
573 PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
574 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.
575
576 transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {
577 return x;
578 } : stringToLowerCase;
579 /* Set configuration parameters */
580
581 ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
582 ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
583 URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent
584 cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent
585 transformCaseFunc // eslint-disable-line indent
586 ) // eslint-disable-line indent
587 : DEFAULT_URI_SAFE_ATTRIBUTES;
588 DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent
589 cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent
590 transformCaseFunc // eslint-disable-line indent
591 ) // eslint-disable-line indent
592 : DEFAULT_DATA_URI_TAGS;
593 FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
594 FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
595 FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
596 USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
597 ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
598
599 ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
600
601 ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
602
603 SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
604
605 WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
606
607 RETURN_DOM = cfg.RETURN_DOM || false; // Default false
608
609 RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
610
611 RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
612
613 FORCE_BODY = cfg.FORCE_BODY || false; // Default false
614
615 SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
616
617 SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
618
619 KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
620
621 IN_PLACE = cfg.IN_PLACE || false; // Default false
622
623 IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
624 NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
625
626 if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
627 CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
628 }
629
630 if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
631 CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
632 }
633
634 if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
635 CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
636 }
637
638 if (SAFE_FOR_TEMPLATES) {
639 ALLOW_DATA_ATTR = false;
640 }
641
642 if (RETURN_DOM_FRAGMENT) {
643 RETURN_DOM = true;
644 }
645 /* Parse profile info */
646
647
648 if (USE_PROFILES) {
649 ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));
650 ALLOWED_ATTR = [];
651
652 if (USE_PROFILES.html === true) {
653 addToSet(ALLOWED_TAGS, html$1);
654 addToSet(ALLOWED_ATTR, html);
655 }
656
657 if (USE_PROFILES.svg === true) {
658 addToSet(ALLOWED_TAGS, svg$1);
659 addToSet(ALLOWED_ATTR, svg);
660 addToSet(ALLOWED_ATTR, xml);
661 }
662
663 if (USE_PROFILES.svgFilters === true) {
664 addToSet(ALLOWED_TAGS, svgFilters);
665 addToSet(ALLOWED_ATTR, svg);
666 addToSet(ALLOWED_ATTR, xml);
667 }
668
669 if (USE_PROFILES.mathMl === true) {
670 addToSet(ALLOWED_TAGS, mathMl$1);
671 addToSet(ALLOWED_ATTR, mathMl);
672 addToSet(ALLOWED_ATTR, xml);
673 }
674 }
675 /* Merge configuration parameters */
676
677
678 if (cfg.ADD_TAGS) {
679 if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
680 ALLOWED_TAGS = clone(ALLOWED_TAGS);
681 }
682
683 addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
684 }
685
686 if (cfg.ADD_ATTR) {
687 if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
688 ALLOWED_ATTR = clone(ALLOWED_ATTR);
689 }
690
691 addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
692 }
693
694 if (cfg.ADD_URI_SAFE_ATTR) {
695 addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
696 }
697
698 if (cfg.FORBID_CONTENTS) {
699 if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
700 FORBID_CONTENTS = clone(FORBID_CONTENTS);
701 }
702
703 addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
704 }
705 /* Add #text in case KEEP_CONTENT is set to true */
706
707
708 if (KEEP_CONTENT) {
709 ALLOWED_TAGS['#text'] = true;
710 }
711 /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
712
713
714 if (WHOLE_DOCUMENT) {
715 addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
716 }
717 /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
718
719
720 if (ALLOWED_TAGS.table) {
721 addToSet(ALLOWED_TAGS, ['tbody']);
722 delete FORBID_TAGS.tbody;
723 } // Prevent further manipulation of configuration.
724 // Not available in IE8, Safari 5, etc.
725
726
727 if (freeze) {
728 freeze(cfg);
729 }
730
731 CONFIG = cfg;
732 };
733
734 var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
735 var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML
736 // namespace. We need to specify them explicitly
737 // so that they don't get erroneously deleted from
738 // HTML namespace.
739
740 var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
741 /* Keep track of all possible SVG and MathML tags
742 * so that we can perform the namespace checks
743 * correctly. */
744
745 var ALL_SVG_TAGS = addToSet({}, svg$1);
746 addToSet(ALL_SVG_TAGS, svgFilters);
747 addToSet(ALL_SVG_TAGS, svgDisallowed);
748 var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
749 addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
750 /**
751 *
752 *
753 * @param {Element} element a DOM element whose namespace is being checked
754 * @returns {boolean} Return false if the element has a
755 * namespace that a spec-compliant parser would never
756 * return. Return true otherwise.
757 */
758
759 var _checkValidNamespace = function _checkValidNamespace(element) {
760 var parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode
761 // can be null. We just simulate parent in this case.
762
763 if (!parent || !parent.tagName) {
764 parent = {
765 namespaceURI: HTML_NAMESPACE,
766 tagName: 'template'
767 };
768 }
769
770 var tagName = stringToLowerCase(element.tagName);
771 var parentTagName = stringToLowerCase(parent.tagName);
772
773 if (element.namespaceURI === SVG_NAMESPACE) {
774 // The only way to switch from HTML namespace to SVG
775 // is via <svg>. If it happens via any other tag, then
776 // it should be killed.
777 if (parent.namespaceURI === HTML_NAMESPACE) {
778 return tagName === 'svg';
779 } // The only way to switch from MathML to SVG is via
780 // svg if parent is either <annotation-xml> or MathML
781 // text integration points.
782
783
784 if (parent.namespaceURI === MATHML_NAMESPACE) {
785 return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
786 } // We only allow elements that are defined in SVG
787 // spec. All others are disallowed in SVG namespace.
788
789
790 return Boolean(ALL_SVG_TAGS[tagName]);
791 }
792
793 if (element.namespaceURI === MATHML_NAMESPACE) {
794 // The only way to switch from HTML namespace to MathML
795 // is via <math>. If it happens via any other tag, then
796 // it should be killed.
797 if (parent.namespaceURI === HTML_NAMESPACE) {
798 return tagName === 'math';
799 } // The only way to switch from SVG to MathML is via
800 // <math> and HTML integration points
801
802
803 if (parent.namespaceURI === SVG_NAMESPACE) {
804 return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
805 } // We only allow elements that are defined in MathML
806 // spec. All others are disallowed in MathML namespace.
807
808
809 return Boolean(ALL_MATHML_TAGS[tagName]);
810 }
811
812 if (element.namespaceURI === HTML_NAMESPACE) {
813 // The only way to switch from SVG to HTML is via
814 // HTML integration points, and from MathML to HTML
815 // is via MathML text integration points
816 if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
817 return false;
818 }
819
820 if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
821 return false;
822 } // We disallow tags that are specific for MathML
823 // or SVG and should never appear in HTML namespace
824
825
826 return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
827 } // The code should never reach this place (this means
828 // that the element somehow got namespace that is not
829 // HTML, SVG or MathML). Return false just in case.
830
831
832 return false;
833 };
834 /**
835 * _forceRemove
836 *
837 * @param {Node} node a DOM node
838 */
839
840
841 var _forceRemove = function _forceRemove(node) {
842 arrayPush(DOMPurify.removed, {
843 element: node
844 });
845
846 try {
847 // eslint-disable-next-line unicorn/prefer-dom-node-remove
848 node.parentNode.removeChild(node);
849 } catch (_) {
850 try {
851 node.outerHTML = emptyHTML;
852 } catch (_) {
853 node.remove();
854 }
855 }
856 };
857 /**
858 * _removeAttribute
859 *
860 * @param {String} name an Attribute name
861 * @param {Node} node a DOM node
862 */
863
864
865 var _removeAttribute = function _removeAttribute(name, node) {
866 try {
867 arrayPush(DOMPurify.removed, {
868 attribute: node.getAttributeNode(name),
869 from: node
870 });
871 } catch (_) {
872 arrayPush(DOMPurify.removed, {
873 attribute: null,
874 from: node
875 });
876 }
877
878 node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes
879
880 if (name === 'is' && !ALLOWED_ATTR[name]) {
881 if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
882 try {
883 _forceRemove(node);
884 } catch (_) {}
885 } else {
886 try {
887 node.setAttribute(name, '');
888 } catch (_) {}
889 }
890 }
891 };
892 /**
893 * _initDocument
894 *
895 * @param {String} dirty a string of dirty markup
896 * @return {Document} a DOM, filled with the dirty markup
897 */
898
899
900 var _initDocument = function _initDocument(dirty) {
901 /* Create a HTML document */
902 var doc;
903 var leadingWhitespace;
904
905 if (FORCE_BODY) {
906 dirty = '<remove></remove>' + dirty;
907 } else {
908 /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
909 var matches = stringMatch(dirty, /^[\r\n\t ]+/);
910 leadingWhitespace = matches && matches[0];
911 }
912
913 if (PARSER_MEDIA_TYPE === 'application/xhtml+xml') {
914 // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
915 dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
916 }
917
918 var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
919 /*
920 * Use the DOMParser API by default, fallback later if needs be
921 * DOMParser not work for svg when has multiple root element.
922 */
923
924 if (NAMESPACE === HTML_NAMESPACE) {
925 try {
926 doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
927 } catch (_) {}
928 }
929 /* Use createHTMLDocument in case DOMParser is not available */
930
931
932 if (!doc || !doc.documentElement) {
933 doc = implementation.createDocument(NAMESPACE, 'template', null);
934
935 try {
936 doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload;
937 } catch (_) {// Syntax error if dirtyPayload is invalid xml
938 }
939 }
940
941 var body = doc.body || doc.documentElement;
942
943 if (dirty && leadingWhitespace) {
944 body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
945 }
946 /* Work on whole document or just its body */
947
948
949 if (NAMESPACE === HTML_NAMESPACE) {
950 return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
951 }
952
953 return WHOLE_DOCUMENT ? doc.documentElement : body;
954 };
955 /**
956 * _createIterator
957 *
958 * @param {Document} root document/fragment to create iterator for
959 * @return {Iterator} iterator instance
960 */
961
962
963 var _createIterator = function _createIterator(root) {
964 return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
965 NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
966 };
967 /**
968 * _isClobbered
969 *
970 * @param {Node} elm element to check for clobbering attacks
971 * @return {Boolean} true if clobbered, false if safe
972 */
973
974
975 var _isClobbered = function _isClobbered(elm) {
976 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');
977 };
978 /**
979 * _isNode
980 *
981 * @param {Node} obj object to check whether it's a DOM node
982 * @return {Boolean} true is object is a DOM node
983 */
984
985
986 var _isNode = function _isNode(object) {
987 return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
988 };
989 /**
990 * _executeHook
991 * Execute user configurable hooks
992 *
993 * @param {String} entryPoint Name of the hook's entry point
994 * @param {Node} currentNode node to work on with the hook
995 * @param {Object} data additional hook parameters
996 */
997
998
999 var _executeHook = function _executeHook(entryPoint, currentNode, data) {
1000 if (!hooks[entryPoint]) {
1001 return;
1002 }
1003
1004 arrayForEach(hooks[entryPoint], function (hook) {
1005 hook.call(DOMPurify, currentNode, data, CONFIG);
1006 });
1007 };
1008 /**
1009 * _sanitizeElements
1010 *
1011 * @protect nodeName
1012 * @protect textContent
1013 * @protect removeChild
1014 *
1015 * @param {Node} currentNode to check for permission to exist
1016 * @return {Boolean} true if node was killed, false if left alive
1017 */
1018
1019
1020 var _sanitizeElements = function _sanitizeElements(currentNode) {
1021 var content;
1022 /* Execute a hook if present */
1023
1024 _executeHook('beforeSanitizeElements', currentNode, null);
1025 /* Check if element is clobbered or can clobber */
1026
1027
1028 if (_isClobbered(currentNode)) {
1029 _forceRemove(currentNode);
1030
1031 return true;
1032 }
1033 /* Check if tagname contains Unicode */
1034
1035
1036 if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) {
1037 _forceRemove(currentNode);
1038
1039 return true;
1040 }
1041 /* Now let's check the element's type and name */
1042
1043
1044 var tagName = transformCaseFunc(currentNode.nodeName);
1045 /* Execute a hook if present */
1046
1047 _executeHook('uponSanitizeElement', currentNode, {
1048 tagName: tagName,
1049 allowedTags: ALLOWED_TAGS
1050 });
1051 /* Detect mXSS attempts abusing namespace confusion */
1052
1053
1054 if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
1055 _forceRemove(currentNode);
1056
1057 return true;
1058 }
1059 /* Mitigate a problem with templates inside select */
1060
1061
1062 if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
1063 _forceRemove(currentNode);
1064
1065 return true;
1066 }
1067 /* Remove element if anything forbids its presence */
1068
1069
1070 if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1071 /* Check if we have a custom element to handle */
1072 if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
1073 if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
1074 if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
1075 }
1076 /* Keep content except for bad-listed elements */
1077
1078
1079 if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1080 var parentNode = getParentNode(currentNode) || currentNode.parentNode;
1081 var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
1082
1083 if (childNodes && parentNode) {
1084 var childCount = childNodes.length;
1085
1086 for (var i = childCount - 1; i >= 0; --i) {
1087 parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
1088 }
1089 }
1090 }
1091
1092 _forceRemove(currentNode);
1093
1094 return true;
1095 }
1096 /* Check whether element has a valid namespace */
1097
1098
1099 if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
1100 _forceRemove(currentNode);
1101
1102 return true;
1103 }
1104
1105 if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
1106 _forceRemove(currentNode);
1107
1108 return true;
1109 }
1110 /* Sanitize element content to be template-safe */
1111
1112
1113 if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
1114 /* Get the element's text content */
1115 content = currentNode.textContent;
1116 content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
1117 content = stringReplace(content, ERB_EXPR$1, ' ');
1118
1119 if (currentNode.textContent !== content) {
1120 arrayPush(DOMPurify.removed, {
1121 element: currentNode.cloneNode()
1122 });
1123 currentNode.textContent = content;
1124 }
1125 }
1126 /* Execute a hook if present */
1127
1128
1129 _executeHook('afterSanitizeElements', currentNode, null);
1130
1131 return false;
1132 };
1133 /**
1134 * _isValidAttribute
1135 *
1136 * @param {string} lcTag Lowercase tag name of containing element.
1137 * @param {string} lcName Lowercase attribute name.
1138 * @param {string} value Attribute value.
1139 * @return {Boolean} Returns true if `value` is valid, otherwise false.
1140 */
1141 // eslint-disable-next-line complexity
1142
1143
1144 var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1145 /* Make sure attribute cannot clobber */
1146 if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1147 return false;
1148 }
1149 /* Allow valid data-* attributes: At least one character after "-"
1150 (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1151 XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1152 We don't need to check the value; it's always URI safe. */
1153
1154
1155 if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
1156 if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1157 // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1158 // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1159 _basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || // Alternative, second condition checks if it's an `is`-attribute, AND
1160 // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1161 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 {
1162 return false;
1163 }
1164 /* Check value is safe. First, is attr inert? If so, is safe */
1165
1166 } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (!value) ; else {
1167 return false;
1168 }
1169
1170 return true;
1171 };
1172 /**
1173 * _basicCustomElementCheck
1174 * checks if at least one dash is included in tagName, and it's not the first char
1175 * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1176 * @param {string} tagName name of the tag of the node to sanitize
1177 */
1178
1179
1180 var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
1181 return tagName.indexOf('-') > 0;
1182 };
1183 /**
1184 * _sanitizeAttributes
1185 *
1186 * @protect attributes
1187 * @protect nodeName
1188 * @protect removeAttribute
1189 * @protect setAttribute
1190 *
1191 * @param {Node} currentNode to sanitize
1192 */
1193
1194
1195 var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1196 var attr;
1197 var value;
1198 var lcName;
1199 var l;
1200 /* Execute a hook if present */
1201
1202 _executeHook('beforeSanitizeAttributes', currentNode, null);
1203
1204 var attributes = currentNode.attributes;
1205 /* Check if we have attributes; if not we might have a text node */
1206
1207 if (!attributes) {
1208 return;
1209 }
1210
1211 var hookEvent = {
1212 attrName: '',
1213 attrValue: '',
1214 keepAttr: true,
1215 allowedAttributes: ALLOWED_ATTR
1216 };
1217 l = attributes.length;
1218 /* Go backwards over all attributes; safely remove bad ones */
1219
1220 while (l--) {
1221 attr = attributes[l];
1222 var _attr = attr,
1223 name = _attr.name,
1224 namespaceURI = _attr.namespaceURI;
1225 value = name === 'value' ? attr.value : stringTrim(attr.value);
1226 lcName = transformCaseFunc(name);
1227 /* Execute a hook if present */
1228
1229 hookEvent.attrName = lcName;
1230 hookEvent.attrValue = value;
1231 hookEvent.keepAttr = true;
1232 hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
1233
1234 _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
1235
1236 value = hookEvent.attrValue;
1237 /* Did the hooks approve of the attribute? */
1238
1239 if (hookEvent.forceKeepAttr) {
1240 continue;
1241 }
1242 /* Remove attribute */
1243
1244
1245 _removeAttribute(name, currentNode);
1246 /* Did the hooks approve of the attribute? */
1247
1248
1249 if (!hookEvent.keepAttr) {
1250 continue;
1251 }
1252 /* Work around a security issue in jQuery 3.0 */
1253
1254
1255 if (regExpTest(/\/>/i, value)) {
1256 _removeAttribute(name, currentNode);
1257
1258 continue;
1259 }
1260 /* Sanitize attribute content to be template-safe */
1261
1262
1263 if (SAFE_FOR_TEMPLATES) {
1264 value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
1265 value = stringReplace(value, ERB_EXPR$1, ' ');
1266 }
1267 /* Is `value` valid for this attribute? */
1268
1269
1270 var lcTag = transformCaseFunc(currentNode.nodeName);
1271
1272 if (!_isValidAttribute(lcTag, lcName, value)) {
1273 continue;
1274 }
1275 /* Full DOM Clobbering protection via namespace isolation,
1276 * Prefix id and name attributes with `user-content-`
1277 */
1278
1279
1280 if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1281 // Remove the attribute with this value
1282 _removeAttribute(name, currentNode); // Prefix the value and later re-create the attribute with the sanitized value
1283
1284
1285 value = SANITIZE_NAMED_PROPS_PREFIX + value;
1286 }
1287 /* Handle attributes that require Trusted Types */
1288
1289
1290 if (trustedTypesPolicy && _typeof(trustedTypes) === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1291 if (namespaceURI) ; else {
1292 switch (trustedTypes.getAttributeType(lcTag, lcName)) {
1293 case 'TrustedHTML':
1294 value = trustedTypesPolicy.createHTML(value);
1295 break;
1296
1297 case 'TrustedScriptURL':
1298 value = trustedTypesPolicy.createScriptURL(value);
1299 break;
1300 }
1301 }
1302 }
1303 /* Handle invalid data-* attribute set by try-catching it */
1304
1305
1306 try {
1307 if (namespaceURI) {
1308 currentNode.setAttributeNS(namespaceURI, name, value);
1309 } else {
1310 /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1311 currentNode.setAttribute(name, value);
1312 }
1313
1314 arrayPop(DOMPurify.removed);
1315 } catch (_) {}
1316 }
1317 /* Execute a hook if present */
1318
1319
1320 _executeHook('afterSanitizeAttributes', currentNode, null);
1321 };
1322 /**
1323 * _sanitizeShadowDOM
1324 *
1325 * @param {DocumentFragment} fragment to iterate over recursively
1326 */
1327
1328
1329 var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1330 var shadowNode;
1331
1332 var shadowIterator = _createIterator(fragment);
1333 /* Execute a hook if present */
1334
1335
1336 _executeHook('beforeSanitizeShadowDOM', fragment, null);
1337
1338 while (shadowNode = shadowIterator.nextNode()) {
1339 /* Execute a hook if present */
1340 _executeHook('uponSanitizeShadowNode', shadowNode, null);
1341 /* Sanitize tags and elements */
1342
1343
1344 if (_sanitizeElements(shadowNode)) {
1345 continue;
1346 }
1347 /* Deep shadow DOM detected */
1348
1349
1350 if (shadowNode.content instanceof DocumentFragment) {
1351 _sanitizeShadowDOM(shadowNode.content);
1352 }
1353 /* Check attributes, sanitize if necessary */
1354
1355
1356 _sanitizeAttributes(shadowNode);
1357 }
1358 /* Execute a hook if present */
1359
1360
1361 _executeHook('afterSanitizeShadowDOM', fragment, null);
1362 };
1363 /**
1364 * Sanitize
1365 * Public method providing core sanitation functionality
1366 *
1367 * @param {String|Node} dirty string or DOM node
1368 * @param {Object} configuration object
1369 */
1370 // eslint-disable-next-line complexity
1371
1372
1373 DOMPurify.sanitize = function (dirty) {
1374 var cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1375 var body;
1376 var importedNode;
1377 var currentNode;
1378 var oldNode;
1379 var returnNode;
1380 /* Make sure we have a string to sanitize.
1381 DO NOT return early, as this will return the wrong type if
1382 the user has requested a DOM object rather than a string */
1383
1384 IS_EMPTY_INPUT = !dirty;
1385
1386 if (IS_EMPTY_INPUT) {
1387 dirty = '<!-->';
1388 }
1389 /* Stringify, in case dirty is an object */
1390
1391
1392 if (typeof dirty !== 'string' && !_isNode(dirty)) {
1393 // eslint-disable-next-line no-negated-condition
1394 if (typeof dirty.toString !== 'function') {
1395 throw typeErrorCreate('toString is not a function');
1396 } else {
1397 dirty = dirty.toString();
1398
1399 if (typeof dirty !== 'string') {
1400 throw typeErrorCreate('dirty is not a string, aborting');
1401 }
1402 }
1403 }
1404 /* Check we can run. Otherwise fall back or ignore */
1405
1406
1407 if (!DOMPurify.isSupported) {
1408 if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
1409 if (typeof dirty === 'string') {
1410 return window.toStaticHTML(dirty);
1411 }
1412
1413 if (_isNode(dirty)) {
1414 return window.toStaticHTML(dirty.outerHTML);
1415 }
1416 }
1417
1418 return dirty;
1419 }
1420 /* Assign config vars */
1421
1422
1423 if (!SET_CONFIG) {
1424 _parseConfig(cfg);
1425 }
1426 /* Clean up removed elements */
1427
1428
1429 DOMPurify.removed = [];
1430 /* Check if dirty is correctly typed for IN_PLACE */
1431
1432 if (typeof dirty === 'string') {
1433 IN_PLACE = false;
1434 }
1435
1436 if (IN_PLACE) {
1437 /* Do some early pre-sanitization to avoid unsafe root nodes */
1438 if (dirty.nodeName) {
1439 var tagName = transformCaseFunc(dirty.nodeName);
1440
1441 if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1442 throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1443 }
1444 }
1445 } else if (dirty instanceof Node) {
1446 /* If dirty is a DOM element, append to an empty document to avoid
1447 elements being stripped by the parser */
1448 body = _initDocument('<!---->');
1449 importedNode = body.ownerDocument.importNode(dirty, true);
1450
1451 if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1452 /* Node is already a body, use as is */
1453 body = importedNode;
1454 } else if (importedNode.nodeName === 'HTML') {
1455 body = importedNode;
1456 } else {
1457 // eslint-disable-next-line unicorn/prefer-dom-node-append
1458 body.appendChild(importedNode);
1459 }
1460 } else {
1461 /* Exit directly if we have nothing to do */
1462 if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
1463 dirty.indexOf('<') === -1) {
1464 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1465 }
1466 /* Initialize the document to work on */
1467
1468
1469 body = _initDocument(dirty);
1470 /* Check we have a DOM node from the data */
1471
1472 if (!body) {
1473 return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
1474 }
1475 }
1476 /* Remove first element node (ours) if FORCE_BODY is set */
1477
1478
1479 if (body && FORCE_BODY) {
1480 _forceRemove(body.firstChild);
1481 }
1482 /* Get node iterator */
1483
1484
1485 var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1486 /* Now start iterating over the created document */
1487
1488
1489 while (currentNode = nodeIterator.nextNode()) {
1490 /* Fix IE's strange behavior with manipulated textNodes #89 */
1491 if (currentNode.nodeType === 3 && currentNode === oldNode) {
1492 continue;
1493 }
1494 /* Sanitize tags and elements */
1495
1496
1497 if (_sanitizeElements(currentNode)) {
1498 continue;
1499 }
1500 /* Shadow DOM detected, sanitize it */
1501
1502
1503 if (currentNode.content instanceof DocumentFragment) {
1504 _sanitizeShadowDOM(currentNode.content);
1505 }
1506 /* Check attributes, sanitize if necessary */
1507
1508
1509 _sanitizeAttributes(currentNode);
1510
1511 oldNode = currentNode;
1512 }
1513
1514 oldNode = null;
1515 /* If we sanitized `dirty` in-place, return it. */
1516
1517 if (IN_PLACE) {
1518 return dirty;
1519 }
1520 /* Return sanitized string or DOM */
1521
1522
1523 if (RETURN_DOM) {
1524 if (RETURN_DOM_FRAGMENT) {
1525 returnNode = createDocumentFragment.call(body.ownerDocument);
1526
1527 while (body.firstChild) {
1528 // eslint-disable-next-line unicorn/prefer-dom-node-append
1529 returnNode.appendChild(body.firstChild);
1530 }
1531 } else {
1532 returnNode = body;
1533 }
1534
1535 if (ALLOWED_ATTR.shadowroot) {
1536 /*
1537 AdoptNode() is not used because internal state is not reset
1538 (e.g. the past names map of a HTMLFormElement), this is safe
1539 in theory but we would rather not risk another attack vector.
1540 The state that is cloned by importNode() is explicitly defined
1541 by the specs.
1542 */
1543 returnNode = importNode.call(originalDocument, returnNode, true);
1544 }
1545
1546 return returnNode;
1547 }
1548
1549 var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1550 /* Serialize doctype if allowed */
1551
1552 if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
1553 serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
1554 }
1555 /* Sanitize final string template-safe */
1556
1557
1558 if (SAFE_FOR_TEMPLATES) {
1559 serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
1560 serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
1561 }
1562
1563 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1564 };
1565 /**
1566 * Public method to set the configuration once
1567 * setConfig
1568 *
1569 * @param {Object} cfg configuration object
1570 */
1571
1572
1573 DOMPurify.setConfig = function (cfg) {
1574 _parseConfig(cfg);
1575
1576 SET_CONFIG = true;
1577 };
1578 /**
1579 * Public method to remove the configuration
1580 * clearConfig
1581 *
1582 */
1583
1584
1585 DOMPurify.clearConfig = function () {
1586 CONFIG = null;
1587 SET_CONFIG = false;
1588 };
1589 /**
1590 * Public method to check if an attribute value is valid.
1591 * Uses last set config, if any. Otherwise, uses config defaults.
1592 * isValidAttribute
1593 *
1594 * @param {string} tag Tag name of containing element.
1595 * @param {string} attr Attribute name.
1596 * @param {string} value Attribute value.
1597 * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1598 */
1599
1600
1601 DOMPurify.isValidAttribute = function (tag, attr, value) {
1602 /* Initialize shared config vars if necessary. */
1603 if (!CONFIG) {
1604 _parseConfig({});
1605 }
1606
1607 var lcTag = transformCaseFunc(tag);
1608 var lcName = transformCaseFunc(attr);
1609 return _isValidAttribute(lcTag, lcName, value);
1610 };
1611 /**
1612 * AddHook
1613 * Public method to add DOMPurify hooks
1614 *
1615 * @param {String} entryPoint entry point for the hook to add
1616 * @param {Function} hookFunction function to execute
1617 */
1618
1619
1620 DOMPurify.addHook = function (entryPoint, hookFunction) {
1621 if (typeof hookFunction !== 'function') {
1622 return;
1623 }
1624
1625 hooks[entryPoint] = hooks[entryPoint] || [];
1626 arrayPush(hooks[entryPoint], hookFunction);
1627 };
1628 /**
1629 * RemoveHook
1630 * Public method to remove a DOMPurify hook at a given entryPoint
1631 * (pops it from the stack of hooks if more are present)
1632 *
1633 * @param {String} entryPoint entry point for the hook to remove
1634 * @return {Function} removed(popped) hook
1635 */
1636
1637
1638 DOMPurify.removeHook = function (entryPoint) {
1639 if (hooks[entryPoint]) {
1640 return arrayPop(hooks[entryPoint]);
1641 }
1642 };
1643 /**
1644 * RemoveHooks
1645 * Public method to remove all DOMPurify hooks at a given entryPoint
1646 *
1647 * @param {String} entryPoint entry point for the hooks to remove
1648 */
1649
1650
1651 DOMPurify.removeHooks = function (entryPoint) {
1652 if (hooks[entryPoint]) {
1653 hooks[entryPoint] = [];
1654 }
1655 };
1656 /**
1657 * RemoveAllHooks
1658 * Public method to remove all DOMPurify hooks
1659 *
1660 */
1661
1662
1663 DOMPurify.removeAllHooks = function () {
1664 hooks = {};
1665 };
1666
1667 return DOMPurify;
1668}
1669
1670var purify = createDOMPurify();
1671
1672module.exports = purify;
1673//# sourceMappingURL=purify.cjs.js.map