@bobfrankston/rmfmail 1.1.225 → 1.1.228

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/client/compose/compose.bundle.js +22 -10
  2. package/client/compose/compose.bundle.js.map +2 -2
  3. package/client/lib/rmf-tiny.js +35 -10
  4. package/client/lib/tinymce/CHANGELOG.md +12 -0
  5. package/client/lib/tinymce/composer.json +1 -1
  6. package/client/lib/tinymce/models/dom/model.js +1 -1
  7. package/client/lib/tinymce/package.json +1 -1
  8. package/client/lib/tinymce/plugins/accordion/plugin.js +1 -1
  9. package/client/lib/tinymce/plugins/advlist/plugin.js +1 -1
  10. package/client/lib/tinymce/plugins/anchor/plugin.js +1 -1
  11. package/client/lib/tinymce/plugins/autolink/plugin.js +1 -1
  12. package/client/lib/tinymce/plugins/autoresize/plugin.js +1 -1
  13. package/client/lib/tinymce/plugins/autosave/plugin.js +1 -1
  14. package/client/lib/tinymce/plugins/charmap/plugin.js +1 -1
  15. package/client/lib/tinymce/plugins/code/plugin.js +1 -1
  16. package/client/lib/tinymce/plugins/codesample/plugin.js +1 -1
  17. package/client/lib/tinymce/plugins/directionality/plugin.js +1 -1
  18. package/client/lib/tinymce/plugins/emoticons/plugin.js +1 -1
  19. package/client/lib/tinymce/plugins/fullscreen/plugin.js +1 -1
  20. package/client/lib/tinymce/plugins/help/plugin.js +1 -1
  21. package/client/lib/tinymce/plugins/image/plugin.js +1 -1
  22. package/client/lib/tinymce/plugins/importcss/plugin.js +1 -1
  23. package/client/lib/tinymce/plugins/insertdatetime/plugin.js +1 -1
  24. package/client/lib/tinymce/plugins/link/plugin.js +1 -1
  25. package/client/lib/tinymce/plugins/lists/plugin.js +1 -1
  26. package/client/lib/tinymce/plugins/media/plugin.js +66 -41
  27. package/client/lib/tinymce/plugins/media/plugin.min.js +1 -1
  28. package/client/lib/tinymce/plugins/nonbreaking/plugin.js +1 -1
  29. package/client/lib/tinymce/plugins/pagebreak/plugin.js +1 -1
  30. package/client/lib/tinymce/plugins/preview/plugin.js +1 -1
  31. package/client/lib/tinymce/plugins/quickbars/plugin.js +1 -1
  32. package/client/lib/tinymce/plugins/save/plugin.js +1 -1
  33. package/client/lib/tinymce/plugins/searchreplace/plugin.js +1 -1
  34. package/client/lib/tinymce/plugins/table/plugin.js +1 -1
  35. package/client/lib/tinymce/plugins/visualblocks/plugin.js +1 -1
  36. package/client/lib/tinymce/plugins/visualchars/plugin.js +1 -1
  37. package/client/lib/tinymce/plugins/wordcount/plugin.js +1 -1
  38. package/client/lib/tinymce/themes/silver/theme.js +345 -155
  39. package/client/lib/tinymce/themes/silver/theme.min.js +3 -1
  40. package/client/lib/tinymce/tinymce.js +393 -171
  41. package/client/lib/tinymce/tinymce.min.js +4 -2
  42. package/docs/outlook.md +215 -0
  43. package/package.json +3 -3
  44. /package/packages/mailx-imap/{node_modules.npmglobalize-stash-62732 → node_modules.npmglobalize-stash-126048}/.package-lock.json +0 -0
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TinyMCE version 8.5.0 (2026-04-29)
2
+ * TinyMCE version 8.6.0 (2026-06-03)
3
3
  */
4
4
 
