@touchvue/chat 1.0.0-beta.50 → 1.0.0-beta.51

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 (25) hide show
  1. package/es/node_modules/.pnpm/{dompurify@3.3.0 → dompurify@3.4.3}/node_modules/dompurify/dist/purify.es.mjs +299 -143
  2. package/es/node_modules/.pnpm/dompurify@3.4.3/node_modules/dompurify/dist/purify.es.mjs.map +1 -0
  3. package/es/package.json.mjs +1 -1
  4. package/es/packages/components/touchchat/component/AiRobot/letter.vue2.mjs +1 -1
  5. package/es/packages/components/touchchat/src/AiChat/AiMessage.vue2.mjs +7 -10
  6. package/es/packages/components/touchchat/src/AiChat/AiMessage.vue2.mjs.map +1 -1
  7. package/es/packages/components/touchchat/src/AiChat/Chat/useMessageRender.mjs +1 -1
  8. package/es/packages/components/touchchat/src/AiChat/ChatInput.vue2.mjs +2 -1
  9. package/es/packages/components/touchchat/src/AiChat/ChatInput.vue2.mjs.map +1 -1
  10. package/es/packages/components/touchchat/src/AiChat/TouchAgent.vue2.mjs +1 -1
  11. package/es/packages/components/touchchat/utils/markdown.mjs +1 -1
  12. package/lib/node_modules/.pnpm/{dompurify@3.3.0 → dompurify@3.4.3}/node_modules/dompurify/dist/purify.es.js +299 -143
  13. package/lib/node_modules/.pnpm/dompurify@3.4.3/node_modules/dompurify/dist/purify.es.js.map +1 -0
  14. package/lib/package.json.js +1 -1
  15. package/lib/packages/components/touchchat/component/AiRobot/letter.vue2.js +1 -1
  16. package/lib/packages/components/touchchat/src/AiChat/AiMessage.vue2.js +7 -10
  17. package/lib/packages/components/touchchat/src/AiChat/AiMessage.vue2.js.map +1 -1
  18. package/lib/packages/components/touchchat/src/AiChat/Chat/useMessageRender.js +1 -1
  19. package/lib/packages/components/touchchat/src/AiChat/ChatInput.vue2.js +2 -1
  20. package/lib/packages/components/touchchat/src/AiChat/ChatInput.vue2.js.map +1 -1
  21. package/lib/packages/components/touchchat/src/AiChat/TouchAgent.vue2.js +1 -1
  22. package/lib/packages/components/touchchat/utils/markdown.js +1 -1
  23. package/package.json +1 -1
  24. package/es/node_modules/.pnpm/dompurify@3.3.0/node_modules/dompurify/dist/purify.es.mjs.map +0 -1
  25. package/lib/node_modules/.pnpm/dompurify@3.3.0/node_modules/dompurify/dist/purify.es.js.map +0 -1
@@ -2,24 +2,65 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- /*! @license DOMPurify 3.3.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.3.0/LICENSE */
5
+ /*! @license DOMPurify 3.4.3 | (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.3/LICENSE */
6
6
 
