@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.
- package/es/node_modules/.pnpm/{dompurify@3.3.0 → dompurify@3.4.3}/node_modules/dompurify/dist/purify.es.mjs +299 -143
- package/es/node_modules/.pnpm/dompurify@3.4.3/node_modules/dompurify/dist/purify.es.mjs.map +1 -0
- package/es/package.json.mjs +1 -1
- package/es/packages/components/touchchat/component/AiRobot/letter.vue2.mjs +1 -1
- package/es/packages/components/touchchat/src/AiChat/AiMessage.vue2.mjs +7 -10
- package/es/packages/components/touchchat/src/AiChat/AiMessage.vue2.mjs.map +1 -1
- package/es/packages/components/touchchat/src/AiChat/Chat/useMessageRender.mjs +1 -1
- package/es/packages/components/touchchat/src/AiChat/ChatInput.vue2.mjs +2 -1
- package/es/packages/components/touchchat/src/AiChat/ChatInput.vue2.mjs.map +1 -1
- package/es/packages/components/touchchat/src/AiChat/TouchAgent.vue2.mjs +1 -1
- package/es/packages/components/touchchat/utils/markdown.mjs +1 -1
- package/lib/node_modules/.pnpm/{dompurify@3.3.0 → dompurify@3.4.3}/node_modules/dompurify/dist/purify.es.js +299 -143
- package/lib/node_modules/.pnpm/dompurify@3.4.3/node_modules/dompurify/dist/purify.es.js.map +1 -0
- package/lib/package.json.js +1 -1
- package/lib/packages/components/touchchat/component/AiRobot/letter.vue2.js +1 -1
- package/lib/packages/components/touchchat/src/AiChat/AiMessage.vue2.js +7 -10
- package/lib/packages/components/touchchat/src/AiChat/AiMessage.vue2.js.map +1 -1
- package/lib/packages/components/touchchat/src/AiChat/Chat/useMessageRender.js +1 -1
- package/lib/packages/components/touchchat/src/AiChat/ChatInput.vue2.js +2 -1
- package/lib/packages/components/touchchat/src/AiChat/ChatInput.vue2.js.map +1 -1
- package/lib/packages/components/touchchat/src/AiChat/TouchAgent.vue2.js +1 -1
- package/lib/packages/components/touchchat/utils/markdown.js +1 -1
- package/package.json +1 -1
- package/es/node_modules/.pnpm/dompurify@3.3.0/node_modules/dompurify/dist/purify.es.mjs.map +0 -1
- 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
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
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 (
|
|
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'
|
|
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', '
|
|
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
|
-
|
|
210
|
-
const
|
|
211
|
-
const
|
|
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
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
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
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
|
585
|
-
NAMESPACE = cfg.NAMESPACE
|
|
586
|
-
MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS
|
|
587
|
-
HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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 (
|
|
593
|
-
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 (
|
|
596
|
-
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
|
|
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
|
|
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)) &&
|
|
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 (
|
|
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
|
|
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
|
-
|
|
1088
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
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
|
-
|
|
1252
|
-
|
|
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
|
-
|
|
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
|
}
|