5
5
  (function () {
@@ -16092,24 +16092,65 @@
16092
16092
  return sc.isFullscreen() ? win() : constrainByMany(box$1(sc.element), scrollableBoxes);
16093
16093
  };
16094
16094
 
16095
- /*! @license DOMPurify 3.3.2 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.3.2/LICENSE */
16096
-
16097
- const {
16098
- entries,
16099
- setPrototypeOf,
16100
- isFrozen,
16101
- getPrototypeOf,
16102
- getOwnPropertyDescriptor
16103
- } = Object;
16104
- let {
16105
- freeze,
16106
- seal,
16107
- create: create$1
16108
- } = Object; // eslint-disable-line import/no-mutable-exports
16109
- let {
16110
- apply,
16111
- construct
16112
- } = typeof Reflect !== 'undefined' && Reflect;
16095
+ /*! @license DOMPurify 3.4.5 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.5/LICENSE */
16096
+
16097
+ function _arrayLikeToArray(r, a) {
16098
+ (null == a || a > r.length) && (a = r.length);
16099
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
16100
+ return n;
16101
+ }
16102
+ function _arrayWithHoles(r) {
16103
+ if (Array.isArray(r)) return r;
16104
+ }
16105
+ function _iterableToArrayLimit(r, l) {
16106
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
16107
+ if (null != t) {
16108
+ var e,
16109
+ n,
16110
+ i,
16111
+ u,
16112
+ a = [],
16113
+ f = true,
16114
+ o = false;
16115
+ try {
16116
+ if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
16117
+ } catch (r) {
16118
+ o = true, n = r;
16119
+ } finally {
16120
+ try {
16121
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
16122
+ } finally {
16123
+ if (o) throw n;
16124
+ }
16125
+ }
16126
+ return a;
16127
+ }
16128
+ }
16129
+ function _nonIterableRest() {
16130
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
16131
+ }
16132
+ function _slicedToArray(r, e) {
16133
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
16134
+ }
16135
+ function _unsupportedIterableToArray(r, a) {
16136
+ if (r) {
16137
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
16138
+ var t = {}.toString.call(r).slice(8, -1);
16139
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
16140
+ }
16141
+ }
16142
+
16143
+ const entries = Object.entries,
16144
+ setPrototypeOf = Object.setPrototypeOf,
16145
+ isFrozen = Object.isFrozen,
16146
+ getPrototypeOf = Object.getPrototypeOf,
16147
+ getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
16148
+ let freeze = Object.freeze,
16149
+ seal = Object.seal,
16150
+ create$1 = Object.create; // eslint-disable-line import/no-mutable-exports
16151
+ let _ref = typeof Reflect !== 'undefined' && Reflect,
16152
+ apply = _ref.apply,
16153
+ construct = _ref.construct;
16113
16154
  if (!freeze) {
16114
16155
  freeze = function freeze(x) {
16115
16156
  return x;
@@ -16141,13 +16182,19 @@
16141
16182
  const arrayPop = unapply(Array.prototype.pop);
16142
16183
  const arrayPush = unapply(Array.prototype.push);
16143
16184
  const arraySplice = unapply(Array.prototype.splice);
16185
+ const arrayIsArray = Array.isArray;
16144
16186
  const stringToLowerCase = unapply(String.prototype.toLowerCase);
16145
16187
  const stringToString = unapply(String.prototype.toString);
16146
16188
  const stringMatch = unapply(String.prototype.match);
16147
16189
  const stringReplace = unapply(String.prototype.replace);
16148
16190
  const stringIndexOf = unapply(String.prototype.indexOf);
16149
16191
  const stringTrim = unapply(String.prototype.trim);
16192
+ const numberToString = unapply(Number.prototype.toString);
16193
+ const booleanToString = unapply(Boolean.prototype.toString);
16194
+ const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
16195
+ const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
16150
16196
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
16197
+ const objectToString = unapply(Object.prototype.toString);
16151
16198
  const regExpTest = unapply(RegExp.prototype.test);
16152
16199
  const typeErrorCreate = unconstruct(TypeError);
16153
16200
  /**
@@ -16197,6 +16244,9 @@
16197
16244
  // Prevent prototype setters from intercepting set as a this value.
16198
16245
  setPrototypeOf(set, null);
16199
16246
  }
16247
+ if (!arrayIsArray(array)) {
16248
+ return set;
16249
+ }
16200
16250
  let l = array.length;
16201
16251
  while (l--) {
16202
16252
  let element = array[l];
@@ -16237,10 +16287,13 @@
16237
16287
  */
16238
16288
  function clone(object) {
16239
16289
  const newObject = create$1(null);
16240
- for (const [property, value] of entries(object)) {
16290
+ for (const _ref2 of entries(object)) {
16291
+ var _ref3 = _slicedToArray(_ref2, 2);
16292
+ const property = _ref3[0];
16293
+ const value = _ref3[1];
16241
16294
  const isPropertyExist = objectHasOwnProperty(object, property);
16242
16295
  if (isPropertyExist) {
16243
- if (Array.isArray(value)) {
16296
+ if (arrayIsArray(value)) {
16244
16297
  newObject[property] = cleanArray(value);
16245
16298
  } else if (value && typeof value === 'object' && value.constructor === Object) {
16246
16299
  newObject[property] = clone(value);
@@ -16251,6 +16304,58 @@
16251
16304
  }
16252
16305
  return newObject;
16253
16306
  }
16307
+ /**
16308
+ * Convert non-node values into strings without depending on direct property access.
16309
+ *
16310
+ * @param value - The value to stringify.
16311
+ * @returns A string representation of the provided value.
16312
+ */
16313
+ function stringifyValue(value) {
16314
+ switch (typeof value) {
16315
+ case 'string':
16316
+ {
16317
+ return value;
16318
+ }
16319
+ case 'number':
16320
+ {
16321
+ return numberToString(value);
16322
+ }
16323
+ case 'boolean':
16324
+ {
16325
+ return booleanToString(value);
16326
+ }
16327
+ case 'bigint':
16328
+ {
16329
+ return bigintToString ? bigintToString(value) : '0';
16330
+ }
16331
+ case 'symbol':
16332
+ {
16333
+ return symbolToString ? symbolToString(value) : 'Symbol()';
16334
+ }
16335
+ case 'undefined':
16336
+ {
16337
+ return objectToString(value);
16338
+ }
16339
+ case 'function':
16340
+ case 'object':
16341
+ {
16342
+ if (value === null) {
16343
+ return objectToString(value);
16344
+ }
16345
+ const valueAsRecord = value;
16346
+ const valueToString = lookupGetter(valueAsRecord, 'toString');
16347
+ if (typeof valueToString === 'function') {
16348
+ const stringified = valueToString(valueAsRecord);
16349
+ return typeof stringified === 'string' ? stringified : objectToString(stringified);
16350
+ }
16351
+ return objectToString(value);
16352
+ }
16353
+ default:
16354
+ {
16355
+ return objectToString(value);
16356
+ }
16357
+ }
16358
+ }
16254
16359
  /**
16255
16360
  * This method automatically checks if the prop is function or getter and behaves accordingly.
16256
16361
  *
@@ -16276,6 +16381,14 @@
16276
16381
  }
16277
16382
  return fallbackValue;
16278
16383
  }
16384
+ function isRegex(value) {
16385
+ try {
16386
+ regExpTest(value, '');
16387
+ return true;
16388
+ } catch (_unused) {
16389
+ return false;
16390
+ }
16391
+ }
16279
16392
 
16280
16393
  const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', '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']);
16281
16394
  const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
@@ -16291,15 +16404,14 @@
16291
16404
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
16292
16405
  const text$1 = freeze(['#text']);
16293
16406
 
16294
- const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', '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', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
16407
+ const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', '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', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);
16295
16408
  const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
16296
- const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
16409
+ const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', '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']);
16297
16410
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
16298
16411
 
16299
- // eslint-disable-next-line unicorn/better-regex
16300
- const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
16301
- const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
16302
- const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
16412
+ const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
16413
+ const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g);
16414
+ const TMPLIT_EXPR = seal(/\${[\w\W]*/g);
16303
16415
  const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
16304
16416
  const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
16305
16417
  const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
@@ -16310,38 +16422,15 @@
16310
16422
  const DOCTYPE_NAME = seal(/^html$/i);
16311
16423
  const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
16312
16424
 
16313
- var EXPRESSIONS = /*#__PURE__*/Object.freeze({
16314
- __proto__: null,
16315
- ARIA_ATTR: ARIA_ATTR,
16316
- ATTR_WHITESPACE: ATTR_WHITESPACE,
16317
- CUSTOM_ELEMENT: CUSTOM_ELEMENT,
16318
- DATA_ATTR: DATA_ATTR,
16319
- DOCTYPE_NAME: DOCTYPE_NAME,
16320
- ERB_EXPR: ERB_EXPR,
16321
- IS_ALLOWED_URI: IS_ALLOWED_URI,
16322
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
16323
- MUSTACHE_EXPR: MUSTACHE_EXPR,
16324
- TMPLIT_EXPR: TMPLIT_EXPR
16325
- });
16326
-
16327
16425
  /* eslint-disable @typescript-eslint/indent */
16328
16426
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
16329
16427
  const NODE_TYPE = {
16330
16428
  element: 1,
16331
- attribute: 2,
16332
16429
  text: 3,
16333
- cdataSection: 4,
16334
- entityReference: 5,
16335
- // Deprecated
16336
- entityNode: 6,
16337
16430
  // Deprecated
16338
16431
  progressingInstruction: 7,
16339
16432
  comment: 8,
16340
- document: 9,
16341
- documentType: 10,
16342
- documentFragment: 11,
16343
- notation: 12 // Deprecated
16344
- };
16433
+ document: 9};
16345
16434
  const getGlobal = function getGlobal() {
16346
16435
  return typeof window === 'undefined' ? null : window;
16347
16436
  };
@@ -16399,7 +16488,7 @@
16399
16488
  function createDOMPurify() {
16400
16489
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
16401
16490
  const DOMPurify = root => createDOMPurify(root);
16402
- DOMPurify.version = '3.3.2';
16491
+ DOMPurify.version = '3.4.5';
16403
16492
  DOMPurify.removed = [];
16404
16493
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
16405
16494
  // Not running in a browser, provide a factory function
@@ -16407,28 +16496,26 @@
16407
16496
  DOMPurify.isSupported = false;
16408
16497
  return DOMPurify;
16409
16498
  }
16410
- let {
16411
- document
16412
- } = window;
16499
+ let document = window.document;
16413
16500
  const originalDocument = document;
16414
16501
  const currentScript = originalDocument.currentScript;
16415
- const {
16416
- DocumentFragment,
16417
- HTMLTemplateElement,
16418
- Node,
16419
- Element,
16420
- NodeFilter,
16421
- NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
16422
- HTMLFormElement,
16423
- DOMParser,
16424
- trustedTypes
16425
- } = window;
16502
+ const DocumentFragment = window.DocumentFragment,
16503
+ HTMLTemplateElement = window.HTMLTemplateElement,
16504
+ Node = window.Node,
16505
+ Element = window.Element,
16506
+ NodeFilter = window.NodeFilter,
16507
+ _window$NamedNodeMap = window.NamedNodeMap,
16508
+ NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
16509
+ HTMLFormElement = window.HTMLFormElement,
16510
+ DOMParser = window.DOMParser,
16511
+ trustedTypes = window.trustedTypes;
16426
16512
  const ElementPrototype = Element.prototype;
16427
16513
  const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
16428
16514
  const remove = lookupGetter(ElementPrototype, 'remove');
16429
16515
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
16430
16516
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
16431
16517
  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
16518
+ const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;
16432
16519
  // As per issue #47, the web-components registry is inherited by a
16433
16520
  // new document created via createHTMLDocument. As per the spec
16434
16521
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
@@ -16443,33 +16530,26 @@
16443
16530
  }
16444
16531
  let trustedTypesPolicy;
16445
16532
  let emptyHTML = '';
16446
- const {
16447
- implementation,
16448
- createNodeIterator,
16449
- createDocumentFragment,
16450
- getElementsByTagName
16451
- } = document;
16452
- const {
16453
- importNode
16454
- } = originalDocument;
16533
+ const _document = document,
16534
+ implementation = _document.implementation,
16535
+ createNodeIterator = _document.createNodeIterator,
16536
+ createDocumentFragment = _document.createDocumentFragment,
16537
+ getElementsByTagName = _document.getElementsByTagName;
16538
+ const importNode = originalDocument.importNode;
16455
16539
  let hooks = _createHooksMap();
16456
16540
  /**
16457
16541
  * Expose whether this browser supports running the full DOMPurify.
16458
16542
  */
16459
16543
  DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
16460
- const {
16461
- MUSTACHE_EXPR,
16462
- ERB_EXPR,
16463
- TMPLIT_EXPR,
16464
- DATA_ATTR,
16465
- ARIA_ATTR,
16466
- IS_SCRIPT_OR_DATA,
16467
- ATTR_WHITESPACE,
16468
- CUSTOM_ELEMENT
16469
- } = EXPRESSIONS;
16470
- let {
16471
- IS_ALLOWED_URI: IS_ALLOWED_URI$1
16472
- } = EXPRESSIONS;
16544
+ const MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
16545
+ ERB_EXPR$1 = ERB_EXPR,
16546
+ TMPLIT_EXPR$1 = TMPLIT_EXPR,
16547
+ DATA_ATTR$1 = DATA_ATTR,
16548
+ ARIA_ATTR$1 = ARIA_ATTR,
16549
+ IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
16550
+ ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
16551
+ CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
16552
+ let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
16473
16553
  /**
16474
16554
  * We consider the elements and attributes below to be safe. Ideally
16475
16555
  * don't add any new ones but feel free to remove unwanted ones.
@@ -16647,15 +16727,15 @@
16647
16727
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
16648
16728
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
16649
16729
  /* Set configuration parameters */
16650
- ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
16651
- ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
16652
- ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
16653
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
16654
- DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
16655
- FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
16656
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
16657
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
16658
- USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
16730
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
16731
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
16732
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
16733
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR) ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
16734
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') && arrayIsArray(cfg.ADD_DATA_URI_TAGS) ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
16735
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
16736
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
16737
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
16738
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
16659
16739
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
16660
16740
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
16661
16741
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
@@ -16671,19 +16751,20 @@
16671
16751
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
16672
16752
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
16673
16753
  IN_PLACE = cfg.IN_PLACE || false; // Default false
16674
- IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
16675
- NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
16676
- MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
16677
- HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
16678
- CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
16679
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
16680
- CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
16754
+ IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
16755
+ NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
16756
+ MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); // Default built-in map
16757
+ HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, ['annotation-xml']); // Default built-in map
16758
+ const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create$1(null);
16759
+ CUSTOM_ELEMENT_HANDLING = create$1(null);
16760
+ if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
16761
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
16681
16762
  }