7
- const {
8
- entries,
9
- setPrototypeOf,
10
- isFrozen,
11
- getPrototypeOf,
12
- getOwnPropertyDescriptor
13
- } = Object;
14
- let {
15
- freeze,
16
- seal,
17
- create
18
- } = Object; // eslint-disable-line import/no-mutable-exports
19
- let {
20
- apply,
21
- construct
22
- } = typeof Reflect !== 'undefined' && Reflect;
7
+ function _arrayLikeToArray(r, a) {
8
+ (null == a || a > r.length) && (a = r.length);
9
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
10
+ return n;
11
+ }
12
+ function _arrayWithHoles(r) {
13
+ if (Array.isArray(r)) return r;
14
+ }
15
+ function _iterableToArrayLimit(r, l) {
16
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
17
+ if (null != t) {
18
+ var e,
19
+ n,
20
+ i,
21
+ u,
22
+ a = [],
23
+ f = true,
24
+ o = false;
25
+ try {
26
+ 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);
27
+ } catch (r) {
28
+ o = true, n = r;
29
+ } finally {
30
+ try {
31
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
32
+ } finally {
33
+ if (o) throw n;
34
+ }
35
+ }
36
+ return a;
37
+ }
38
+ }
39
+ function _nonIterableRest() {
40
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
41
+ }
42
+ function _slicedToArray(r, e) {
43
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
44
+ }
45
+ function _unsupportedIterableToArray(r, a) {
46
+ if (r) {
47
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
48
+ var t = {}.toString.call(r).slice(8, -1);
49
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
50
+ }
51
+ }
52
+
53
+ const entries = Object.entries,
54
+ setPrototypeOf = Object.setPrototypeOf,
55
+ isFrozen = Object.isFrozen,
56
+ getPrototypeOf = Object.getPrototypeOf,
57
+ getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
58
+ let freeze = Object.freeze,
59
+ seal = Object.seal,
60
+ create = Object.create; // eslint-disable-line import/no-mutable-exports
61
+ let _ref = typeof Reflect !== 'undefined' && Reflect,
62
+ apply = _ref.apply,
63
+ construct = _ref.construct;
23
64
  if (!freeze) {
24
65
  freeze = function freeze(x) {
25
66
  return x;
@@ -51,13 +92,19 @@ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
51
92
  const arrayPop = unapply(Array.prototype.pop);
52
93
  const arrayPush = unapply(Array.prototype.push);
53
94
  const arraySplice = unapply(Array.prototype.splice);
95
+ const arrayIsArray = Array.isArray;
54
96
  const stringToLowerCase = unapply(String.prototype.toLowerCase);
55
97
  const stringToString = unapply(String.prototype.toString);
56
98
  const stringMatch = unapply(String.prototype.match);
57
99
  const stringReplace = unapply(String.prototype.replace);
58
100
  const stringIndexOf = unapply(String.prototype.indexOf);
59
101
  const stringTrim = unapply(String.prototype.trim);
102
+ const numberToString = unapply(Number.prototype.toString);
103
+ const booleanToString = unapply(Boolean.prototype.toString);
104
+ const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
105
+ const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
60
106
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
107
+ const objectToString = unapply(Object.prototype.toString);
61
108
  const regExpTest = unapply(RegExp.prototype.test);
62
109
  const typeErrorCreate = unconstruct(TypeError);
63
110
  /**
@@ -107,6 +154,9 @@ function addToSet(set, array) {
107
154
  // Prevent prototype setters from intercepting set as a this value.
108
155
  setPrototypeOf(set, null);
109
156
  }
157
+ if (!arrayIsArray(array)) {
158
+ return set;
159
+ }
110
160
  let l = array.length;
111
161
  while (l--) {
112
162
  let element = array[l];
@@ -147,10 +197,13 @@ function cleanArray(array) {
147
197
  */
148
198
  function clone(object) {
149
199
  const newObject = create(null);
150
- for (const [property, value] of entries(object)) {
200
+ for (const _ref2 of entries(object)) {
201
+ var _ref3 = _slicedToArray(_ref2, 2);
202
+ const property = _ref3[0];
203
+ const value = _ref3[1];
151
204
  const isPropertyExist = objectHasOwnProperty(object, property);
152
205
  if (isPropertyExist) {
153
- if (Array.isArray(value)) {
206
+ if (arrayIsArray(value)) {
154
207
  newObject[property] = cleanArray(value);
155
208
  } else if (value && typeof value === 'object' && value.constructor === Object) {
156
209
  newObject[property] = clone(value);
@@ -161,6 +214,58 @@ function clone(object) {
161
214
  }
162
215
  return newObject;
163
216
  }
217
+ /**
218
+ * Convert non-node values into strings without depending on direct property access.
219
+ *
220
+ * @param value - The value to stringify.
221
+ * @returns A string representation of the provided value.
222
+ */
223
+ function stringifyValue(value) {
224
+ switch (typeof value) {
225
+ case 'string':
226
+ {
227
+ return value;
228
+ }
229
+ case 'number':
230
+ {
231
+ return numberToString(value);
232
+ }
233
+ case 'boolean':
234
+ {
235
+ return booleanToString(value);
236
+ }
237
+ case 'bigint':
238
+ {
239
+ return bigintToString ? bigintToString(value) : '0';
240
+ }
241
+ case 'symbol':
242
+ {
243
+ return symbolToString ? symbolToString(value) : 'Symbol()';
244
+ }
245
+ case 'undefined':
246
+ {
247
+ return objectToString(value);
248
+ }
249
+ case 'function':
250
+ case 'object':
251
+ {
252
+ if (value === null) {
253
+ return objectToString(value);
254
+ }
255
+ const valueAsRecord = value;
256
+ const valueToString = lookupGetter(valueAsRecord, 'toString');
257
+ if (typeof valueToString === 'function') {
258
+ const stringified = valueToString(valueAsRecord);
259
+ return typeof stringified === 'string' ? stringified : objectToString(stringified);
260
+ }
261
+ return objectToString(value);
262
+ }
263
+ default:
264
+ {
265
+ return objectToString(value);
266
+ }
267
+ }
268
+ }
164
269
  /**
165
270
  * This method automatically checks if the prop is function or getter and behaves accordingly.
166
271
  *
@@ -186,6 +291,14 @@ function lookupGetter(object, prop) {
186
291
  }
187
292
  return fallbackValue;
188
293
  }
294
+ function isRegex(value) {
295
+ try {
296
+ regExpTest(value, '');
297
+ return true;
298
+ } catch (_unused) {
299
+ return false;
300
+ }
301
+ }
189
302
 
190
303
  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']);
191
304
  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']);
@@ -201,15 +314,14 @@ const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mgly
201
314
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
202
315
  const text = freeze(['#text']);
203
316
 
204
- 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']);
317
+ 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']);
205
318
  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']);
206
- 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']);
319
+ 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']);
207
320
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
208
321
 
209
- // eslint-disable-next-line unicorn/better-regex
210
- const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
211
- const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
212
- const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
322
+ const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
323
+ const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g);
324
+ const TMPLIT_EXPR = seal(/\${[\w\W]*/g);
213
325
  const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
214
326
  const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
215
327
  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
@@ -220,38 +332,15 @@ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205
220
332
  const DOCTYPE_NAME = seal(/^html$/i);
221
333
  const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
222
334
 
223
- var EXPRESSIONS = /*#__PURE__*/Object.freeze({
224
- __proto__: null,
225
- ARIA_ATTR: ARIA_ATTR,
226
- ATTR_WHITESPACE: ATTR_WHITESPACE,
227
- CUSTOM_ELEMENT: CUSTOM_ELEMENT,
228
- DATA_ATTR: DATA_ATTR,
229
- DOCTYPE_NAME: DOCTYPE_NAME,
230
- ERB_EXPR: ERB_EXPR,
231
- IS_ALLOWED_URI: IS_ALLOWED_URI,
232
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
233
- MUSTACHE_EXPR: MUSTACHE_EXPR,
234
- TMPLIT_EXPR: TMPLIT_EXPR
235
- });
236
-
237
335
  /* eslint-disable @typescript-eslint/indent */
238
336
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
239
337
  const NODE_TYPE = {
240
338
  element: 1,
241
- attribute: 2,
242
339
  text: 3,
243
- cdataSection: 4,
244
- entityReference: 5,
245
- // Deprecated
246
- entityNode: 6,
247
340
  // Deprecated
248
341
  progressingInstruction: 7,
249
342
  comment: 8,
250
- document: 9,
251
- documentType: 10,
252
- documentFragment: 11,
253
- notation: 12 // Deprecated
254
- };
343
+ document: 9};
255
344
  const getGlobal = function getGlobal() {
256
345
  return typeof window === 'undefined' ? null : window;
257
346
  };
@@ -309,7 +398,7 @@ const _createHooksMap = function _createHooksMap() {
309
398
  function createDOMPurify() {
310
399
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
311
400
  const DOMPurify = root => createDOMPurify(root);
312
- DOMPurify.version = '3.3.0';
401
+ DOMPurify.version = '3.4.3';
313
402
  DOMPurify.removed = [];
314
403
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
315
404
  // Not running in a browser, provide a factory function
@@ -317,22 +406,19 @@ function createDOMPurify() {
317
406
  DOMPurify.isSupported = false;
318
407
  return DOMPurify;
319
408
  }
320
- let {
321
- document
322
- } = window;
409
+ let document = window.document;
323
410
  const originalDocument = document;
324
411
  const currentScript = originalDocument.currentScript;
325
- const {
326
- DocumentFragment,
327
- HTMLTemplateElement,
328
- Node,
329
- Element,
330
- NodeFilter,
331
- NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
332
- HTMLFormElement,
333
- DOMParser,
334
- trustedTypes
335
- } = window;
412
+ const DocumentFragment = window.DocumentFragment,
413
+ HTMLTemplateElement = window.HTMLTemplateElement,
414
+ Node = window.Node,
415
+ Element = window.Element,
416
+ NodeFilter = window.NodeFilter,
417
+ _window$NamedNodeMap = window.NamedNodeMap,
418
+ NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
419
+ HTMLFormElement = window.HTMLFormElement,
420
+ DOMParser = window.DOMParser,
421
+ trustedTypes = window.trustedTypes;
336
422
  const ElementPrototype = Element.prototype;
337
423
  const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
338
424
  const remove = lookupGetter(ElementPrototype, 'remove');
@@ -353,33 +439,26 @@ function createDOMPurify() {
353
439
  }
354
440
  let trustedTypesPolicy;
355
441
  let emptyHTML = '';
356
- const {
357
- implementation,
358
- createNodeIterator,
359
- createDocumentFragment,
360
- getElementsByTagName
361
- } = document;
362
- const {
363
- importNode
364
- } = originalDocument;
442
+ const _document = document,
443
+ implementation = _document.implementation,
444
+ createNodeIterator = _document.createNodeIterator,
445
+ createDocumentFragment = _document.createDocumentFragment,
446
+ getElementsByTagName = _document.getElementsByTagName;
447
+ const importNode = originalDocument.importNode;
365
448
  let hooks = _createHooksMap();
366
449
  /**
367
450
  * Expose whether this browser supports running the full DOMPurify.
368
451
  */
369
452
  DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
370
- const {
371
- MUSTACHE_EXPR,
372
- ERB_EXPR,
373
- TMPLIT_EXPR,
374
- DATA_ATTR,
375
- ARIA_ATTR,
376
- IS_SCRIPT_OR_DATA,
377
- ATTR_WHITESPACE,
378
- CUSTOM_ELEMENT
379
- } = EXPRESSIONS;
380
- let {
381
- IS_ALLOWED_URI: IS_ALLOWED_URI$1
382
- } = EXPRESSIONS;
453
+ const MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
454
+ ERB_EXPR$1 = ERB_EXPR,
455
+ TMPLIT_EXPR$1 = TMPLIT_EXPR,
456
+ DATA_ATTR$1 = DATA_ATTR,
457
+ ARIA_ATTR$1 = ARIA_ATTR,
458
+ IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
459
+ ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
460
+ CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
461
+ let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
383
462
  /**
384
463
  * We consider the elements and attributes below to be safe. Ideally
385
464
  * don't add any new ones but feel free to remove unwanted ones.
@@ -557,15 +636,15 @@ function createDOMPurify() {
557
636
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
558
637
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
559
638
  /* Set configuration parameters */
560
- ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
561
- ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
562
- ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
563
- 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;
564
- 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;
565
- FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
566
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
567
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
568
- USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
639
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
640
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
641
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
642
+ 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;
643
+ 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;
644
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
645
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
646
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
647
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
569
648
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
570
649
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
571
650
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
@@ -581,19 +660,20 @@ function createDOMPurify() {
581
660
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
582
661
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
583
662
  IN_PLACE = cfg.IN_PLACE || false; // Default false
584
- IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
585
- NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
586
- MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
587
- HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
588
- CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
589
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
590
- CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
663
+ IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
664
+ NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
665
+ 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
666
+ 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
667
+ const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
668
+ CUSTOM_ELEMENT_HANDLING = create(null);
669
+ if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
670
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
591
671
  }
592
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
593
- CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
672
+ if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
673
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
594
674
  }
595
- if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
596
- CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
675
+ if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
676
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
597
677
  }
598
678
  if (SAFE_FOR_TEMPLATES) {
599
679
  ALLOW_DATA_ATTR = false;
@@ -604,7 +684,7 @@ function createDOMPurify() {
604
684
  /* Parse profile info */
605
685
  if (USE_PROFILES) {
606
686
  ALLOWED_TAGS = addToSet({}, text);
607
- ALLOWED_ATTR = [];
687
+ ALLOWED_ATTR = create(null);
608
688
  if (USE_PROFILES.html === true) {
609
689
  addToSet(ALLOWED_TAGS, html$1);
610
690
  addToSet(ALLOWED_ATTR, html);
@@ -625,36 +705,46 @@ function createDOMPurify() {
625
705
  addToSet(ALLOWED_ATTR, xml);
626
706
  }
627
707
  }
708
+ /* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
709
+ * leaking across calls when switching from function to array config */
710
+ EXTRA_ELEMENT_HANDLING.tagCheck = null;
711
+ EXTRA_ELEMENT_HANDLING.attributeCheck = null;
628
712
  /* Merge configuration parameters */
629
- if (cfg.ADD_TAGS) {
713
+ if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
630
714
  if (typeof cfg.ADD_TAGS === 'function') {
631
715
  EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
632
- } else {
716
+ } else if (arrayIsArray(cfg.ADD_TAGS)) {
633
717
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
634
718
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
635
719
  }
636
720
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
637
721
  }
638
722
  }
639
- if (cfg.ADD_ATTR) {
723
+ if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
640
724
  if (typeof cfg.ADD_ATTR === 'function') {
641
725
  EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
642
- } else {
726
+ } else if (arrayIsArray(cfg.ADD_ATTR)) {
643
727
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
644
728
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
645
729
  }
646
730
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
647
731
  }
648
732
  }
649
- if (cfg.ADD_URI_SAFE_ATTR) {
733
+ if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
650
734
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
651
735
  }
652
- if (cfg.FORBID_CONTENTS) {
736
+ if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
653
737
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
654
738
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
655
739
  }
656
740
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
657
741
  }
742
+ if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
743
+ if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
744
+ FORBID_CONTENTS = clone(FORBID_CONTENTS);
745
+ }
746
+ addToSet(FORBID_CONTENTS, cfg.ADD_FORBID_CONTENTS, transformCaseFunc);
747
+ }
658
748
  /* Add #text in case KEEP_CONTENT is set to true */
659
749
  if (KEEP_CONTENT) {
660
750
  ALLOWED_TAGS['#text'] = true;
@@ -941,6 +1031,11 @@ function createDOMPurify() {
941
1031
  _forceRemove(currentNode);
942
1032
  return true;
943
1033
  }
1034
+ /* Remove risky CSS construction leading to mXSS */
1035
+ if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
1036
+ _forceRemove(currentNode);
1037
+ return true;
1038
+ }
944
1039
  /* Remove any occurrence of processing instructions */
945
1040
  if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
946
1041
  _forceRemove(currentNode);
@@ -952,7 +1047,7 @@ function createDOMPurify() {
952
1047
  return true;
953
1048
  }
954
1049
  /* Remove element if anything forbids its presence */
955
- if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {
1050
+ if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
956
1051
  /* Check if we have a custom element to handle */
957
1052
  if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
958
1053
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
@@ -970,7 +1065,6 @@ function createDOMPurify() {
970
1065
  const childCount = childNodes.length;
971
1066
  for (let i = childCount - 1; i >= 0; --i) {
972
1067
  const childClone = cloneNode(childNodes[i], true);
973
- childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
974
1068
  parentNode.insertBefore(childClone, getNextSibling(currentNode));
975
1069
  }
976
1070
  }
@@ -992,7 +1086,7 @@ function createDOMPurify() {
992
1086
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
993
1087
  /* Get the element's text content */
994
1088
  content = currentNode.textContent;
995
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1089
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
996
1090
  content = stringReplace(content, expr, ' ');
997
1091
  });
998
1092
  if (currentNode.textContent !== content) {
@@ -1016,15 +1110,20 @@ function createDOMPurify() {
1016
1110
  */
1017
1111
  // eslint-disable-next-line complexity
1018
1112
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1113
+ /* FORBID_ATTR must always win, even if ADD_ATTR predicate would allow it */
1114
+ if (FORBID_ATTR[lcName]) {
1115
+ return false;
1116
+ }
1019
1117
  /* Make sure attribute cannot clobber */
1020
1118
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1021
1119
  return false;
1022
1120
  }
1121
+ const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
1023
1122
  /* Allow valid data-* attributes: At least one character after "-"
1024
1123
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1025
1124
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1026
1125
  We don't need to check the value; it's always URI safe. */
1027
- 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]) {
1126
+ 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]) {
1028
1127
  if (
1029
1128
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1030
1129
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -1036,11 +1135,15 @@ function createDOMPurify() {
1036
1135
  return false;
1037
1136
  }
1038
1137
  /* Check value is safe. First, is attr inert? If so, is safe */
1039
- } 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) {
1138
+ } 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) {
1040
1139
  return false;
1041
1140
  } else ;
