PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.5
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.5
2.4.5 2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 All 69 releases
fluent-support / assets / libs / purify / purify.js

purify.js in Fluent Support – Helpdesk & Customer Support Ticket System 2.4.5, at assets/libs/purify/purify.js

2,468 lines 85.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*! @license DOMPurify 3.4.16 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.16/LICENSE */
2 (function(global, factory) {
3 typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define([], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, global.DOMPurify = factory());
4 })(this, function() {
5 "use strict";
6 function _OverloadYield(e, d) {
7 this.v = e, this.k = d;
8 }
9 function _arrayLikeToArray(r, a) {
10 (null == a || a > r.length) && (a = r.length);
11 for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
12 return n;
13 }
14 function _arrayWithHoles(r) {
15 if (Array.isArray(r)) return r;
16 }
17 function _iterableToArrayLimit(r, l) {
18 var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
19 if (null != t) {
20 var e, n, i, u, a = [], f = !0, o = !1;
21 try {
22 if (i = (t = t.call(r)).next, 0 === l) {
23 if (Object(t) !== t) return;
24 f = !1;
25 } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
26 } catch (r) {
27 o = !0, n = r;
28 } finally {
29 try {
30 if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
31 } finally {
32 if (o) throw n;
33 }
34 }
35 return a;
36 }
37 }
38 function _nonIterableRest() {
39 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
40 }
41 /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
42 function _slicedToArray(r, e) {
43 return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
44 }
45 function _unsupportedIterableToArray(r, a) {
46 if (r) {
47 if ("string" == typeof r) return _arrayLikeToArray(r, a);
48 var t = {}.toString.call(r).slice(8, -1);
49 return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
50 }
51 }
52 function AsyncGenerator(e) {
53 var t, n;
54 function resume(t, n) {
55 try {
56 var r = e[t](n), o = r.value, u = o instanceof _OverloadYield;
57 Promise.resolve(u ? o.v : o).then(function(n) {
58 if (u) {
59 var i = "return" === t && o.k ? t : "next";
60 if (!o.k || n.done) return resume(i, n);
61 n = e[i](n).value;
62 }
63 settle(!!r.done, n);
64 }, function(e) {
65 resume("throw", e);
66 });
67 } catch (e) {
68 settle(2, e);
69 }
70 }
71 function settle(e, r) {
72 2 === e ? t.reject(r) : t.resolve({
73 value: r,
74 done: e
75 }), (t = t.next) ? resume(t.key, t.arg) : n = null;
76 }
77 this._invoke = function(e, r) {
78 return new Promise(function(o, u) {
79 var i = {
80 key: e,
81 arg: r,
82 resolve: o,
83 reject: u,
84 next: null
85 };
86 n ? n = n.next = i : (t = n = i, resume(e, r));
87 });
88 }, "function" != typeof e.return && (this.return = void 0);
89 }
90 AsyncGenerator.prototype["function" == typeof Symbol && Symbol.asyncIterator || "@@asyncIterator"] = function() {
91 return this;
92 }, AsyncGenerator.prototype.next = function(e) {
93 return this._invoke("next", e);
94 }, AsyncGenerator.prototype.throw = function(e) {
95 return this._invoke("throw", e);
96 }, AsyncGenerator.prototype.return = function(e) {
97 return this._invoke("return", e);
98 };
99 const entries = Object.entries;
100 const setPrototypeOf = Object.setPrototypeOf;
101 const isFrozen = Object.isFrozen;
102 const getPrototypeOf = Object.getPrototypeOf;
103 const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
104 let freeze = Object.freeze;
105 let seal = Object.seal;
106 let create = Object.create;
107 let _ref = typeof Reflect !== "undefined" && Reflect;
108 let apply = _ref.apply;
109 let construct = _ref.construct;
110 if (!freeze) freeze = function freeze(x) {
111 return x;
112 };
113 if (!seal) seal = function seal(x) {
114 return x;
115 };
116 if (!apply) apply = function apply(func, thisArg) {
117 for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) args[_key - 2] = arguments[_key];
118 return func.apply(thisArg, args);
119 };
120 if (!construct) construct = function construct(Func) {
121 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) args[_key2 - 1] = arguments[_key2];
122 return new Func(...args);
123 };
124 const arrayForEach = unapply(Array.prototype.forEach);
125 Array.prototype.indexOf;
126 const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
127 const arrayPop = unapply(Array.prototype.pop);
128 const arrayPush = unapply(Array.prototype.push);
129 Array.prototype.slice;
130 const arraySplice = unapply(Array.prototype.splice);
131 const arrayIsArray = Array.isArray;
132 const stringToLowerCase = unapply(String.prototype.toLowerCase);
133 const stringToString = unapply(String.prototype.toString);
134 const stringMatch = unapply(String.prototype.match);
135 const stringReplace = unapply(String.prototype.replace);
136 const stringIndexOf = unapply(String.prototype.indexOf);
137 const stringTrim = unapply(String.prototype.trim);
138 const numberToString = unapply(Number.prototype.toString);
139 const booleanToString = unapply(Boolean.prototype.toString);
140 const bigintToString = typeof BigInt === "undefined" ? null : unapply(BigInt.prototype.toString);
141 const symbolToString = typeof Symbol === "undefined" ? null : unapply(Symbol.prototype.toString);
142 const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
143 const objectToString = unapply(Object.prototype.toString);
144 const regExpTest = unapply(RegExp.prototype.test);
145 const typeErrorCreate = unconstruct(TypeError);
146 /**
147 * Creates a new function that calls the given function with a specified thisArg and arguments.
148 *
149 * @param func - The function to be wrapped and called.
150 * @returns A new function that calls the given function with a specified thisArg and arguments.
151 */
152 function unapply(func) {
153 return function(thisArg) {
154 if (thisArg instanceof RegExp) thisArg.lastIndex = 0;
155 for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) args[_key3 - 1] = arguments[_key3];
156 return apply(func, thisArg, args);
157 };
158 }
159 /**
160 * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
161 *
162 * @param func - The constructor function to be wrapped and called.
163 * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
164 */
165 function unconstruct(Func) {
166 return function() {
167 for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) args[_key4] = arguments[_key4];
168 return construct(Func, args);
169 };
170 }
171 /**
172 * Add properties to a lookup table
173 *
174 * @param set - The set to which elements will be added.
175 * @param array - The array containing elements to be added to the set.
176 * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
177 * @returns The modified set with added elements.
178 */
179 function addToSet(set, array) {
180 let transformCaseFunc = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : stringToLowerCase;
181 if (setPrototypeOf) setPrototypeOf(set, null);
182 if (!arrayIsArray(array)) return set;
183 let l = array.length;
184 while (l--) {
185 let element = array[l];
186 if (typeof element === "string") {
187 const lcElement = transformCaseFunc(element);
188 if (lcElement !== element) {
189 if (!isFrozen(array)) array[l] = lcElement;
190 element = lcElement;
191 }
192 }
193 set[element] = true;
194 }
195 return set;
196 }
197 /**
198 * Clean up an array to harden against CSPP
199 *
200 * @param array - The array to be cleaned.
201 * @returns The cleaned version of the array
202 */
203 function cleanArray(array) {
204 for (let index = 0; index < array.length; index++) if (!objectHasOwnProperty(array, index)) array[index] = null;
205 return array;
206 }
207 /**
208 * Shallow clone an object
209 *
210 * @param object - The object to be cloned.
211 * @returns A new object that copies the original.
212 */
213 function clone(object) {
214 const newObject = create(null);
215 for (const _ref2 of entries(object)) {
216 var _ref3 = _slicedToArray(_ref2, 2);
217 const property = _ref3[0];
218 const value = _ref3[1];
219 if (objectHasOwnProperty(object, property)) {
220 if (arrayIsArray(value)) newObject[property] = cleanArray(value);
221 else if (value && typeof value === "object" && value.constructor === Object) newObject[property] = clone(value);
222 else newObject[property] = value;
223 }
224 }
225 return newObject;
226 }
227 /**
228 * Convert non-node values into strings without depending on direct property access.
229 *
230 * @param value - The value to stringify.
231 * @returns A string representation of the provided value.
232 */
233 function stringifyValue(value) {
234 switch (typeof value) {
235 case "string": return value;
236 case "number": return numberToString(value);
237 case "boolean": return booleanToString(value);
238 case "bigint": return bigintToString ? bigintToString(value) : "0";
239 case "symbol": return symbolToString ? symbolToString(value) : "Symbol()";
240 case "undefined": return objectToString(value);
241 case "function":
242 case "object": {
243 if (value === null) return objectToString(value);
244 const valueAsRecord = value;
245 const valueToString = lookupGetter(valueAsRecord, "toString");
246 if (typeof valueToString === "function") {
247 const stringified = valueToString(valueAsRecord);
248 return typeof stringified === "string" ? stringified : objectToString(stringified);
249 }
250 return objectToString(value);
251 }
252 default: return objectToString(value);
253 }
254 }
255 /**
256 * This method automatically checks if the prop is function or getter and behaves accordingly.
257 *
258 * @param object - The object to look up the getter function in its prototype chain.
259 * @param prop - The property name for which to find the getter function.
260 * @returns The getter function found in the prototype chain or a fallback function.
261 */
262 function lookupGetter(object, prop) {
263 while (object !== null) {
264 const desc = getOwnPropertyDescriptor(object, prop);
265 if (desc) {
266 if (desc.get) return unapply(desc.get);
267 if (typeof desc.value === "function") return unapply(desc.value);
268 }
269 object = getPrototypeOf(object);
270 }
271 function fallbackValue() {
272 return null;
273 }
274 return fallbackValue;
275 }
276 function isRegex(value) {
277 try {
278 regExpTest(value, "");
279 return true;
280 } catch (_unused) {
281 return false;
282 }
283 }
284 const html$1 = freeze([
285 "a",
286 "abbr",
287 "acronym",
288 "address",
289 "area",
290 "article",
291 "aside",
292 "audio",
293 "b",
294 "bdi",
295 "bdo",
296 "big",
297 "blink",
298 "blockquote",
299 "body",
300 "br",
301 "button",
302 "canvas",
303 "caption",
304 "center",
305 "cite",
306 "code",
307 "col",
308 "colgroup",
309 "content",
310 "data",
311 "datalist",
312 "dd",
313 "decorator",
314 "del",
315 "details",
316 "dfn",
317 "dialog",
318 "dir",
319 "div",
320 "dl",
321 "dt",
322 "element",
323 "em",
324 "fieldset",
325 "figcaption",
326 "figure",
327 "font",
328 "footer",
329 "form",
330 "h1",
331 "h2",
332 "h3",
333 "h4",
334 "h5",
335 "h6",
336 "head",
337 "header",
338 "hgroup",
339 "hr",
340 "html",
341 "i",
342 "img",
343 "input",
344 "ins",
345 "kbd",
346 "label",
347 "legend",
348 "li",
349 "main",
350 "map",
351 "mark",
352 "marquee",
353 "menu",
354 "menuitem",
355 "meter",
356 "nav",
357 "nobr",
358 "ol",
359 "optgroup",
360 "option",
361 "output",
362 "p",
363 "picture",
364 "pre",
365 "progress",
366 "q",
367 "rp",
368 "rt",
369 "ruby",
370 "s",
371 "samp",
372 "search",
373 "section",
374 "select",
375 "shadow",
376 "slot",
377 "small",
378 "source",
379 "spacer",
380 "span",
381 "strike",
382 "strong",
383 "style",
384 "sub",
385 "summary",
386 "sup",
387 "table",
388 "tbody",
389 "td",
390 "template",
391 "textarea",
392 "tfoot",
393 "th",
394 "thead",
395 "time",
396 "tr",
397 "track",
398 "tt",
399 "u",
400 "ul",
401 "var",
402 "video",
403 "wbr"
404 ]);
405 const svg$1 = freeze([
406 "svg",
407 "a",
408 "altglyph",
409 "altglyphdef",
410 "altglyphitem",
411 "animatecolor",
412 "animatemotion",
413 "animatetransform",
414 "circle",
415 "clippath",
416 "defs",
417 "desc",
418 "ellipse",
419 "enterkeyhint",
420 "exportparts",
421 "filter",
422 "font",
423 "g",
424 "glyph",
425 "glyphref",
426 "hkern",
427 "image",
428 "inputmode",
429 "line",
430 "lineargradient",
431 "marker",
432 "mask",
433 "metadata",
434 "mpath",
435 "part",
436 "path",
437 "pattern",
438 "polygon",
439 "polyline",
440 "radialgradient",
441 "rect",
442 "stop",
443 "style",
444 "switch",
445 "symbol",
446 "text",
447 "textpath",
448 "title",
449 "tref",
450 "tspan",
451 "view",
452 "vkern"
453 ]);
454 const svgFilters = freeze([
455 "feBlend",
456 "feColorMatrix",
457 "feComponentTransfer",
458 "feComposite",
459 "feConvolveMatrix",
460 "feDiffuseLighting",
461 "feDisplacementMap",
462 "feDistantLight",
463 "feDropShadow",
464 "feFlood",
465 "feFuncA",
466 "feFuncB",
467 "feFuncG",
468 "feFuncR",
469 "feGaussianBlur",
470 "feImage",
471 "feMerge",
472 "feMergeNode",
473 "feMorphology",
474 "feOffset",
475 "fePointLight",
476 "feSpecularLighting",
477 "feSpotLight",
478 "feTile",
479 "feTurbulence"
480 ]);
481 const svgDisallowed = freeze([
482 "animate",
483 "color-profile",
484 "cursor",
485 "discard",
486 "font-face",
487 "font-face-format",
488 "font-face-name",
489 "font-face-src",
490 "font-face-uri",
491 "foreignobject",
492 "hatch",
493 "hatchpath",
494 "mesh",
495 "meshgradient",
496 "meshpatch",
497 "meshrow",
498 "missing-glyph",
499 "script",
500 "set",
501 "solidcolor",
502 "unknown",
503 "use"
504 ]);
505 const mathMl$1 = freeze([
506 "math",
507 "menclose",
508 "merror",
509 "mfenced",
510 "mfrac",
511 "mglyph",
512 "mi",
513 "mlabeledtr",
514 "mmultiscripts",
515 "mn",
516 "mo",
517 "mover",
518 "mpadded",
519 "mphantom",
520 "mroot",
521 "mrow",
522 "ms",
523 "mspace",
524 "msqrt",
525 "mstyle",
526 "msub",
527 "msup",
528 "msubsup",
529 "mtable",
530 "mtd",
531 "mtext",
532 "mtr",
533 "munder",
534 "munderover",
535 "mprescripts"
536 ]);
537 const mathMlDisallowed = freeze([
538 "maction",
539 "maligngroup",
540 "malignmark",
541 "mlongdiv",
542 "mscarries",
543 "mscarry",
544 "msgroup",
545 "mstack",
546 "msline",
547 "msrow",
548 "semantics",
549 "annotation",
550 "annotation-xml",
551 "mprescripts",
552 "none"
553 ]);
554 const text = freeze(["#text"]);
555 const html = freeze([
556 "accept",
557 "action",
558 "align",
559 "alt",
560 "autocapitalize",
561 "autocomplete",
562 "autopictureinpicture",
563 "autoplay",
564 "background",
565 "bgcolor",
566 "border",
567 "capture",
568 "cellpadding",
569 "cellspacing",
570 "checked",
571 "cite",
572 "class",
573 "clear",
574 "color",
575 "cols",
576 "colspan",
577 "command",
578 "commandfor",
579 "controls",
580 "controlslist",
581 "coords",
582 "crossorigin",
583 "datetime",
584 "decoding",
585 "default",
586 "dir",
587 "disabled",
588 "disablepictureinpicture",
589 "disableremoteplayback",
590 "download",
591 "draggable",
592 "enctype",
593 "enterkeyhint",
594 "exportparts",
595 "face",
596 "for",
597 "headers",
598 "height",
599 "hidden",
600 "high",
601 "href",
602 "hreflang",
603 "id",
604 "inert",
605 "inputmode",
606 "integrity",
607 "ismap",
608 "kind",
609 "label",
610 "lang",
611 "list",
612 "loading",
613 "loop",
614 "low",
615 "max",
616 "maxlength",
617 "media",
618 "method",
619 "min",
620 "minlength",
621 "multiple",
622 "muted",
623 "name",
624 "nonce",
625 "noshade",
626 "novalidate",
627 "nowrap",
628 "open",
629 "optimum",
630 "part",
631 "pattern",
632 "placeholder",
633 "playsinline",
634 "popover",
635 "popovertarget",
636 "popovertargetaction",
637 "poster",
638 "preload",
639 "pubdate",
640 "radiogroup",
641 "readonly",
642 "rel",
643 "required",
644 "rev",
645 "reversed",
646 "role",
647 "rows",
648 "rowspan",
649 "spellcheck",
650 "scope",
651 "selected",
652 "shape",
653 "size",
654 "sizes",
655 "slot",
656 "span",
657 "srclang",
658 "start",
659 "src",
660 "srcset",
661 "step",
662 "style",
663 "summary",
664 "tabindex",
665 "title",
666 "translate",
667 "type",
668 "usemap",
669 "valign",
670 "value",
671 "width",
672 "wrap",
673 "xmlns"
674 ]);
675 const svg = freeze([
676 "accent-height",
677 "accumulate",
678 "additive",
679 "alignment-baseline",
680 "amplitude",
681 "ascent",
682 "attributename",
683 "attributetype",
684 "azimuth",
685 "basefrequency",
686 "baseline-shift",
687 "begin",
688 "bias",
689 "by",
690 "class",
691 "clip",
692 "clippathunits",
693 "clip-path",
694 "clip-rule",
695 "color",
696 "color-interpolation",
697 "color-interpolation-filters",
698 "color-profile",
699 "color-rendering",
700 "cx",
701 "cy",
702 "d",
703 "dx",
704 "dy",
705 "diffuseconstant",
706 "direction",
707 "display",
708 "divisor",
709 "dominant-baseline",
710 "dur",
711 "edgemode",
712 "elevation",
713 "end",
714 "exponent",
715 "fill",
716 "fill-opacity",
717 "fill-rule",
718 "filter",
719 "filterunits",
720 "flood-color",
721 "flood-opacity",
722 "font-family",
723 "font-size",
724 "font-size-adjust",
725 "font-stretch",
726 "font-style",
727 "font-variant",
728 "font-weight",
729 "fx",
730 "fy",
731 "g1",
732 "g2",
733 "glyph-name",
734 "glyphref",
735 "gradientunits",
736 "gradienttransform",
737 "height",
738 "href",
739 "id",
740 "image-rendering",
741 "in",
742 "in2",
743 "intercept",
744 "k",
745 "k1",
746 "k2",
747 "k3",
748 "k4",
749 "kerning",
750 "keypoints",
751 "keysplines",
752 "keytimes",
753 "lang",
754 "lengthadjust",
755 "letter-spacing",
756 "kernelmatrix",
757 "kernelunitlength",
758 "lighting-color",
759 "local",
760 "marker-end",
761 "marker-mid",
762 "marker-start",
763 "markerheight",
764 "markerunits",
765 "markerwidth",
766 "maskcontentunits",
767 "maskunits",
768 "max",
769 "mask",
770 "mask-type",
771 "media",
772 "method",
773 "mode",
774 "min",
775 "name",
776 "numoctaves",
777 "offset",
778 "operator",
779 "opacity",
780 "order",
781 "orient",
782 "orientation",
783 "origin",
784 "overflow",
785 "paint-order",
786 "path",
787 "pathlength",
788 "patterncontentunits",
789 "patterntransform",
790 "patternunits",
791 "pointer-events",
792 "points",
793 "preservealpha",
794 "preserveaspectratio",
795 "primitiveunits",
796 "r",
797 "rx",
798 "ry",
799 "radius",
800 "refx",
801 "refy",
802 "repeatcount",
803 "repeatdur",
804 "restart",
805 "result",
806 "rotate",
807 "scale",
808 "seed",
809 "shape-rendering",
810 "slope",
811 "specularconstant",
812 "specularexponent",
813 "spreadmethod",
814 "startoffset",
815 "stddeviation",
816 "stitchtiles",
817 "stop-color",
818 "stop-opacity",
819 "stroke-dasharray",
820 "stroke-dashoffset",
821 "stroke-linecap",
822 "stroke-linejoin",
823 "stroke-miterlimit",
824 "stroke-opacity",
825 "stroke",
826 "stroke-width",
827 "style",
828 "surfacescale",
829 "systemlanguage",
830 "tabindex",
831 "tablevalues",
832 "targetx",
833 "targety",
834 "transform",
835 "transform-origin",
836 "text-anchor",
837 "text-decoration",
838 "text-orientation",
839 "text-rendering",
840 "textlength",
841 "type",
842 "u1",
843 "u2",
844 "unicode",
845 "values",
846 "vector-effect",
847 "viewbox",
848 "visibility",
849 "version",
850 "vert-adv-y",
851 "vert-origin-x",
852 "vert-origin-y",
853 "width",
854 "word-spacing",
855 "wrap",
856 "writing-mode",
857 "xchannelselector",
858 "ychannelselector",
859 "x",
860 "x1",
861 "x2",
862 "xmlns",
863 "y",
864 "y1",
865 "y2",
866 "z",
867 "zoomandpan"
868 ]);
869 const mathMl = freeze([
870 "accent",
871 "accentunder",
872 "align",
873 "bevelled",
874 "close",
875 "columnalign",
876 "columnlines",
877 "columnspacing",
878 "columnspan",
879 "denomalign",
880 "depth",
881 "dir",
882 "display",
883 "displaystyle",
884 "encoding",
885 "fence",
886 "frame",
887 "height",
888 "href",
889 "id",
890 "largeop",
891 "length",
892 "linethickness",
893 "lquote",
894 "lspace",
895 "mathbackground",
896 "mathcolor",
897 "mathsize",
898 "mathvariant",
899 "maxsize",
900 "minsize",
901 "movablelimits",
902 "notation",
903 "numalign",
904 "open",
905 "rowalign",
906 "rowlines",
907 "rowspacing",
908 "rowspan",
909 "rspace",
910 "rquote",
911 "scriptlevel",
912 "scriptminsize",
913 "scriptsizemultiplier",
914 "selection",
915 "separator",
916 "separators",
917 "stretchy",
918 "subscriptshift",
919 "supscriptshift",
920 "symmetric",
921 "voffset",
922 "width",
923 "xmlns"
924 ]);
925 const xml = freeze([
926 "xlink:href",
927 "xml:id",
928 "xlink:title",
929 "xml:space",
930 "xmlns:xlink"
931 ]);
932 const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
933 const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g);
934 const TMPLIT_EXPR = seal(/\${[\w\W]*/g);
935 const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/);
936 const ARIA_ATTR = seal(/^aria-[\-\w]+$/);
937 const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i);
938 const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
939 const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g);
940 const DOCTYPE_NAME = seal(/^html$/i);
941 const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
942 const ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g);
943 const COMMENT_MARKUP_PROBE = seal(/<[/\w]/g);
944 const FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i);
945 const SELF_CLOSING_TAG = seal(/\/>/i);
946 const NODE_TYPE = {
947 element: 1,
948 attribute: 2,
949 text: 3,
950 cdataSection: 4,
951 entityReference: 5,
952 entityNode: 6,
953 processingInstruction: 7,
954 comment: 8,
955 document: 9,
956 documentType: 10,
957 documentFragment: 11,
958 notation: 12
959 };
960 const LITERAL_TEXT_ELEMENT_NAMES = [
961 "style",
962 "script",
963 "xmp",
964 "iframe",
965 "noembed",
966 "noframes",
967 "plaintext",
968 "noscript"
969 ];
970 const LITERAL_TEXT_ELEMENTS = freeze(addToSet({}, LITERAL_TEXT_ELEMENT_NAMES));
971 const LITERAL_TEXT_CLOSE = function() {
972 const map = {};
973 arrayForEach(LITERAL_TEXT_ELEMENT_NAMES, (name) => {
974 map[name] = seal(new RegExp("</" + name + "(?=[\\t\\n\\f\\r />])", "i"));
975 });
976 return freeze(map);
977 }();
978 const getGlobal = function getGlobal() {
979 return typeof window === "undefined" ? null : window;
980 };
981 /**
982 * Creates a no-op policy for internal use only.
983 * Don't export this function outside this module!
984 * @param trustedTypes The policy factory.
985 * @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
986 * @return The policy created (or null, if Trusted Types
987 * are not supported or creating the policy failed).
988 */
989 const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
990 if (typeof trustedTypes !== "object" || typeof trustedTypes.createPolicy !== "function") return null;
991 let suffix = null;
992 const ATTR_NAME = "data-tt-policy-suffix";
993 if (purifyHostElement && purifyHostElement.hasAttribute(ATTR_NAME)) suffix = purifyHostElement.getAttribute(ATTR_NAME);
994 const policyName = "dompurify" + (suffix ? "#" + suffix : "");
995 try {
996 return trustedTypes.createPolicy(policyName, {
997 createHTML(html) {
998 return html;
999 },
1000 createScriptURL(scriptUrl) {
1001 return scriptUrl;
1002 }
1003 });
1004 } catch (_) {
1005 console.warn("TrustedTypes policy " + policyName + " could not be created.");
1006 return null;
1007 }
1008 };
1009 const _createHooksMap = function _createHooksMap() {
1010 return {
1011 afterSanitizeAttributes: [],
1012 afterSanitizeElements: [],
1013 afterSanitizeShadowDOM: [],
1014 beforeSanitizeAttributes: [],
1015 beforeSanitizeElements: [],
1016 beforeSanitizeShadowDOM: [],
1017 uponSanitizeAttribute: [],
1018 uponSanitizeElement: [],
1019 uponSanitizeShadowNode: []
1020 };
1021 };
1022 /**
1023 * Resolve a set-valued configuration option: a fresh set built from
1024 * cfg[key] when it is an own array property (seeded with a clone of
1025 * options.base when given, case-normalized via options.transform),
1026 * the fallback set otherwise.
1027 *
1028 * @param cfg the cloned, prototype-free configuration object
1029 * @param key the configuration property to read
1030 * @param fallback the set to use when the option is absent or not an array
1031 * @param options transform and optional base set to merge into
1032 * @returns the resolved set
1033 */
1034 const _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options) {
1035 return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;
1036 };
1037 /**
1038 * Resolve an object-valued configuration option: a prototype-free clone
1039 * of cfg[key] when it is an own, truthy object property, else a fresh
1040 * fallback built by makeFallback (fresh on every parse, so a previous
1041 * parse can never leak state into the next one).
1042 *
1043 * @param cfg the cloned, prototype-free configuration object
1044 * @param key the configuration property to read
1045 * @param makeFallback builds the fallback value when the option is absent
1046 * @returns the resolved object
1047 */
1048 const _resolveObjectOption = function _resolveObjectOption(cfg, key, makeFallback) {
1049 const value = objectHasOwnProperty(cfg, key) ? cfg[key] : void 0;
1050 return value && typeof value === "object" ? clone(value) : makeFallback();
1051 };
1052 function createDOMPurify() {
1053 let window = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
1054 const DOMPurify = (root) => createDOMPurify(root);
1055 DOMPurify.version = "3.4.16";
1056 DOMPurify.removed = [];
1057 if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
1058 DOMPurify.isSupported = false;
1059 return DOMPurify;
1060 }
1061 let document = window.document;
1062 const originalDocument = document;
1063 const currentScript = originalDocument.currentScript;
1064 window.DocumentFragment;
1065 const HTMLTemplateElement = window.HTMLTemplateElement, Node = window.Node, Element = window.Element, NodeFilter = window.NodeFilter;
1066 window.NamedNodeMap === void 0 && (window.NamedNodeMap || window.MozNamedAttrMap);
1067 window.HTMLFormElement;
1068 const DOMParser = window.DOMParser, trustedTypes = window.trustedTypes;
1069 const ElementPrototype = Element.prototype;
1070 const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
1071 const remove = lookupGetter(ElementPrototype, "remove");
1072 const removeAttributeNode = lookupGetter(ElementPrototype, "removeAttributeNode");
1073 const getNextSibling = lookupGetter(ElementPrototype, "nextSibling");
1074 const getChildNodes = lookupGetter(ElementPrototype, "childNodes");
1075 const getParentNode = lookupGetter(ElementPrototype, "parentNode");
1076 const getShadowRoot = lookupGetter(ElementPrototype, "shadowRoot");
1077 const getAttributes = lookupGetter(ElementPrototype, "attributes");
1078 const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeType") : null;
1079 const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeName") : null;
1080 const getOwnerDocument = Node && Node.prototype ? lookupGetter(Node.prototype, "ownerDocument") : null;
1081 const _readNodeType = function _readNodeType(node) {
1082 return getNodeType ? getNodeType(node) : node.nodeType;
1083 };
1084 const _readNodeName = function _readNodeName(node) {
1085 return getNodeName ? getNodeName(node) : node.nodeName;
1086 };
1087 if (typeof HTMLTemplateElement === "function") {
1088 const template = document.createElement("template");
1089 if (template.content && template.content.ownerDocument) document = template.content.ownerDocument;
1090 }
1091 let trustedTypesPolicy;
1092 let emptyHTML = "";
1093 let defaultTrustedTypesPolicy;
1094 let defaultTrustedTypesPolicyResolved = false;
1095 let IN_TRUSTED_TYPES_POLICY = 0;
1096 const _assertNotInTrustedTypesPolicy = function _assertNotInTrustedTypesPolicy() {
1097 if (IN_TRUSTED_TYPES_POLICY > 0) throw typeErrorCreate("A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the \"DOMPurify and Trusted Types\" section of the README.");
1098 };
1099 const _createTrustedHTML = function _createTrustedHTML(html) {
1100 _assertNotInTrustedTypesPolicy();
1101 IN_TRUSTED_TYPES_POLICY++;
1102 try {
1103 return trustedTypesPolicy.createHTML(html);
1104 } finally {
1105 IN_TRUSTED_TYPES_POLICY--;
1106 }
1107 };
1108 const _createTrustedScriptURL = function _createTrustedScriptURL(scriptUrl) {
1109 _assertNotInTrustedTypesPolicy();
1110 IN_TRUSTED_TYPES_POLICY++;
1111 try {
1112 return trustedTypesPolicy.createScriptURL(scriptUrl);
1113 } finally {
1114 IN_TRUSTED_TYPES_POLICY--;
1115 }
1116 };
1117 const _getDefaultTrustedTypesPolicy = function _getDefaultTrustedTypesPolicy() {
1118 if (!defaultTrustedTypesPolicyResolved) {
1119 defaultTrustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
1120 defaultTrustedTypesPolicyResolved = true;
1121 }
1122 return defaultTrustedTypesPolicy;
1123 };
1124 const _document = document, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName;
1125 const importNode = originalDocument.importNode;
1126 let hooks = _createHooksMap();
1127 /**
1128 * Expose whether this browser supports running the full DOMPurify.
1129 */
1130 DOMPurify.isSupported = typeof entries === "function" && typeof getParentNode === "function" && implementation && implementation.createHTMLDocument !== void 0;
1131 const MUSTACHE_EXPR$1 = MUSTACHE_EXPR, ERB_EXPR$1 = ERB_EXPR, TMPLIT_EXPR$1 = TMPLIT_EXPR, DATA_ATTR$1 = DATA_ATTR, ARIA_ATTR$1 = ARIA_ATTR, IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA, ATTR_WHITESPACE$1 = ATTR_WHITESPACE, CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
1132 let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
1133 /**
1134 * We consider the elements and attributes below to be safe. Ideally
1135 * don't add any new ones but feel free to remove unwanted ones.
1136 */
1137 let ALLOWED_TAGS = null;
1138 const DEFAULT_ALLOWED_TAGS = addToSet({}, [
1139 ...html$1,
1140 ...svg$1,
1141 ...svgFilters,
1142 ...mathMl$1,
1143 ...text
1144 ]);
1145 let ALLOWED_ATTR = null;
1146 const DEFAULT_ALLOWED_ATTR = addToSet({}, [
1147 ...html,
1148 ...svg,
1149 ...mathMl,
1150 ...xml
1151 ]);
1152 let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
1153 tagNameCheck: {
1154 writable: true,
1155 configurable: false,
1156 enumerable: true,
1157 value: null
1158 },
1159 attributeNameCheck: {
1160 writable: true,
1161 configurable: false,
1162 enumerable: true,
1163 value: null
1164 },
1165 allowCustomizedBuiltInElements: {
1166 writable: true,
1167 configurable: false,
1168 enumerable: true,
1169 value: false
1170 }
1171 }));
1172 let FORBID_TAGS = null;
1173 let FORBID_ATTR = null;
1174 const EXTRA_ELEMENT_HANDLING = Object.seal(create(null, {
1175 tagCheck: {
1176 writable: true,
1177 configurable: false,
1178 enumerable: true,
1179 value: null
1180 },
1181 attributeCheck: {
1182 writable: true,
1183 configurable: false,
1184 enumerable: true,
1185 value: null
1186 }
1187 }));
1188 let ALLOW_ARIA_ATTR = true;
1189 let ALLOW_DATA_ATTR = true;
1190 let ALLOW_UNKNOWN_PROTOCOLS = false;
1191 let ALLOW_SELF_CLOSE_IN_ATTR = true;
1192 let SAFE_FOR_TEMPLATES = false;
1193 let SAFE_FOR_XML = true;
1194 let WHOLE_DOCUMENT = false;
1195 let SET_CONFIG = false;
1196 let SET_CONFIG_ALLOWED_TAGS = null;
1197 let SET_CONFIG_ALLOWED_ATTR = null;
1198 let FORCE_BODY = false;
1199 let RETURN_DOM = false;
1200 let RETURN_DOM_FRAGMENT = false;
1201 let RETURN_TRUSTED_TYPE = false;
1202 let SANITIZE_DOM = true;
1203 let SANITIZE_NAMED_PROPS = false;
1204 const SANITIZE_NAMED_PROPS_PREFIX = "user-content-";
1205 let KEEP_CONTENT = true;
1206 let IN_PLACE = false;
1207 let USE_PROFILES = {};
1208 let FORBID_CONTENTS = null;
1209 const DEFAULT_FORBID_CONTENTS = addToSet({}, [
1210 "annotation-xml",
1211 "audio",
1212 "colgroup",
1213 "desc",
1214 "foreignobject",
1215 "head",
1216 "iframe",
1217 "math",
1218 "mi",
1219 "mn",
1220 "mo",
1221 "ms",
1222 "mtext",
1223 "noembed",
1224 "noframes",
1225 "noscript",
1226 "plaintext",
1227 "script",
1228 "selectedcontent",
1229 "style",
1230 "svg",
1231 "template",
1232 "thead",
1233 "title",
1234 "video",
1235 "xmp"
1236 ]);
1237 let DATA_URI_TAGS = null;
1238 const DEFAULT_DATA_URI_TAGS = addToSet({}, [
1239 "audio",
1240 "video",
1241 "img",
1242 "source",
1243 "image",
1244 "track"
1245 ]);
1246 let URI_SAFE_ATTRIBUTES = null;
1247 const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, [
1248 "alt",
1249 "class",
1250 "for",
1251 "id",
1252 "label",
1253 "name",
1254 "pattern",
1255 "placeholder",
1256 "role",
1257 "summary",
1258 "title",
1259 "value",
1260 "style",
1261 "xmlns"
1262 ]);
1263 const MATHML_NAMESPACE = "http://www.w3.org/1998/Math/MathML";
1264 const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
1265 const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
1266 let NAMESPACE = HTML_NAMESPACE;
1267 let IS_EMPTY_INPUT = false;
1268 let ALLOWED_NAMESPACES = null;
1269 const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [
1270 MATHML_NAMESPACE,
1271 SVG_NAMESPACE,
1272 HTML_NAMESPACE
1273 ], stringToString);
1274 const DEFAULT_MATHML_TEXT_INTEGRATION_POINTS = freeze([
1275 "mi",
1276 "mo",
1277 "mn",
1278 "ms",
1279 "mtext"
1280 ]);
1281 let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
1282 const DEFAULT_HTML_INTEGRATION_POINTS = freeze(["annotation-xml"]);
1283 let HTML_INTEGRATION_POINTS = addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
1284 const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, [
1285 "title",
1286 "style",
1287 "font",
1288 "a",
1289 "script"
1290 ]);
1291 let PARSER_MEDIA_TYPE = null;
1292 const SUPPORTED_PARSER_MEDIA_TYPES = ["application/xhtml+xml", "text/html"];
1293 const DEFAULT_PARSER_MEDIA_TYPE = "text/html";
1294 let transformCaseFunc = null;
1295 let CONFIG = null;
1296 const formElement = document.createElement("form");
1297 const isRegexOrFunction = function isRegexOrFunction(testValue) {
1298 return testValue instanceof RegExp || testValue instanceof Function;
1299 };
1300 /**
1301 * _parseConfig
1302 *
1303 * @param cfg optional config literal
1304 */
1305 const _parseConfig = function _parseConfig() {
1306 let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
1307 if (CONFIG && CONFIG === cfg) return;
1308 if (!cfg || typeof cfg !== "object") cfg = {};
1309 cfg = clone(cfg);
1310 PARSER_MEDIA_TYPE = SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
1311 transformCaseFunc = PARSER_MEDIA_TYPE === "application/xhtml+xml" ? stringToString : stringToLowerCase;
1312 ALLOWED_TAGS = _resolveSetOption(cfg, "ALLOWED_TAGS", DEFAULT_ALLOWED_TAGS, { transform: transformCaseFunc });
1313 ALLOWED_ATTR = _resolveSetOption(cfg, "ALLOWED_ATTR", DEFAULT_ALLOWED_ATTR, { transform: transformCaseFunc });
1314 ALLOWED_NAMESPACES = _resolveSetOption(cfg, "ALLOWED_NAMESPACES", DEFAULT_ALLOWED_NAMESPACES, { transform: stringToString });
1315 URI_SAFE_ATTRIBUTES = _resolveSetOption(cfg, "ADD_URI_SAFE_ATTR", DEFAULT_URI_SAFE_ATTRIBUTES, {
1316 transform: transformCaseFunc,
1317 base: DEFAULT_URI_SAFE_ATTRIBUTES
1318 });
1319 DATA_URI_TAGS = _resolveSetOption(cfg, "ADD_DATA_URI_TAGS", DEFAULT_DATA_URI_TAGS, {
1320 transform: transformCaseFunc,
1321 base: DEFAULT_DATA_URI_TAGS
1322 });
1323 FORBID_CONTENTS = _resolveSetOption(cfg, "FORBID_CONTENTS", DEFAULT_FORBID_CONTENTS, { transform: transformCaseFunc });
1324 FORBID_TAGS = _resolveSetOption(cfg, "FORBID_TAGS", clone({}), { transform: transformCaseFunc });
1325 FORBID_ATTR = _resolveSetOption(cfg, "FORBID_ATTR", clone({}), { transform: transformCaseFunc });
1326 USE_PROFILES = objectHasOwnProperty(cfg, "USE_PROFILES") ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === "object" ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
1327 ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false;
1328 ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false;
1329 ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false;
1330 ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false;
1331 SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false;
1332 SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false;
1333 WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false;
1334 RETURN_DOM = cfg.RETURN_DOM || false;
1335 RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false;
1336 RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false;
1337 FORCE_BODY = cfg.FORCE_BODY || false;
1338 SANITIZE_DOM = cfg.SANITIZE_DOM !== false;
1339 SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false;
1340 KEEP_CONTENT = cfg.KEEP_CONTENT !== false;
1341 IN_PLACE = cfg.IN_PLACE || false;
1342 IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI;
1343 NAMESPACE = typeof cfg.NAMESPACE === "string" ? cfg.NAMESPACE : HTML_NAMESPACE;
1344 MATHML_TEXT_INTEGRATION_POINTS = _resolveObjectOption(cfg, "MATHML_TEXT_INTEGRATION_POINTS", () => addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS));
1345 HTML_INTEGRATION_POINTS = _resolveObjectOption(cfg, "HTML_INTEGRATION_POINTS", () => addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS));
1346 const customElementHandling = _resolveObjectOption(cfg, "CUSTOM_ELEMENT_HANDLING", () => create(null));
1347 CUSTOM_ELEMENT_HANDLING = create(null);
1348 if (objectHasOwnProperty(customElementHandling, "tagNameCheck") && isRegexOrFunction(customElementHandling.tagNameCheck)) CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck;
1349 if (objectHasOwnProperty(customElementHandling, "attributeNameCheck") && isRegexOrFunction(customElementHandling.attributeNameCheck)) CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck;
1350 if (objectHasOwnProperty(customElementHandling, "allowCustomizedBuiltInElements") && typeof customElementHandling.allowCustomizedBuiltInElements === "boolean") CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements;
1351 seal(CUSTOM_ELEMENT_HANDLING);
1352 if (SAFE_FOR_TEMPLATES) ALLOW_DATA_ATTR = false;
1353 if (RETURN_DOM_FRAGMENT) RETURN_DOM = true;
1354 if (USE_PROFILES) {
1355 ALLOWED_TAGS = addToSet({}, text);
1356 ALLOWED_ATTR = create(null);
1357 if (USE_PROFILES.html === true) {
1358 addToSet(ALLOWED_TAGS, html$1);
1359 addToSet(ALLOWED_ATTR, html);
1360 }
1361 if (USE_PROFILES.svg === true) {
1362 addToSet(ALLOWED_TAGS, svg$1);
1363 addToSet(ALLOWED_ATTR, svg);
1364 addToSet(ALLOWED_ATTR, xml);
1365 }
1366 if (USE_PROFILES.svgFilters === true) {
1367 addToSet(ALLOWED_TAGS, svgFilters);
1368 addToSet(ALLOWED_ATTR, svg);
1369 addToSet(ALLOWED_ATTR, xml);
1370 }
1371 if (USE_PROFILES.mathMl === true) {
1372 addToSet(ALLOWED_TAGS, mathMl$1);
1373 addToSet(ALLOWED_ATTR, mathMl);
1374 addToSet(ALLOWED_ATTR, xml);
1375 }
1376 }
1377 EXTRA_ELEMENT_HANDLING.tagCheck = null;
1378 EXTRA_ELEMENT_HANDLING.attributeCheck = null;
1379 if (objectHasOwnProperty(cfg, "ADD_TAGS")) {
1380 if (typeof cfg.ADD_TAGS === "function") EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
1381 else if (arrayIsArray(cfg.ADD_TAGS)) {
1382 if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) ALLOWED_TAGS = clone(ALLOWED_TAGS);
1383 addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
1384 }
1385 }
1386 if (objectHasOwnProperty(cfg, "ADD_ATTR")) {
1387 if (typeof cfg.ADD_ATTR === "function") EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
1388 else if (arrayIsArray(cfg.ADD_ATTR)) {
1389 if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) ALLOWED_ATTR = clone(ALLOWED_ATTR);
1390 addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
1391 }
1392 }
1393 if (objectHasOwnProperty(cfg, "ADD_FORBID_CONTENTS") && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
1394 if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) FORBID_CONTENTS = clone(FORBID_CONTENTS);
1395 addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);
1396 }
1397 if (KEEP_CONTENT) ALLOWED_TAGS["#text"] = true;
1398 if (WHOLE_DOCUMENT) addToSet(ALLOWED_TAGS, [
1399 "html",
1400 "head",
1401 "body"
1402 ]);
1403 if (ALLOWED_TAGS.table) {
1404 addToSet(ALLOWED_TAGS, ["tbody"]);
1405 delete FORBID_TAGS.tbody;
1406 }
1407 if (cfg.TRUSTED_TYPES_POLICY) {
1408 if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== "function") throw typeErrorCreate("TRUSTED_TYPES_POLICY configuration option must provide a \"createHTML\" hook.");
1409 if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== "function") throw typeErrorCreate("TRUSTED_TYPES_POLICY configuration option must provide a \"createScriptURL\" hook.");
1410 const previousTrustedTypesPolicy = trustedTypesPolicy;
1411 trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
1412 try {
1413 emptyHTML = _createTrustedHTML("");
1414 } catch (error) {
1415 trustedTypesPolicy = previousTrustedTypesPolicy;
1416 throw error;
1417 }
1418 } else if (cfg.TRUSTED_TYPES_POLICY === null) {
1419 trustedTypesPolicy = void 0;
1420 emptyHTML = "";
1421 } else {
1422 if (trustedTypesPolicy === void 0) trustedTypesPolicy = _getDefaultTrustedTypesPolicy();
1423 if (trustedTypesPolicy && typeof emptyHTML === "string") emptyHTML = _createTrustedHTML("");
1424 }
1425 if (freeze) freeze(cfg);
1426 CONFIG = cfg;
1427 };
1428 const ALL_SVG_TAGS = addToSet({}, [
1429 ...svg$1,
1430 ...svgFilters,
1431 ...svgDisallowed
1432 ]);
1433 const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
1434 /**
1435 * Namespace rules for an element in the SVG namespace.
1436 *
1437 * @param tagName the element's lowercase tag name
1438 * @param parent the (possibly simulated) parent node
1439 * @param parentTagName the parent's lowercase tag name
1440 * @returns true if a spec-compliant parser could produce this element
1441 */
1442 const _checkSvgNamespace = function _checkSvgNamespace(tagName, parent, parentTagName) {
1443 if (parent.namespaceURI === HTML_NAMESPACE) return tagName === "svg";
1444 if (parent.namespaceURI === MATHML_NAMESPACE) return tagName === "svg" && (parentTagName === "annotation-xml" || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
1445 return Boolean(ALL_SVG_TAGS[tagName]);
1446 };
1447 /**
1448 * Namespace rules for an element in the MathML namespace.
1449 *
1450 * @param tagName the element's lowercase tag name
1451 * @param parent the (possibly simulated) parent node
1452 * @param parentTagName the parent's lowercase tag name
1453 * @returns true if a spec-compliant parser could produce this element
1454 */
1455 const _checkMathMlNamespace = function _checkMathMlNamespace(tagName, parent, parentTagName) {
1456 if (parent.namespaceURI === HTML_NAMESPACE) return tagName === "math";
1457 if (parent.namespaceURI === SVG_NAMESPACE) return tagName === "math" && HTML_INTEGRATION_POINTS[parentTagName];
1458 return Boolean(ALL_MATHML_TAGS[tagName]);
1459 };
1460 /**
1461 * Namespace rules for an element in the HTML namespace.
1462 *
1463 * @param tagName the element's lowercase tag name
1464 * @param parent the (possibly simulated) parent node
1465 * @param parentTagName the parent's lowercase tag name
1466 * @returns true if a spec-compliant parser could produce this element
1467 */
1468 const _checkHtmlNamespace = function _checkHtmlNamespace(tagName, parent, parentTagName) {
1469 if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) return false;
1470 if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) return false;
1471 return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
1472 };
1473 /**
1474 * @param element a DOM element whose namespace is being checked
1475 * @returns Return false if the element has a
1476 * namespace that a spec-compliant parser would never
1477 * return. Return true otherwise.
1478 */
1479 const _checkValidNamespace = function _checkValidNamespace(element) {
1480 let parent = getParentNode(element);
1481 if (!parent || !parent.tagName) parent = {
1482 namespaceURI: NAMESPACE,
1483 tagName: "template"
1484 };
1485 const tagName = stringToLowerCase(element.tagName);
1486 const parentTagName = stringToLowerCase(parent.tagName);
1487 if (!ALLOWED_NAMESPACES[element.namespaceURI]) return false;
1488 if (element.namespaceURI === SVG_NAMESPACE) return _checkSvgNamespace(tagName, parent, parentTagName);
1489 if (element.namespaceURI === MATHML_NAMESPACE) return _checkMathMlNamespace(tagName, parent, parentTagName);
1490 if (element.namespaceURI === HTML_NAMESPACE) return _checkHtmlNamespace(tagName, parent, parentTagName);
1491 if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && ALLOWED_NAMESPACES[element.namespaceURI]) return true;
1492 return false;
1493 };
1494 /**
1495 * _forceRemove
1496 *
1497 * @param node a DOM node
1498 */
1499 const _forceRemove = function _forceRemove(node) {
1500 arrayPush(DOMPurify.removed, { element: node });
1501 try {
1502 getParentNode(node).removeChild(node);
1503 } catch (_) {
1504 remove(node);
1505 if (!getParentNode(node)) throw typeErrorCreate("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place");
1506 }
1507 };
1508 /**
1509 * _stripAttributeNode
1510 *
1511 * Remove a single Attr node case/namespace-exactly on an attribute-teardown
1512 * path. Name-based removeAttribute() ASCII-lowercases its lookup key for an
1513 * HTML element in an HTML document and so silently misses a case-preserved
1514 * handler (e.g. `ONERROR` off an XML/XHTML import) - the same defect
1515 * _removeAttribute() was fixed for, which a name-based call would reintroduce
1516 * on these IN_PLACE teardown paths. Unlike _removeAttribute this does not
1517 * record into DOMPurify.removed: the neutralize passes intentionally do not
1518 * book-keep. A clobbered/detached node falls back to best-effort name-based
1519 * removal.
1520 *
1521 * @param element the element to strip the attribute from
1522 * @param attribute the Attr node to remove
1523 * @param name the attribute's name, for the fallback path
1524 */
1525 const _stripAttributeNode = function _stripAttributeNode(element, attribute, name) {
1526 try {
1527 removeAttributeNode(element, attribute);
1528 } catch (_) {
1529 try {
1530 element.removeAttribute(name);
1531 } catch (_) {}
1532 }
1533 };
1534 /**
1535 * _neutralizeRoot
1536 *
1537 * Fail-closed teardown of an in-place root after the sanitize walk aborts
1538 * (campaign-3 F2). An internal throw mid-walk — e.g. a page-registered
1539 * custom element's reaction detaches a node so `_forceRemove`'s deliberate
1540 * parentless guard throws, or any other re-entrant engine mutation — would
1541 * otherwise leave the caller's *live* tree half-sanitized, with everything
1542 * after the abort point still carrying its handlers. There is no safe way
1543 * to resume the walk (the tree mutated under us), so we strip the root bare:
1544 * remove every child and every attribute, then let the caller's catch see
1545 * the original error. Clobber-safe (cached `remove`/`childNodes`/`attributes`
1546 * getters; the root was already clobber-pre-flighted at the IN_PLACE entry).
1547 *
1548 * @param root the in-place root to empty
1549 */
1550 const _neutralizeRoot = function _neutralizeRoot(root) {
1551 _neutralizeSubtree(root);
1552 const childNodes = getChildNodes(root);
1553 if (childNodes) {
1554 const snapshot = [];
1555 arrayForEach(childNodes, (child) => {
1556 arrayPush(snapshot, child);
1557 });
1558 arrayForEach(snapshot, (child) => {
1559 try {
1560 remove(child);
1561 } catch (_) {}
1562 });
1563 }
1564 const attributes = getAttributes(root);
1565 if (attributes) for (let i = attributes.length - 1; i >= 0; --i) {
1566 const attribute = attributes[i];
1567 const name = attribute && attribute.name;
1568 if (typeof name === "string") _stripAttributeNode(root, attribute, name);
1569 }
1570 };
1571 /**
1572 * _removeAttribute
1573 *
1574 * Name-based getAttributeNode()/removeAttribute() ASCII-lowercase their
1575 * lookup key for HTML elements in an HTML document, so they silently miss an
1576 * attribute whose stored qualified name still contains uppercase ASCII
1577 * letters. That happens when the node came from a case-preserving source
1578 * (an XML/XHTML document imported via importNode(), or createAttributeNS()),
1579 * where e.g. `ONERROR` survives the walk: the policy check lowercases to
1580 * `onerror` and rejects it, but `removeAttribute('ONERROR')` looks up
1581 * `onerror` and finds nothing. Remove the exact Attr node instead, which is
1582 * case- and namespace-exact, and fall back to name-based removal only when
1583 * the caller could not supply the node.
1584 *
1585 * @param name an Attribute name
1586 * @param element a DOM node
1587 * @param attr the exact Attr node to remove, when the caller has it
1588 */
1589 const _removeAttribute = function _removeAttribute(name, element, attr) {
1590 if (!attr) try {
1591 attr = element.getAttributeNode(name);
1592 } catch (_) {
1593 attr = null;
1594 }
1595 arrayPush(DOMPurify.removed, {
1596 attribute: attr || null,
1597 from: element
1598 });
1599 try {
1600 if (attr) removeAttributeNode(element, attr);
1601 else element.removeAttribute(name);
1602 } catch (_) {
1603 try {
1604 element.removeAttribute(name);
1605 } catch (_) {}
1606 }
1607 if (name === "is") {
1608 if (RETURN_DOM || RETURN_DOM_FRAGMENT) try {
1609 _forceRemove(element);
1610 } catch (_) {}
1611 else try {
1612 element.setAttribute(name, "");
1613 } catch (_) {}
1614 }
1615 };
1616 /**
1617 * _stripDisallowedAttributes
1618 *
1619 * Removes every attribute the active configuration does not allow from a
1620 * single element, using the same allowlist as the main attribute pass (so
1621 * `on*` handlers go, but no `/^on/` blocklist is introduced). Used only to
1622 * neutralise nodes that are being discarded from an in-place tree.
1623 *
1624 * @param element the element to strip
1625 */
1626 const _stripDisallowedAttributes = function _stripDisallowedAttributes(element) {
1627 const attributes = getAttributes(element);
1628 if (!attributes) return;
1629 for (let i = attributes.length - 1; i >= 0; --i) {
1630 const attribute = attributes[i];
1631 const name = attribute && attribute.name;
1632 if (typeof name !== "string" || ALLOWED_ATTR[transformCaseFunc(name)]) continue;
1633 _stripAttributeNode(element, attribute, name);
1634 }
1635 };
1636 /**
1637 * _neutralizeSubtree
1638 *
1639 * Completes the audit-5 F1 fix across every removal path. The KEEP_CONTENT
1640 * move-hoist neutralises only disallowed-tag removals; clobber, mXSS-canary,
1641 * namespace, comment, processing-instruction and KEEP_CONTENT:false removals
1642 * all drop their subtree wholesale via `_forceRemove`. On the IN_PLACE path
1643 * those dropped nodes are detached from the caller's LIVE tree but a
1644 * handler-bearing original among them (an `<img onerror>`/`<video>` that was
1645 * loading) keeps its queued resource event, which fires in page scope after
1646 * sanitize returns. This walks a removed subtree and strips every attribute
1647 * the active configuration does not allow — so `on*` handlers are cancelled
1648 * through the SAME allowlist that governs kept nodes, not a separate `/^on/`
1649 * blocklist. Run synchronously before sanitize returns, i.e. before any
1650 * queued event can fire. Hook-free by design: these nodes leave the output,
1651 * so firing attribute hooks for them would be surprising. Clobber-safe reads;
1652 * a doomed clobbered node may shadow `removeAttribute` (its own attributes are
1653 * irrelevant — it is discarded — while its non-clobbered descendants, e.g.
1654 * the `<img>`, are reached and scrubbed).
1655 *
1656 * @param root the root of a removed subtree to neutralise
1657 */
1658 const _neutralizeSubtree = function _neutralizeSubtree(root) {
1659 const stack = [root];
1660 while (stack.length > 0) {
1661 const node = stack.pop();
1662 if (_readNodeType(node) === NODE_TYPE.element) _stripDisallowedAttributes(node);
1663 const childNodes = getChildNodes(node);
1664 if (childNodes) for (let i = childNodes.length - 1; i >= 0; --i) stack.push(childNodes[i]);
1665 }
1666 };
1667 /**
1668 * _neutralizePatchLinkage
1669 *
1670 * IN_PLACE entry pre-pass (declarative-partial-updates / streaming
1671 * hardening, https://github.com/WICG/declarative-partial-updates).
1672 *
1673 * The main walk strips patch linkage (`for`/`patchsrc`) and removes range
1674 * markers (PIs / markup comments) node-by-node, in document order, AS it
1675 * reaches each node. On a live in-place root that leaves a window: from the
1676 * moment the root is connected until the walk arrives at a given node, that
1677 * node's linkage is live. A patch applied on connection/stream can fire as
1678 * a microtask during the walk and inject or teleport an unsanitized DOM
1679 * range into a region the iterator has already passed and will not revisit,
1680 * so the post-return "tree is sanitized" contract is violated. Sweep the
1681 * whole tree once up front and sever every linkage before the walk begins,
1682 * closing that window.
1683 *
1684 * This CANNOT undo a patch that already fired before sanitize ran — that is
1685 * the irreducible "do not IN_PLACE a live-connected attacker tree" caveat —
1686 * but it closes everything from sanitize-start onward. Gated on SAFE_FOR_XML
1687 * to group with the rest of the declarative-partial-updates handling and
1688 * stay overridable, consistent with the codebase.
1689 *
1690 * Clobber-safe traversal (cached childNodes getter); per-node try/catch so a
1691 * clobbered root cannot defeat the sweep of its non-clobbered descendants.
1692 *
1693 * NOTE (pending real-Chrome confirmation, see test/declarative-patch-probe
1694 * .html Q1): this mirrors the existing policy of keeping `for` on
1695 * <label>/<output>. If the shipping feature can drive a patch through a
1696 * surviving `for`-on-label/output + `id` pair, this pre-pass and the
1697 * attribute check at _isBasicCustomElement's caller must additionally drop
1698 * that pair on the IN_PLACE path. Left as-is until the taxonomy is verified.
1699 *
1700 * @param root the in-place root to sweep
1701 */
1702 /**
1703 * Central policy for declarative-partial-updates patch-linkage attributes,
1704 * shared by the _neutralizePatchLinkage pre-pass and _isValidAttribute so
1705 * the two sites cannot drift: `patchsrc` always links, `for` links
1706 * everywhere except on <label>/<output>, and the whole policy is gated on
1707 * SAFE_FOR_XML (see the rationale block in _isValidAttribute).
1708 *
1709 * @param lcName the transformCaseFunc'd attribute name
1710 * @param lcTag the transformCaseFunc'd tag name of the carrying element
1711 * @return true if the attribute is patch linkage and must be dropped
1712 */
1713 const _isPatchLinkageAttribute = function _isPatchLinkageAttribute(lcName, lcTag) {
1714 if (!SAFE_FOR_XML) return false;
1715 if (lcName === "patchsrc") return true;
1716 return lcName === "for" && lcTag !== "label" && lcTag !== "output";
1717 };
1718 const _neutralizePatchLinkage = function _neutralizePatchLinkage(root) {
1719 if (!SAFE_FOR_XML) return;
1720 const stack = [root];
1721 while (stack.length > 0) {
1722 const node = stack.pop();
1723 const nodeType = _readNodeType(node);
1724 if (nodeType === NODE_TYPE.processingInstruction || nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, node.data)) {
1725 try {
1726 remove(node);
1727 } catch (_) {}
1728 continue;
1729 }
1730 if (nodeType === NODE_TYPE.element) {
1731 const element = node;
1732 const lcTag = transformCaseFunc(_readNodeName(node));
1733 try {
1734 if (element.hasAttribute && element.hasAttribute("patchsrc")) element.removeAttribute("patchsrc");
1735 if (element.hasAttribute && element.hasAttribute("for") && _isPatchLinkageAttribute("for", lcTag)) element.removeAttribute("for");
1736 } catch (_) {}
1737 }
1738 const childNodes = getChildNodes(node);
1739 if (childNodes) for (let i = childNodes.length - 1; i >= 0; --i) stack.push(childNodes[i]);
1740 }
1741 };
1742 /**
1743 * _initDocument
1744 *
1745 * @param dirty - a string of dirty markup
1746 * @return a DOM, filled with the dirty markup
1747 */
1748 const _initDocument = function _initDocument(dirty) {
1749 let doc = null;
1750 let leadingWhitespace = null;
1751 if (FORCE_BODY) dirty = "<remove></remove>" + dirty;
1752 else {
1753 const matches = stringMatch(dirty, /^[\r\n\t ]+/);
1754 leadingWhitespace = matches && matches[0];
1755 }
1756 if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && NAMESPACE === HTML_NAMESPACE) dirty = "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head></head><body>" + dirty + "</body></html>";
1757 const dirtyPayload = trustedTypesPolicy ? _createTrustedHTML(dirty) : dirty;
1758 if (NAMESPACE === HTML_NAMESPACE) try {
1759 doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
1760 } catch (_) {}
1761 if (!doc || !doc.documentElement) {
1762 doc = implementation.createDocument(NAMESPACE, "template", null);
1763 try {
1764 doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
1765 } catch (_) {}
1766 }
1767 const body = doc.body || doc.documentElement;
1768 if (dirty && leadingWhitespace) body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
1769 if (NAMESPACE === HTML_NAMESPACE) return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? "html" : "body")[0];
1770 return WHOLE_DOCUMENT ? doc.documentElement : body;
1771 };
1772 /**
1773 * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
1774 *
1775 * @param root The root element or node to start traversing on.
1776 * @return The created NodeIterator
1777 */
1778 const _createNodeIterator = function _createNodeIterator(root) {
1779 const doc = getOwnerDocument ? getOwnerDocument(root) : root.ownerDocument;
1780 return createNodeIterator.call(doc || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
1781 };
1782 /**
1783 * Replace template expression syntax (mustache, ERB, template
1784 * literal) with a space; shared by all SAFE_FOR_TEMPLATES scrub
1785 * sites. Order matters: mustache, then ERB, then template literal.
1786 *
1787 * @param value the string to scrub
1788 * @returns the scrubbed string
1789 */
1790 const _stripTemplateExpressions = function _stripTemplateExpressions(value) {
1791 value = stringReplace(value, MUSTACHE_EXPR$1, " ");
1792 value = stringReplace(value, ERB_EXPR$1, " ");
1793 value = stringReplace(value, TMPLIT_EXPR$1, " ");
1794 return value;
1795 };
1796 /**
1797 * Strip template-engine expressions ({{...}}, ${...}, <%...%>) from the
1798 * character data of an element subtree. Used as the final safety net for
1799 * SAFE_FOR_TEMPLATES on every DOM-returning code path so that expressions
1800 * which only form after text-node normalization (e.g. fragments split across
1801 * stripped elements) cannot survive into a template-evaluating framework.
1802 *
1803 * Walks text/comment/CDATA/processing-instruction nodes and mutates `.data`
1804 * in place rather than round-tripping through innerHTML. This preserves
1805 * descendant node references (important for IN_PLACE callers), avoids a
1806 * serialize/reparse cycle, and reads literal character data — which means
1807 * `<%...%>` in text content matches the ERB regex against its real bytes
1808 * instead of the HTML-entity-escaped form innerHTML would produce.
1809 *
1810 * Attribute values are not visited here; SAFE_FOR_TEMPLATES handling for
1811 * attributes is performed during the per-node `_sanitizeAttributes` pass.
1812 *
1813 * @param node The root element whose character data should be scrubbed.
1814 */
1815 const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
1816 var _node$querySelectorAl;
1817 node.normalize();
1818 const doc = getOwnerDocument ? getOwnerDocument(node) : node.ownerDocument;
1819 const walker = createNodeIterator.call(doc || node, node, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
1820 let currentNode = walker.nextNode();
1821 while (currentNode) {
1822 currentNode.data = _stripTemplateExpressions(currentNode.data);
1823 currentNode = walker.nextNode();
1824 }
1825 const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, "template");
1826 if (templates) arrayForEach(templates, (tmpl) => {
1827 if (_isDocumentFragment(tmpl.content)) _scrubTemplateExpressions2(tmpl.content);
1828 });
1829 };
1830 /**
1831 * _isClobbered
1832 *
1833 * Detect DOM-clobbering on HTMLFormElement nodes. Form is the only HTML
1834 * interface with [LegacyOverrideBuiltIns]; a descendant element with a
1835 * `name` attribute matching a prototype property shadows that property
1836 * on direct reads. We use this check at the IN_PLACE entry-point and
1837 * during attribute sanitization to refuse clobbered forms.
1838 *
1839 * @param element element to check for clobbering attacks
1840 * @return true if clobbered, false if safe
1841 */
1842 const _isClobbered = function _isClobbered(element) {
1843 const realTagName = getNodeName ? getNodeName(element) : null;
1844 if (typeof realTagName !== "string") return false;
1845 if (transformCaseFunc(realTagName) !== "form") return false;
1846 return typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || element.attributes !== getAttributes(element) || typeof element.removeAttribute !== "function" || typeof element.removeAttributeNode !== "function" || typeof element.getAttributeNode !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function" || element.nodeType !== getNodeType(element) || element.childNodes !== getChildNodes(element);
1847 };
1848 /**
1849 * Checks whether the given value is a DocumentFragment from any realm.
1850 *
1851 * The realm-independent replacement reads `nodeType` through the cached
1852 * Node.prototype getter and compares to the DOCUMENT_FRAGMENT_NODE
1853 * constant (11). nodeType is a numeric value resolved from the node's
1854 * internal slot, identical across realms for the same kind of node.
1855 *
1856 * @param value object to check
1857 * @return true if value is a DocumentFragment-shaped node from any realm
1858 */
1859 const _isDocumentFragment = function _isDocumentFragment(value) {
1860 if (!getNodeType || typeof value !== "object" || value === null) return false;
1861 try {
1862 return getNodeType(value) === NODE_TYPE.documentFragment;
1863 } catch (_) {
1864 return false;
1865 }
1866 };
1867 /**
1868 * Checks whether the given object is a DOM node, including nodes that
1869 * originate from a different window/realm (e.g. an iframe's
1870 * contentDocument). The previous `value instanceof Node` check was
1871 * realm-bound: nodes from a different window failed it, causing
1872 * sanitize() to silently stringify them and reset IN_PLACE to false,
1873 * returning the original node unsanitized. See GHSA-4w3q-35jp-p934.
1874 *
1875 * @param value object to check whether it's a DOM node
1876 * @return true if value is a DOM node from any realm
1877 */
1878 const _isNode = function _isNode(value) {
1879 if (!getNodeType || typeof value !== "object" || value === null) return false;
1880 try {
1881 return typeof getNodeType(value) === "number";
1882 } catch (_) {
1883 return false;
1884 }
1885 };
1886 function _executeHooks(hooks, currentNode, data) {
1887 if (hooks.length === 0) return;
1888 arrayForEach(hooks, (hook) => {
1889 hook.call(DOMPurify, currentNode, data, CONFIG);
1890 });
1891 }
1892 /**
1893 * Structural-threat checks that condemn a node regardless of the
1894 * allowlists: mXSS via namespace confusion, risky CSS construction,
1895 * processing instructions, markup-bearing comments. Pure predicate;
1896 * the caller removes. Check order is load-bearing.
1897 *
1898 * @param currentNode the node to inspect
1899 * @param tagName the node's transformCaseFunc'd tag name
1900 * @return true if the node must be removed
1901 */
1902 const _isUnsafeNode = function _isUnsafeNode(currentNode, tagName) {
1903 if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) return true;
1904 if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && LITERAL_TEXT_ELEMENTS[tagName] && (_isNode(currentNode.firstElementChild) || typeof currentNode.textContent === "string" && regExpTest(LITERAL_TEXT_CLOSE[tagName], currentNode.textContent))) return true;
1905 if (currentNode.nodeType === NODE_TYPE.processingInstruction) return true;
1906 if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) return true;
1907 return false;
1908 };
1909 /**
1910 * Evaluate a CUSTOM_ELEMENT_HANDLING check (a RegExp or a predicate
1911 * function, per the validation in _parseConfig) against a name.
1912 * Additional arguments are forwarded to predicate functions - the
1913 * attributeNameCheck predicate receives the tag name as its second
1914 * argument. A null/absent check never matches.
1915 *
1916 * @param check the configured tagNameCheck / attributeNameCheck value
1917 * @param name the name to test
1918 * @param args extra arguments forwarded to a predicate function
1919 * @return true if the check matches the name
1920 */
1921 const _matchesNameCheck = function _matchesNameCheck(check, name) {
1922 if (check instanceof RegExp) return regExpTest(check, name);
1923 if (check instanceof Function) {
1924 for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) args[_key - 2] = arguments[_key];
1925 return Boolean(check(name, ...args));
1926 }
1927 return false;
1928 };
1929 /**
1930 * Handle a node whose tag is forbidden or not allowlisted: keep
1931 * allowed custom elements (false return exits _sanitizeElements
1932 * early - the namespace and fallback-tag removal checks are
1933 * intentionally skipped for kept custom elements), else hoist
1934 * content per KEEP_CONTENT and remove.
1935 *
1936 * A kept custom element is the ONLY case in which this function
1937 * returns false, so the caller uses that return value to run the
1938 * afterSanitizeElements hook on the kept element and keep the
1939 * element-hook lifecycle consistent with normal allowlisted
1940 * elements (GHSA-c2j3-45gr-mqc4).
1941 *
1942 * @param currentNode the disallowed node
1943 * @param tagName the node's transformCaseFunc'd tag name
1944 * @return true if the node was removed, false if kept
1945 */
1946 const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName, root) {
1947 if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
1948 if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1949 const parentNode = getParentNode(currentNode);
1950 const childNodes = getChildNodes(currentNode);
1951 if (childNodes && parentNode) {
1952 const childCount = childNodes.length;
1953 for (let i = childCount - 1; i >= 0; --i) {
1954 const hoisted = currentNode === root ? cloneNode(childNodes[i], true) : childNodes[i];
1955 parentNode.insertBefore(hoisted, getNextSibling(currentNode));
1956 }
1957 }
1958 }
1959 _forceRemove(currentNode);
1960 return true;
1961 };
1962 /**
1963 * Fork a hook-mutable allowlist off its shared binding the first time a
1964 * (possibly lazily-installed) uponSanitize* hook is about to see it, so the
1965 * hook cannot widen the per-instance default or the setConfig binding by
1966 * reference and leak past the call. Returns the set unchanged once it is
1967 * already call-local, so repeated calls across elements are idempotent.
1968 *
1969 * @param hookList the uponSanitize* hook array for this event
1970 * @param set the current ALLOWED_TAGS / ALLOWED_ATTR binding
1971 * @param defaultSet the per-instance DEFAULT_ALLOWED_* constant
1972 * @param setConfigSet the captured setConfig() binding, or null
1973 * @return a call-local clone if a hook is present and set is still shared,
1974 * else set unchanged
1975 */
1976 const _forkSharedAllowlist = function _forkSharedAllowlist(hookList, set, defaultSet, setConfigSet) {
1977 if (hookList.length === 0) return set;
1978 return set === defaultSet || set === setConfigSet ? clone(set) : set;
1979 };
1980 /**
1981 * Shared guard for a node that a hook has detached from the walk tree,
1982 * used after each element-hook site in _sanitizeElements. Detaching is a
1983 * long-standing user pattern (issue #469; draw.io-style foreignObject
1984 * filtering). Per the cached, unclobberable parentNode getter the node is
1985 * genuinely out of the tree, so it can reach neither the serialized
1986 * output nor an IN_PLACE live tree; treat it as removed and stop
1987 * processing it. Without this guard, the unsafe-node / namespace checks
1988 * would call _forceRemove on a parentless node and hit the REPORT-3
1989 * fail-closed throw — which exists for nodes DOMPurify wants gone but
1990 * *cannot* detach (clobbered / parentless roots), the opposite of a node
1991 * that is already safely gone. The walk root is exempt: a detached
1992 * IN_PLACE root is legitimate input and must still be fully sanitized,
1993 * and a kill-decision on it must keep hitting the REPORT-3 throw.
1994 *
1995 * Nodes detached by hooks stay the hook's responsibility for placement:
1996 * they are not recorded in DOMPurify.removed, so the post-walk IN_PLACE
1997 * pass (which iterates DOMPurify.removed) does not reach them. But a
1998 * hook-detached subtree can still hold a queued resource-event handler -
1999 * e.g. an <img onload> that began loading when the caller built the live
2000 * tree - which fires in page scope after sanitize returns even though the
2001 * handler never reached the returned tree. That is the audit-5 F1 hazard,
2002 * and the documented node.remove() hook pattern walks straight into it.
2003 * So on the IN_PLACE path we neutralize the detached subtree inline,
2004 * stripping its non-allow-listed attributes before returning, exactly as
2005 * the post-walk pass does for _forceRemove'd subtrees.
2006 *
2007 * @param currentNode the node a hook may have detached
2008 * @param root the current walk root
2009 * @return true if the node is detached and now handled, false otherwise
2010 */
2011 const _handleHookDetachedNode = function _handleHookDetachedNode(currentNode, root) {
2012 if (currentNode === root || getParentNode(currentNode) !== null) return false;
2013 if (IN_PLACE) _neutralizeSubtree(currentNode);
2014 return true;
2015 };
2016 /**
2017 * _sanitizeElements
2018 *
2019 * @protect nodeName
2020 * @protect textContent
2021 * @protect removeChild
2022 * @param currentNode to check for permission to exist
2023 * @return true if node was killed, false if left alive
2024 */
2025 const _sanitizeElements = function _sanitizeElements(currentNode, root) {
2026 _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
2027 if (_handleHookDetachedNode(currentNode, root)) return true;
2028 if (_isClobbered(currentNode)) {
2029 _forceRemove(currentNode);
2030 return true;
2031 }
2032 const tagName = transformCaseFunc(_readNodeName(currentNode));
2033 ALLOWED_TAGS = _forkSharedAllowlist(hooks.uponSanitizeElement, ALLOWED_TAGS, DEFAULT_ALLOWED_TAGS, SET_CONFIG_ALLOWED_TAGS);
2034 _executeHooks(hooks.uponSanitizeElement, currentNode, {
2035 tagName,
2036 allowedTags: ALLOWED_TAGS
2037 });
2038 if (_handleHookDetachedNode(currentNode, root)) return true;
2039 if (_isUnsafeNode(currentNode, tagName)) {
2040 _forceRemove(currentNode);
2041 return true;
2042 }
2043 if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
2044 const removed = _sanitizeDisallowedNode(currentNode, tagName, root);
2045 if (removed === false) {
2046 _executeHooks(hooks.afterSanitizeElements, currentNode, null);
2047 if (_handleHookDetachedNode(currentNode, root)) return true;
2048 }
2049 return removed;
2050 }
2051 if (_readNodeType(currentNode) === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
2052 _forceRemove(currentNode);
2053 return true;
2054 }
2055 if ((tagName === "noscript" || tagName === "noembed" || tagName === "noframes") && regExpTest(FALLBACK_TAG_CLOSE, currentNode.innerHTML)) {
2056 _forceRemove(currentNode);
2057 return true;
2058 }
2059 if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
2060 const content = _stripTemplateExpressions(currentNode.textContent);
2061 if (currentNode.textContent !== content) {
2062 arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
2063 currentNode.textContent = content;
2064 }
2065 }
2066 _executeHooks(hooks.afterSanitizeElements, currentNode, null);
2067 return _handleHookDetachedNode(currentNode, root);
2068 };
2069 /**
2070 * _isValidAttribute
2071 *
2072 * @param lcTag Lowercase tag name of containing element.
2073 * @param lcName Lowercase attribute name.
2074 * @param value Attribute value.
2075 * @return Returns true if `value` is valid, otherwise false.
2076 */
2077 const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
2078 if (FORBID_ATTR[lcName]) return false;
2079 if (_isPatchLinkageAttribute(lcName, lcTag)) return false;
2080 if (SANITIZE_DOM && (lcName === "id" || lcName === "name") && (value in document || value in formElement)) return false;
2081 const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
2082 if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName)) return true;
2083 if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) return true;
2084 if (!nameIsPermitted) return _isBasicCustomElement(lcTag) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName, lcTag) || lcName === "is" && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value);
2085 if (URI_SAFE_ATTRIBUTES[lcName]) return true;
2086 if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ""))) return true;
2087 if ((lcName === "src" || lcName === "xlink:href" || lcName === "href") && lcTag !== "script" && stringIndexOf(value, "data:") === 0 && DATA_URI_TAGS[lcTag]) return true;
2088 if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ""))) return true;
2089 return !value;
2090 };
2091 const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, [
2092 "annotation-xml",
2093 "color-profile",
2094 "font-face",
2095 "font-face-format",
2096 "font-face-name",
2097 "font-face-src",
2098 "font-face-uri",
2099 "missing-glyph"
2100 ]);
2101 /**
2102 * _isBasicCustomElement
2103 * checks if at least one dash is included in tagName, and it's not the first char
2104 * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
2105 *
2106 * @param tagName name of the tag of the node to sanitize
2107 * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
2108 */
2109 const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
2110 return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
2111 };
2112 /**
2113 * Wrap an attribute value in the matching Trusted Types object when
2114 * the active policy requires it. Namespaced attributes pass through
2115 * unchanged (no TT support yet, see
2116 * https://bugs.chromium.org/p/chromium/issues/detail?id=1305293).
2117 *
2118 * @param lcTag lowercase tag name of the containing element
2119 * @param lcName lowercase attribute name
2120 * @param namespaceURI the attribute's namespace, if any
2121 * @param value the attribute value to wrap
2122 * @return the value, wrapped when Trusted Types demand it
2123 */
2124 const _applyTrustedTypesToAttribute = function _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value) {
2125 if (trustedTypesPolicy && typeof trustedTypes === "object" && typeof trustedTypes.getAttributeType === "function" && !namespaceURI) switch (trustedTypes.getAttributeType(lcTag, lcName)) {
2126 case "TrustedHTML": return _createTrustedHTML(value);
2127 case "TrustedScriptURL": return _createTrustedScriptURL(value);
2128 }
2129 return value;
2130 };
2131 /**
2132 * Write a modified attribute value back onto the element. On
2133 * success, re-probe for clobbering introduced by the new value and
2134 * remove the element when found; otherwise, when this writeback is the
2135 * recreate half of the SANITIZE_NAMED_PROPS remove-and-recreate, pop the
2136 * removal entry that path recorded so it does not show as removed. On
2137 * failure, remove the attribute instead.
2138 *
2139 * Returns true only on a clean write (the value was set and the new value
2140 * introduced no clobbering). The caller uses that, together with its own
2141 * knowledge of whether this attribute pushed a DOMPurify.removed record, to
2142 * decide whether to pop that record. The pop must happen ONLY for the
2143 * named-prop remove-and-recreate; popping on any other value change (trim,
2144 * template scrubbing, Trusted Types) would consume an unrelated _forceRemove
2145 * subtree-cleanup record and let that detached subtree keep a live event
2146 * handler through the IN_PLACE neutralization pass (SO-001).
2147 *
2148 * @param currentNode the element carrying the attribute
2149 * @param name the attribute name as present on the element
2150 * @param namespaceURI the attribute's namespace, if any
2151 * @param value the new attribute value
2152 * @return true if the value was written without introducing clobbering
2153 */
2154 const _setAttributeValue = function _setAttributeValue(currentNode, name, namespaceURI, value) {
2155 try {
2156 if (namespaceURI) currentNode.setAttributeNS(namespaceURI, name, value);
2157 else currentNode.setAttribute(name, value);
2158 if (_isClobbered(currentNode)) {
2159 _forceRemove(currentNode);
2160 return false;
2161 }
2162 return true;
2163 } catch (_) {
2164 _removeAttribute(name, currentNode);
2165 return false;
2166 }
2167 };
2168 /**
2169 * _sanitizeAttributes
2170 *
2171 * @protect attributes
2172 * @protect nodeName
2173 * @protect removeAttribute
2174 * @protect setAttribute
2175 *
2176 * @param currentNode to sanitize
2177 * @param root the current walk root
2178 */
2179 const _sanitizeAttributes = function _sanitizeAttributes(currentNode, root) {
2180 _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
2181 if (_handleHookDetachedNode(currentNode, root)) return;
2182 const attributes = currentNode.attributes;
2183 if (!attributes || _isClobbered(currentNode)) return;
2184 ALLOWED_ATTR = _forkSharedAllowlist(hooks.uponSanitizeAttribute, ALLOWED_ATTR, DEFAULT_ALLOWED_ATTR, SET_CONFIG_ALLOWED_ATTR);
2185 const hookEvent = {
2186 attrName: "",
2187 attrValue: "",
2188 keepAttr: true,
2189 allowedAttributes: ALLOWED_ATTR,
2190 forceKeepAttr: void 0
2191 };
2192 let l = attributes.length;
2193 const lcTag = transformCaseFunc(currentNode.nodeName);
2194 while (l--) {
2195 const attr = attributes[l];
2196 const name = attr.name, namespaceURI = attr.namespaceURI, attrValue = attr.value;
2197 const lcName = transformCaseFunc(name);
2198 const initValue = attrValue;
2199 let value = name === "value" ? initValue : stringTrim(initValue);
2200 let recreatedNamedProp = false;
2201 hookEvent.attrName = lcName;
2202 hookEvent.attrValue = value;
2203 hookEvent.keepAttr = true;
2204 hookEvent.forceKeepAttr = void 0;
2205 _executeHooks(hooks.uponSanitizeAttribute, currentNode, hookEvent);
2206 value = hookEvent.attrValue;
2207 if (SANITIZE_NAMED_PROPS && (lcName === "id" || lcName === "name") && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
2208 _removeAttribute(name, currentNode, attr);
2209 value = SANITIZE_NAMED_PROPS_PREFIX + value;
2210 recreatedNamedProp = true;
2211 }
2212 if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
2213 _removeAttribute(name, currentNode, attr);
2214 continue;
2215 }
2216 if (lcName === "attributename" && stringMatch(value, "href")) {
2217 _removeAttribute(name, currentNode, attr);
2218 continue;
2219 }
2220 if (hookEvent.forceKeepAttr) continue;
2221 if (!hookEvent.keepAttr) {
2222 _removeAttribute(name, currentNode, attr);
2223 continue;
2224 }
2225 if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value)) {
2226 _removeAttribute(name, currentNode, attr);
2227 continue;
2228 }
2229 if (SAFE_FOR_TEMPLATES) value = _stripTemplateExpressions(value);
2230 if (!_isValidAttribute(lcTag, lcName, value)) {
2231 _removeAttribute(name, currentNode, attr);
2232 continue;
2233 }
2234 value = _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value);
2235 if (value !== initValue) {
2236 if (_setAttributeValue(currentNode, name, namespaceURI, value) && recreatedNamedProp) arrayPop(DOMPurify.removed);
2237 }
2238 }
2239 _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
2240 _handleHookDetachedNode(currentNode, root);
2241 };
2242 /**
2243 * _sanitizeShadowDOM
2244 *
2245 * @param fragment to iterate over recursively
2246 */
2247 const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
2248 let shadowNode = null;
2249 const shadowIterator = _createNodeIterator(fragment);
2250 _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
2251 while (shadowNode = shadowIterator.nextNode()) {
2252 _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
2253 _sanitizeElements(shadowNode, fragment);
2254 _sanitizeAttributes(shadowNode, fragment);
2255 if (_isDocumentFragment(shadowNode.content)) _sanitizeShadowDOM2(shadowNode.content);
2256 if (_readNodeType(shadowNode) === NODE_TYPE.element) {
2257 const innerSr = getShadowRoot(shadowNode);
2258 if (_isDocumentFragment(innerSr)) {
2259 _sanitizeAttachedShadowRoots(innerSr);
2260 _sanitizeShadowDOM2(innerSr);
2261 }
2262 }
2263 }
2264 _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
2265 };
2266 /**
2267 * _sanitizeAttachedShadowRoots
2268 *
2269 * Walks `root` and feeds every attached shadow root we encounter into
2270 * the existing _sanitizeShadowDOM pipeline. The default node iterator
2271 * does not descend into shadow trees, so nodes inside an attached
2272 * shadow root would otherwise be skipped entirely.
2273 *
2274 * Two real input paths put attached shadow roots in front of us:
2275 * 1. IN_PLACE on a DOM node that already has shadow roots attached.
2276 * 2. DOM-node input where importNode(dirty, true) deep-clones the
2277 * shadow root because it was created with `clonable: true`.
2278 *
2279 * This pass runs once, up front, so the main iteration loop (and the
2280 * existing _sanitizeShadowDOM template-content recursion) stay
2281 * untouched — string-input paths are not affected.
2282 *
2283 * @param root the subtree root to walk for attached shadow roots
2284 */
2285 const _sanitizeAttachedShadowRoots = function _sanitizeAttachedShadowRoots(root) {
2286 const stack = [{
2287 node: root,
2288 shadow: null
2289 }];
2290 while (stack.length > 0) {
2291 const item = stack.pop();
2292 if (item.shadow) {
2293 _sanitizeShadowDOM2(item.shadow);
2294 continue;
2295 }
2296 const node = item.node;
2297 const isElement = _readNodeType(node) === NODE_TYPE.element;
2298 const childNodes = getChildNodes(node);
2299 if (childNodes) for (let i = childNodes.length - 1; i >= 0; --i) stack.push({
2300 node: childNodes[i],
2301 shadow: null
2302 });
2303 if (isElement) {
2304 const rootName = getNodeName ? getNodeName(node) : null;
2305 if (typeof rootName === "string" && transformCaseFunc(rootName) === "template") {
2306 const content = node.content;
2307 if (_isDocumentFragment(content)) stack.push({
2308 node: content,
2309 shadow: null
2310 });
2311 }
2312 }
2313 if (isElement) {
2314 const sr = getShadowRoot(node);
2315 if (_isDocumentFragment(sr)) stack.push({
2316 node: null,
2317 shadow: sr
2318 }, {
2319 node: sr,
2320 shadow: null
2321 });
2322 }
2323 }
2324 };
2325 DOMPurify.sanitize = function(dirty) {
2326 let cfg = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
2327 let body = null;
2328 let importedNode = null;
2329 let currentNode = null;
2330 let returnNode = null;
2331 IS_EMPTY_INPUT = !dirty;
2332 if (IS_EMPTY_INPUT) dirty = "<!-->";
2333 if (typeof dirty !== "string" && !_isNode(dirty)) {
2334 dirty = stringifyValue(dirty);
2335 if (typeof dirty !== "string") throw typeErrorCreate("dirty is not a string, aborting");
2336 }
2337 if (!DOMPurify.isSupported) return dirty;
2338 if (SET_CONFIG) {
2339 ALLOWED_TAGS = SET_CONFIG_ALLOWED_TAGS;
2340 ALLOWED_ATTR = SET_CONFIG_ALLOWED_ATTR;
2341 } else _parseConfig(cfg);
2342 if (hooks.uponSanitizeElement.length > 0 || hooks.uponSanitizeAttribute.length > 0) ALLOWED_TAGS = clone(ALLOWED_TAGS);
2343 if (hooks.uponSanitizeAttribute.length > 0) ALLOWED_ATTR = clone(ALLOWED_ATTR);
2344 DOMPurify.removed = [];
2345 const inPlace = IN_PLACE && typeof dirty !== "string" && _isNode(dirty);
2346 if (inPlace) {
2347 _neutralizePatchLinkage(dirty);
2348 const nn = _readNodeName(dirty);
2349 if (typeof nn === "string") {
2350 const tagName = transformCaseFunc(nn);
2351 if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
2352 _neutralizeRoot(dirty);
2353 throw typeErrorCreate("root node is forbidden and cannot be sanitized in-place");
2354 }
2355 }
2356 if (_isClobbered(dirty)) {
2357 _neutralizeRoot(dirty);
2358 throw typeErrorCreate("root node is clobbered and cannot be sanitized in-place");
2359 }
2360 try {
2361 _sanitizeAttachedShadowRoots(dirty);
2362 } catch (error) {
2363 _neutralizeRoot(dirty);
2364 throw error;
2365 }
2366 } else if (_isNode(dirty)) {
2367 body = _initDocument("<!---->");
2368 importedNode = body.ownerDocument.importNode(dirty, true);
2369 if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === "BODY") body = importedNode;
2370 else if (importedNode.nodeName === "HTML") body = importedNode;
2371 else body.appendChild(importedNode);
2372 _sanitizeAttachedShadowRoots(body);
2373 } else {
2374 if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && dirty.indexOf("<") === -1) return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(dirty) : dirty;
2375 body = _initDocument(dirty);
2376 if (!body) return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : "";
2377 }
2378 if (body && FORCE_BODY) _forceRemove(body.firstChild);
2379 const walkRoot = inPlace ? dirty : body;
2380 try {
2381 const nodeIterator = _createNodeIterator(walkRoot);
2382 while (currentNode = nodeIterator.nextNode()) {
2383 _sanitizeElements(currentNode, walkRoot);
2384 _sanitizeAttributes(currentNode, walkRoot);
2385 if (_isDocumentFragment(currentNode.content)) _sanitizeShadowDOM2(currentNode.content);
2386 }
2387 } catch (error) {
2388 if (inPlace) {
2389 _neutralizeRoot(dirty);
2390 arrayForEach(DOMPurify.removed, (entry) => {
2391 if (entry.element) _neutralizeSubtree(entry.element);
2392 });
2393 }
2394 throw error;
2395 }
2396 if (inPlace) {
2397 let rootWasRemoved = false;
2398 arrayForEach(DOMPurify.removed, (entry) => {
2399 if (entry.element) {
2400 if (entry.element === dirty) rootWasRemoved = true;
2401 _neutralizeSubtree(entry.element);
2402 }
2403 });
2404 if (rootWasRemoved) throw typeErrorCreate("a node selected for removal could not be safely returned; refusing to sanitize in place");
2405 if (SAFE_FOR_TEMPLATES) _scrubTemplateExpressions2(dirty);
2406 return dirty;
2407 }
2408 if (RETURN_DOM) {
2409 if (SAFE_FOR_TEMPLATES) _scrubTemplateExpressions2(body);
2410 if (RETURN_DOM_FRAGMENT) {
2411 returnNode = createDocumentFragment.call(body.ownerDocument);
2412 while (body.firstChild) returnNode.appendChild(body.firstChild);
2413 } else returnNode = body;
2414 if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) returnNode = importNode.call(originalDocument, returnNode, true);
2415 return returnNode;
2416 }
2417 let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
2418 if (WHOLE_DOCUMENT && ALLOWED_TAGS["!doctype"] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) serializedHTML = "<!DOCTYPE " + body.ownerDocument.doctype.name + ">\n" + serializedHTML;
2419 if (SAFE_FOR_TEMPLATES) serializedHTML = _stripTemplateExpressions(serializedHTML);
2420 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(serializedHTML) : serializedHTML;
2421 };
2422 DOMPurify.setConfig = function() {
2423 let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
2424 _parseConfig(cfg);
2425 SET_CONFIG = true;
2426 SET_CONFIG_ALLOWED_TAGS = ALLOWED_TAGS;
2427 SET_CONFIG_ALLOWED_ATTR = ALLOWED_ATTR;
2428 };
2429 DOMPurify.clearConfig = function() {
2430 CONFIG = null;
2431 SET_CONFIG = false;
2432 SET_CONFIG_ALLOWED_TAGS = null;
2433 SET_CONFIG_ALLOWED_ATTR = null;
2434 trustedTypesPolicy = defaultTrustedTypesPolicy;
2435 emptyHTML = "";
2436 };
2437 DOMPurify.isValidAttribute = function(tag, attr, value) {
2438 if (!CONFIG) _parseConfig({});
2439 const lcTag = transformCaseFunc(tag);
2440 const lcName = transformCaseFunc(attr);
2441 return _isValidAttribute(lcTag, lcName, value);
2442 };
2443 DOMPurify.addHook = function(entryPoint, hookFunction) {
2444 if (typeof hookFunction !== "function") return;
2445 if (!objectHasOwnProperty(hooks, entryPoint)) return;
2446 arrayPush(hooks[entryPoint], hookFunction);
2447 };
2448 DOMPurify.removeHook = function(entryPoint, hookFunction) {
2449 if (!objectHasOwnProperty(hooks, entryPoint)) return;
2450 if (hookFunction !== void 0) {
2451 const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
2452 return index === -1 ? void 0 : arraySplice(hooks[entryPoint], index, 1)[0];
2453 }
2454 return arrayPop(hooks[entryPoint]);
2455 };
2456 DOMPurify.removeHooks = function(entryPoint) {
2457 if (!objectHasOwnProperty(hooks, entryPoint)) return;
2458 hooks[entryPoint] = [];
2459 };
2460 DOMPurify.removeAllHooks = function() {
2461 hooks = _createHooksMap();
2462 };
2463 return DOMPurify;
2464 }
2465 return createDOMPurify();
2466 });
2467
2468 //# sourceMappingURL=purify.js.map