16682
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
16683
- CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
16763
+ if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
16764
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
16684
16765
  }
16685
- if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
16686
- CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
16766
+ if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
16767
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
16687
16768
  }
16688
16769
  if (SAFE_FOR_TEMPLATES) {
16689
16770
  ALLOW_DATA_ATTR = false;
@@ -16715,44 +16796,41 @@
16715
16796
  addToSet(ALLOWED_ATTR, xml);
16716
16797
  }
16717
16798
  }
16718
- /* Prevent function-based ADD_ATTR / ADD_TAGS from leaking across calls */
16719
- if (!objectHasOwnProperty(cfg, 'ADD_TAGS')) {
16720
- EXTRA_ELEMENT_HANDLING.tagCheck = null;
16721
- }
16722
- if (!objectHasOwnProperty(cfg, 'ADD_ATTR')) {
16723
- EXTRA_ELEMENT_HANDLING.attributeCheck = null;
16724
- }
16799
+ /* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
16800
+ * leaking across calls when switching from function to array config */
16801
+ EXTRA_ELEMENT_HANDLING.tagCheck = null;
16802
+ EXTRA_ELEMENT_HANDLING.attributeCheck = null;
16725
16803
  /* Merge configuration parameters */
16726
- if (cfg.ADD_TAGS) {
16804
+ if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
16727
16805
  if (typeof cfg.ADD_TAGS === 'function') {
16728
16806
  EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
16729
- } else {
16807
+ } else if (arrayIsArray(cfg.ADD_TAGS)) {
16730
16808
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
16731
16809
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
16732
16810
  }
16733
16811
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
16734
16812
  }
