High severityNVD Advisory· Published Oct 11, 2024· Updated Nov 3, 2025
DOMPurify nesting-based mXSS
CVE-2024-47875
Description
DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG. DOMpurify was vulnerable to nesting-based mXSS. This vulnerability is fixed in 2.5.0 and 3.1.3.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
dompurifynpm | < 2.5.0 | 2.5.0 |
dompurifynpm | >= 3.0.0, < 3.1.3 | 3.1.3 |
Affected products
1Patches
26ea80cd8b476Merge pull request #943 from cure53/main
12 files changed · +326 −29
dist/purify.cjs.js+49 −2 modified@@ -517,6 +517,9 @@ function createDOMPurify() { /* Keep a reference to config to pass to hooks */ let CONFIG = null; + /* Specify the maximum element nesting depth to prevent mXSS */ + const MAX_NESTING_DEPTH = 500; + /* Ideally, do not touch anything below this line */ /* ______________________________________________ */ @@ -927,7 +930,11 @@ function createDOMPurify() { * @return {Boolean} true if clobbered, false if safe */ const _isClobbered = function _isClobbered(elm) { - return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); + return elm instanceof HTMLFormElement && ( + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' || + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__removalCount !== 'undefined' && typeof elm.__removalCount !== 'number' || typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); }; /** @@ -1025,7 +1032,9 @@ function createDOMPurify() { if (childNodes && parentNode) { const childCount = childNodes.length; for (let i = childCount - 1; i >= 0; --i) { - parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode)); + const childClone = cloneNode(childNodes[i], true); + childClone.__removalCount = (currentNode.__removalCount || 0) + 1; + parentNode.insertBefore(childClone, getNextSibling(currentNode)); } } } @@ -1258,8 +1267,27 @@ function createDOMPurify() { continue; } + /* Set the nesting depth of an element */ + if (shadowNode.nodeType === 1) { + if (shadowNode.parentNode && shadowNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + shadowNode.__depth = (shadowNode.__removalCount || 0) + shadowNode.parentNode.__depth + 1; + } else { + shadowNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (shadowNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(shadowNode); + } + /* Deep shadow DOM detected */ if (shadowNode.content instanceof DocumentFragment) { + shadowNode.content.__depth = shadowNode.__depth; _sanitizeShadowDOM(shadowNode.content); } @@ -1376,8 +1404,27 @@ function createDOMPurify() { continue; } + /* Set the nesting depth of an element */ + if (currentNode.nodeType === 1) { + if (currentNode.parentNode && currentNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + currentNode.__depth = (currentNode.__removalCount || 0) + currentNode.parentNode.__depth + 1; + } else { + currentNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (currentNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(currentNode); + } + /* Shadow DOM detected, sanitize it */ if (currentNode.content instanceof DocumentFragment) { + currentNode.content.__depth = currentNode.__depth; _sanitizeShadowDOM(currentNode.content); }
dist/purify.cjs.js.map+1 −1 modifieddist/purify.es.mjs+49 −2 modified@@ -515,6 +515,9 @@ function createDOMPurify() { /* Keep a reference to config to pass to hooks */ let CONFIG = null; + /* Specify the maximum element nesting depth to prevent mXSS */ + const MAX_NESTING_DEPTH = 500; + /* Ideally, do not touch anything below this line */ /* ______________________________________________ */ @@ -925,7 +928,11 @@ function createDOMPurify() { * @return {Boolean} true if clobbered, false if safe */ const _isClobbered = function _isClobbered(elm) { - return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); + return elm instanceof HTMLFormElement && ( + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' || + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__removalCount !== 'undefined' && typeof elm.__removalCount !== 'number' || typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); }; /** @@ -1023,7 +1030,9 @@ function createDOMPurify() { if (childNodes && parentNode) { const childCount = childNodes.length; for (let i = childCount - 1; i >= 0; --i) { - parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode)); + const childClone = cloneNode(childNodes[i], true); + childClone.__removalCount = (currentNode.__removalCount || 0) + 1; + parentNode.insertBefore(childClone, getNextSibling(currentNode)); } } } @@ -1256,8 +1265,27 @@ function createDOMPurify() { continue; } + /* Set the nesting depth of an element */ + if (shadowNode.nodeType === 1) { + if (shadowNode.parentNode && shadowNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + shadowNode.__depth = (shadowNode.__removalCount || 0) + shadowNode.parentNode.__depth + 1; + } else { + shadowNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (shadowNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(shadowNode); + } + /* Deep shadow DOM detected */ if (shadowNode.content instanceof DocumentFragment) { + shadowNode.content.__depth = shadowNode.__depth; _sanitizeShadowDOM(shadowNode.content); } @@ -1374,8 +1402,27 @@ function createDOMPurify() { continue; } + /* Set the nesting depth of an element */ + if (currentNode.nodeType === 1) { + if (currentNode.parentNode && currentNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + currentNode.__depth = (currentNode.__removalCount || 0) + currentNode.parentNode.__depth + 1; + } else { + currentNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (currentNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(currentNode); + } + /* Shadow DOM detected, sanitize it */ if (currentNode.content instanceof DocumentFragment) { + currentNode.content.__depth = currentNode.__depth; _sanitizeShadowDOM(currentNode.content); }
dist/purify.es.mjs.map+1 −1 modifieddist/purify.js+49 −2 modified@@ -521,6 +521,9 @@ /* Keep a reference to config to pass to hooks */ let CONFIG = null; + /* Specify the maximum element nesting depth to prevent mXSS */ + const MAX_NESTING_DEPTH = 500; + /* Ideally, do not touch anything below this line */ /* ______________________________________________ */ @@ -931,7 +934,11 @@ * @return {Boolean} true if clobbered, false if safe */ const _isClobbered = function _isClobbered(elm) { - return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); + return elm instanceof HTMLFormElement && ( + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' || + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__removalCount !== 'undefined' && typeof elm.__removalCount !== 'number' || typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); }; /** @@ -1029,7 +1036,9 @@ if (childNodes && parentNode) { const childCount = childNodes.length; for (let i = childCount - 1; i >= 0; --i) { - parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode)); + const childClone = cloneNode(childNodes[i], true); + childClone.__removalCount = (currentNode.__removalCount || 0) + 1; + parentNode.insertBefore(childClone, getNextSibling(currentNode)); } } } @@ -1262,8 +1271,27 @@ continue; } + /* Set the nesting depth of an element */ + if (shadowNode.nodeType === 1) { + if (shadowNode.parentNode && shadowNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + shadowNode.__depth = (shadowNode.__removalCount || 0) + shadowNode.parentNode.__depth + 1; + } else { + shadowNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (shadowNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(shadowNode); + } + /* Deep shadow DOM detected */ if (shadowNode.content instanceof DocumentFragment) { + shadowNode.content.__depth = shadowNode.__depth; _sanitizeShadowDOM(shadowNode.content); } @@ -1380,8 +1408,27 @@ continue; } + /* Set the nesting depth of an element */ + if (currentNode.nodeType === 1) { + if (currentNode.parentNode && currentNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + currentNode.__depth = (currentNode.__removalCount || 0) + currentNode.parentNode.__depth + 1; + } else { + currentNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (currentNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(currentNode); + } + /* Shadow DOM detected, sanitize it */ if (currentNode.content instanceof DocumentFragment) { + currentNode.content.__depth = currentNode.__depth; _sanitizeShadowDOM(currentNode.content); }
dist/purify.js.map+1 −1 modifieddist/purify.min.js+1 −1 modified@@ -1,3 +1,3 @@ /*! @license DOMPurify 3.1.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.1.0/LICENSE */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).DOMPurify=t()}(this,(function(){"use strict";const{entries:e,setPrototypeOf:t,isFrozen:n,getPrototypeOf:o,getOwnPropertyDescriptor:r}=Object;let{freeze:i,seal:a,create:l}=Object,{apply:c,construct:s}="undefined"!=typeof Reflect&&Reflect;i||(i=function(e){return e}),a||(a=function(e){return e}),c||(c=function(e,t,n){return e.apply(t,n)}),s||(s=function(e,t){return new e(...t)});const u=b(Array.prototype.forEach),m=b(Array.prototype.pop),p=b(Array.prototype.push),f=b(String.prototype.toLowerCase),d=b(String.prototype.toString),h=b(String.prototype.match),g=b(String.prototype.replace),T=b(String.prototype.indexOf),y=b(String.prototype.trim),E=b(Object.prototype.hasOwnProperty),A=b(RegExp.prototype.test),_=(N=TypeError,function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return s(N,t)});var N;function b(e){return function(t){for(var n=arguments.length,o=new Array(n>1?n-1:0),r=1;r<n;r++)o[r-1]=arguments[r];return c(e,t,o)}}function S(e,o){let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:f;t&&t(e,null);let i=o.length;for(;i--;){let t=o[i];if("string"==typeof t){const e=r(t);e!==t&&(n(o)||(o[i]=e),t=e)}e[t]=!0}return e}function R(e){for(let t=0;t<e.length;t++){E(e,t)||(e[t]=null)}return e}function w(t){const n=l(null);for(const[o,r]of e(t)){E(t,o)&&(Array.isArray(r)?n[o]=R(r):r&&"object"==typeof r&&r.constructor===Object?n[o]=w(r):n[o]=r)}return n}function L(e,t){for(;null!==e;){const n=r(e,t);if(n){if(n.get)return b(n.get);if("function"==typeof n.value)return b(n.value)}e=o(e)}return function(){return null}}const D=i(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),C=i(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","filter","font","g","glyph","glyphref","hkern","image","line","lineargradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),O=i(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),x=i(["animate","color-profile","cursor","discard","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor","unknown","use"]),v=i(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover","mprescripts"]),k=i(["maction","maligngroup","malignmark","mlongdiv","mscarries","mscarry","msgroup","mstack","msline","msrow","semantics","annotation","annotation-xml","mprescripts","none"]),M=i(["#text"]),I=i(["accept","action","align","alt","autocapitalize","autocomplete","autopictureinpicture","autoplay","background","bgcolor","border","capture","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","controls","controlslist","coords","crossorigin","datetime","decoding","default","dir","disabled","disablepictureinpicture","disableremoteplayback","download","draggable","enctype","enterkeyhint","face","for","headers","height","hidden","high","href","hreflang","id","inputmode","integrity","ismap","kind","label","lang","list","loading","loop","low","max","maxlength","media","method","min","minlength","multiple","muted","name","nonce","noshade","novalidate","nowrap","open","optimum","pattern","placeholder","playsinline","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","span","srclang","start","src","srcset","step","style","summary","tabindex","title","translate","type","usemap","valign","value","width","wrap","xmlns","slot"]),U=i(["accent-height","accumulate","additive","alignment-baseline","ascent","attributename","attributetype","azimuth","basefrequency","baseline-shift","begin","bias","by","class","clip","clippathunits","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","cx","cy","d","dx","dy","diffuseconstant","direction","display","divisor","dur","edgemode","elevation","end","fill","fill-opacity","fill-rule","filter","filterunits","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","fx","fy","g1","g2","glyph-name","glyphref","gradientunits","gradienttransform","height","href","id","image-rendering","in","in2","k","k1","k2","k3","k4","kerning","keypoints","keysplines","keytimes","lang","lengthadjust","letter-spacing","kernelmatrix","kernelunitlength","lighting-color","local","marker-end","marker-mid","marker-start","markerheight","markerunits","markerwidth","maskcontentunits","maskunits","max","mask","media","method","mode","min","name","numoctaves","offset","operator","opacity","order","orient","orientation","origin","overflow","paint-order","path","pathlength","patterncontentunits","patterntransform","patternunits","points","preservealpha","preserveaspectratio","primitiveunits","r","rx","ry","radius","refx","refy","repeatcount","repeatdur","restart","result","rotate","scale","seed","shape-rendering","specularconstant","specularexponent","spreadmethod","startoffset","stddeviation","stitchtiles","stop-color","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke","stroke-width","style","surfacescale","systemlanguage","tabindex","targetx","targety","transform","transform-origin","text-anchor","text-decoration","text-rendering","textlength","type","u1","u2","unicode","values","viewbox","visibility","version","vert-adv-y","vert-origin-x","vert-origin-y","width","word-spacing","wrap","writing-mode","xchannelselector","ychannelselector","x","x1","x2","xmlns","y","y1","y2","z","zoomandpan"]),P=i(["accent","accentunder","align","bevelled","close","columnsalign","columnlines","columnspan","denomalign","depth","dir","display","displaystyle","encoding","fence","frame","height","href","id","largeop","length","linethickness","lspace","lquote","mathbackground","mathcolor","mathsize","mathvariant","maxsize","minsize","movablelimits","notation","numalign","open","rowalign","rowlines","rowspacing","rowspan","rspace","rquote","scriptlevel","scriptminsize","scriptsizemultiplier","selection","separator","separators","stretchy","subscriptshift","supscriptshift","symmetric","voffset","width","xmlns"]),F=i(["xlink:href","xml:id","xlink:title","xml:space","xmlns:xlink"]),H=a(/\{\{[\w\W]*|[\w\W]*\}\}/gm),z=a(/<%[\w\W]*|[\w\W]*%>/gm),B=a(/\${[\w\W]*}/gm),W=a(/^data-[\-\w.\u00B7-\uFFFF]/),G=a(/^aria-[\-\w]+$/),Y=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),j=a(/^(?:\w+script|data):/i),X=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),q=a(/^html$/i),$=a(/^[a-z][.\w]*(-[.\w]+)+$/i);var K=Object.freeze({__proto__:null,MUSTACHE_EXPR:H,ERB_EXPR:z,TMPLIT_EXPR:B,DATA_ATTR:W,ARIA_ATTR:G,IS_ALLOWED_URI:Y,IS_SCRIPT_OR_DATA:j,ATTR_WHITESPACE:X,DOCTYPE_NAME:q,CUSTOM_ELEMENT:$});const V=function(){return"undefined"==typeof window?null:window},Z=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const o="data-tt-policy-suffix";t&&t.hasAttribute(o)&&(n=t.getAttribute(o));const r="dompurify"+(n?"#"+n:"");try{return e.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+r+" could not be created."),null}};var J=function t(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:V();const o=e=>t(e);if(o.version="3.1.0",o.removed=[],!n||!n.document||9!==n.document.nodeType)return o.isSupported=!1,o;let{document:r}=n;const a=r,c=a.currentScript,{DocumentFragment:s,HTMLTemplateElement:N,Node:b,Element:R,NodeFilter:H,NamedNodeMap:z=n.NamedNodeMap||n.MozNamedAttrMap,HTMLFormElement:B,DOMParser:W,trustedTypes:G}=n,j=R.prototype,X=L(j,"cloneNode"),$=L(j,"nextSibling"),J=L(j,"childNodes"),Q=L(j,"parentNode");if("function"==typeof N){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let ee,te="";const{implementation:ne,createNodeIterator:oe,createDocumentFragment:re,getElementsByTagName:ie}=r,{importNode:ae}=a;let le={};o.isSupported="function"==typeof e&&"function"==typeof Q&&ne&&void 0!==ne.createHTMLDocument;const{MUSTACHE_EXPR:ce,ERB_EXPR:se,TMPLIT_EXPR:ue,DATA_ATTR:me,ARIA_ATTR:pe,IS_SCRIPT_OR_DATA:fe,ATTR_WHITESPACE:de,CUSTOM_ELEMENT:he}=K;let{IS_ALLOWED_URI:ge}=K,Te=null;const ye=S({},[...D,...C,...O,...v,...M]);let Ee=null;const Ae=S({},[...I,...U,...P,...F]);let _e=Object.seal(l(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Ne=null,be=null,Se=!0,Re=!0,we=!1,Le=!0,De=!1,Ce=!0,Oe=!1,xe=!1,ve=!1,ke=!1,Me=!1,Ie=!1,Ue=!0,Pe=!1;const Fe="user-content-";let He=!0,ze=!1,Be={},We=null;const Ge=S({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let Ye=null;const je=S({},["audio","video","img","source","image","track"]);let Xe=null;const qe=S({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),$e="http://www.w3.org/1998/Math/MathML",Ke="http://www.w3.org/2000/svg",Ve="http://www.w3.org/1999/xhtml";let Ze=Ve,Je=!1,Qe=null;const et=S({},[$e,Ke,Ve],d);let tt=null;const nt=["application/xhtml+xml","text/html"],ot="text/html";let rt=null,it=null;const at=r.createElement("form"),lt=function(e){return e instanceof RegExp||e instanceof Function},ct=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!it||it!==e){if(e&&"object"==typeof e||(e={}),e=w(e),tt=-1===nt.indexOf(e.PARSER_MEDIA_TYPE)?ot:e.PARSER_MEDIA_TYPE,rt="application/xhtml+xml"===tt?d:f,Te=E(e,"ALLOWED_TAGS")?S({},e.ALLOWED_TAGS,rt):ye,Ee=E(e,"ALLOWED_ATTR")?S({},e.ALLOWED_ATTR,rt):Ae,Qe=E(e,"ALLOWED_NAMESPACES")?S({},e.ALLOWED_NAMESPACES,d):et,Xe=E(e,"ADD_URI_SAFE_ATTR")?S(w(qe),e.ADD_URI_SAFE_ATTR,rt):qe,Ye=E(e,"ADD_DATA_URI_TAGS")?S(w(je),e.ADD_DATA_URI_TAGS,rt):je,We=E(e,"FORBID_CONTENTS")?S({},e.FORBID_CONTENTS,rt):Ge,Ne=E(e,"FORBID_TAGS")?S({},e.FORBID_TAGS,rt):{},be=E(e,"FORBID_ATTR")?S({},e.FORBID_ATTR,rt):{},Be=!!E(e,"USE_PROFILES")&&e.USE_PROFILES,Se=!1!==e.ALLOW_ARIA_ATTR,Re=!1!==e.ALLOW_DATA_ATTR,we=e.ALLOW_UNKNOWN_PROTOCOLS||!1,Le=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,De=e.SAFE_FOR_TEMPLATES||!1,Ce=!1!==e.SAFE_FOR_XML,Oe=e.WHOLE_DOCUMENT||!1,ke=e.RETURN_DOM||!1,Me=e.RETURN_DOM_FRAGMENT||!1,Ie=e.RETURN_TRUSTED_TYPE||!1,ve=e.FORCE_BODY||!1,Ue=!1!==e.SANITIZE_DOM,Pe=e.SANITIZE_NAMED_PROPS||!1,He=!1!==e.KEEP_CONTENT,ze=e.IN_PLACE||!1,ge=e.ALLOWED_URI_REGEXP||Y,Ze=e.NAMESPACE||Ve,_e=e.CUSTOM_ELEMENT_HANDLING||{},e.CUSTOM_ELEMENT_HANDLING&<(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(_e.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&<(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(_e.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(_e.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),De&&(Re=!1),Me&&(ke=!0),Be&&(Te=S({},M),Ee=[],!0===Be.html&&(S(Te,D),S(Ee,I)),!0===Be.svg&&(S(Te,C),S(Ee,U),S(Ee,F)),!0===Be.svgFilters&&(S(Te,O),S(Ee,U),S(Ee,F)),!0===Be.mathMl&&(S(Te,v),S(Ee,P),S(Ee,F))),e.ADD_TAGS&&(Te===ye&&(Te=w(Te)),S(Te,e.ADD_TAGS,rt)),e.ADD_ATTR&&(Ee===Ae&&(Ee=w(Ee)),S(Ee,e.ADD_ATTR,rt)),e.ADD_URI_SAFE_ATTR&&S(Xe,e.ADD_URI_SAFE_ATTR,rt),e.FORBID_CONTENTS&&(We===Ge&&(We=w(We)),S(We,e.FORBID_CONTENTS,rt)),He&&(Te["#text"]=!0),Oe&&S(Te,["html","head","body"]),Te.table&&(S(Te,["tbody"]),delete Ne.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw _('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw _('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');ee=e.TRUSTED_TYPES_POLICY,te=ee.createHTML("")}else void 0===ee&&(ee=Z(G,c)),null!==ee&&"string"==typeof te&&(te=ee.createHTML(""));i&&i(e),it=e}},st=S({},["mi","mo","mn","ms","mtext"]),ut=S({},["foreignobject","desc","title","annotation-xml"]),mt=S({},["title","style","font","a","script"]),pt=S({},[...C,...O,...x]),ft=S({},[...v,...k]),dt=function(e){let t=Q(e);t&&t.tagName||(t={namespaceURI:Ze,tagName:"template"});const n=f(e.tagName),o=f(t.tagName);return!!Qe[e.namespaceURI]&&(e.namespaceURI===Ke?t.namespaceURI===Ve?"svg"===n:t.namespaceURI===$e?"svg"===n&&("annotation-xml"===o||st[o]):Boolean(pt[n]):e.namespaceURI===$e?t.namespaceURI===Ve?"math"===n:t.namespaceURI===Ke?"math"===n&&ut[o]:Boolean(ft[n]):e.namespaceURI===Ve?!(t.namespaceURI===Ke&&!ut[o])&&(!(t.namespaceURI===$e&&!st[o])&&(!ft[n]&&(mt[n]||!pt[n]))):!("application/xhtml+xml"!==tt||!Qe[e.namespaceURI]))},ht=function(e){p(o.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){e.remove()}},gt=function(e,t){try{p(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){p(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!Ee[e])if(ke||Me)try{ht(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},Tt=function(e){let t=null,n=null;if(ve)e="<remove></remove>"+e;else{const t=h(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===tt&&Ze===Ve&&(e='<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>'+e+"</body></html>");const o=ee?ee.createHTML(e):e;if(Ze===Ve)try{t=(new W).parseFromString(o,tt)}catch(e){}if(!t||!t.documentElement){t=ne.createDocument(Ze,"template",null);try{t.documentElement.innerHTML=Je?te:o}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(r.createTextNode(n),i.childNodes[0]||null),Ze===Ve?ie.call(t,Oe?"html":"body")[0]:Oe?t.documentElement:i},yt=function(e){return oe.call(e.ownerDocument||e,e,H.SHOW_ELEMENT|H.SHOW_COMMENT|H.SHOW_TEXT|H.SHOW_PROCESSING_INSTRUCTION|H.SHOW_CDATA_SECTION,null)},Et=function(e){return e instanceof B&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof z)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},At=function(e){return"function"==typeof b&&e instanceof b},_t=function(e,t,n){le[e]&&u(le[e],(e=>{e.call(o,t,n,it)}))},Nt=function(e){let t=null;if(_t("beforeSanitizeElements",e,null),Et(e))return ht(e),!0;const n=rt(e.nodeName);if(_t("uponSanitizeElement",e,{tagName:n,allowedTags:Te}),e.hasChildNodes()&&!At(e.firstElementChild)&&A(/<[/\w]/g,e.innerHTML)&&A(/<[/\w]/g,e.textContent))return ht(e),!0;if(7===e.nodeType)return ht(e),!0;if(Ce&&8===e.nodeType&&A(/<[/\w]/g,e.data))return ht(e),!0;if(!Te[n]||Ne[n]){if(!Ne[n]&&St(n)){if(_e.tagNameCheck instanceof RegExp&&A(_e.tagNameCheck,n))return!1;if(_e.tagNameCheck instanceof Function&&_e.tagNameCheck(n))return!1}if(He&&!We[n]){const t=Q(e)||e.parentNode,n=J(e)||e.childNodes;if(n&&t){for(let o=n.length-1;o>=0;--o)t.insertBefore(X(n[o],!0),$(e))}}return ht(e),!0}return e instanceof R&&!dt(e)?(ht(e),!0):"noscript"!==n&&"noembed"!==n&&"noframes"!==n||!A(/<\/no(script|embed|frames)/i,e.innerHTML)?(De&&3===e.nodeType&&(t=e.textContent,u([ce,se,ue],(e=>{t=g(t,e," ")})),e.textContent!==t&&(p(o.removed,{element:e.cloneNode()}),e.textContent=t)),_t("afterSanitizeElements",e,null),!1):(ht(e),!0)},bt=function(e,t,n){if(Ue&&("id"===t||"name"===t)&&(n in r||n in at))return!1;if(Re&&!be[t]&&A(me,t));else if(Se&&A(pe,t));else if(!Ee[t]||be[t]){if(!(St(e)&&(_e.tagNameCheck instanceof RegExp&&A(_e.tagNameCheck,e)||_e.tagNameCheck instanceof Function&&_e.tagNameCheck(e))&&(_e.attributeNameCheck instanceof RegExp&&A(_e.attributeNameCheck,t)||_e.attributeNameCheck instanceof Function&&_e.attributeNameCheck(t))||"is"===t&&_e.allowCustomizedBuiltInElements&&(_e.tagNameCheck instanceof RegExp&&A(_e.tagNameCheck,n)||_e.tagNameCheck instanceof Function&&_e.tagNameCheck(n))))return!1}else if(Xe[t]);else if(A(ge,g(n,de,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==T(n,"data:")||!Ye[e]){if(we&&!A(fe,g(n,de,"")));else if(n)return!1}else;return!0},St=function(e){return"annotation-xml"!==e&&h(e,he)},Rt=function(e){_t("beforeSanitizeAttributes",e,null);const{attributes:t}=e;if(!t)return;const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:Ee};let r=t.length;for(;r--;){const i=t[r],{name:a,namespaceURI:l,value:c}=i,s=rt(a);let p="value"===a?c:y(c);if(n.attrName=s,n.attrValue=p,n.keepAttr=!0,n.forceKeepAttr=void 0,_t("uponSanitizeAttribute",e,n),p=n.attrValue,n.forceKeepAttr)continue;if(gt(a,e),!n.keepAttr)continue;if(!Le&&A(/\/>/i,p)){gt(a,e);continue}De&&u([ce,se,ue],(e=>{p=g(p,e," ")}));const f=rt(e.nodeName);if(bt(f,s,p)){if(!Pe||"id"!==s&&"name"!==s||(gt(a,e),p=Fe+p),ee&&"object"==typeof G&&"function"==typeof G.getAttributeType)if(l);else switch(G.getAttributeType(f,s)){case"TrustedHTML":p=ee.createHTML(p);break;case"TrustedScriptURL":p=ee.createScriptURL(p)}try{l?e.setAttributeNS(l,a,p):e.setAttribute(a,p),m(o.removed)}catch(e){}}}_t("afterSanitizeAttributes",e,null)},wt=function e(t){let n=null;const o=yt(t);for(_t("beforeSanitizeShadowDOM",t,null);n=o.nextNode();)_t("uponSanitizeShadowNode",n,null),Nt(n)||(n.content instanceof s&&e(n.content),Rt(n));_t("afterSanitizeShadowDOM",t,null)};return o.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=null,r=null,i=null,l=null;if(Je=!e,Je&&(e="\x3c!--\x3e"),"string"!=typeof e&&!At(e)){if("function"!=typeof e.toString)throw _("toString is not a function");if("string"!=typeof(e=e.toString()))throw _("dirty is not a string, aborting")}if(!o.isSupported)return e;if(xe||ct(t),o.removed=[],"string"==typeof e&&(ze=!1),ze){if(e.nodeName){const t=rt(e.nodeName);if(!Te[t]||Ne[t])throw _("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof b)n=Tt("\x3c!----\x3e"),r=n.ownerDocument.importNode(e,!0),1===r.nodeType&&"BODY"===r.nodeName||"HTML"===r.nodeName?n=r:n.appendChild(r);else{if(!ke&&!De&&!Oe&&-1===e.indexOf("<"))return ee&&Ie?ee.createHTML(e):e;if(n=Tt(e),!n)return ke?null:Ie?te:""}n&&ve&&ht(n.firstChild);const c=yt(ze?e:n);for(;i=c.nextNode();)Nt(i)||(i.content instanceof s&&wt(i.content),Rt(i));if(ze)return e;if(ke){if(Me)for(l=re.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return(Ee.shadowroot||Ee.shadowrootmode)&&(l=ae.call(a,l,!0)),l}let m=Oe?n.outerHTML:n.innerHTML;return Oe&&Te["!doctype"]&&n.ownerDocument&&n.ownerDocument.doctype&&n.ownerDocument.doctype.name&&A(q,n.ownerDocument.doctype.name)&&(m="<!DOCTYPE "+n.ownerDocument.doctype.name+">\n"+m),De&&u([ce,se,ue],(e=>{m=g(m,e," ")})),ee&&Ie?ee.createHTML(m):m},o.setConfig=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};ct(e),xe=!0},o.clearConfig=function(){it=null,xe=!1},o.isValidAttribute=function(e,t,n){it||ct({});const o=rt(e),r=rt(t);return bt(o,r,n)},o.addHook=function(e,t){"function"==typeof t&&(le[e]=le[e]||[],p(le[e],t))},o.removeHook=function(e){if(le[e])return m(le[e])},o.removeHooks=function(e){le[e]&&(le[e]=[])},o.removeAllHooks=function(){le={}},o}();return J})); +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).DOMPurify=t()}(this,(function(){"use strict";const{entries:e,setPrototypeOf:t,isFrozen:n,getPrototypeOf:o,getOwnPropertyDescriptor:r}=Object;let{freeze:i,seal:a,create:l}=Object,{apply:c,construct:s}="undefined"!=typeof Reflect&&Reflect;i||(i=function(e){return e}),a||(a=function(e){return e}),c||(c=function(e,t,n){return e.apply(t,n)}),s||(s=function(e,t){return new e(...t)});const u=b(Array.prototype.forEach),m=b(Array.prototype.pop),p=b(Array.prototype.push),f=b(String.prototype.toLowerCase),d=b(String.prototype.toString),h=b(String.prototype.match),g=b(String.prototype.replace),T=b(String.prototype.indexOf),_=b(String.prototype.trim),y=b(Object.prototype.hasOwnProperty),E=b(RegExp.prototype.test),A=(N=TypeError,function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return s(N,t)});var N;function b(e){return function(t){for(var n=arguments.length,o=new Array(n>1?n-1:0),r=1;r<n;r++)o[r-1]=arguments[r];return c(e,t,o)}}function S(e,o){let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:f;t&&t(e,null);let i=o.length;for(;i--;){let t=o[i];if("string"==typeof t){const e=r(t);e!==t&&(n(o)||(o[i]=e),t=e)}e[t]=!0}return e}function R(e){for(let t=0;t<e.length;t++){y(e,t)||(e[t]=null)}return e}function w(t){const n=l(null);for(const[o,r]of e(t)){y(t,o)&&(Array.isArray(r)?n[o]=R(r):r&&"object"==typeof r&&r.constructor===Object?n[o]=w(r):n[o]=r)}return n}function C(e,t){for(;null!==e;){const n=r(e,t);if(n){if(n.get)return b(n.get);if("function"==typeof n.value)return b(n.value)}e=o(e)}return function(){return null}}const v=i(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),L=i(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","filter","font","g","glyph","glyphref","hkern","image","line","lineargradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),D=i(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),O=i(["animate","color-profile","cursor","discard","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor","unknown","use"]),x=i(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover","mprescripts"]),k=i(["maction","maligngroup","malignmark","mlongdiv","mscarries","mscarry","msgroup","mstack","msline","msrow","semantics","annotation","annotation-xml","mprescripts","none"]),M=i(["#text"]),I=i(["accept","action","align","alt","autocapitalize","autocomplete","autopictureinpicture","autoplay","background","bgcolor","border","capture","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","controls","controlslist","coords","crossorigin","datetime","decoding","default","dir","disabled","disablepictureinpicture","disableremoteplayback","download","draggable","enctype","enterkeyhint","face","for","headers","height","hidden","high","href","hreflang","id","inputmode","integrity","ismap","kind","label","lang","list","loading","loop","low","max","maxlength","media","method","min","minlength","multiple","muted","name","nonce","noshade","novalidate","nowrap","open","optimum","pattern","placeholder","playsinline","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","span","srclang","start","src","srcset","step","style","summary","tabindex","title","translate","type","usemap","valign","value","width","wrap","xmlns","slot"]),U=i(["accent-height","accumulate","additive","alignment-baseline","ascent","attributename","attributetype","azimuth","basefrequency","baseline-shift","begin","bias","by","class","clip","clippathunits","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","cx","cy","d","dx","dy","diffuseconstant","direction","display","divisor","dur","edgemode","elevation","end","fill","fill-opacity","fill-rule","filter","filterunits","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","fx","fy","g1","g2","glyph-name","glyphref","gradientunits","gradienttransform","height","href","id","image-rendering","in","in2","k","k1","k2","k3","k4","kerning","keypoints","keysplines","keytimes","lang","lengthadjust","letter-spacing","kernelmatrix","kernelunitlength","lighting-color","local","marker-end","marker-mid","marker-start","markerheight","markerunits","markerwidth","maskcontentunits","maskunits","max","mask","media","method","mode","min","name","numoctaves","offset","operator","opacity","order","orient","orientation","origin","overflow","paint-order","path","pathlength","patterncontentunits","patterntransform","patternunits","points","preservealpha","preserveaspectratio","primitiveunits","r","rx","ry","radius","refx","refy","repeatcount","repeatdur","restart","result","rotate","scale","seed","shape-rendering","specularconstant","specularexponent","spreadmethod","startoffset","stddeviation","stitchtiles","stop-color","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke","stroke-width","style","surfacescale","systemlanguage","tabindex","targetx","targety","transform","transform-origin","text-anchor","text-decoration","text-rendering","textlength","type","u1","u2","unicode","values","viewbox","visibility","version","vert-adv-y","vert-origin-x","vert-origin-y","width","word-spacing","wrap","writing-mode","xchannelselector","ychannelselector","x","x1","x2","xmlns","y","y1","y2","z","zoomandpan"]),P=i(["accent","accentunder","align","bevelled","close","columnsalign","columnlines","columnspan","denomalign","depth","dir","display","displaystyle","encoding","fence","frame","height","href","id","largeop","length","linethickness","lspace","lquote","mathbackground","mathcolor","mathsize","mathvariant","maxsize","minsize","movablelimits","notation","numalign","open","rowalign","rowlines","rowspacing","rowspan","rspace","rquote","scriptlevel","scriptminsize","scriptsizemultiplier","selection","separator","separators","stretchy","subscriptshift","supscriptshift","symmetric","voffset","width","xmlns"]),F=i(["xlink:href","xml:id","xlink:title","xml:space","xmlns:xlink"]),H=a(/\{\{[\w\W]*|[\w\W]*\}\}/gm),z=a(/<%[\w\W]*|[\w\W]*%>/gm),B=a(/\${[\w\W]*}/gm),W=a(/^data-[\-\w.\u00B7-\uFFFF]/),G=a(/^aria-[\-\w]+$/),Y=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),j=a(/^(?:\w+script|data):/i),X=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),q=a(/^html$/i),$=a(/^[a-z][.\w]*(-[.\w]+)+$/i);var K=Object.freeze({__proto__:null,MUSTACHE_EXPR:H,ERB_EXPR:z,TMPLIT_EXPR:B,DATA_ATTR:W,ARIA_ATTR:G,IS_ALLOWED_URI:Y,IS_SCRIPT_OR_DATA:j,ATTR_WHITESPACE:X,DOCTYPE_NAME:q,CUSTOM_ELEMENT:$});const V=function(){return"undefined"==typeof window?null:window},Z=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const o="data-tt-policy-suffix";t&&t.hasAttribute(o)&&(n=t.getAttribute(o));const r="dompurify"+(n?"#"+n:"");try{return e.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+r+" could not be created."),null}};var J=function t(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:V();const o=e=>t(e);if(o.version="3.1.0",o.removed=[],!n||!n.document||9!==n.document.nodeType)return o.isSupported=!1,o;let{document:r}=n;const a=r,c=a.currentScript,{DocumentFragment:s,HTMLTemplateElement:N,Node:b,Element:R,NodeFilter:H,NamedNodeMap:z=n.NamedNodeMap||n.MozNamedAttrMap,HTMLFormElement:B,DOMParser:W,trustedTypes:G}=n,j=R.prototype,X=C(j,"cloneNode"),$=C(j,"nextSibling"),J=C(j,"childNodes"),Q=C(j,"parentNode");if("function"==typeof N){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let ee,te="";const{implementation:ne,createNodeIterator:oe,createDocumentFragment:re,getElementsByTagName:ie}=r,{importNode:ae}=a;let le={};o.isSupported="function"==typeof e&&"function"==typeof Q&&ne&&void 0!==ne.createHTMLDocument;const{MUSTACHE_EXPR:ce,ERB_EXPR:se,TMPLIT_EXPR:ue,DATA_ATTR:me,ARIA_ATTR:pe,IS_SCRIPT_OR_DATA:fe,ATTR_WHITESPACE:de,CUSTOM_ELEMENT:he}=K;let{IS_ALLOWED_URI:ge}=K,Te=null;const _e=S({},[...v,...L,...D,...x,...M]);let ye=null;const Ee=S({},[...I,...U,...P,...F]);let Ae=Object.seal(l(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Ne=null,be=null,Se=!0,Re=!0,we=!1,Ce=!0,ve=!1,Le=!0,De=!1,Oe=!1,xe=!1,ke=!1,Me=!1,Ie=!1,Ue=!0,Pe=!1;const Fe="user-content-";let He=!0,ze=!1,Be={},We=null;const Ge=S({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let Ye=null;const je=S({},["audio","video","img","source","image","track"]);let Xe=null;const qe=S({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),$e="http://www.w3.org/1998/Math/MathML",Ke="http://www.w3.org/2000/svg",Ve="http://www.w3.org/1999/xhtml";let Ze=Ve,Je=!1,Qe=null;const et=S({},[$e,Ke,Ve],d);let tt=null;const nt=["application/xhtml+xml","text/html"],ot="text/html";let rt=null,it=null;const at=500,lt=r.createElement("form"),ct=function(e){return e instanceof RegExp||e instanceof Function},st=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!it||it!==e){if(e&&"object"==typeof e||(e={}),e=w(e),tt=-1===nt.indexOf(e.PARSER_MEDIA_TYPE)?ot:e.PARSER_MEDIA_TYPE,rt="application/xhtml+xml"===tt?d:f,Te=y(e,"ALLOWED_TAGS")?S({},e.ALLOWED_TAGS,rt):_e,ye=y(e,"ALLOWED_ATTR")?S({},e.ALLOWED_ATTR,rt):Ee,Qe=y(e,"ALLOWED_NAMESPACES")?S({},e.ALLOWED_NAMESPACES,d):et,Xe=y(e,"ADD_URI_SAFE_ATTR")?S(w(qe),e.ADD_URI_SAFE_ATTR,rt):qe,Ye=y(e,"ADD_DATA_URI_TAGS")?S(w(je),e.ADD_DATA_URI_TAGS,rt):je,We=y(e,"FORBID_CONTENTS")?S({},e.FORBID_CONTENTS,rt):Ge,Ne=y(e,"FORBID_TAGS")?S({},e.FORBID_TAGS,rt):{},be=y(e,"FORBID_ATTR")?S({},e.FORBID_ATTR,rt):{},Be=!!y(e,"USE_PROFILES")&&e.USE_PROFILES,Se=!1!==e.ALLOW_ARIA_ATTR,Re=!1!==e.ALLOW_DATA_ATTR,we=e.ALLOW_UNKNOWN_PROTOCOLS||!1,Ce=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,ve=e.SAFE_FOR_TEMPLATES||!1,Le=!1!==e.SAFE_FOR_XML,De=e.WHOLE_DOCUMENT||!1,ke=e.RETURN_DOM||!1,Me=e.RETURN_DOM_FRAGMENT||!1,Ie=e.RETURN_TRUSTED_TYPE||!1,xe=e.FORCE_BODY||!1,Ue=!1!==e.SANITIZE_DOM,Pe=e.SANITIZE_NAMED_PROPS||!1,He=!1!==e.KEEP_CONTENT,ze=e.IN_PLACE||!1,ge=e.ALLOWED_URI_REGEXP||Y,Ze=e.NAMESPACE||Ve,Ae=e.CUSTOM_ELEMENT_HANDLING||{},e.CUSTOM_ELEMENT_HANDLING&&ct(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(Ae.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&&ct(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(Ae.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(Ae.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),ve&&(Re=!1),Me&&(ke=!0),Be&&(Te=S({},M),ye=[],!0===Be.html&&(S(Te,v),S(ye,I)),!0===Be.svg&&(S(Te,L),S(ye,U),S(ye,F)),!0===Be.svgFilters&&(S(Te,D),S(ye,U),S(ye,F)),!0===Be.mathMl&&(S(Te,x),S(ye,P),S(ye,F))),e.ADD_TAGS&&(Te===_e&&(Te=w(Te)),S(Te,e.ADD_TAGS,rt)),e.ADD_ATTR&&(ye===Ee&&(ye=w(ye)),S(ye,e.ADD_ATTR,rt)),e.ADD_URI_SAFE_ATTR&&S(Xe,e.ADD_URI_SAFE_ATTR,rt),e.FORBID_CONTENTS&&(We===Ge&&(We=w(We)),S(We,e.FORBID_CONTENTS,rt)),He&&(Te["#text"]=!0),De&&S(Te,["html","head","body"]),Te.table&&(S(Te,["tbody"]),delete Ne.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw A('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw A('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');ee=e.TRUSTED_TYPES_POLICY,te=ee.createHTML("")}else void 0===ee&&(ee=Z(G,c)),null!==ee&&"string"==typeof te&&(te=ee.createHTML(""));i&&i(e),it=e}},ut=S({},["mi","mo","mn","ms","mtext"]),mt=S({},["foreignobject","desc","title","annotation-xml"]),pt=S({},["title","style","font","a","script"]),ft=S({},[...L,...D,...O]),dt=S({},[...x,...k]),ht=function(e){let t=Q(e);t&&t.tagName||(t={namespaceURI:Ze,tagName:"template"});const n=f(e.tagName),o=f(t.tagName);return!!Qe[e.namespaceURI]&&(e.namespaceURI===Ke?t.namespaceURI===Ve?"svg"===n:t.namespaceURI===$e?"svg"===n&&("annotation-xml"===o||ut[o]):Boolean(ft[n]):e.namespaceURI===$e?t.namespaceURI===Ve?"math"===n:t.namespaceURI===Ke?"math"===n&&mt[o]:Boolean(dt[n]):e.namespaceURI===Ve?!(t.namespaceURI===Ke&&!mt[o])&&(!(t.namespaceURI===$e&&!ut[o])&&(!dt[n]&&(pt[n]||!ft[n]))):!("application/xhtml+xml"!==tt||!Qe[e.namespaceURI]))},gt=function(e){p(o.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){e.remove()}},Tt=function(e,t){try{p(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){p(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!ye[e])if(ke||Me)try{gt(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},_t=function(e){let t=null,n=null;if(xe)e="<remove></remove>"+e;else{const t=h(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===tt&&Ze===Ve&&(e='<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>'+e+"</body></html>");const o=ee?ee.createHTML(e):e;if(Ze===Ve)try{t=(new W).parseFromString(o,tt)}catch(e){}if(!t||!t.documentElement){t=ne.createDocument(Ze,"template",null);try{t.documentElement.innerHTML=Je?te:o}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(r.createTextNode(n),i.childNodes[0]||null),Ze===Ve?ie.call(t,De?"html":"body")[0]:De?t.documentElement:i},yt=function(e){return oe.call(e.ownerDocument||e,e,H.SHOW_ELEMENT|H.SHOW_COMMENT|H.SHOW_TEXT|H.SHOW_PROCESSING_INSTRUCTION|H.SHOW_CDATA_SECTION,null)},Et=function(e){return e instanceof B&&(void 0!==e.__depth&&"number"!=typeof e.__depth||void 0!==e.__removalCount&&"number"!=typeof e.__removalCount||"string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof z)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},At=function(e){return"function"==typeof b&&e instanceof b},Nt=function(e,t,n){le[e]&&u(le[e],(e=>{e.call(o,t,n,it)}))},bt=function(e){let t=null;if(Nt("beforeSanitizeElements",e,null),Et(e))return gt(e),!0;const n=rt(e.nodeName);if(Nt("uponSanitizeElement",e,{tagName:n,allowedTags:Te}),e.hasChildNodes()&&!At(e.firstElementChild)&&E(/<[/\w]/g,e.innerHTML)&&E(/<[/\w]/g,e.textContent))return gt(e),!0;if(7===e.nodeType)return gt(e),!0;if(Le&&8===e.nodeType&&E(/<[/\w]/g,e.data))return gt(e),!0;if(!Te[n]||Ne[n]){if(!Ne[n]&&Rt(n)){if(Ae.tagNameCheck instanceof RegExp&&E(Ae.tagNameCheck,n))return!1;if(Ae.tagNameCheck instanceof Function&&Ae.tagNameCheck(n))return!1}if(He&&!We[n]){const t=Q(e)||e.parentNode,n=J(e)||e.childNodes;if(n&&t){for(let o=n.length-1;o>=0;--o){const r=X(n[o],!0);r.__removalCount=(e.__removalCount||0)+1,t.insertBefore(r,$(e))}}}return gt(e),!0}return e instanceof R&&!ht(e)?(gt(e),!0):"noscript"!==n&&"noembed"!==n&&"noframes"!==n||!E(/<\/no(script|embed|frames)/i,e.innerHTML)?(ve&&3===e.nodeType&&(t=e.textContent,u([ce,se,ue],(e=>{t=g(t,e," ")})),e.textContent!==t&&(p(o.removed,{element:e.cloneNode()}),e.textContent=t)),Nt("afterSanitizeElements",e,null),!1):(gt(e),!0)},St=function(e,t,n){if(Ue&&("id"===t||"name"===t)&&(n in r||n in lt))return!1;if(Re&&!be[t]&&E(me,t));else if(Se&&E(pe,t));else if(!ye[t]||be[t]){if(!(Rt(e)&&(Ae.tagNameCheck instanceof RegExp&&E(Ae.tagNameCheck,e)||Ae.tagNameCheck instanceof Function&&Ae.tagNameCheck(e))&&(Ae.attributeNameCheck instanceof RegExp&&E(Ae.attributeNameCheck,t)||Ae.attributeNameCheck instanceof Function&&Ae.attributeNameCheck(t))||"is"===t&&Ae.allowCustomizedBuiltInElements&&(Ae.tagNameCheck instanceof RegExp&&E(Ae.tagNameCheck,n)||Ae.tagNameCheck instanceof Function&&Ae.tagNameCheck(n))))return!1}else if(Xe[t]);else if(E(ge,g(n,de,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==T(n,"data:")||!Ye[e]){if(we&&!E(fe,g(n,de,"")));else if(n)return!1}else;return!0},Rt=function(e){return"annotation-xml"!==e&&h(e,he)},wt=function(e){Nt("beforeSanitizeAttributes",e,null);const{attributes:t}=e;if(!t)return;const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:ye};let r=t.length;for(;r--;){const i=t[r],{name:a,namespaceURI:l,value:c}=i,s=rt(a);let p="value"===a?c:_(c);if(n.attrName=s,n.attrValue=p,n.keepAttr=!0,n.forceKeepAttr=void 0,Nt("uponSanitizeAttribute",e,n),p=n.attrValue,n.forceKeepAttr)continue;if(Tt(a,e),!n.keepAttr)continue;if(!Ce&&E(/\/>/i,p)){Tt(a,e);continue}ve&&u([ce,se,ue],(e=>{p=g(p,e," ")}));const f=rt(e.nodeName);if(St(f,s,p)){if(!Pe||"id"!==s&&"name"!==s||(Tt(a,e),p=Fe+p),ee&&"object"==typeof G&&"function"==typeof G.getAttributeType)if(l);else switch(G.getAttributeType(f,s)){case"TrustedHTML":p=ee.createHTML(p);break;case"TrustedScriptURL":p=ee.createScriptURL(p)}try{l?e.setAttributeNS(l,a,p):e.setAttribute(a,p),m(o.removed)}catch(e){}}}Nt("afterSanitizeAttributes",e,null)},Ct=function e(t){let n=null;const o=yt(t);for(Nt("beforeSanitizeShadowDOM",t,null);n=o.nextNode();)Nt("uponSanitizeShadowNode",n,null),bt(n)||(1===n.nodeType&&(n.parentNode&&n.parentNode.__depth?n.__depth=(n.__removalCount||0)+n.parentNode.__depth+1:n.__depth=1),n.__depth>=at&>(n),n.content instanceof s&&(n.content.__depth=n.__depth,e(n.content)),wt(n));Nt("afterSanitizeShadowDOM",t,null)};return o.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=null,r=null,i=null,l=null;if(Je=!e,Je&&(e="\x3c!--\x3e"),"string"!=typeof e&&!At(e)){if("function"!=typeof e.toString)throw A("toString is not a function");if("string"!=typeof(e=e.toString()))throw A("dirty is not a string, aborting")}if(!o.isSupported)return e;if(Oe||st(t),o.removed=[],"string"==typeof e&&(ze=!1),ze){if(e.nodeName){const t=rt(e.nodeName);if(!Te[t]||Ne[t])throw A("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof b)n=_t("\x3c!----\x3e"),r=n.ownerDocument.importNode(e,!0),1===r.nodeType&&"BODY"===r.nodeName||"HTML"===r.nodeName?n=r:n.appendChild(r);else{if(!ke&&!ve&&!De&&-1===e.indexOf("<"))return ee&&Ie?ee.createHTML(e):e;if(n=_t(e),!n)return ke?null:Ie?te:""}n&&xe&>(n.firstChild);const c=yt(ze?e:n);for(;i=c.nextNode();)bt(i)||(1===i.nodeType&&(i.parentNode&&i.parentNode.__depth?i.__depth=(i.__removalCount||0)+i.parentNode.__depth+1:i.__depth=1),i.__depth>=at&>(i),i.content instanceof s&&(i.content.__depth=i.__depth,Ct(i.content)),wt(i));if(ze)return e;if(ke){if(Me)for(l=re.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return(ye.shadowroot||ye.shadowrootmode)&&(l=ae.call(a,l,!0)),l}let m=De?n.outerHTML:n.innerHTML;return De&&Te["!doctype"]&&n.ownerDocument&&n.ownerDocument.doctype&&n.ownerDocument.doctype.name&&E(q,n.ownerDocument.doctype.name)&&(m="<!DOCTYPE "+n.ownerDocument.doctype.name+">\n"+m),ve&&u([ce,se,ue],(e=>{m=g(m,e," ")})),ee&&Ie?ee.createHTML(m):m},o.setConfig=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};st(e),Oe=!0},o.clearConfig=function(){it=null,Oe=!1},o.isValidAttribute=function(e,t,n){it||st({});const o=rt(e),r=rt(t);return St(o,r,n)},o.addHook=function(e,t){"function"==typeof t&&(le[e]=le[e]||[],p(le[e],t))},o.removeHook=function(e){if(le[e])return m(le[e])},o.removeHooks=function(e){le[e]&&(le[e]=[])},o.removeAllHooks=function(){le={}},o}();return J})); //# sourceMappingURL=purify.min.js.map
dist/purify.min.js.map+1 −1 modifiedREADME.md+14 −5 modified@@ -73,7 +73,7 @@ After sanitizing your markup, you can also have a look at the property `DOMPurif DOMPurify technically also works server-side with Node.js. Our support strives to follow the [Node.js release cycle](https://nodejs.org/en/about/releases/). -Running DOMPurify on the server requires a DOM to be present, which is probably no surprise. Usually, [jsdom](https://github.com/jsdom/jsdom) is the tool of choice and we **strongly recommend** to use the latest version of _jsdom_. +Running DOMPurify on the server requires a DOM to be present, which is probably no surprise. Usually, [jsdom](https://github.com/jsdom/jsdom) is the tool of choice and we **strongly recommend** to use the latest version of _jsdom_. Why? Because older versions of _jsdom_ are known to be buggy in ways that result in XSS _even if_ DOMPurify does everything 100% correctly. There are **known attack vectors** in, e.g. _jsdom v19.0.0_ that are fixed in _jsdom v20.0.0_ - and we really recommend to keep _jsdom_ up to date because of that. @@ -158,6 +158,15 @@ In version 2.0.0, a config flag was added to control DOMPurify's behavior regard When `DOMPurify.sanitize` is used in an environment where the Trusted Types API is available and `RETURN_TRUSTED_TYPE` is set to `true`, it tries to return a `TrustedHTML` value instead of a string (the behavior for `RETURN_DOM` and `RETURN_DOM_FRAGMENT` config options does not change). +Note that in order to create a policy in `trustedTypes` using DOMPurify, `RETURN_TRUSTED_TYPE: false` is required, as `createHTML` expects a normal string, not `TrustedHTML`. The example below shows this. + +```js +window.trustedTypes!.createPolicy('default', { + createHTML: (to_escape) => + DOMPurify.sanitize(to_escape, { RETURN_TRUSTED_TYPE: false }), +}); +``` + ## Can I configure DOMPurify? Yes. The included default configuration values are pretty good already - but you can of course override them. Check out the [`/demos`](https://github.com/cure53/DOMPurify/tree/main/demos) folder to see a bunch of examples on how you can [customize DOMPurify](https://github.com/cure53/DOMPurify/tree/main/demos#what-is-this). @@ -360,11 +369,11 @@ _Example_: ```js DOMPurify.addHook( - 'beforeSanitizeElements', + 'uponSanitizeAttribute', function (currentNode, hookEvent, config) { - // Do something with the current node and return it - // You can also mutate hookEvent (i.e. set hookEvent.forceKeepAttr = true) - return currentNode; + // Do something with the current node + // You can also mutate hookEvent for current node (i.e. set hookEvent.forceKeepAttr = true) + // For other than 'uponSanitizeAttribute' hook types hookEvent equals to null } ); ```
src/purify.js+57 −5 modified@@ -388,6 +388,9 @@ function createDOMPurify(window = getGlobal()) { /* Keep a reference to config to pass to hooks */ let CONFIG = null; + /* Specify the maximum element nesting depth to prevent mXSS */ + const MAX_NESTING_DEPTH = 500; + /* Ideally, do not touch anything below this line */ /* ______________________________________________ */ @@ -934,7 +937,13 @@ function createDOMPurify(window = getGlobal()) { const _isClobbered = function (elm) { return ( elm instanceof HTMLFormElement && - (typeof elm.nodeName !== 'string' || + // eslint-disable-next-line unicorn/no-typeof-undefined + ((typeof elm.__depth !== 'undefined' && + typeof elm.__depth !== 'number') || + // eslint-disable-next-line unicorn/no-typeof-undefined + (typeof elm.__removalCount !== 'undefined' && + typeof elm.__removalCount !== 'number') || + typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || @@ -1060,10 +1069,9 @@ function createDOMPurify(window = getGlobal()) { const childCount = childNodes.length; for (let i = childCount - 1; i >= 0; --i) { - parentNode.insertBefore( - cloneNode(childNodes[i], true), - getNextSibling(currentNode) - ); + const childClone = cloneNode(childNodes[i], true); + childClone.__removalCount = (currentNode.__removalCount || 0) + 1; + parentNode.insertBefore(childClone, getNextSibling(currentNode)); } } } @@ -1371,8 +1379,30 @@ function createDOMPurify(window = getGlobal()) { continue; } + /* Set the nesting depth of an element */ + if (shadowNode.nodeType === 1) { + if (shadowNode.parentNode && shadowNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + shadowNode.__depth = + (shadowNode.__removalCount || 0) + + shadowNode.parentNode.__depth + + 1; + } else { + shadowNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (shadowNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(shadowNode); + } + /* Deep shadow DOM detected */ if (shadowNode.content instanceof DocumentFragment) { + shadowNode.content.__depth = shadowNode.__depth; _sanitizeShadowDOM(shadowNode.content); } @@ -1497,8 +1527,30 @@ function createDOMPurify(window = getGlobal()) { continue; } + /* Set the nesting depth of an element */ + if (currentNode.nodeType === 1) { + if (currentNode.parentNode && currentNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + currentNode.__depth = + (currentNode.__removalCount || 0) + + currentNode.parentNode.__depth + + 1; + } else { + currentNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (currentNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(currentNode); + } + /* Shadow DOM detected, sanitize it */ if (currentNode.content instanceof DocumentFragment) { + currentNode.content.__depth = currentNode.__depth; _sanitizeShadowDOM(currentNode.content); }
test/karma.custom-launchers.config.js+16 −8 modified@@ -74,14 +74,6 @@ const customLaunchers = { browser: 'edge', os_version: '10', }, - bs_win10_firefox_60: { - base: 'BrowserStack', - device: null, - os: 'Windows', - browser_version: '60.0', - browser: 'firefox', - os_version: '10', - }, bs_win10_firefox_70: { base: 'BrowserStack', device: null, @@ -130,6 +122,14 @@ const customLaunchers = { browser: 'firefox', os_version: '11', }, + bs_win10_firefox_125: { + base: 'BrowserStack', + device: null, + os: 'Windows', + browser_version: '125.0', + browser: 'firefox', + os_version: '11', + }, bs_win10_chrome_60: { base: 'BrowserStack', device: null, @@ -186,6 +186,14 @@ const customLaunchers = { browser: 'chrome', os_version: '11', }, + bs_win10_chrome_124: { + base: 'BrowserStack', + device: null, + os: 'Windows', + browser_version: '124.0', + browser: 'chrome', + os_version: '11', + }, }; const getAllBrowsers = () => Object.keys(customLaunchers);
test/test-suite.js+87 −0 modified@@ -2104,5 +2104,92 @@ let clean = DOMPurify.sanitize(dirty, config); assert.contains(clean, expected); }); + + QUnit.test('Test proper handling of nesting-based mXSS 1/3', function (assert) { + + let dirty = `${`<div>`.repeat(496)}${`</div>`.repeat(496)}<img>`; + let expected = `${`<div>`.repeat(496)}${`</div>`.repeat(496)}<img>`; + let clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `${`<div>`.repeat(500)}${`</div>`.repeat(500)}<img>`; + expected = `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `${`<div>`.repeat(502)}${`</div>`.repeat(502)}<img>`; + expected = `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<template>${`<div>`.repeat(502)}${`</div>`.repeat(502)}<img>`; + expected = `<template>${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<div><template>${`<r>`.repeat(497)}<img>${`</r>`.repeat( + 497 + )}</template></div><img>`; + expected = `<div><template></template></div><img>`; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + }); + + QUnit.test('Test proper handling of nesting-based mXSS 2/3', function (assert) { + + let dirty = `<form><input name="__depth">${`<div>`.repeat(500)}${`</div>`.repeat(500)}<img>`; + let expected = [ + ``, + `<form><input name="__depth">${`<div>`.repeat(497)}${`</div>`.repeat(497)}<img></form>`, + ]; + let clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<form><input name="__depth"></form>${`<div>`.repeat(500)}${`</div>`.repeat(500)}<img>`; + expected = [ + `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`, + `<form><input name="__depth"></form>${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>` + ]; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<form><input name="__removalCount">${`<div>`.repeat( + 500 + )}${`</div>`.repeat(500)}<img>`; + expected = [ + ``, + `<form><input name="__removalCount">${`<div>`.repeat( + 497 + )}${`</div>`.repeat(497)}<img></form>`, + ]; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<form><input name="__removalCount"></form>${`<div>`.repeat( + 500 + )}${`</div>`.repeat(500)}<img>`; + expected = [ + `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`, + `<form><input name="__removalCount"></form>${`<div>`.repeat( + 498 + )}${`</div>`.repeat(498)}<img>`, + ]; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + }); + + QUnit.test('Test proper handling of nesting-based mXSS 3/3', function (assert) { + + let dirty = `<form><input name="__depth">`; + let expected = [``, `<form><input name="__depth"></form>`]; + let clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<form><input name="__removalCount">`; + expected = [``, `<form><input name="__removalCount"></form>`]; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + }); }; }); \ No newline at end of file
0ef5e537a514chore: Updated 2.x branch with relevant fixes for nesting-based mXSS
10 files changed · +302 −16
dist/purify.cjs.js+51 −2 modified@@ -224,9 +224,11 @@ var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-us var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape ); + var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex ); + var DOCTYPE_NAME = seal(/^html$/i); var CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i); @@ -509,6 +511,9 @@ function createDOMPurify() { /* Keep a reference to config to pass to hooks */ var CONFIG = null; + /* Specify the maximum element nesting depth to prevent mXSS */ + var MAX_NESTING_DEPTH = 500; + /* Ideally, do not touch anything below this line */ /* ______________________________________________ */ @@ -903,7 +908,11 @@ function createDOMPurify() { * @return {Boolean} true if clobbered, false if safe */ var _isClobbered = function _isClobbered(elm) { - return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); + return elm instanceof HTMLFormElement && ( + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' || + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__removalCount !== 'undefined' && typeof elm.__removalCount !== 'number' || typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); }; /** @@ -1009,7 +1018,9 @@ function createDOMPurify() { if (childNodes && parentNode) { var childCount = childNodes.length; for (var i = childCount - 1; i >= 0; --i) { - parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode)); + var childClone = cloneNode(childNodes[i], true); + childClone.__removalCount = (currentNode.__removalCount || 0) + 1; + parentNode.insertBefore(childClone, getNextSibling(currentNode)); } } } @@ -1240,8 +1251,27 @@ function createDOMPurify() { continue; } + /* Set the nesting depth of an element */ + if (shadowNode.nodeType === 1) { + if (shadowNode.parentNode && shadowNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + shadowNode.__depth = (shadowNode.__removalCount || 0) + shadowNode.parentNode.__depth + 1; + } else { + shadowNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (shadowNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(shadowNode); + } + /* Deep shadow DOM detected */ if (shadowNode.content instanceof DocumentFragment) { + shadowNode.content.__depth = shadowNode.__depth; _sanitizeShadowDOM(shadowNode.content); } @@ -1372,8 +1402,27 @@ function createDOMPurify() { continue; } + /* Set the nesting depth of an element */ + if (currentNode.nodeType === 1) { + if (currentNode.parentNode && currentNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + currentNode.__depth = (currentNode.__removalCount || 0) + currentNode.parentNode.__depth + 1; + } else { + currentNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (currentNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(currentNode); + } + /* Shadow DOM detected, sanitize it */ if (currentNode.content instanceof DocumentFragment) { + currentNode.content.__depth = currentNode.__depth; _sanitizeShadowDOM(currentNode.content); }
dist/purify.cjs.js.map+1 −1 modifieddist/purify.es.js+51 −2 modified@@ -222,9 +222,11 @@ var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-us var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape ); + var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex ); + var DOCTYPE_NAME = seal(/^html$/i); var CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i); @@ -507,6 +509,9 @@ function createDOMPurify() { /* Keep a reference to config to pass to hooks */ var CONFIG = null; + /* Specify the maximum element nesting depth to prevent mXSS */ + var MAX_NESTING_DEPTH = 500; + /* Ideally, do not touch anything below this line */ /* ______________________________________________ */ @@ -901,7 +906,11 @@ function createDOMPurify() { * @return {Boolean} true if clobbered, false if safe */ var _isClobbered = function _isClobbered(elm) { - return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); + return elm instanceof HTMLFormElement && ( + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' || + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__removalCount !== 'undefined' && typeof elm.__removalCount !== 'number' || typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); }; /** @@ -1007,7 +1016,9 @@ function createDOMPurify() { if (childNodes && parentNode) { var childCount = childNodes.length; for (var i = childCount - 1; i >= 0; --i) { - parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode)); + var childClone = cloneNode(childNodes[i], true); + childClone.__removalCount = (currentNode.__removalCount || 0) + 1; + parentNode.insertBefore(childClone, getNextSibling(currentNode)); } } } @@ -1238,8 +1249,27 @@ function createDOMPurify() { continue; } + /* Set the nesting depth of an element */ + if (shadowNode.nodeType === 1) { + if (shadowNode.parentNode && shadowNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + shadowNode.__depth = (shadowNode.__removalCount || 0) + shadowNode.parentNode.__depth + 1; + } else { + shadowNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (shadowNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(shadowNode); + } + /* Deep shadow DOM detected */ if (shadowNode.content instanceof DocumentFragment) { + shadowNode.content.__depth = shadowNode.__depth; _sanitizeShadowDOM(shadowNode.content); } @@ -1370,8 +1400,27 @@ function createDOMPurify() { continue; } + /* Set the nesting depth of an element */ + if (currentNode.nodeType === 1) { + if (currentNode.parentNode && currentNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + currentNode.__depth = (currentNode.__removalCount || 0) + currentNode.parentNode.__depth + 1; + } else { + currentNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (currentNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(currentNode); + } + /* Shadow DOM detected, sanitize it */ if (currentNode.content instanceof DocumentFragment) { + currentNode.content.__depth = currentNode.__depth; _sanitizeShadowDOM(currentNode.content); }
dist/purify.es.js.map+1 −1 modifieddist/purify.js+51 −2 modified@@ -228,9 +228,11 @@ var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape ); + var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex ); + var DOCTYPE_NAME = seal(/^html$/i); var CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i); @@ -513,6 +515,9 @@ /* Keep a reference to config to pass to hooks */ var CONFIG = null; + /* Specify the maximum element nesting depth to prevent mXSS */ + var MAX_NESTING_DEPTH = 500; + /* Ideally, do not touch anything below this line */ /* ______________________________________________ */ @@ -907,7 +912,11 @@ * @return {Boolean} true if clobbered, false if safe */ var _isClobbered = function _isClobbered(elm) { - return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); + return elm instanceof HTMLFormElement && ( + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' || + // eslint-disable-next-line unicorn/no-typeof-undefined + typeof elm.__removalCount !== 'undefined' && typeof elm.__removalCount !== 'number' || typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); }; /** @@ -1013,7 +1022,9 @@ if (childNodes && parentNode) { var childCount = childNodes.length; for (var i = childCount - 1; i >= 0; --i) { - parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode)); + var childClone = cloneNode(childNodes[i], true); + childClone.__removalCount = (currentNode.__removalCount || 0) + 1; + parentNode.insertBefore(childClone, getNextSibling(currentNode)); } } } @@ -1244,8 +1255,27 @@ continue; } + /* Set the nesting depth of an element */ + if (shadowNode.nodeType === 1) { + if (shadowNode.parentNode && shadowNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + shadowNode.__depth = (shadowNode.__removalCount || 0) + shadowNode.parentNode.__depth + 1; + } else { + shadowNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (shadowNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(shadowNode); + } + /* Deep shadow DOM detected */ if (shadowNode.content instanceof DocumentFragment) { + shadowNode.content.__depth = shadowNode.__depth; _sanitizeShadowDOM(shadowNode.content); } @@ -1376,8 +1406,27 @@ continue; } + /* Set the nesting depth of an element */ + if (currentNode.nodeType === 1) { + if (currentNode.parentNode && currentNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + currentNode.__depth = (currentNode.__removalCount || 0) + currentNode.parentNode.__depth + 1; + } else { + currentNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (currentNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(currentNode); + } + /* Shadow DOM detected, sanitize it */ if (currentNode.content instanceof DocumentFragment) { + currentNode.content.__depth = currentNode.__depth; _sanitizeShadowDOM(currentNode.content); }
dist/purify.js.map+1 −1 modifieddist/purify.min.js+1 −1 modified@@ -1,3 +1,3 @@ /*! @license DOMPurify 2.5.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.5.0/LICENSE */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).DOMPurify=t()}(this,(function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(e,n){return t=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},t(e,n)}function n(e,r,o){return n=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}()?Reflect.construct:function(e,n,r){var o=[null];o.push.apply(o,n);var a=new(Function.bind.apply(e,o));return r&&t(a,r.prototype),a},n.apply(null,arguments)}function r(e){return function(e){if(Array.isArray(e))return o(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return o(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return o(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function o(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var a=Object.hasOwnProperty,i=Object.setPrototypeOf,l=Object.isFrozen,c=Object.getPrototypeOf,u=Object.getOwnPropertyDescriptor,s=Object.freeze,m=Object.seal,f=Object.create,p="undefined"!=typeof Reflect&&Reflect,d=p.apply,h=p.construct;d||(d=function(e,t,n){return e.apply(t,n)}),s||(s=function(e){return e}),m||(m=function(e){return e}),h||(h=function(e,t){return n(e,r(t))});var g,y=O(Array.prototype.forEach),b=O(Array.prototype.pop),T=O(Array.prototype.push),v=O(String.prototype.toLowerCase),N=O(String.prototype.toString),E=O(String.prototype.match),A=O(String.prototype.replace),S=O(String.prototype.indexOf),w=O(String.prototype.trim),_=O(RegExp.prototype.test),x=(g=TypeError,function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return h(g,t)});function O(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return d(e,t,r)}}function k(e,t,n){var r;n=null!==(r=n)&&void 0!==r?r:v,i&&i(e,null);for(var o=t.length;o--;){var a=t[o];if("string"==typeof a){var c=n(a);c!==a&&(l(t)||(t[o]=c),a=c)}e[a]=!0}return e}function L(e){var t,n=f(null);for(t in e)!0===d(a,e,[t])&&(n[t]=e[t]);return n}function C(e,t){for(;null!==e;){var n=u(e,t);if(n){if(n.get)return O(n.get);if("function"==typeof n.value)return O(n.value)}e=c(e)}return function(e){return console.warn("fallback value for",e),null}}var D=s(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),R=s(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","filter","font","g","glyph","glyphref","hkern","image","line","lineargradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),M=s(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),I=s(["animate","color-profile","cursor","discard","fedropshadow","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor","unknown","use"]),F=s(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover"]),U=s(["maction","maligngroup","malignmark","mlongdiv","mscarries","mscarry","msgroup","mstack","msline","msrow","semantics","annotation","annotation-xml","mprescripts","none"]),H=s(["#text"]),z=s(["accept","action","align","alt","autocapitalize","autocomplete","autopictureinpicture","autoplay","background","bgcolor","border","capture","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","controls","controlslist","coords","crossorigin","datetime","decoding","default","dir","disabled","disablepictureinpicture","disableremoteplayback","download","draggable","enctype","enterkeyhint","face","for","headers","height","hidden","high","href","hreflang","id","inputmode","integrity","ismap","kind","label","lang","list","loading","loop","low","max","maxlength","media","method","min","minlength","multiple","muted","name","nonce","noshade","novalidate","nowrap","open","optimum","pattern","placeholder","playsinline","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","span","srclang","start","src","srcset","step","style","summary","tabindex","title","translate","type","usemap","valign","value","width","xmlns","slot"]),P=s(["accent-height","accumulate","additive","alignment-baseline","ascent","attributename","attributetype","azimuth","basefrequency","baseline-shift","begin","bias","by","class","clip","clippathunits","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","cx","cy","d","dx","dy","diffuseconstant","direction","display","divisor","dur","edgemode","elevation","end","fill","fill-opacity","fill-rule","filter","filterunits","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","fx","fy","g1","g2","glyph-name","glyphref","gradientunits","gradienttransform","height","href","id","image-rendering","in","in2","k","k1","k2","k3","k4","kerning","keypoints","keysplines","keytimes","lang","lengthadjust","letter-spacing","kernelmatrix","kernelunitlength","lighting-color","local","marker-end","marker-mid","marker-start","markerheight","markerunits","markerwidth","maskcontentunits","maskunits","max","mask","media","method","mode","min","name","numoctaves","offset","operator","opacity","order","orient","orientation","origin","overflow","paint-order","path","pathlength","patterncontentunits","patterntransform","patternunits","points","preservealpha","preserveaspectratio","primitiveunits","r","rx","ry","radius","refx","refy","repeatcount","repeatdur","restart","result","rotate","scale","seed","shape-rendering","specularconstant","specularexponent","spreadmethod","startoffset","stddeviation","stitchtiles","stop-color","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke","stroke-width","style","surfacescale","systemlanguage","tabindex","targetx","targety","transform","transform-origin","text-anchor","text-decoration","text-rendering","textlength","type","u1","u2","unicode","values","viewbox","visibility","version","vert-adv-y","vert-origin-x","vert-origin-y","width","word-spacing","wrap","writing-mode","xchannelselector","ychannelselector","x","x1","x2","xmlns","y","y1","y2","z","zoomandpan"]),j=s(["accent","accentunder","align","bevelled","close","columnsalign","columnlines","columnspan","denomalign","depth","dir","display","displaystyle","encoding","fence","frame","height","href","id","largeop","length","linethickness","lspace","lquote","mathbackground","mathcolor","mathsize","mathvariant","maxsize","minsize","movablelimits","notation","numalign","open","rowalign","rowlines","rowspacing","rowspan","rspace","rquote","scriptlevel","scriptminsize","scriptsizemultiplier","selection","separator","separators","stretchy","subscriptshift","supscriptshift","symmetric","voffset","width","xmlns"]),B=s(["xlink:href","xml:id","xlink:title","xml:space","xmlns:xlink"]),G=m(/\{\{[\w\W]*|[\w\W]*\}\}/gm),W=m(/<%[\w\W]*|[\w\W]*%>/gm),q=m(/\${[\w\W]*}/gm),Y=m(/^data-[\-\w.\u00B7-\uFFFF]/),$=m(/^aria-[\-\w]+$/),K=m(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),V=m(/^(?:\w+script|data):/i),X=m(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),Z=m(/^html$/i),J=m(/^[a-z][.\w]*(-[.\w]+)+$/i),Q=function(){return"undefined"==typeof window?null:window};var ee=function t(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Q(),o=function(e){return t(e)};if(o.version="2.5.0",o.removed=[],!n||!n.document||9!==n.document.nodeType)return o.isSupported=!1,o;var a=n.document,i=n.document,l=n.DocumentFragment,c=n.HTMLTemplateElement,u=n.Node,m=n.Element,f=n.NodeFilter,p=n.NamedNodeMap,d=void 0===p?n.NamedNodeMap||n.MozNamedAttrMap:p,h=n.HTMLFormElement,g=n.DOMParser,O=n.trustedTypes,ee=m.prototype,te=C(ee,"cloneNode"),ne=C(ee,"nextSibling"),re=C(ee,"childNodes"),oe=C(ee,"parentNode");if("function"==typeof c){var ae=i.createElement("template");ae.content&&ae.content.ownerDocument&&(i=ae.content.ownerDocument)}var ie=function(t,n){if("object"!==e(t)||"function"!=typeof t.createPolicy)return null;var r=null,o="data-tt-policy-suffix";n.currentScript&&n.currentScript.hasAttribute(o)&&(r=n.currentScript.getAttribute(o));var a="dompurify"+(r?"#"+r:"");try{return t.createPolicy(a,{createHTML:function(e){return e},createScriptURL:function(e){return e}})}catch(e){return console.warn("TrustedTypes policy "+a+" could not be created."),null}}(O,a),le=ie?ie.createHTML(""):"",ce=i,ue=ce.implementation,se=ce.createNodeIterator,me=ce.createDocumentFragment,fe=ce.getElementsByTagName,pe=a.importNode,de={};try{de=L(i).documentMode?i.documentMode:{}}catch(e){}var he={};o.isSupported="function"==typeof oe&&ue&&void 0!==ue.createHTMLDocument&&9!==de;var ge,ye,be=G,Te=W,ve=q,Ne=Y,Ee=$,Ae=V,Se=X,we=J,_e=K,xe=null,Oe=k({},[].concat(r(D),r(R),r(M),r(F),r(H))),ke=null,Le=k({},[].concat(r(z),r(P),r(j),r(B))),Ce=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),De=null,Re=null,Me=!0,Ie=!0,Fe=!1,Ue=!0,He=!1,ze=!0,Pe=!1,je=!1,Be=!1,Ge=!1,We=!1,qe=!1,Ye=!0,$e=!1,Ke=!0,Ve=!1,Xe={},Ze=null,Je=k({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),Qe=null,et=k({},["audio","video","img","source","image","track"]),tt=null,nt=k({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),rt="http://www.w3.org/1998/Math/MathML",ot="http://www.w3.org/2000/svg",at="http://www.w3.org/1999/xhtml",it=at,lt=!1,ct=null,ut=k({},[rt,ot,at],N),st=["application/xhtml+xml","text/html"],mt=null,ft=i.createElement("form"),pt=function(e){return e instanceof RegExp||e instanceof Function},dt=function(t){mt&&mt===t||(t&&"object"===e(t)||(t={}),t=L(t),ge=ge=-1===st.indexOf(t.PARSER_MEDIA_TYPE)?"text/html":t.PARSER_MEDIA_TYPE,ye="application/xhtml+xml"===ge?N:v,xe="ALLOWED_TAGS"in t?k({},t.ALLOWED_TAGS,ye):Oe,ke="ALLOWED_ATTR"in t?k({},t.ALLOWED_ATTR,ye):Le,ct="ALLOWED_NAMESPACES"in t?k({},t.ALLOWED_NAMESPACES,N):ut,tt="ADD_URI_SAFE_ATTR"in t?k(L(nt),t.ADD_URI_SAFE_ATTR,ye):nt,Qe="ADD_DATA_URI_TAGS"in t?k(L(et),t.ADD_DATA_URI_TAGS,ye):et,Ze="FORBID_CONTENTS"in t?k({},t.FORBID_CONTENTS,ye):Je,De="FORBID_TAGS"in t?k({},t.FORBID_TAGS,ye):{},Re="FORBID_ATTR"in t?k({},t.FORBID_ATTR,ye):{},Xe="USE_PROFILES"in t&&t.USE_PROFILES,Me=!1!==t.ALLOW_ARIA_ATTR,Ie=!1!==t.ALLOW_DATA_ATTR,Fe=t.ALLOW_UNKNOWN_PROTOCOLS||!1,Ue=!1!==t.ALLOW_SELF_CLOSE_IN_ATTR,He=t.SAFE_FOR_TEMPLATES||!1,ze=!1!==t.SAFE_FOR_XML,Pe=t.WHOLE_DOCUMENT||!1,Ge=t.RETURN_DOM||!1,We=t.RETURN_DOM_FRAGMENT||!1,qe=t.RETURN_TRUSTED_TYPE||!1,Be=t.FORCE_BODY||!1,Ye=!1!==t.SANITIZE_DOM,$e=t.SANITIZE_NAMED_PROPS||!1,Ke=!1!==t.KEEP_CONTENT,Ve=t.IN_PLACE||!1,_e=t.ALLOWED_URI_REGEXP||_e,it=t.NAMESPACE||at,Ce=t.CUSTOM_ELEMENT_HANDLING||{},t.CUSTOM_ELEMENT_HANDLING&&pt(t.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(Ce.tagNameCheck=t.CUSTOM_ELEMENT_HANDLING.tagNameCheck),t.CUSTOM_ELEMENT_HANDLING&&pt(t.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(Ce.attributeNameCheck=t.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),t.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof t.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(Ce.allowCustomizedBuiltInElements=t.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),He&&(Ie=!1),We&&(Ge=!0),Xe&&(xe=k({},r(H)),ke=[],!0===Xe.html&&(k(xe,D),k(ke,z)),!0===Xe.svg&&(k(xe,R),k(ke,P),k(ke,B)),!0===Xe.svgFilters&&(k(xe,M),k(ke,P),k(ke,B)),!0===Xe.mathMl&&(k(xe,F),k(ke,j),k(ke,B))),t.ADD_TAGS&&(xe===Oe&&(xe=L(xe)),k(xe,t.ADD_TAGS,ye)),t.ADD_ATTR&&(ke===Le&&(ke=L(ke)),k(ke,t.ADD_ATTR,ye)),t.ADD_URI_SAFE_ATTR&&k(tt,t.ADD_URI_SAFE_ATTR,ye),t.FORBID_CONTENTS&&(Ze===Je&&(Ze=L(Ze)),k(Ze,t.FORBID_CONTENTS,ye)),Ke&&(xe["#text"]=!0),Pe&&k(xe,["html","head","body"]),xe.table&&(k(xe,["tbody"]),delete De.tbody),s&&s(t),mt=t)},ht=k({},["mi","mo","mn","ms","mtext"]),gt=k({},["foreignobject","desc","title","annotation-xml"]),yt=k({},["title","style","font","a","script"]),bt=k({},R);k(bt,M),k(bt,I);var Tt=k({},F);k(Tt,U);var vt=function(e){T(o.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){try{e.outerHTML=le}catch(t){e.remove()}}},Nt=function(e,t){try{T(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){T(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!ke[e])if(Ge||We)try{vt(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},Et=function(e){var t,n;if(Be)e="<remove></remove>"+e;else{var r=E(e,/^[\r\n\t ]+/);n=r&&r[0]}"application/xhtml+xml"===ge&&it===at&&(e='<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>'+e+"</body></html>");var o=ie?ie.createHTML(e):e;if(it===at)try{t=(new g).parseFromString(o,ge)}catch(e){}if(!t||!t.documentElement){t=ue.createDocument(it,"template",null);try{t.documentElement.innerHTML=lt?le:o}catch(e){}}var a=t.body||t.documentElement;return e&&n&&a.insertBefore(i.createTextNode(n),a.childNodes[0]||null),it===at?fe.call(t,Pe?"html":"body")[0]:Pe?t.documentElement:a},At=function(e){return se.call(e.ownerDocument||e,e,f.SHOW_ELEMENT|f.SHOW_COMMENT|f.SHOW_TEXT|f.SHOW_PROCESSING_INSTRUCTION|f.SHOW_CDATA_SECTION,null,!1)},St=function(t){return"object"===e(u)?t instanceof u:t&&"object"===e(t)&&"number"==typeof t.nodeType&&"string"==typeof t.nodeName},wt=function(e,t,n){he[e]&&y(he[e],(function(e){e.call(o,t,n,mt)}))},_t=function(e){var t,n;if(wt("beforeSanitizeElements",e,null),(n=e)instanceof h&&("string"!=typeof n.nodeName||"string"!=typeof n.textContent||"function"!=typeof n.removeChild||!(n.attributes instanceof d)||"function"!=typeof n.removeAttribute||"function"!=typeof n.setAttribute||"string"!=typeof n.namespaceURI||"function"!=typeof n.insertBefore||"function"!=typeof n.hasChildNodes))return vt(e),!0;if(_(/[\u0080-\uFFFF]/,e.nodeName))return vt(e),!0;var r=ye(e.nodeName);if(wt("uponSanitizeElement",e,{tagName:r,allowedTags:xe}),e.hasChildNodes()&&!St(e.firstElementChild)&&(!St(e.content)||!St(e.content.firstElementChild))&&_(/<[/\w]/g,e.innerHTML)&&_(/<[/\w]/g,e.textContent))return vt(e),!0;if("select"===r&&_(/<template/i,e.innerHTML))return vt(e),!0;if(7===e.nodeType)return vt(e),!0;if(ze&&8===e.nodeType&&_(/<[/\w]/g,e.data))return vt(e),!0;if(!xe[r]||De[r]){if(!De[r]&&Ot(r)){if(Ce.tagNameCheck instanceof RegExp&&_(Ce.tagNameCheck,r))return!1;if(Ce.tagNameCheck instanceof Function&&Ce.tagNameCheck(r))return!1}if(Ke&&!Ze[r]){var a=oe(e)||e.parentNode,i=re(e)||e.childNodes;if(i&&a)for(var l=i.length-1;l>=0;--l)a.insertBefore(te(i[l],!0),ne(e))}return vt(e),!0}return e instanceof m&&!function(e){var t=oe(e);t&&t.tagName||(t={namespaceURI:it,tagName:"template"});var n=v(e.tagName),r=v(t.tagName);return!!ct[e.namespaceURI]&&(e.namespaceURI===ot?t.namespaceURI===at?"svg"===n:t.namespaceURI===rt?"svg"===n&&("annotation-xml"===r||ht[r]):Boolean(bt[n]):e.namespaceURI===rt?t.namespaceURI===at?"math"===n:t.namespaceURI===ot?"math"===n&>[r]:Boolean(Tt[n]):e.namespaceURI===at?!(t.namespaceURI===ot&&!gt[r])&&!(t.namespaceURI===rt&&!ht[r])&&!Tt[n]&&(yt[n]||!bt[n]):!("application/xhtml+xml"!==ge||!ct[e.namespaceURI]))}(e)?(vt(e),!0):"noscript"!==r&&"noembed"!==r&&"noframes"!==r||!_(/<\/no(script|embed|frames)/i,e.innerHTML)?(He&&3===e.nodeType&&(t=e.textContent,t=A(t,be," "),t=A(t,Te," "),t=A(t,ve," "),e.textContent!==t&&(T(o.removed,{element:e.cloneNode()}),e.textContent=t)),wt("afterSanitizeElements",e,null),!1):(vt(e),!0)},xt=function(e,t,n){if(Ye&&("id"===t||"name"===t)&&(n in i||n in ft))return!1;if(Ie&&!Re[t]&&_(Ne,t));else if(Me&&_(Ee,t));else if(!ke[t]||Re[t]){if(!(Ot(e)&&(Ce.tagNameCheck instanceof RegExp&&_(Ce.tagNameCheck,e)||Ce.tagNameCheck instanceof Function&&Ce.tagNameCheck(e))&&(Ce.attributeNameCheck instanceof RegExp&&_(Ce.attributeNameCheck,t)||Ce.attributeNameCheck instanceof Function&&Ce.attributeNameCheck(t))||"is"===t&&Ce.allowCustomizedBuiltInElements&&(Ce.tagNameCheck instanceof RegExp&&_(Ce.tagNameCheck,n)||Ce.tagNameCheck instanceof Function&&Ce.tagNameCheck(n))))return!1}else if(tt[t]);else if(_(_e,A(n,Se,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==S(n,"data:")||!Qe[e]){if(Fe&&!_(Ae,A(n,Se,"")));else if(n)return!1}else;return!0},Ot=function(e){return"annotation-xml"!==e&&E(e,we)},kt=function(t){var n,r,a,i;wt("beforeSanitizeAttributes",t,null);var l=t.attributes;if(l){var c={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:ke};for(i=l.length;i--;){var u=n=l[i],s=u.name,m=u.namespaceURI;if(r="value"===s?n.value:w(n.value),a=ye(s),c.attrName=a,c.attrValue=r,c.keepAttr=!0,c.forceKeepAttr=void 0,wt("uponSanitizeAttribute",t,c),r=c.attrValue,!c.forceKeepAttr&&(Nt(s,t),c.keepAttr))if(Ue||!_(/\/>/i,r)){He&&(r=A(r,be," "),r=A(r,Te," "),r=A(r,ve," "));var f=ye(t.nodeName);if(xt(f,a,r)){if(!$e||"id"!==a&&"name"!==a||(Nt(s,t),r="user-content-"+r),ie&&"object"===e(O)&&"function"==typeof O.getAttributeType)if(m);else switch(O.getAttributeType(f,a)){case"TrustedHTML":r=ie.createHTML(r);break;case"TrustedScriptURL":r=ie.createScriptURL(r)}try{m?t.setAttributeNS(m,s,r):t.setAttribute(s,r),b(o.removed)}catch(e){}}}else Nt(s,t)}wt("afterSanitizeAttributes",t,null)}},Lt=function e(t){var n,r=At(t);for(wt("beforeSanitizeShadowDOM",t,null);n=r.nextNode();)wt("uponSanitizeShadowNode",n,null),_t(n)||(n.content instanceof l&&e(n.content),kt(n));wt("afterSanitizeShadowDOM",t,null)};return o.sanitize=function(t){var r,i,c,s,m,f=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if((lt=!t)&&(t="\x3c!--\x3e"),"string"!=typeof t&&!St(t)){if("function"!=typeof t.toString)throw x("toString is not a function");if("string"!=typeof(t=t.toString()))throw x("dirty is not a string, aborting")}if(!o.isSupported){if("object"===e(n.toStaticHTML)||"function"==typeof n.toStaticHTML){if("string"==typeof t)return n.toStaticHTML(t);if(St(t))return n.toStaticHTML(t.outerHTML)}return t}if(je||dt(f),o.removed=[],"string"==typeof t&&(Ve=!1),Ve){if(t.nodeName){var p=ye(t.nodeName);if(!xe[p]||De[p])throw x("root node is forbidden and cannot be sanitized in-place")}}else if(t instanceof u)1===(i=(r=Et("\x3c!----\x3e")).ownerDocument.importNode(t,!0)).nodeType&&"BODY"===i.nodeName||"HTML"===i.nodeName?r=i:r.appendChild(i);else{if(!Ge&&!He&&!Pe&&-1===t.indexOf("<"))return ie&&qe?ie.createHTML(t):t;if(!(r=Et(t)))return Ge?null:qe?le:""}r&&Be&&vt(r.firstChild);for(var d=At(Ve?t:r);c=d.nextNode();)3===c.nodeType&&c===s||_t(c)||(c.content instanceof l&&Lt(c.content),kt(c),s=c);if(s=null,Ve)return t;if(Ge){if(We)for(m=me.call(r.ownerDocument);r.firstChild;)m.appendChild(r.firstChild);else m=r;return(ke.shadowroot||ke.shadowrootmod)&&(m=pe.call(a,m,!0)),m}var h=Pe?r.outerHTML:r.innerHTML;return Pe&&xe["!doctype"]&&r.ownerDocument&&r.ownerDocument.doctype&&r.ownerDocument.doctype.name&&_(Z,r.ownerDocument.doctype.name)&&(h="<!DOCTYPE "+r.ownerDocument.doctype.name+">\n"+h),He&&(h=A(h,be," "),h=A(h,Te," "),h=A(h,ve," ")),ie&&qe?ie.createHTML(h):h},o.setConfig=function(e){dt(e),je=!0},o.clearConfig=function(){mt=null,je=!1},o.isValidAttribute=function(e,t,n){mt||dt({});var r=ye(e),o=ye(t);return xt(r,o,n)},o.addHook=function(e,t){"function"==typeof t&&(he[e]=he[e]||[],T(he[e],t))},o.removeHook=function(e){if(he[e])return b(he[e])},o.removeHooks=function(e){he[e]&&(he[e]=[])},o.removeAllHooks=function(){he={}},o}();return ee})); +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).DOMPurify=t()}(this,(function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(e,n){return t=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},t(e,n)}function n(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}function r(e,o,a){return r=n()?Reflect.construct:function(e,n,r){var o=[null];o.push.apply(o,n);var a=new(Function.bind.apply(e,o));return r&&t(a,r.prototype),a},r.apply(null,arguments)}function o(e){return function(e){if(Array.isArray(e))return a(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return a(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return a(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var i=Object.hasOwnProperty,l=Object.setPrototypeOf,c=Object.isFrozen,u=Object.getPrototypeOf,s=Object.getOwnPropertyDescriptor,m=Object.freeze,p=Object.seal,f=Object.create,d="undefined"!=typeof Reflect&&Reflect,h=d.apply,g=d.construct;h||(h=function(e,t,n){return e.apply(t,n)}),m||(m=function(e){return e}),p||(p=function(e){return e}),g||(g=function(e,t){return r(e,o(t))});var y,b=C(Array.prototype.forEach),_=C(Array.prototype.pop),v=C(Array.prototype.push),T=C(String.prototype.toLowerCase),N=C(String.prototype.toString),E=C(String.prototype.match),A=C(String.prototype.replace),S=C(String.prototype.indexOf),w=C(String.prototype.trim),x=C(RegExp.prototype.test),O=(y=TypeError,function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return g(y,t)});function C(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return h(e,t,r)}}function k(e,t,n){var r;n=null!==(r=n)&&void 0!==r?r:T,l&&l(e,null);for(var o=t.length;o--;){var a=t[o];if("string"==typeof a){var i=n(a);i!==a&&(c(t)||(t[o]=i),a=i)}e[a]=!0}return e}function L(e){var t,n=f(null);for(t in e)!0===h(i,e,[t])&&(n[t]=e[t]);return n}function D(e,t){for(;null!==e;){var n=s(e,t);if(n){if(n.get)return C(n.get);if("function"==typeof n.value)return C(n.value)}e=u(e)}return function(e){return console.warn("fallback value for",e),null}}var R=m(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),M=m(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","filter","font","g","glyph","glyphref","hkern","image","line","lineargradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),I=m(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),F=m(["animate","color-profile","cursor","discard","fedropshadow","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor","unknown","use"]),U=m(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover"]),H=m(["maction","maligngroup","malignmark","mlongdiv","mscarries","mscarry","msgroup","mstack","msline","msrow","semantics","annotation","annotation-xml","mprescripts","none"]),z=m(["#text"]),P=m(["accept","action","align","alt","autocapitalize","autocomplete","autopictureinpicture","autoplay","background","bgcolor","border","capture","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","controls","controlslist","coords","crossorigin","datetime","decoding","default","dir","disabled","disablepictureinpicture","disableremoteplayback","download","draggable","enctype","enterkeyhint","face","for","headers","height","hidden","high","href","hreflang","id","inputmode","integrity","ismap","kind","label","lang","list","loading","loop","low","max","maxlength","media","method","min","minlength","multiple","muted","name","nonce","noshade","novalidate","nowrap","open","optimum","pattern","placeholder","playsinline","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","span","srclang","start","src","srcset","step","style","summary","tabindex","title","translate","type","usemap","valign","value","width","xmlns","slot"]),j=m(["accent-height","accumulate","additive","alignment-baseline","ascent","attributename","attributetype","azimuth","basefrequency","baseline-shift","begin","bias","by","class","clip","clippathunits","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","cx","cy","d","dx","dy","diffuseconstant","direction","display","divisor","dur","edgemode","elevation","end","fill","fill-opacity","fill-rule","filter","filterunits","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","fx","fy","g1","g2","glyph-name","glyphref","gradientunits","gradienttransform","height","href","id","image-rendering","in","in2","k","k1","k2","k3","k4","kerning","keypoints","keysplines","keytimes","lang","lengthadjust","letter-spacing","kernelmatrix","kernelunitlength","lighting-color","local","marker-end","marker-mid","marker-start","markerheight","markerunits","markerwidth","maskcontentunits","maskunits","max","mask","media","method","mode","min","name","numoctaves","offset","operator","opacity","order","orient","orientation","origin","overflow","paint-order","path","pathlength","patterncontentunits","patterntransform","patternunits","points","preservealpha","preserveaspectratio","primitiveunits","r","rx","ry","radius","refx","refy","repeatcount","repeatdur","restart","result","rotate","scale","seed","shape-rendering","specularconstant","specularexponent","spreadmethod","startoffset","stddeviation","stitchtiles","stop-color","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke","stroke-width","style","surfacescale","systemlanguage","tabindex","targetx","targety","transform","transform-origin","text-anchor","text-decoration","text-rendering","textlength","type","u1","u2","unicode","values","viewbox","visibility","version","vert-adv-y","vert-origin-x","vert-origin-y","width","word-spacing","wrap","writing-mode","xchannelselector","ychannelselector","x","x1","x2","xmlns","y","y1","y2","z","zoomandpan"]),B=m(["accent","accentunder","align","bevelled","close","columnsalign","columnlines","columnspan","denomalign","depth","dir","display","displaystyle","encoding","fence","frame","height","href","id","largeop","length","linethickness","lspace","lquote","mathbackground","mathcolor","mathsize","mathvariant","maxsize","minsize","movablelimits","notation","numalign","open","rowalign","rowlines","rowspacing","rowspan","rspace","rquote","scriptlevel","scriptminsize","scriptsizemultiplier","selection","separator","separators","stretchy","subscriptshift","supscriptshift","symmetric","voffset","width","xmlns"]),G=m(["xlink:href","xml:id","xlink:title","xml:space","xmlns:xlink"]),W=p(/\{\{[\w\W]*|[\w\W]*\}\}/gm),q=p(/<%[\w\W]*|[\w\W]*%>/gm),Y=p(/\${[\w\W]*}/gm),$=p(/^data-[\-\w.\u00B7-\uFFFF]/),K=p(/^aria-[\-\w]+$/),V=p(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),X=p(/^(?:\w+script|data):/i),Z=p(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),J=p(/^html$/i),Q=p(/^[a-z][.\w]*(-[.\w]+)+$/i),ee=function(){return"undefined"==typeof window?null:window},te=function(t,n){if("object"!==e(t)||"function"!=typeof t.createPolicy)return null;var r=null,o="data-tt-policy-suffix";n.currentScript&&n.currentScript.hasAttribute(o)&&(r=n.currentScript.getAttribute(o));var a="dompurify"+(r?"#"+r:"");try{return t.createPolicy(a,{createHTML:function(e){return e},createScriptURL:function(e){return e}})}catch(e){return console.warn("TrustedTypes policy "+a+" could not be created."),null}};var ne=function t(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:ee(),r=function(e){return t(e)};if(r.version="2.5.0",r.removed=[],!n||!n.document||9!==n.document.nodeType)return r.isSupported=!1,r;var a=n.document,i=n.document,l=n.DocumentFragment,c=n.HTMLTemplateElement,u=n.Node,s=n.Element,p=n.NodeFilter,f=n.NamedNodeMap,d=void 0===f?n.NamedNodeMap||n.MozNamedAttrMap:f,h=n.HTMLFormElement,g=n.DOMParser,y=n.trustedTypes,C=s.prototype,ne=D(C,"cloneNode"),re=D(C,"nextSibling"),oe=D(C,"childNodes"),ae=D(C,"parentNode");if("function"==typeof c){var ie=i.createElement("template");ie.content&&ie.content.ownerDocument&&(i=ie.content.ownerDocument)}var le=te(y,a),ce=le?le.createHTML(""):"",ue=i,se=ue.implementation,me=ue.createNodeIterator,pe=ue.createDocumentFragment,fe=ue.getElementsByTagName,de=a.importNode,he={};try{he=L(i).documentMode?i.documentMode:{}}catch(e){}var ge={};r.isSupported="function"==typeof ae&&se&&void 0!==se.createHTMLDocument&&9!==he;var ye,be,_e=W,ve=q,Te=Y,Ne=$,Ee=K,Ae=X,Se=Z,we=Q,xe=V,Oe=null,Ce=k({},[].concat(o(R),o(M),o(I),o(U),o(z))),ke=null,Le=k({},[].concat(o(P),o(j),o(B),o(G))),De=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Re=null,Me=null,Ie=!0,Fe=!0,Ue=!1,He=!0,ze=!1,Pe=!0,je=!1,Be=!1,Ge=!1,We=!1,qe=!1,Ye=!1,$e=!0,Ke=!1,Ve="user-content-",Xe=!0,Ze=!1,Je={},Qe=null,et=k({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),tt=null,nt=k({},["audio","video","img","source","image","track"]),rt=null,ot=k({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),at="http://www.w3.org/1998/Math/MathML",it="http://www.w3.org/2000/svg",lt="http://www.w3.org/1999/xhtml",ct=lt,ut=!1,st=null,mt=k({},[at,it,lt],N),pt=["application/xhtml+xml","text/html"],ft="text/html",dt=null,ht=500,gt=i.createElement("form"),yt=function(e){return e instanceof RegExp||e instanceof Function},bt=function(t){dt&&dt===t||(t&&"object"===e(t)||(t={}),t=L(t),ye=ye=-1===pt.indexOf(t.PARSER_MEDIA_TYPE)?ft:t.PARSER_MEDIA_TYPE,be="application/xhtml+xml"===ye?N:T,Oe="ALLOWED_TAGS"in t?k({},t.ALLOWED_TAGS,be):Ce,ke="ALLOWED_ATTR"in t?k({},t.ALLOWED_ATTR,be):Le,st="ALLOWED_NAMESPACES"in t?k({},t.ALLOWED_NAMESPACES,N):mt,rt="ADD_URI_SAFE_ATTR"in t?k(L(ot),t.ADD_URI_SAFE_ATTR,be):ot,tt="ADD_DATA_URI_TAGS"in t?k(L(nt),t.ADD_DATA_URI_TAGS,be):nt,Qe="FORBID_CONTENTS"in t?k({},t.FORBID_CONTENTS,be):et,Re="FORBID_TAGS"in t?k({},t.FORBID_TAGS,be):{},Me="FORBID_ATTR"in t?k({},t.FORBID_ATTR,be):{},Je="USE_PROFILES"in t&&t.USE_PROFILES,Ie=!1!==t.ALLOW_ARIA_ATTR,Fe=!1!==t.ALLOW_DATA_ATTR,Ue=t.ALLOW_UNKNOWN_PROTOCOLS||!1,He=!1!==t.ALLOW_SELF_CLOSE_IN_ATTR,ze=t.SAFE_FOR_TEMPLATES||!1,Pe=!1!==t.SAFE_FOR_XML,je=t.WHOLE_DOCUMENT||!1,We=t.RETURN_DOM||!1,qe=t.RETURN_DOM_FRAGMENT||!1,Ye=t.RETURN_TRUSTED_TYPE||!1,Ge=t.FORCE_BODY||!1,$e=!1!==t.SANITIZE_DOM,Ke=t.SANITIZE_NAMED_PROPS||!1,Xe=!1!==t.KEEP_CONTENT,Ze=t.IN_PLACE||!1,xe=t.ALLOWED_URI_REGEXP||xe,ct=t.NAMESPACE||lt,De=t.CUSTOM_ELEMENT_HANDLING||{},t.CUSTOM_ELEMENT_HANDLING&&yt(t.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(De.tagNameCheck=t.CUSTOM_ELEMENT_HANDLING.tagNameCheck),t.CUSTOM_ELEMENT_HANDLING&&yt(t.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(De.attributeNameCheck=t.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),t.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof t.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(De.allowCustomizedBuiltInElements=t.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),ze&&(Fe=!1),qe&&(We=!0),Je&&(Oe=k({},o(z)),ke=[],!0===Je.html&&(k(Oe,R),k(ke,P)),!0===Je.svg&&(k(Oe,M),k(ke,j),k(ke,G)),!0===Je.svgFilters&&(k(Oe,I),k(ke,j),k(ke,G)),!0===Je.mathMl&&(k(Oe,U),k(ke,B),k(ke,G))),t.ADD_TAGS&&(Oe===Ce&&(Oe=L(Oe)),k(Oe,t.ADD_TAGS,be)),t.ADD_ATTR&&(ke===Le&&(ke=L(ke)),k(ke,t.ADD_ATTR,be)),t.ADD_URI_SAFE_ATTR&&k(rt,t.ADD_URI_SAFE_ATTR,be),t.FORBID_CONTENTS&&(Qe===et&&(Qe=L(Qe)),k(Qe,t.FORBID_CONTENTS,be)),Xe&&(Oe["#text"]=!0),je&&k(Oe,["html","head","body"]),Oe.table&&(k(Oe,["tbody"]),delete Re.tbody),m&&m(t),dt=t)},_t=k({},["mi","mo","mn","ms","mtext"]),vt=k({},["foreignobject","desc","title","annotation-xml"]),Tt=k({},["title","style","font","a","script"]),Nt=k({},M);k(Nt,I),k(Nt,F);var Et=k({},U);k(Et,H);var At=function(e){var t=ae(e);t&&t.tagName||(t={namespaceURI:ct,tagName:"template"});var n=T(e.tagName),r=T(t.tagName);return!!st[e.namespaceURI]&&(e.namespaceURI===it?t.namespaceURI===lt?"svg"===n:t.namespaceURI===at?"svg"===n&&("annotation-xml"===r||_t[r]):Boolean(Nt[n]):e.namespaceURI===at?t.namespaceURI===lt?"math"===n:t.namespaceURI===it?"math"===n&&vt[r]:Boolean(Et[n]):e.namespaceURI===lt?!(t.namespaceURI===it&&!vt[r])&&(!(t.namespaceURI===at&&!_t[r])&&(!Et[n]&&(Tt[n]||!Nt[n]))):!("application/xhtml+xml"!==ye||!st[e.namespaceURI]))},St=function(e){v(r.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){try{e.outerHTML=ce}catch(t){e.remove()}}},wt=function(e,t){try{v(r.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){v(r.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!ke[e])if(We||qe)try{St(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},xt=function(e){var t,n;if(Ge)e="<remove></remove>"+e;else{var r=E(e,/^[\r\n\t ]+/);n=r&&r[0]}"application/xhtml+xml"===ye&&ct===lt&&(e='<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>'+e+"</body></html>");var o=le?le.createHTML(e):e;if(ct===lt)try{t=(new g).parseFromString(o,ye)}catch(e){}if(!t||!t.documentElement){t=se.createDocument(ct,"template",null);try{t.documentElement.innerHTML=ut?ce:o}catch(e){}}var a=t.body||t.documentElement;return e&&n&&a.insertBefore(i.createTextNode(n),a.childNodes[0]||null),ct===lt?fe.call(t,je?"html":"body")[0]:je?t.documentElement:a},Ot=function(e){return me.call(e.ownerDocument||e,e,p.SHOW_ELEMENT|p.SHOW_COMMENT|p.SHOW_TEXT|p.SHOW_PROCESSING_INSTRUCTION|p.SHOW_CDATA_SECTION,null,!1)},Ct=function(e){return e instanceof h&&(void 0!==e.__depth&&"number"!=typeof e.__depth||void 0!==e.__removalCount&&"number"!=typeof e.__removalCount||"string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof d)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},kt=function(t){return"object"===e(u)?t instanceof u:t&&"object"===e(t)&&"number"==typeof t.nodeType&&"string"==typeof t.nodeName},Lt=function(e,t,n){ge[e]&&b(ge[e],(function(e){e.call(r,t,n,dt)}))},Dt=function(e){var t;if(Lt("beforeSanitizeElements",e,null),Ct(e))return St(e),!0;if(x(/[\u0080-\uFFFF]/,e.nodeName))return St(e),!0;var n=be(e.nodeName);if(Lt("uponSanitizeElement",e,{tagName:n,allowedTags:Oe}),e.hasChildNodes()&&!kt(e.firstElementChild)&&(!kt(e.content)||!kt(e.content.firstElementChild))&&x(/<[/\w]/g,e.innerHTML)&&x(/<[/\w]/g,e.textContent))return St(e),!0;if("select"===n&&x(/<template/i,e.innerHTML))return St(e),!0;if(7===e.nodeType)return St(e),!0;if(Pe&&8===e.nodeType&&x(/<[/\w]/g,e.data))return St(e),!0;if(!Oe[n]||Re[n]){if(!Re[n]&&Mt(n)){if(De.tagNameCheck instanceof RegExp&&x(De.tagNameCheck,n))return!1;if(De.tagNameCheck instanceof Function&&De.tagNameCheck(n))return!1}if(Xe&&!Qe[n]){var o=ae(e)||e.parentNode,a=oe(e)||e.childNodes;if(a&&o)for(var i=a.length-1;i>=0;--i){var l=ne(a[i],!0);l.__removalCount=(e.__removalCount||0)+1,o.insertBefore(l,re(e))}}return St(e),!0}return e instanceof s&&!At(e)?(St(e),!0):"noscript"!==n&&"noembed"!==n&&"noframes"!==n||!x(/<\/no(script|embed|frames)/i,e.innerHTML)?(ze&&3===e.nodeType&&(t=e.textContent,t=A(t,_e," "),t=A(t,ve," "),t=A(t,Te," "),e.textContent!==t&&(v(r.removed,{element:e.cloneNode()}),e.textContent=t)),Lt("afterSanitizeElements",e,null),!1):(St(e),!0)},Rt=function(e,t,n){if($e&&("id"===t||"name"===t)&&(n in i||n in gt))return!1;if(Fe&&!Me[t]&&x(Ne,t));else if(Ie&&x(Ee,t));else if(!ke[t]||Me[t]){if(!(Mt(e)&&(De.tagNameCheck instanceof RegExp&&x(De.tagNameCheck,e)||De.tagNameCheck instanceof Function&&De.tagNameCheck(e))&&(De.attributeNameCheck instanceof RegExp&&x(De.attributeNameCheck,t)||De.attributeNameCheck instanceof Function&&De.attributeNameCheck(t))||"is"===t&&De.allowCustomizedBuiltInElements&&(De.tagNameCheck instanceof RegExp&&x(De.tagNameCheck,n)||De.tagNameCheck instanceof Function&&De.tagNameCheck(n))))return!1}else if(rt[t]);else if(x(xe,A(n,Se,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==S(n,"data:")||!tt[e]){if(Ue&&!x(Ae,A(n,Se,"")));else if(n)return!1}else;return!0},Mt=function(e){return"annotation-xml"!==e&&E(e,we)},It=function(t){var n,o,a,i;Lt("beforeSanitizeAttributes",t,null);var l=t.attributes;if(l){var c={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:ke};for(i=l.length;i--;){var u=n=l[i],s=u.name,m=u.namespaceURI;if(o="value"===s?n.value:w(n.value),a=be(s),c.attrName=a,c.attrValue=o,c.keepAttr=!0,c.forceKeepAttr=void 0,Lt("uponSanitizeAttribute",t,c),o=c.attrValue,!c.forceKeepAttr&&(wt(s,t),c.keepAttr))if(He||!x(/\/>/i,o)){ze&&(o=A(o,_e," "),o=A(o,ve," "),o=A(o,Te," "));var p=be(t.nodeName);if(Rt(p,a,o)){if(!Ke||"id"!==a&&"name"!==a||(wt(s,t),o=Ve+o),le&&"object"===e(y)&&"function"==typeof y.getAttributeType)if(m);else switch(y.getAttributeType(p,a)){case"TrustedHTML":o=le.createHTML(o);break;case"TrustedScriptURL":o=le.createScriptURL(o)}try{m?t.setAttributeNS(m,s,o):t.setAttribute(s,o),_(r.removed)}catch(e){}}}else wt(s,t)}Lt("afterSanitizeAttributes",t,null)}},Ft=function e(t){var n,r=Ot(t);for(Lt("beforeSanitizeShadowDOM",t,null);n=r.nextNode();)Lt("uponSanitizeShadowNode",n,null),Dt(n)||(1===n.nodeType&&(n.parentNode&&n.parentNode.__depth?n.__depth=(n.__removalCount||0)+n.parentNode.__depth+1:n.__depth=1),n.__depth>=ht&&St(n),n.content instanceof l&&(n.content.__depth=n.__depth,e(n.content)),It(n));Lt("afterSanitizeShadowDOM",t,null)};return r.sanitize=function(t){var o,i,c,s,m,p=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if((ut=!t)&&(t="\x3c!--\x3e"),"string"!=typeof t&&!kt(t)){if("function"!=typeof t.toString)throw O("toString is not a function");if("string"!=typeof(t=t.toString()))throw O("dirty is not a string, aborting")}if(!r.isSupported){if("object"===e(n.toStaticHTML)||"function"==typeof n.toStaticHTML){if("string"==typeof t)return n.toStaticHTML(t);if(kt(t))return n.toStaticHTML(t.outerHTML)}return t}if(Be||bt(p),r.removed=[],"string"==typeof t&&(Ze=!1),Ze){if(t.nodeName){var f=be(t.nodeName);if(!Oe[f]||Re[f])throw O("root node is forbidden and cannot be sanitized in-place")}}else if(t instanceof u)1===(i=(o=xt("\x3c!----\x3e")).ownerDocument.importNode(t,!0)).nodeType&&"BODY"===i.nodeName||"HTML"===i.nodeName?o=i:o.appendChild(i);else{if(!We&&!ze&&!je&&-1===t.indexOf("<"))return le&&Ye?le.createHTML(t):t;if(!(o=xt(t)))return We?null:Ye?ce:""}o&&Ge&&St(o.firstChild);for(var d=Ot(Ze?t:o);c=d.nextNode();)3===c.nodeType&&c===s||Dt(c)||(1===c.nodeType&&(c.parentNode&&c.parentNode.__depth?c.__depth=(c.__removalCount||0)+c.parentNode.__depth+1:c.__depth=1),c.__depth>=ht&&St(c),c.content instanceof l&&(c.content.__depth=c.__depth,Ft(c.content)),It(c),s=c);if(s=null,Ze)return t;if(We){if(qe)for(m=pe.call(o.ownerDocument);o.firstChild;)m.appendChild(o.firstChild);else m=o;return(ke.shadowroot||ke.shadowrootmod)&&(m=de.call(a,m,!0)),m}var h=je?o.outerHTML:o.innerHTML;return je&&Oe["!doctype"]&&o.ownerDocument&&o.ownerDocument.doctype&&o.ownerDocument.doctype.name&&x(J,o.ownerDocument.doctype.name)&&(h="<!DOCTYPE "+o.ownerDocument.doctype.name+">\n"+h),ze&&(h=A(h,_e," "),h=A(h,ve," "),h=A(h,Te," ")),le&&Ye?le.createHTML(h):h},r.setConfig=function(e){bt(e),Be=!0},r.clearConfig=function(){dt=null,Be=!1},r.isValidAttribute=function(e,t,n){dt||bt({});var r=be(e),o=be(t);return Rt(r,o,n)},r.addHook=function(e,t){"function"==typeof t&&(ge[e]=ge[e]||[],v(ge[e],t))},r.removeHook=function(e){if(ge[e])return _(ge[e])},r.removeHooks=function(e){ge[e]&&(ge[e]=[])},r.removeAllHooks=function(){ge={}},r}();return ne})); //# sourceMappingURL=purify.min.js.map
dist/purify.min.js.map+1 −1 modifiedsrc/purify.js+57 −5 modified@@ -393,6 +393,9 @@ function createDOMPurify(window = getGlobal()) { /* Keep a reference to config to pass to hooks */ let CONFIG = null; + /* Specify the maximum element nesting depth to prevent mXSS */ + const MAX_NESTING_DEPTH = 500; + /* Ideally, do not touch anything below this line */ /* ______________________________________________ */ @@ -915,7 +918,13 @@ function createDOMPurify(window = getGlobal()) { const _isClobbered = function (elm) { return ( elm instanceof HTMLFormElement && - (typeof elm.nodeName !== 'string' || + // eslint-disable-next-line unicorn/no-typeof-undefined + ((typeof elm.__depth !== 'undefined' && + typeof elm.__depth !== 'number') || + // eslint-disable-next-line unicorn/no-typeof-undefined + (typeof elm.__removalCount !== 'undefined' && + typeof elm.__removalCount !== 'number') || + typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || @@ -1060,10 +1069,9 @@ function createDOMPurify(window = getGlobal()) { const childCount = childNodes.length; for (let i = childCount - 1; i >= 0; --i) { - parentNode.insertBefore( - cloneNode(childNodes[i], true), - getNextSibling(currentNode) - ); + const childClone = cloneNode(childNodes[i], true); + childClone.__removalCount = (currentNode.__removalCount || 0) + 1; + parentNode.insertBefore(childClone, getNextSibling(currentNode)); } } } @@ -1370,8 +1378,30 @@ function createDOMPurify(window = getGlobal()) { continue; } + /* Set the nesting depth of an element */ + if (shadowNode.nodeType === 1) { + if (shadowNode.parentNode && shadowNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + shadowNode.__depth = + (shadowNode.__removalCount || 0) + + shadowNode.parentNode.__depth + + 1; + } else { + shadowNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (shadowNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(shadowNode); + } + /* Deep shadow DOM detected */ if (shadowNode.content instanceof DocumentFragment) { + shadowNode.content.__depth = shadowNode.__depth; _sanitizeShadowDOM(shadowNode.content); } @@ -1515,8 +1545,30 @@ function createDOMPurify(window = getGlobal()) { continue; } + /* Set the nesting depth of an element */ + if (currentNode.nodeType === 1) { + if (currentNode.parentNode && currentNode.parentNode.__depth) { + /* + We want the depth of the node in the original tree, which can + change when it's removed from its parent. + */ + currentNode.__depth = + (currentNode.__removalCount || 0) + + currentNode.parentNode.__depth + + 1; + } else { + currentNode.__depth = 1; + } + } + + /* Remove an element if nested too deeply to avoid mXSS */ + if (currentNode.__depth >= MAX_NESTING_DEPTH) { + _forceRemove(currentNode); + } + /* Shadow DOM detected, sanitize it */ if (currentNode.content instanceof DocumentFragment) { + currentNode.content.__depth = currentNode.__depth; _sanitizeShadowDOM(currentNode.content); }
test/test-suite.js+87 −0 modified@@ -2095,6 +2095,93 @@ }); }); + QUnit.test('Test proper handling of nesting-based mXSS 1/3', function (assert) { + + let dirty = `${`<div>`.repeat(496)}${`</div>`.repeat(496)}<img>`; + let expected = `${`<div>`.repeat(496)}${`</div>`.repeat(496)}<img>`; + let clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `${`<div>`.repeat(500)}${`</div>`.repeat(500)}<img>`; + expected = `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `${`<div>`.repeat(502)}${`</div>`.repeat(502)}<img>`; + expected = `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<template>${`<div>`.repeat(502)}${`</div>`.repeat(502)}<img>`; + expected = `<template>${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<div><template>${`<r>`.repeat(497)}<img>${`</r>`.repeat( + 497 + )}</template></div><img>`; + expected = `<div><template></template></div><img>`; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + }); + + QUnit.test('Test proper handling of nesting-based mXSS 2/3', function (assert) { + + let dirty = `<form><input name="__depth">${`<div>`.repeat(500)}${`</div>`.repeat(500)}<img>`; + let expected = [ + ``, + `<form><input name="__depth">${`<div>`.repeat(497)}${`</div>`.repeat(497)}<img></form>`, + ]; + let clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<form><input name="__depth"></form>${`<div>`.repeat(500)}${`</div>`.repeat(500)}<img>`; + expected = [ + `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`, + `<form><input name="__depth"></form>${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>` + ]; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<form><input name="__removalCount">${`<div>`.repeat( + 500 + )}${`</div>`.repeat(500)}<img>`; + expected = [ + ``, + `<form><input name="__removalCount">${`<div>`.repeat( + 497 + )}${`</div>`.repeat(497)}<img></form>`, + ]; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<form><input name="__removalCount"></form>${`<div>`.repeat( + 500 + )}${`</div>`.repeat(500)}<img>`; + expected = [ + `${`<div>`.repeat(498)}${`</div>`.repeat(498)}<img>`, + `<form><input name="__removalCount"></form>${`<div>`.repeat( + 498 + )}${`</div>`.repeat(498)}<img>`, + ]; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + }); + + QUnit.test('Test proper handling of nesting-based mXSS 3/3', function (assert) { + + let dirty = `<form><input name="__depth">`; + let expected = [``, `<form><input name="__depth"></form>`]; + let clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + + dirty = `<form><input name="__removalCount">`; + expected = [``, `<form><input name="__removalCount"></form>`]; + clean = DOMPurify.sanitize(dirty); + assert.contains(clean, expected); + }); + QUnit.test('removeHook returns hook function', function (assert) { const entryPoint = 'afterSanitizeAttributes'; const dirty = '<div class="hello"></div>';
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-gx9m-whjm-85jfghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-47875ghsaADVISORY
- seclists.org/fulldisclosure/2025/Apr/14ghsaWEB
- github.com/cure53/DOMPurify/blob/0ef5e537a514f904b6aa1d7ad9e749e365d7185f/test/test-suite.jsghsax_refsource_MISCWEB
- github.com/cure53/DOMPurify/commit/0ef5e537a514f904b6aa1d7ad9e749e365d7185fghsax_refsource_MISCWEB
- github.com/cure53/DOMPurify/commit/6ea80cd8b47640c20f2f230c7920b1f4ce4fdf7aghsax_refsource_MISCWEB
- github.com/cure53/DOMPurify/security/advisories/GHSA-gx9m-whjm-85jfghsax_refsource_CONFIRMWEB
- lists.debian.org/debian-lts-announce/2025/02/msg00010.htmlghsaWEB
News mentions
0No linked articles in our index yet.