1042
1141
  return true;
1043
1142
  };
1143
+ /* Names the HTML spec reserves from valid-custom-element-name; these must
1144
+ * never be treated as basic custom elements even when a permissive
1145
+ * CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
1146
+ 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']);
1044
1147
  /**
1045
1148
  * _isBasicCustomElement
1046
1149
  * checks if at least one dash is included in tagName, and it's not the first char
@@ -1050,7 +1153,7 @@ function createDOMPurify() {
1050
1153
  * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1051
1154
  */
1052
1155
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
1053
- return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
1156
+ return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
1054
1157
  };
1055
1158
  /**
1056
1159
  * _sanitizeAttributes
@@ -1065,9 +1168,7 @@ function createDOMPurify() {
1065
1168
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1066
1169
  /* Execute a hook if present */
1067
1170
  _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
1068
- const {
1069
- attributes
1070
- } = currentNode;
1171
+ const attributes = currentNode.attributes;
1071
1172
  /* Check if we have attributes; if not we might have a text node */
1072
1173
  if (!attributes || _isClobbered(currentNode)) {
1073
1174
  return;
@@ -1083,11 +1184,9 @@ function createDOMPurify() {
1083
1184
  /* Go backwards over all attributes; safely remove bad ones */
1084
1185
  while (l--) {
1085
1186
  const attr = attributes[l];
1086
- const {
1087
- name,
1088
- namespaceURI,
1089
- value: attrValue
1090
- } = attr;
1187
+ const name = attr.name,
1188
+ namespaceURI = attr.namespaceURI,
1189
+ attrValue = attr.value;
1091
1190
  const lcName = transformCaseFunc(name);
1092
1191
  const initValue = attrValue;
1093
1192
  let value = name === 'value' ? initValue : stringTrim(initValue);
@@ -1101,14 +1200,16 @@ function createDOMPurify() {
1101
1200
  /* Full DOM Clobbering protection via namespace isolation,
1102
1201
  * Prefix id and name attributes with `user-content-`
1103
1202
  */
1104
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1203
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
1105
1204
  // Remove the attribute with this value
1106
1205
  _removeAttribute(name, currentNode);
1107
1206
  // Prefix the value and later re-create the attribute with the sanitized value
1108
1207
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
1109
1208
  }
1209
+ // Else: already prefixed, leave the attribute alone — the prefix is
1210
+ // itself the clobbering protection, and re-applying it is incorrect.
1110
1211
  /* Work around a security issue with comments inside attributes */
1111
- if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title|textarea)/i, value)) {
1212
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
1112
1213
  _removeAttribute(name, currentNode);
1113
1214
  continue;
1114
1215
  }