16735
16813
  }
16736
- if (cfg.ADD_ATTR) {
16814
+ if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
16737
16815
  if (typeof cfg.ADD_ATTR === 'function') {
16738
16816
  EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
16739
- } else {
16817
+ } else if (arrayIsArray(cfg.ADD_ATTR)) {
16740
16818
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
16741
16819
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
16742
16820
  }
16743
16821
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
16744
16822
  }
16745
16823
  }
16746
- if (cfg.ADD_URI_SAFE_ATTR) {
16824
+ if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
16747
16825
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
16748
16826
  }
16749
- if (cfg.FORBID_CONTENTS) {
16827
+ if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
16750
16828
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
16751
16829
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
16752
16830
  }
16753
16831
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
16754
16832
  }
16755
- if (cfg.ADD_FORBID_CONTENTS) {
16833
+ if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
16756
16834
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
16757
16835
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
16758
16836
  }
@@ -16991,6 +17069,40 @@
16991
17069
  // eslint-disable-next-line no-bitwise
16992
17070
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
16993
17071
  };
17072
+ /**
17073
+ * Strip template-engine expressions ({{...}}, ${...}, <%...%>) from the
17074
+ * character data of an element subtree. Used as the final safety net for
17075
+ * SAFE_FOR_TEMPLATES on every DOM-returning code path so that expressions
17076
+ * which only form after text-node normalization (e.g. fragments split across
17077
+ * stripped elements) cannot survive into a template-evaluating framework.
17078
+ *
17079
+ * Walks text/comment/CDATA/processing-instruction nodes and mutates `.data`
17080
+ * in place rather than round-tripping through innerHTML. This preserves
17081
+ * descendant node references (important for IN_PLACE callers), avoids a
17082
+ * serialize/reparse cycle, and reads literal character data — which means
17083
+ * `<%...%>` in text content matches the ERB regex against its real bytes
17084
+ * instead of the HTML-entity-escaped form innerHTML would produce.
17085
+ *
17086
+ * Attribute values are not visited here; SAFE_FOR_TEMPLATES handling for
17087
+ * attributes is performed during the per-node `_sanitizeAttributes` pass.
17088
+ *
17089
+ * @param node The root element whose character data should be scrubbed.
17090
+ */
17091
+ const _scrubTemplateExpressions = function _scrubTemplateExpressions(node) {
17092
+ node.normalize();
17093
+ const walker = createNodeIterator.call(node.ownerDocument || node, node,
17094
+ // eslint-disable-next-line no-bitwise
17095
+ NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
17096
+ let currentNode = walker.nextNode();
17097
+ while (currentNode) {
17098
+ let data = currentNode.data;
17099
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
17100
+ data = stringReplace(data, expr, ' ');
17101
+ });
17102
+ currentNode.data = data;
17103
+ currentNode = walker.nextNode();
17104
+ }
17105
+ };
16994
17106
  /**
16995
17107
  * _isClobbered
16996
17108
  *
@@ -17001,13 +17113,31 @@
17001
17113
  return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');
17002
17114
  };
17003
17115
  /**
17004
- * Checks whether the given object is a DOM node.
17116
+ * Checks whether the given object is a DOM node, including nodes that
17117
+ * originate from a different window/realm (e.g. an iframe's
17118
+ * contentDocument). The previous `value instanceof Node` check was
17119
+ * realm-bound: nodes from a different window failed it, causing
17120
+ * sanitize() to silently stringify them and reset IN_PLACE to false,
17121
+ * returning the original node unsanitized. See GHSA-4w3q-35jp-p934.
17122
+ *
17123
+ * Implementation: call the cached `nodeType` getter from Node.prototype
17124
+ * directly on the value. This bypasses any clobbered instance property
17125
+ * (e.g. a child element named "nodeType") and works across realms
17126
+ * because the WebIDL `nodeType` getter reads an internal slot that
17127
+ * every real Node has, regardless of which window minted it.
17005
17128
  *
17006
17129
  * @param value object to check whether it's a DOM node
17007
- * @return true is object is a DOM node
17130
+ * @return true if value is a DOM node from any realm
17008
17131
  */
