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