@@ -1133,7 +1234,7 @@ function createDOMPurify() {
1133
1234
  }
1134
1235
  /* Sanitize attribute content to be template-safe */
1135
1236
  if (SAFE_FOR_TEMPLATES) {
1136
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1237
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
1137
1238
  value = stringReplace(value, expr, ' ');
1138
1239
  });
1139
1240
  }
@@ -1187,7 +1288,7 @@ function createDOMPurify() {
1187
1288
  *
1188
1289
  * @param fragment to iterate over recursively
1189
1290
  */
1190
- const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1291
+ const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
1191
1292
  let shadowNode = null;
1192
1293
  const shadowIterator = _createNodeIterator(fragment);
1193
1294
  /* Execute a hook if present */
@@ -1201,12 +1302,55 @@ function createDOMPurify() {
1201
1302
  _sanitizeAttributes(shadowNode);
1202
1303
  /* Deep shadow DOM detected */
1203
1304
  if (shadowNode.content instanceof DocumentFragment) {
1204
- _sanitizeShadowDOM(shadowNode.content);
1305
+ _sanitizeShadowDOM2(shadowNode.content);
1205
1306
  }
1206
1307
  }
1207
1308
  /* Execute a hook if present */
1208
1309
  _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
1209
1310
  };
1311
+ /**
1312
+ * _sanitizeAttachedShadowRoots
1313
+ *
1314
+ * Walks `root` and feeds every attached shadow root we encounter into
1315
+ * the existing _sanitizeShadowDOM pipeline. The default node iterator
1316
+ * does not descend into shadow trees, so nodes inside an attached
1317
+ * shadow root would otherwise be skipped entirely.
1318
+ *
1319
+ * Two real input paths put attached shadow roots in front of us:
1320
+ * 1. IN_PLACE on a DOM node that already has shadow roots attached.
1321
+ * 2. DOM-node input where importNode(dirty, true) deep-clones the
1322
+ * shadow root because it was created with `clonable: true`.
1323
+ *
1324
+ * This pass runs once, up front, so the main iteration loop (and the
1325
+ * existing _sanitizeShadowDOM template-content recursion) stay
1326
+ * untouched — string-input paths are not affected.
1327
+ *
1328
+ * @param root the subtree root to walk for attached shadow roots
1329
+ */
1330
+ const _sanitizeAttachedShadowRoots2 = function _sanitizeAttachedShadowRoots(root) {
1331
+ if (root.nodeType === NODE_TYPE.element && root.shadowRoot instanceof DocumentFragment) {
1332
+ const sr = root.shadowRoot;
1333
+ // Recurse first so that nested shadow roots are reached even if
1334
+ // _sanitizeShadowDOM removes hosts at this level.
1335
+ _sanitizeAttachedShadowRoots2(sr);
1336
+ _sanitizeShadowDOM2(sr);
1337
+ }
1338
+ // Snapshot children before recursing. Sanitization of one subtree
1339
+ // (e.g. via an uponSanitizeShadowNode hook) may detach siblings,
1340
+ // and naive nextSibling traversal would silently skip the rest of
1341
+ // the list once a node is detached.
1342
+ const childNodes = root.childNodes;
1343
+ if (!childNodes) {
1344
+ return;
1345
+ }
1346
+ const snapshot = [];
1347
+ arrayForEach(childNodes, child => {
1348
+ arrayPush(snapshot, child);
1349
+ });
1350
+ for (const child of snapshot) {
1351
+ _sanitizeAttachedShadowRoots2(child);
1352
+ }
1353
+ };
1210
1354
  // eslint-disable-next-line complexity