17009
17132
  const _isNode = function _isNode(value) {
17010
- return typeof Node === 'function' && value instanceof Node;
17133
+ if (!getNodeType || typeof value !== 'object' || value === null) {
17134
+ return false;
17135
+ }
17136
+ try {
17137
+ return typeof getNodeType(value) === 'number';
17138
+ } catch (_) {
17139
+ return false;
17140
+ }
17011
17141
  };
17012
17142
  function _executeHooks(hooks, currentNode, data) {
17013
17143
  arrayForEach(hooks, hook => {
@@ -17044,6 +17174,11 @@
17044
17174
  _forceRemove(currentNode);
17045
17175
  return true;
17046
17176
  }
17177
+ /* Remove risky CSS construction leading to mXSS */
17178
+ if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
17179
+ _forceRemove(currentNode);
17180
+ return true;
17181
+ }
17047
17182
  /* Remove any occurrence of processing instructions */
17048
17183
  if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
17049
17184
  _forceRemove(currentNode);
@@ -17055,7 +17190,7 @@
17055
17190
  return true;
17056
17191
  }
17057
17192
  /* Remove element if anything forbids its presence */
17058
- if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {
17193
+ if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
17059
17194
  /* Check if we have a custom element to handle */
17060
17195
  if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
17061
17196
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
@@ -17073,7 +17208,6 @@
17073
17208
  const childCount = childNodes.length;
17074
17209
  for (let i = childCount - 1; i >= 0; --i) {
17075
17210
  const childClone = cloneNode(childNodes[i], true);
17076
- childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
17077
17211
  parentNode.insertBefore(childClone, getNextSibling(currentNode));
17078
17212
  }
17079
17213
  }
