UNPKG

49 kBJavaScriptView Raw
1/*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.0.8/LICENSE */
2
3(function (global, factory) {
4 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
5 typeof define === 'function' && define.amd ? define(factory) :
6 (global = global || self, global.DOMPurify = factory());
7}(this, function () { 'use strict';
8
9 function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
10
11 var hasOwnProperty = Object.hasOwnProperty,
12 setPrototypeOf = Object.setPrototypeOf,
13 isFrozen = Object.isFrozen,
14 objectKeys = Object.keys;
15 var freeze = Object.freeze,
16 seal = Object.seal; // eslint-disable-line import/no-mutable-exports
17
18 var _ref = typeof Reflect !== 'undefined' && Reflect,
19 apply = _ref.apply,
20 construct = _ref.construct;
21
22 if (!apply) {
23 apply = function apply(fun, thisValue, args) {
24 return fun.apply(thisValue, args);
25 };
26 }
27
28 if (!freeze) {
29 freeze = function freeze(x) {
30 return x;
31 };
32 }
33
34 if (!seal) {
35 seal = function seal(x) {
36 return x;
37 };
38 }
39
40 if (!construct) {
41 construct = function construct(Func, args) {
42 return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))();
43 };
44 }
45
46 var arrayForEach = unapply(Array.prototype.forEach);
47 var arrayIndexOf = unapply(Array.prototype.indexOf);
48 var arrayJoin = unapply(Array.prototype.join);
49 var arrayPop = unapply(Array.prototype.pop);
50 var arrayPush = unapply(Array.prototype.push);
51 var arraySlice = unapply(Array.prototype.slice);
52
53 var stringToLowerCase = unapply(String.prototype.toLowerCase);
54 var stringMatch = unapply(String.prototype.match);
55 var stringReplace = unapply(String.prototype.replace);
56 var stringIndexOf = unapply(String.prototype.indexOf);
57 var stringTrim = unapply(String.prototype.trim);
58
59 var regExpTest = unapply(RegExp.prototype.test);
60 var regExpCreate = unconstruct(RegExp);
61
62 var typeErrorCreate = unconstruct(TypeError);
63
64 function unapply(func) {
65 return function (thisArg) {
66 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
67 args[_key - 1] = arguments[_key];
68 }
69
70 return apply(func, thisArg, args);
71 };
72 }
73
74 function unconstruct(func) {
75 return function () {
76 for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
77 args[_key2] = arguments[_key2];
78 }
79
80 return construct(func, args);
81 };
82 }
83
84 /* Add properties to a lookup table */
85 function addToSet(set, array) {
86 if (setPrototypeOf) {
87 // Make 'in' and truthy checks like Boolean(set.constructor)
88 // independent of any properties defined on Object.prototype.
89 // Prevent prototype setters from intercepting set as a this value.
90 setPrototypeOf(set, null);
91 }
92
93 var l = array.length;
94 while (l--) {
95 var element = array[l];
96 if (typeof element === 'string') {
97 var lcElement = stringToLowerCase(element);
98 if (lcElement !== element) {
99 // Config presets (e.g. tags.js, attrs.js) are immutable.
100 if (!isFrozen(array)) {
101 array[l] = lcElement;
102 }
103
104 element = lcElement;
105 }
106 }
107
108 set[element] = true;
109 }
110
111 return set;
112 }
113
114 /* Shallow clone an object */
115 function clone(object) {
116 var newObject = {};
117
118 var property = void 0;
119 for (property in object) {
120 if (apply(hasOwnProperty, object, [property])) {
121 newObject[property] = object[property];
122 }
123 }
124
125 return newObject;
126 }
127
128 var html = 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', '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']);
129
130 // SVG
131 var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'audio', 'canvas', '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', 'video', 'view', 'vkern']);
132
133 var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
134
135 var mathMl = 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']);
136
137 var text = freeze(['#text']);
138
139 var html$1 = 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', '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']);
140
141 var svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', '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', 'tabindex', 'targetx', 'targety', 'transform', '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']);
142
143 var mathMl$1 = 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']);
144
145 var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
146
147 // eslint-disable-next-line unicorn/better-regex
148 var MUSTACHE_EXPR = seal(/\{\{[\s\S]*|[\s\S]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
149 var ERB_EXPR = seal(/<%[\s\S]*|[\s\S]*%>/gm);
150 var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
151 var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
152 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
153 );
154 var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
155 var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g // eslint-disable-line no-control-regex
156 );
157
158 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
159
160 function _toConsumableArray$1(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
161
162 var getGlobal = function getGlobal() {
163 return typeof window === 'undefined' ? null : window;
164 };
165
166 /**
167 * Creates a no-op policy for internal use only.
168 * Don't export this function outside this module!
169 * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
170 * @param {Document} document The document object (to determine policy name suffix)
171 * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
172 * are not supported).
173 */
174 var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
175 if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
176 return null;
177 }
178
179 // Allow the callers to control the unique policy name
180 // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
181 // Policy creation with duplicate names throws in Trusted Types.
182 var suffix = null;
183 var ATTR_NAME = 'data-tt-policy-suffix';
184 if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
185 suffix = document.currentScript.getAttribute(ATTR_NAME);
186 }
187
188 var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
189
190 try {
191 return trustedTypes.createPolicy(policyName, {
192 createHTML: function createHTML(html$$1) {
193 return html$$1;
194 }
195 });
196 } catch (_) {
197 // Policy creation failed (most likely another DOMPurify script has
198 // already run). Skip creating the policy, as this will only cause errors
199 // if TT are enforced.
200 console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
201 return null;
202 }
203 };
204
205 function createDOMPurify() {
206 var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
207
208 var DOMPurify = function DOMPurify(root) {
209 return createDOMPurify(root);
210 };
211
212 /**
213 * Version label, exposed for easier checks
214 * if DOMPurify is up to date or not
215 */
216 DOMPurify.version = '2.0.12';
217
218 /**
219 * Array of elements that DOMPurify removed during sanitation.
220 * Empty if nothing was removed.
221 */
222 DOMPurify.removed = [];
223
224 if (!window || !window.document || window.document.nodeType !== 9) {
225 // Not running in a browser, provide a factory function
226 // so that you can pass your own Window
227 DOMPurify.isSupported = false;
228
229 return DOMPurify;
230 }
231
232 var originalDocument = window.document;
233 var removeTitle = false;
234
235 var document = window.document;
236 var DocumentFragment = window.DocumentFragment,
237 HTMLTemplateElement = window.HTMLTemplateElement,
238 Node = window.Node,
239 NodeFilter = window.NodeFilter,
240 _window$NamedNodeMap = window.NamedNodeMap,
241 NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
242 Text = window.Text,
243 Comment = window.Comment,
244 DOMParser = window.DOMParser,
245 trustedTypes = window.trustedTypes;
246
247 // As per issue #47, the web-components registry is inherited by a
248 // new document created via createHTMLDocument. As per the spec
249 // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
250 // a new empty registry is used when creating a template contents owner
251 // document, so we use that as our parent document to ensure nothing
252 // is inherited.
253
254 if (typeof HTMLTemplateElement === 'function') {
255 var template = document.createElement('template');
256 if (template.content && template.content.ownerDocument) {
257 document = template.content.ownerDocument;
258 }
259 }
260
261 var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
262 var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : '';
263
264 var _document = document,
265 implementation = _document.implementation,
266 createNodeIterator = _document.createNodeIterator,
267 getElementsByTagName = _document.getElementsByTagName,
268 createDocumentFragment = _document.createDocumentFragment;
269 var importNode = originalDocument.importNode;
270
271
272 var hooks = {};
273
274 /**
275 * Expose whether this browser supports running the full DOMPurify.
276 */
277 DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && document.documentMode !== 9;
278
279 var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
280 ERB_EXPR$$1 = ERB_EXPR,
281 DATA_ATTR$$1 = DATA_ATTR,
282 ARIA_ATTR$$1 = ARIA_ATTR,
283 IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
284 ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
285 var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;
286
287 /**
288 * We consider the elements and attributes below to be safe. Ideally
289 * don't add any new ones but feel free to remove unwanted ones.
290 */
291
292 /* allowed element names */
293
294 var ALLOWED_TAGS = null;
295 var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(html), _toConsumableArray$1(svg), _toConsumableArray$1(svgFilters), _toConsumableArray$1(mathMl), _toConsumableArray$1(text)));
296
297 /* Allowed attribute names */
298 var ALLOWED_ATTR = null;
299 var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml)));
300
301 /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
302 var FORBID_TAGS = null;
303
304 /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
305 var FORBID_ATTR = null;
306
307 /* Decide if ARIA attributes are okay */
308 var ALLOW_ARIA_ATTR = true;
309
310 /* Decide if custom data attributes are okay */
311 var ALLOW_DATA_ATTR = true;
312
313 /* Decide if unknown protocols are okay */
314 var ALLOW_UNKNOWN_PROTOCOLS = false;
315
316 /* Output should be safe for jQuery's $() factory? */
317 var SAFE_FOR_JQUERY = false;
318
319 /* Output should be safe for common template engines.
320 * This means, DOMPurify removes data attributes, mustaches and ERB
321 */
322 var SAFE_FOR_TEMPLATES = false;
323
324 /* Decide if document with <html>... should be returned */
325 var WHOLE_DOCUMENT = false;
326
327 /* Track whether config is already set on this instance of DOMPurify. */
328 var SET_CONFIG = false;
329
330 /* Decide if all elements (e.g. style, script) must be children of
331 * document.body. By default, browsers might move them to document.head */
332 var FORCE_BODY = false;
333
334 /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
335 * string (or a TrustedHTML object if Trusted Types are supported).
336 * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
337 */
338 var RETURN_DOM = false;
339
340 /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
341 * string (or a TrustedHTML object if Trusted Types are supported) */
342 var RETURN_DOM_FRAGMENT = false;
343
344 /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM
345 * `Node` is imported into the current `Document`. If this flag is not enabled the
346 * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by
347 * DOMPurify. */
348 var RETURN_DOM_IMPORT = false;
349
350 /* Try to return a Trusted Type object instead of a string, retrun a string in
351 * case Trusted Types are not supported */
352 var RETURN_TRUSTED_TYPE = false;
353
354 /* Output should be free from DOM clobbering attacks? */
355 var SANITIZE_DOM = true;
356
357 /* Keep element content when removing element? */
358 var KEEP_CONTENT = true;
359
360 /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
361 * of importing it into a new Document and returning a sanitized copy */
362 var IN_PLACE = false;
363
364 /* Allow usage of profiles like html, svg and mathMl */
365 var USE_PROFILES = {};
366
367 /* Tags to ignore content of when KEEP_CONTENT is true */
368 var FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
369
370 /* Tags that are safe for data: URIs */
371 var DATA_URI_TAGS = null;
372 var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
373
374 /* Attributes safe for values like "javascript:" */
375 var URI_SAFE_ATTRIBUTES = null;
376 var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']);
377
378 /* Keep a reference to config to pass to hooks */
379 var CONFIG = null;
380
381 /* Ideally, do not touch anything below this line */
382 /* ______________________________________________ */
383
384 var formElement = document.createElement('form');
385
386 /**
387 * _parseConfig
388 *
389 * @param {Object} cfg optional config literal
390 */
391 // eslint-disable-next-line complexity
392 var _parseConfig = function _parseConfig(cfg) {
393 if (CONFIG && CONFIG === cfg) {
394 return;
395 }
396
397 /* Shield configuration object from tampering */
398 if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
399 cfg = {};
400 }
401
402 /* Set configuration parameters */
403 ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
404 ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
405 URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
406 DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
407 FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
408 FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
409 USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
410 ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
411 ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
412 ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
413 SAFE_FOR_JQUERY = cfg.SAFE_FOR_JQUERY || false; // Default false
414 SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
415 WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
416 RETURN_DOM = cfg.RETURN_DOM || false; // Default false
417 RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
418 RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT || false; // Default false
419 RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
420 FORCE_BODY = cfg.FORCE_BODY || false; // Default false
421 SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
422 KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
423 IN_PLACE = cfg.IN_PLACE || false; // Default false
424 IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
425 if (SAFE_FOR_TEMPLATES) {
426 ALLOW_DATA_ATTR = false;
427 }
428
429 if (RETURN_DOM_FRAGMENT) {
430 RETURN_DOM = true;
431 }
432
433 /* Parse profile info */
434 if (USE_PROFILES) {
435 ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text)));
436 ALLOWED_ATTR = [];
437 if (USE_PROFILES.html === true) {
438 addToSet(ALLOWED_TAGS, html);
439 addToSet(ALLOWED_ATTR, html$1);
440 }
441
442 if (USE_PROFILES.svg === true) {
443 addToSet(ALLOWED_TAGS, svg);
444 addToSet(ALLOWED_ATTR, svg$1);
445 addToSet(ALLOWED_ATTR, xml);
446 }
447
448 if (USE_PROFILES.svgFilters === true) {
449 addToSet(ALLOWED_TAGS, svgFilters);
450 addToSet(ALLOWED_ATTR, svg$1);
451 addToSet(ALLOWED_ATTR, xml);
452 }
453
454 if (USE_PROFILES.mathMl === true) {
455 addToSet(ALLOWED_TAGS, mathMl);
456 addToSet(ALLOWED_ATTR, mathMl$1);
457 addToSet(ALLOWED_ATTR, xml);
458 }
459 }
460
461 /* Merge configuration parameters */
462 if (cfg.ADD_TAGS) {
463 if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
464 ALLOWED_TAGS = clone(ALLOWED_TAGS);
465 }
466
467 addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
468 }
469
470 if (cfg.ADD_ATTR) {
471 if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
472 ALLOWED_ATTR = clone(ALLOWED_ATTR);
473 }
474
475 addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
476 }
477
478 if (cfg.ADD_URI_SAFE_ATTR) {
479 addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
480 }
481
482 /* Add #text in case KEEP_CONTENT is set to true */
483 if (KEEP_CONTENT) {
484 ALLOWED_TAGS['#text'] = true;
485 }
486
487 /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
488 if (WHOLE_DOCUMENT) {
489 addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
490 }
491
492 /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
493 if (ALLOWED_TAGS.table) {
494 addToSet(ALLOWED_TAGS, ['tbody']);
495 delete FORBID_TAGS.tbody;
496 }
497
498 // Prevent further manipulation of configuration.
499 // Not available in IE8, Safari 5, etc.
500 if (freeze) {
501 freeze(cfg);
502 }
503
504 CONFIG = cfg;
505 };
506
507 /**
508 * _forceRemove
509 *
510 * @param {Node} node a DOM node
511 */
512 var _forceRemove = function _forceRemove(node) {
513 arrayPush(DOMPurify.removed, { element: node });
514 try {
515 // eslint-disable-next-line unicorn/prefer-node-remove
516 node.parentNode.removeChild(node);
517 } catch (_) {
518 node.outerHTML = emptyHTML;
519 }
520 };
521
522 /**
523 * _removeAttribute
524 *
525 * @param {String} name an Attribute name
526 * @param {Node} node a DOM node
527 */
528 var _removeAttribute = function _removeAttribute(name, node) {
529 try {
530 arrayPush(DOMPurify.removed, {
531 attribute: node.getAttributeNode(name),
532 from: node
533 });
534 } catch (_) {
535 arrayPush(DOMPurify.removed, {
536 attribute: null,
537 from: node
538 });
539 }
540
541 node.removeAttribute(name);
542 };
543
544 /**
545 * _initDocument
546 *
547 * @param {String} dirty a string of dirty markup
548 * @return {Document} a DOM, filled with the dirty markup
549 */
550 var _initDocument = function _initDocument(dirty) {
551 /* Create a HTML document */
552 var doc = void 0;
553 var leadingWhitespace = void 0;
554
555 if (FORCE_BODY) {
556 dirty = '<remove></remove>' + dirty;
557 } else {
558 /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
559 var matches = stringMatch(dirty, /^[\r\n\t ]+/);
560 leadingWhitespace = matches && matches[0];
561 }
562
563 var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
564 /* Use the DOMParser API by default, fallback later if needs be */
565 try {
566 doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
567 } catch (_) {}
568
569 /* Remove title to fix a mXSS bug in older MS Edge */
570 if (removeTitle) {
571 addToSet(FORBID_TAGS, ['title']);
572 }
573
574 /* Use createHTMLDocument in case DOMParser is not available */
575 if (!doc || !doc.documentElement) {
576 doc = implementation.createHTMLDocument('');
577 var _doc = doc,
578 body = _doc.body;
579
580 body.parentNode.removeChild(body.parentNode.firstElementChild);
581 body.outerHTML = dirtyPayload;
582 }
583
584 if (dirty && leadingWhitespace) {
585 doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
586 }
587
588 /* Work on whole document or just its body */
589 return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
590 };
591
592 /* Here we test for a broken feature in Edge that might cause mXSS */
593 if (DOMPurify.isSupported) {
594 (function () {
595 try {
596 var doc = _initDocument('<x/><title>&lt;/title&gt;&lt;img&gt;');
597 if (regExpTest(/<\/title/, doc.querySelector('title').innerHTML)) {
598 removeTitle = true;
599 }
600 } catch (_) {}
601 })();
602 }
603
604 /**
605 * _createIterator
606 *
607 * @param {Document} root document/fragment to create iterator for
608 * @return {Iterator} iterator instance
609 */
610 var _createIterator = function _createIterator(root) {
611 return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () {
612 return NodeFilter.FILTER_ACCEPT;
613 }, false);
614 };
615
616 /**
617 * _isClobbered
618 *
619 * @param {Node} elm element to check for clobbering attacks
620 * @return {Boolean} true if clobbered, false if safe
621 */
622 var _isClobbered = function _isClobbered(elm) {
623 if (elm instanceof Text || elm instanceof Comment) {
624 return false;
625 }
626
627 if (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') {
628 return true;
629 }
630
631 return false;
632 };
633
634 /**
635 * _isNode
636 *
637 * @param {Node} obj object to check whether it's a DOM node
638 * @return {Boolean} true is object is a DOM node
639 */
640 var _isNode = function _isNode(object) {
641 return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
642 };
643
644 /**
645 * _executeHook
646 * Execute user configurable hooks
647 *
648 * @param {String} entryPoint Name of the hook's entry point
649 * @param {Node} currentNode node to work on with the hook
650 * @param {Object} data additional hook parameters
651 */
652 var _executeHook = function _executeHook(entryPoint, currentNode, data) {
653 if (!hooks[entryPoint]) {
654 return;
655 }
656
657 arrayForEach(hooks[entryPoint], function (hook) {
658 hook.call(DOMPurify, currentNode, data, CONFIG);
659 });
660 };
661
662 /**
663 * _sanitizeElements
664 *
665 * @protect nodeName
666 * @protect textContent
667 * @protect removeChild
668 *
669 * @param {Node} currentNode to check for permission to exist
670 * @return {Boolean} true if node was killed, false if left alive
671 */
672 // eslint-disable-next-line complexity
673 var _sanitizeElements = function _sanitizeElements(currentNode) {
674 var content = void 0;
675
676 /* Execute a hook if present */
677 _executeHook('beforeSanitizeElements', currentNode, null);
678
679 /* Check if element is clobbered or can clobber */
680 if (_isClobbered(currentNode)) {
681 _forceRemove(currentNode);
682 return true;
683 }
684
685 /* Now let's check the element's type and name */
686 var tagName = stringToLowerCase(currentNode.nodeName);
687
688 /* Execute a hook if present */
689 _executeHook('uponSanitizeElement', currentNode, {
690 tagName: tagName,
691 allowedTags: ALLOWED_TAGS
692 });
693
694 /* Take care of an mXSS pattern using p, br inside svg, math */
695 if ((tagName === 'svg' || tagName === 'math') && currentNode.querySelectorAll('p, br').length !== 0) {
696 _forceRemove(currentNode);
697 return true;
698 }
699
700 /* Remove element if anything forbids its presence */
701 if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
702 /* Keep content except for bad-listed elements */
703 if (KEEP_CONTENT && !FORBID_CONTENTS[tagName] && typeof currentNode.insertAdjacentHTML === 'function') {
704 try {
705 var htmlToInsert = currentNode.innerHTML;
706 currentNode.insertAdjacentHTML('AfterEnd', trustedTypesPolicy ? trustedTypesPolicy.createHTML(htmlToInsert) : htmlToInsert);
707 } catch (_) {}
708 }
709
710 _forceRemove(currentNode);
711 return true;
712 }
713
714 /* Remove in case a noscript/noembed XSS is suspected */
715 if (tagName === 'noscript' && regExpTest(/<\/noscript/i, currentNode.innerHTML)) {
716 _forceRemove(currentNode);
717 return true;
718 }
719
720 if (tagName === 'noembed' && regExpTest(/<\/noembed/i, currentNode.innerHTML)) {
721 _forceRemove(currentNode);
722 return true;
723 }
724
725 /* Convert markup to cover jQuery behavior */
726 if (SAFE_FOR_JQUERY && !currentNode.firstElementChild && (!currentNode.content || !currentNode.content.firstElementChild) && regExpTest(/</g, currentNode.textContent)) {
727 arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
728 if (currentNode.innerHTML) {
729 currentNode.innerHTML = stringReplace(currentNode.innerHTML, /</g, '&lt;');
730 } else {
731 currentNode.innerHTML = stringReplace(currentNode.textContent, /</g, '&lt;');
732 }
733 }
734
735 /* Sanitize element content to be template-safe */
736 if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
737 /* Get the element's text content */
738 content = currentNode.textContent;
739 content = stringReplace(content, MUSTACHE_EXPR$$1, ' ');
740 content = stringReplace(content, ERB_EXPR$$1, ' ');
741 if (currentNode.textContent !== content) {
742 arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
743 currentNode.textContent = content;
744 }
745 }
746
747 /* Execute a hook if present */
748 _executeHook('afterSanitizeElements', currentNode, null);
749
750 return false;
751 };
752
753 /**
754 * _isValidAttribute
755 *
756 * @param {string} lcTag Lowercase tag name of containing element.
757 * @param {string} lcName Lowercase attribute name.
758 * @param {string} value Attribute value.
759 * @return {Boolean} Returns true if `value` is valid, otherwise false.
760 */
761 // eslint-disable-next-line complexity
762 var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
763 /* Make sure attribute cannot clobber */
764 if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
765 return false;
766 }
767
768 /* Allow valid data-* attributes: At least one character after "-"
769 (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
770 XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
771 We don't need to check the value; it's always URI safe. */
772 if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
773 return false;
774
775 /* Check value is safe. First, is attr inert? If so, is safe */
776 } 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 {
777 return false;
778 }
779
780 return true;
781 };
782
783 /**
784 * _sanitizeAttributes
785 *
786 * @protect attributes
787 * @protect nodeName
788 * @protect removeAttribute
789 * @protect setAttribute
790 *
791 * @param {Node} currentNode to sanitize
792 */
793 // eslint-disable-next-line complexity
794 var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
795 var attr = void 0;
796 var value = void 0;
797 var lcName = void 0;
798 var idAttr = void 0;
799 var l = void 0;
800 /* Execute a hook if present */
801 _executeHook('beforeSanitizeAttributes', currentNode, null);
802
803 var attributes = currentNode.attributes;
804
805 /* Check if we have attributes; if not we might have a text node */
806
807 if (!attributes) {
808 return;
809 }
810
811 var hookEvent = {
812 attrName: '',
813 attrValue: '',
814 keepAttr: true,
815 allowedAttributes: ALLOWED_ATTR
816 };
817 l = attributes.length;
818
819 /* Go backwards over all attributes; safely remove bad ones */
820 while (l--) {
821 attr = attributes[l];
822 var _attr = attr,
823 name = _attr.name,
824 namespaceURI = _attr.namespaceURI;
825
826 value = stringTrim(attr.value);
827 lcName = stringToLowerCase(name);
828
829 /* Execute a hook if present */
830 hookEvent.attrName = lcName;
831 hookEvent.attrValue = value;
832 hookEvent.keepAttr = true;
833 hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
834 _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
835 value = hookEvent.attrValue;
836 /* Did the hooks approve of the attribute? */
837 if (hookEvent.forceKeepAttr) {
838 continue;
839 }
840
841 /* Remove attribute */
842 // Safari (iOS + Mac), last tested v8.0.5, crashes if you try to
843 // remove a "name" attribute from an <img> tag that has an "id"
844 // attribute at the time.
845 if (lcName === 'name' && currentNode.nodeName === 'IMG' && attributes.id) {
846 idAttr = attributes.id;
847 attributes = arraySlice(attributes, []);
848 _removeAttribute('id', currentNode);
849 _removeAttribute(name, currentNode);
850 if (arrayIndexOf(attributes, idAttr) > l) {
851 currentNode.setAttribute('id', idAttr.value);
852 }
853 } else if (
854 // This works around a bug in Safari, where input[type=file]
855 // cannot be dynamically set after type has been removed
856 currentNode.nodeName === 'INPUT' && lcName === 'type' && value === 'file' && hookEvent.keepAttr && (ALLOWED_ATTR[lcName] || !FORBID_ATTR[lcName])) {
857 continue;
858 } else {
859 // This avoids a crash in Safari v9.0 with double-ids.
860 // The trick is to first set the id to be empty and then to
861 // remove the attribute
862 if (name === 'id') {
863 currentNode.setAttribute(name, '');
864 }
865
866 _removeAttribute(name, currentNode);
867 }
868
869 /* Did the hooks approve of the attribute? */
870 if (!hookEvent.keepAttr) {
871 continue;
872 }
873
874 /* Work around a security issue in jQuery 3.0 */
875 if (SAFE_FOR_JQUERY && regExpTest(/\/>/i, value)) {
876 _removeAttribute(name, currentNode);
877 continue;
878 }
879
880 /* Take care of an mXSS pattern using namespace switches */
881 if (regExpTest(/svg|math/i, currentNode.namespaceURI) && regExpTest(regExpCreate('</(' + arrayJoin(objectKeys(FORBID_CONTENTS), '|') + ')', 'i'), value)) {
882 _removeAttribute(name, currentNode);
883 continue;
884 }
885
886 /* Sanitize attribute content to be template-safe */
887 if (SAFE_FOR_TEMPLATES) {
888 value = stringReplace(value, MUSTACHE_EXPR$$1, ' ');
889 value = stringReplace(value, ERB_EXPR$$1, ' ');
890 }
891
892 /* Is `value` valid for this attribute? */
893 var lcTag = currentNode.nodeName.toLowerCase();
894 if (!_isValidAttribute(lcTag, lcName, value)) {
895 continue;
896 }
897
898 /* Handle invalid data-* attribute set by try-catching it */
899 try {
900 if (namespaceURI) {
901 currentNode.setAttributeNS(namespaceURI, name, value);
902 } else {
903 /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
904 currentNode.setAttribute(name, value);
905 }
906
907 arrayPop(DOMPurify.removed);
908 } catch (_) {}
909 }
910
911 /* Execute a hook if present */
912 _executeHook('afterSanitizeAttributes', currentNode, null);
913 };
914
915 /**
916 * _sanitizeShadowDOM
917 *
918 * @param {DocumentFragment} fragment to iterate over recursively
919 */
920 var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
921 var shadowNode = void 0;
922 var shadowIterator = _createIterator(fragment);
923
924 /* Execute a hook if present */
925 _executeHook('beforeSanitizeShadowDOM', fragment, null);
926
927 while (shadowNode = shadowIterator.nextNode()) {
928 /* Execute a hook if present */
929 _executeHook('uponSanitizeShadowNode', shadowNode, null);
930
931 /* Sanitize tags and elements */
932 if (_sanitizeElements(shadowNode)) {
933 continue;
934 }
935
936 /* Deep shadow DOM detected */
937 if (shadowNode.content instanceof DocumentFragment) {
938 _sanitizeShadowDOM(shadowNode.content);
939 }
940
941 /* Check attributes, sanitize if necessary */
942 _sanitizeAttributes(shadowNode);
943 }
944
945 /* Execute a hook if present */
946 _executeHook('afterSanitizeShadowDOM', fragment, null);
947 };
948
949 /**
950 * Sanitize
951 * Public method providing core sanitation functionality
952 *
953 * @param {String|Node} dirty string or DOM node
954 * @param {Object} configuration object
955 */
956 // eslint-disable-next-line complexity
957 DOMPurify.sanitize = function (dirty, cfg) {
958 var body = void 0;
959 var importedNode = void 0;
960 var currentNode = void 0;
961 var oldNode = void 0;
962 var returnNode = void 0;
963 /* Make sure we have a string to sanitize.
964 DO NOT return early, as this will return the wrong type if
965 the user has requested a DOM object rather than a string */
966 if (!dirty) {
967 dirty = '<!-->';
968 }
969
970 /* Stringify, in case dirty is an object */
971 if (typeof dirty !== 'string' && !_isNode(dirty)) {
972 // eslint-disable-next-line no-negated-condition
973 if (typeof dirty.toString !== 'function') {
974 throw typeErrorCreate('toString is not a function');
975 } else {
976 dirty = dirty.toString();
977 if (typeof dirty !== 'string') {
978 throw typeErrorCreate('dirty is not a string, aborting');
979 }
980 }
981 }
982
983 /* Check we can run. Otherwise fall back or ignore */
984 if (!DOMPurify.isSupported) {
985 if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
986 if (typeof dirty === 'string') {
987 return window.toStaticHTML(dirty);
988 }
989
990 if (_isNode(dirty)) {
991 return window.toStaticHTML(dirty.outerHTML);
992 }
993 }
994
995 return dirty;
996 }
997
998 /* Assign config vars */
999 if (!SET_CONFIG) {
1000 _parseConfig(cfg);
1001 }
1002
1003 /* Clean up removed elements */
1004 DOMPurify.removed = [];
1005
1006 /* Check if dirty is correctly typed for IN_PLACE */
1007 if (typeof dirty === 'string') {
1008 IN_PLACE = false;
1009 }
1010
1011 if (IN_PLACE) ; else if (dirty instanceof Node) {
1012 /* If dirty is a DOM element, append to an empty document to avoid
1013 elements being stripped by the parser */
1014 body = _initDocument('<!-->');
1015 importedNode = body.ownerDocument.importNode(dirty, true);
1016 if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1017 /* Node is already a body, use as is */
1018 body = importedNode;
1019 } else if (importedNode.nodeName === 'HTML') {
1020 body = importedNode;
1021 } else {
1022 // eslint-disable-next-line unicorn/prefer-node-append
1023 body.appendChild(importedNode);
1024 }
1025 } else {
1026 /* Exit directly if we have nothing to do */
1027 if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
1028 // eslint-disable-next-line unicorn/prefer-includes
1029 dirty.indexOf('<') === -1) {
1030 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
1031 }
1032
1033 /* Initialize the document to work on */
1034 body = _initDocument(dirty);
1035
1036 /* Check we have a DOM node from the data */
1037 if (!body) {
1038 return RETURN_DOM ? null : emptyHTML;
1039 }
1040 }
1041
1042 /* Remove first element node (ours) if FORCE_BODY is set */
1043 if (body && FORCE_BODY) {
1044 _forceRemove(body.firstChild);
1045 }
1046
1047 /* Get node iterator */
1048 var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1049
1050 /* Now start iterating over the created document */
1051 while (currentNode = nodeIterator.nextNode()) {
1052 /* Fix IE's strange behavior with manipulated textNodes #89 */
1053 if (currentNode.nodeType === 3 && currentNode === oldNode) {
1054 continue;
1055 }
1056
1057 /* Sanitize tags and elements */
1058 if (_sanitizeElements(currentNode)) {
1059 continue;
1060 }
1061
1062 /* Shadow DOM detected, sanitize it */
1063 if (currentNode.content instanceof DocumentFragment) {
1064 _sanitizeShadowDOM(currentNode.content);
1065 }
1066
1067 /* Check attributes, sanitize if necessary */
1068 _sanitizeAttributes(currentNode);
1069
1070 oldNode = currentNode;
1071 }
1072
1073 oldNode = null;
1074
1075 /* If we sanitized `dirty` in-place, return it. */
1076 if (IN_PLACE) {
1077 return dirty;
1078 }
1079
1080 /* Return sanitized string or DOM */
1081 if (RETURN_DOM) {
1082 if (RETURN_DOM_FRAGMENT) {
1083 returnNode = createDocumentFragment.call(body.ownerDocument);
1084
1085 while (body.firstChild) {
1086 // eslint-disable-next-line unicorn/prefer-node-append
1087 returnNode.appendChild(body.firstChild);
1088 }
1089 } else {
1090 returnNode = body;
1091 }
1092
1093 if (RETURN_DOM_IMPORT) {
1094 /*
1095 AdoptNode() is not used because internal state is not reset
1096 (e.g. the past names map of a HTMLFormElement), this is safe
1097 in theory but we would rather not risk another attack vector.
1098 The state that is cloned by importNode() is explicitly defined
1099 by the specs.
1100 */
1101 returnNode = importNode.call(originalDocument, returnNode, true);
1102 }
1103
1104 return returnNode;
1105 }
1106
1107 var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1108
1109 /* Sanitize final string template-safe */
1110 if (SAFE_FOR_TEMPLATES) {
1111 serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' ');
1112 serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');
1113 }
1114
1115 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1116 };
1117
1118 /**
1119 * Public method to set the configuration once
1120 * setConfig
1121 *
1122 * @param {Object} cfg configuration object
1123 */
1124 DOMPurify.setConfig = function (cfg) {
1125 _parseConfig(cfg);
1126 SET_CONFIG = true;
1127 };
1128
1129 /**
1130 * Public method to remove the configuration
1131 * clearConfig
1132 *
1133 */
1134 DOMPurify.clearConfig = function () {
1135 CONFIG = null;
1136 SET_CONFIG = false;
1137 };
1138
1139 /**
1140 * Public method to check if an attribute value is valid.
1141 * Uses last set config, if any. Otherwise, uses config defaults.
1142 * isValidAttribute
1143 *
1144 * @param {string} tag Tag name of containing element.
1145 * @param {string} attr Attribute name.
1146 * @param {string} value Attribute value.
1147 * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1148 */
1149 DOMPurify.isValidAttribute = function (tag, attr, value) {
1150 /* Initialize shared config vars if necessary. */
1151 if (!CONFIG) {
1152 _parseConfig({});
1153 }
1154
1155 var lcTag = stringToLowerCase(tag);
1156 var lcName = stringToLowerCase(attr);
1157 return _isValidAttribute(lcTag, lcName, value);
1158 };
1159
1160 /**
1161 * AddHook
1162 * Public method to add DOMPurify hooks
1163 *
1164 * @param {String} entryPoint entry point for the hook to add
1165 * @param {Function} hookFunction function to execute
1166 */
1167 DOMPurify.addHook = function (entryPoint, hookFunction) {
1168 if (typeof hookFunction !== 'function') {
1169 return;
1170 }
1171
1172 hooks[entryPoint] = hooks[entryPoint] || [];
1173 arrayPush(hooks[entryPoint], hookFunction);
1174 };
1175
1176 /**
1177 * RemoveHook
1178 * Public method to remove a DOMPurify hook at a given entryPoint
1179 * (pops it from the stack of hooks if more are present)
1180 *
1181 * @param {String} entryPoint entry point for the hook to remove
1182 */
1183 DOMPurify.removeHook = function (entryPoint) {
1184 if (hooks[entryPoint]) {
1185 arrayPop(hooks[entryPoint]);
1186 }
1187 };
1188
1189 /**
1190 * RemoveHooks
1191 * Public method to remove all DOMPurify hooks at a given entryPoint
1192 *
1193 * @param {String} entryPoint entry point for the hooks to remove
1194 */
1195 DOMPurify.removeHooks = function (entryPoint) {
1196 if (hooks[entryPoint]) {
1197 hooks[entryPoint] = [];
1198 }
1199 };
1200
1201 /**
1202 * RemoveAllHooks
1203 * Public method to remove all DOMPurify hooks
1204 *
1205 */
1206 DOMPurify.removeAllHooks = function () {
1207 hooks = {};
1208 };
1209
1210 return DOMPurify;
1211 }
1212
1213 var purify = createDOMPurify();
1214
1215 return purify;
1216
1217}));
1218//# sourceMappingURL=purify.js.map
1219
\No newline at end of file