1211
1355
  DOMPurify.sanitize = function (dirty) {
1212
1356
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -1223,13 +1367,9 @@ function createDOMPurify() {
1223
1367
  }
1224
1368
  /* Stringify, in case dirty is an object */
1225
1369
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
1226
- if (typeof dirty.toString === 'function') {
1227
- dirty = dirty.toString();
1228
- if (typeof dirty !== 'string') {
1229
- throw typeErrorCreate('dirty is not a string, aborting');
1230
- }
1231
- } else {
1232
- throw typeErrorCreate('toString is not a function');
1370
+ dirty = stringifyValue(dirty);
1371
+ if (typeof dirty !== 'string') {
1372
+ throw typeErrorCreate('dirty is not a string, aborting');
1233
1373
  }
1234
1374
  }
1235
1375
  /* Return dirty HTML if DOMPurify cannot run */
@@ -1248,12 +1388,16 @@ function createDOMPurify() {
1248
1388
  }
1249
1389
  if (IN_PLACE) {
1250
1390
  /* Do some early pre-sanitization to avoid unsafe root nodes */
1251
- if (dirty.nodeName) {
1252
- const tagName = transformCaseFunc(dirty.nodeName);
1391
+ const nn = dirty.nodeName;
1392
+ if (typeof nn === 'string') {
1393
+ const tagName = transformCaseFunc(nn);
1253
1394
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1254
1395
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1255
1396
  }
1256
1397
  }
1398
+ /* Sanitize attached shadow roots before the main iterator runs.
1399
+ The iterator does not descend into shadow trees. */
1400
+ _sanitizeAttachedShadowRoots2(dirty);
1257
1401
  } else if (dirty instanceof Node) {
1258
1402
  /* If dirty is a DOM element, append to an empty document to avoid
1259
1403
  elements being stripped by the parser */
@@ -1268,6 +1412,10 @@ function createDOMPurify() {
1268
1412
  // eslint-disable-next-line unicorn/prefer-dom-node-append
1269
1413
  body.appendChild(importedNode);
1270
1414
  }
1415
+ /* Clonable shadow roots are deep-cloned by importNode(); sanitize
1416
+ them before the main iterator runs, since the iterator does not
1417
+ descend into shadow trees. */
1418
+ _sanitizeAttachedShadowRoots2(importedNode);
1271
1419
  } else {
1272
1420
  /* Exit directly if we have nothing to do */
1273
1421
  if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
@@ -1296,7 +1444,7 @@ function createDOMPurify() {
1296
1444
  _sanitizeAttributes(currentNode);
1297
1445
  /* Shadow DOM detected, sanitize it */
1298
1446
  if (currentNode.content instanceof DocumentFragment) {
1299
- _sanitizeShadowDOM(currentNode.content);
1447
+ _sanitizeShadowDOM2(currentNode.content);
1300
1448
  }
1301
1449
  }
1302
1450
  /* If we sanitized `dirty` in-place, return it. */
@@ -1305,6 +1453,14 @@ function createDOMPurify() {
1305
1453
  }
1306
1454
  /* Return sanitized string or DOM */
1307
1455
  if (RETURN_DOM) {
1456
+ if (SAFE_FOR_TEMPLATES) {
1457
+ body.normalize();
1458
+ let html = body.innerHTML;
1459
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
1460
+ html = stringReplace(html, expr, ' ');
1461
+ });
1462
+ body.innerHTML = html;
1463
+ }
1308
1464
  if (RETURN_DOM_FRAGMENT) {
1309
1465
  returnNode = createDocumentFragment.call(body.ownerDocument);
1310
1466
  while (body.firstChild) {
@@ -1333,7 +1489,7 @@ function createDOMPurify() {
1333
1489
  }
1334
1490
  /* Sanitize final string template-safe */
1335
1491
  if (SAFE_FOR_TEMPLATES) {
1336
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1492
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
1337
1493
  serializedHTML = stringReplace(serializedHTML, expr, ' ');
1338
1494
  });
1339
1495
  }