@@ -17095,7 +17229,7 @@
17095
17229
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
17096
17230
  /* Get the element's text content */
17097
17231
  content = currentNode.textContent;
17098
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
17232
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
17099
17233
  content = stringReplace(content, expr, ' ');
17100
17234
  });
17101
17235
  if (currentNode.textContent !== content) {
@@ -17127,11 +17261,12 @@
17127
17261
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
17128
17262
  return false;
17129
17263
  }
17264
+ const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
17130
17265
  /* Allow valid data-* attributes: At least one character after "-"
17131
17266
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
17132
17267
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
17133
17268
  We don't need to check the value; it's always URI safe. */
17134
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
17269
+ if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!nameIsPermitted || FORBID_ATTR[lcName]) {
17135
17270
  if (
17136
17271
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
17137
17272
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -17143,11 +17278,15 @@
17143
17278
  return false;
17144
17279
  }
17145
17280
  /* Check value is safe. First, is attr inert? If so, is safe */
17146
- } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
17281
+ } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (value) {
17147
17282
  return false;
17148
17283
  } else ;
17149
17284
  return true;
17150
17285
  };
17286
+ /* Names the HTML spec reserves from valid-custom-element-name; these must
17287
+ * never be treated as basic custom elements even when a permissive
17288
+ * CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
17289
+ const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, ['annotation-xml', 'color-profile', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'missing-glyph']);
17151
17290
  /**
17152
17291
  * _isBasicCustomElement
17153
17292
  * checks if at least one dash is included in tagName, and it's not the first char
@@ -17157,7 +17296,7 @@
17157
17296
  * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
17158
17297
  */
17159
17298
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
17160
- return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
17299
+ return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
17161
17300
  };
17162
17301
  /**
17163
17302
  * _sanitizeAttributes
@@ -17172,9 +17311,7 @@
17172
17311
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
17173
17312
  /* Execute a hook if present */
17174
17313
  _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
17175
- const {
17176
- attributes
17177
- } = currentNode;
17314
+ const attributes = currentNode.attributes;
17178
17315
  /* Check if we have attributes; if not we might have a text node */
17179
17316
  if (!attributes || _isClobbered(currentNode)) {
17180
17317
  return;
@@ -17190,11 +17327,9 @@
17190
17327
  /* Go backwards over all attributes; safely remove bad ones */
17191
17328
  while (l--) {
17192
17329
  const attr = attributes[l];
17193
- const {
17194
- name,
17195
- namespaceURI,
17196
- value: attrValue
17197
- } = attr;
17330
+ const name = attr.name,
17331
+ namespaceURI = attr.namespaceURI,
17332
+ attrValue = attr.value;
17198
17333
  const lcName = transformCaseFunc(name);
17199
17334
  const initValue = attrValue;
17200
17335
  let value = name === 'value' ? initValue : stringTrim(initValue);
@@ -17208,12 +17343,14 @@
17208
17343
  /* Full DOM Clobbering protection via namespace isolation,
17209
17344
  * Prefix id and name attributes with `user-content-`
17210
17345
  */
17211
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
17346
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
17212
17347
  // Remove the attribute with this value
17213
17348
  _removeAttribute(name, currentNode);
17214
17349
  // Prefix the value and later re-create the attribute with the sanitized value
17215
17350
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
17216
17351
  }
17352
+ // Else: already prefixed, leave the attribute alone — the prefix is
17353
+ // itself the clobbering protection, and re-applying it is incorrect.
17217
17354
  /* Work around a security issue with comments inside attributes */
17218
17355
  if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
17219
17356
  _removeAttribute(name, currentNode);
@@ -17240,7 +17377,7 @@
17240
17377
  }
17241
17378
  /* Sanitize attribute content to be template-safe */
17242
17379
  if (SAFE_FOR_TEMPLATES) {
17243
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
17380
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
17244
17381
  value = stringReplace(value, expr, ' ');
17245
17382
  });
17246
17383
  }
@@ -17294,7 +17431,7 @@
17294
17431
  *
17295
17432
  * @param fragment to iterate over recursively
17296
17433
  */
17297
- const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
17434
+ const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
17298
17435
  let shadowNode = null;
17299
17436
  const shadowIterator = _createNodeIterator(fragment);
17300
17437
  /* Execute a hook if present */
@@ -17308,12 +17445,55 @@
17308
17445
  _sanitizeAttributes(shadowNode);
17309
17446
  /* Deep shadow DOM detected */
17310
17447
  if (shadowNode.content instanceof DocumentFragment) {
17311
- _sanitizeShadowDOM(shadowNode.content);
17448
+ _sanitizeShadowDOM2(shadowNode.content);
17312
17449
  }
17313
17450
  }
17314
17451
  /* Execute a hook if present */
17315
17452
  _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
17316
17453
  };
17454
+ /**
17455
+ * _sanitizeAttachedShadowRoots
17456
+ *
17457
+ * Walks `root` and feeds every attached shadow root we encounter into
17458
+ * the existing _sanitizeShadowDOM pipeline. The default node iterator
17459
+ * does not descend into shadow trees, so nodes inside an attached
17460
+ * shadow root would otherwise be skipped entirely.
17461
+ *
17462
+ * Two real input paths put attached shadow roots in front of us:
17463
+ * 1. IN_PLACE on a DOM node that already has shadow roots attached.
17464
+ * 2. DOM-node input where importNode(dirty, true) deep-clones the
17465
+ * shadow root because it was created with `clonable: true`.
17466
+ *
17467
+ * This pass runs once, up front, so the main iteration loop (and the
17468
+ * existing _sanitizeShadowDOM template-content recursion) stay
17469
+ * untouched — string-input paths are not affected.
17470
+ *
17471
+ * @param root the subtree root to walk for attached shadow roots
17472
+ */
17473
+ const _sanitizeAttachedShadowRoots2 = function _sanitizeAttachedShadowRoots(root) {
17474
+ if (root.nodeType === NODE_TYPE.element && root.shadowRoot instanceof DocumentFragment) {
17475
+ const sr = root.shadowRoot;
17476
+ // Recurse first so that nested shadow roots are reached even if
17477
+ // _sanitizeShadowDOM removes hosts at this level.
17478
+ _sanitizeAttachedShadowRoots2(sr);
17479
+ _sanitizeShadowDOM2(sr);
17480
+ }
17481
+ // Snapshot children before recursing. Sanitization of one subtree
17482
+ // (e.g. via an uponSanitizeShadowNode hook) may detach siblings,
17483
+ // and naive nextSibling traversal would silently skip the rest of
17484
+ // the list once a node is detached.
17485
+ const childNodes = root.childNodes;
17486
+ if (!childNodes) {
17487
+ return;
17488
+ }
17489
+ const snapshot = [];
17490
+ arrayForEach(childNodes, child => {
17491
+ arrayPush(snapshot, child);
17492
+ });
17493
+ for (const child of snapshot) {
17494
+ _sanitizeAttachedShadowRoots2(child);
17495
+ }
17496
+ };
17317
17497
  // eslint-disable-next-line complexity
17318
17498
  DOMPurify.sanitize = function (dirty) {
17319
17499
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -17330,13 +17510,9 @@
17330
17510
  }
17331
17511
  /* Stringify, in case dirty is an object */
17332
17512
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
17333
- if (typeof dirty.toString === 'function') {
17334
- dirty = dirty.toString();
17335
- if (typeof dirty !== 'string') {
17336
- throw typeErrorCreate('dirty is not a string, aborting');
17337
- }
17338
- } else {
17339
- throw typeErrorCreate('toString is not a function');
17513
+ dirty = stringifyValue(dirty);
17514
+ if (typeof dirty !== 'string') {
17515
+ throw typeErrorCreate('dirty is not a string, aborting');
17340
17516
  }
17341
17517
  }
17342
17518
  /* Return dirty HTML if DOMPurify cannot run */
@@ -17355,13 +17531,17 @@
17355
17531
  }
17356
17532
  if (IN_PLACE) {
17357
17533
  /* Do some early pre-sanitization to avoid unsafe root nodes */
17358
- if (dirty.nodeName) {
17359
- const tagName = transformCaseFunc(dirty.nodeName);
17534
+ const nn = dirty.nodeName;
17535
+ if (typeof nn === 'string') {
17536
+ const tagName = transformCaseFunc(nn);
17360
17537
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
17361
17538
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
17362
17539
  }
17363
17540
  }
17364
- } else if (dirty instanceof Node) {
17541
+ /* Sanitize attached shadow roots before the main iterator runs.
17542
+ The iterator does not descend into shadow trees. */
17543
+ _sanitizeAttachedShadowRoots2(dirty);
17544
+ } else if (_isNode(dirty)) {
17365
17545
  /* If dirty is a DOM element, append to an empty document to avoid
17366
17546
  elements being stripped by the parser */
17367
17547
  body = _initDocument('<!---->');
@@ -17375,6 +17555,10 @@
17375
17555
  // eslint-disable-next-line unicorn/prefer-dom-node-append
17376
17556
  body.appendChild(importedNode);
17377
17557
  }
17558
+ /* Clonable shadow roots are deep-cloned by importNode(); sanitize
17559
+ them before the main iterator runs, since the iterator does not
17560
+ descend into shadow trees. */
17561
+ _sanitizeAttachedShadowRoots2(importedNode);
17378
17562
  } else {
17379
17563
  /* Exit directly if we have nothing to do */
17380
17564
  if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
@@ -17403,15 +17587,21 @@
17403
17587
  _sanitizeAttributes(currentNode);
17404
17588
  /* Shadow DOM detected, sanitize it */
17405
17589
  if (currentNode.content instanceof DocumentFragment) {
17406
- _sanitizeShadowDOM(currentNode.content);
17590
+ _sanitizeShadowDOM2(currentNode.content);
17407
17591
  }
17408
17592
  }
17409
17593
  /* If we sanitized `dirty` in-place, return it. */
17410
17594
  if (IN_PLACE) {
17595
+ if (SAFE_FOR_TEMPLATES) {
17596
+ _scrubTemplateExpressions(dirty);
17597
+ }
17411
17598
  return dirty;
17412
17599
  }
17413
17600
  /* Return sanitized string or DOM */
17414
17601
  if (RETURN_DOM) {
17602
+ if (SAFE_FOR_TEMPLATES) {
17603
+ _scrubTemplateExpressions(body);
17604
+ }
17415
17605
  if (RETURN_DOM_FRAGMENT) {
17416
17606
  returnNode = createDocumentFragment.call(body.ownerDocument);
17417
17607
  while (body.firstChild) {
@@ -17440,7 +17630,7 @@
17440
17630
  }
17441
17631
  /* Sanitize final string template-safe */
17442
17632
  if (SAFE_FOR_TEMPLATES) {
17443
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
17633
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
17444
17634
  serializedHTML = stringReplace(serializedHTML, expr, ' ');
17445
17635
  });
17446
17636
  }