dompurify 3.4.2 → 3.4.3
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/README.md +2 -2
- package/dist/purify.cjs.d.ts +1 -1
- package/dist/purify.cjs.js +153 -88
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.d.mts +1 -1
- package/dist/purify.es.mjs +153 -88
- package/dist/purify.es.mjs.map +1 -1
- package/dist/purify.js +1454 -1389
- package/dist/purify.js.map +1 -1
- package/dist/purify.min.js +2 -2
- package/dist/purify.min.js.map +1 -1
- package/package.json +8 -8
- package/src/purify.ts +59 -0
- package/src/regexp.ts +3 -4
package/dist/purify.es.mjs
CHANGED
|
@@ -1,21 +1,62 @@
|
|
|
1
|
-
/*! @license DOMPurify 3.4.
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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;
|
|
@@ -152,7 +193,10 @@ function cleanArray(array) {
|
|
|
152
193
|
*/
|
|
153
194
|
function clone(object) {
|
|
154
195
|
const newObject = create(null);
|
|
155
|
-
for (const
|
|
196
|
+
for (const _ref2 of entries(object)) {
|
|
197
|
+
var _ref3 = _slicedToArray(_ref2, 2);
|
|
198
|
+
const property = _ref3[0];
|
|
199
|
+
const value = _ref3[1];
|
|
156
200
|
const isPropertyExist = objectHasOwnProperty(object, property);
|
|
157
201
|
if (isPropertyExist) {
|
|
158
202
|
if (arrayIsArray(value)) {
|
|
@@ -271,10 +315,9 @@ const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseli
|
|
|
271
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']);
|
|
272
316
|
const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
273
317
|
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
const
|
|
277
|
-
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);
|
|
278
321
|
const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
|
|
279
322
|
const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
280
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
|
|
@@ -285,20 +328,6 @@ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205
|
|
|
285
328
|
const DOCTYPE_NAME = seal(/^html$/i);
|
|
286
329
|
const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
|
|
287
330
|
|
|
288
|
-
var EXPRESSIONS = /*#__PURE__*/Object.freeze({
|
|
289
|
-
__proto__: null,
|
|
290
|
-
ARIA_ATTR: ARIA_ATTR,
|
|
291
|
-
ATTR_WHITESPACE: ATTR_WHITESPACE,
|
|
292
|
-
CUSTOM_ELEMENT: CUSTOM_ELEMENT,
|
|
293
|
-
DATA_ATTR: DATA_ATTR,
|
|
294
|
-
DOCTYPE_NAME: DOCTYPE_NAME,
|
|
295
|
-
ERB_EXPR: ERB_EXPR,
|
|
296
|
-
IS_ALLOWED_URI: IS_ALLOWED_URI,
|
|
297
|
-
IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
|
|
298
|
-
MUSTACHE_EXPR: MUSTACHE_EXPR,
|
|
299
|
-
TMPLIT_EXPR: TMPLIT_EXPR
|
|
300
|
-
});
|
|
301
|
-
|
|
302
331
|
/* eslint-disable @typescript-eslint/indent */
|
|
303
332
|
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
|
|
304
333
|
const NODE_TYPE = {
|
|
@@ -365,7 +394,7 @@ const _createHooksMap = function _createHooksMap() {
|
|
|
365
394
|
function createDOMPurify() {
|
|
366
395
|
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
367
396
|
const DOMPurify = root => createDOMPurify(root);
|
|
368
|
-
DOMPurify.version = '3.4.
|
|
397
|
+
DOMPurify.version = '3.4.3';
|
|
369
398
|
DOMPurify.removed = [];
|
|
370
399
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
371
400
|
// Not running in a browser, provide a factory function
|
|
@@ -373,22 +402,19 @@ function createDOMPurify() {
|
|
|
373
402
|
DOMPurify.isSupported = false;
|
|
374
403
|
return DOMPurify;
|
|
375
404
|
}
|
|
376
|
-
let
|
|
377
|
-
document
|
|
378
|
-
} = window;
|
|
405
|
+
let document = window.document;
|
|
379
406
|
const originalDocument = document;
|
|
380
407
|
const currentScript = originalDocument.currentScript;
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
|
|
388
|
-
HTMLFormElement,
|
|
389
|
-
DOMParser,
|
|
390
|
-
trustedTypes
|
|
391
|
-
} = 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;
|
|
392
418
|
const ElementPrototype = Element.prototype;
|
|
393
419
|
const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
394
420
|
const remove = lookupGetter(ElementPrototype, 'remove');
|
|
@@ -409,33 +435,26 @@ function createDOMPurify() {
|
|
|
409
435
|
}
|
|
410
436
|
let trustedTypesPolicy;
|
|
411
437
|
let emptyHTML = '';
|
|
412
|
-
const
|
|
413
|
-
implementation,
|
|
414
|
-
createNodeIterator,
|
|
415
|
-
createDocumentFragment,
|
|
416
|
-
getElementsByTagName
|
|
417
|
-
|
|
418
|
-
const {
|
|
419
|
-
importNode
|
|
420
|
-
} = 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;
|
|
421
444
|
let hooks = _createHooksMap();
|
|
422
445
|
/**
|
|
423
446
|
* Expose whether this browser supports running the full DOMPurify.
|
|
424
447
|
*/
|
|
425
448
|
DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
} = EXPRESSIONS;
|
|
436
|
-
let {
|
|
437
|
-
IS_ALLOWED_URI: IS_ALLOWED_URI$1
|
|
438
|
-
} = 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;
|
|
439
458
|
/**
|
|
440
459
|
* We consider the elements and attributes below to be safe. Ideally
|
|
441
460
|
* don't add any new ones but feel free to remove unwanted ones.
|
|
@@ -1063,7 +1082,7 @@ function createDOMPurify() {
|
|
|
1063
1082
|
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
|
|
1064
1083
|
/* Get the element's text content */
|
|
1065
1084
|
content = currentNode.textContent;
|
|
1066
|
-
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
1085
|
+
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
|
|
1067
1086
|
content = stringReplace(content, expr, ' ');
|
|
1068
1087
|
});
|
|
1069
1088
|
if (currentNode.textContent !== content) {
|
|
@@ -1100,7 +1119,7 @@ function createDOMPurify() {
|
|
|
1100
1119
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
1101
1120
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
1102
1121
|
We don't need to check the value; it's always URI safe. */
|
|
1103
|
-
if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (!nameIsPermitted || 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]) {
|
|
1104
1123
|
if (
|
|
1105
1124
|
// First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
1106
1125
|
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
@@ -1112,7 +1131,7 @@ function createDOMPurify() {
|
|
|
1112
1131
|
return false;
|
|
1113
1132
|
}
|
|
1114
1133
|
/* Check value is safe. First, is attr inert? If so, is safe */
|
|
1115
|
-
} 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) {
|
|
1116
1135
|
return false;
|
|
1117
1136
|
} else ;
|
|
1118
1137
|
return true;
|
|
@@ -1130,7 +1149,7 @@ function createDOMPurify() {
|
|
|
1130
1149
|
* @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
|
|
1131
1150
|
*/
|
|
1132
1151
|
const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
|
|
1133
|
-
return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT, tagName);
|
|
1152
|
+
return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
|
|
1134
1153
|
};
|
|
1135
1154
|
/**
|
|
1136
1155
|
* _sanitizeAttributes
|
|
@@ -1145,9 +1164,7 @@ function createDOMPurify() {
|
|
|
1145
1164
|
const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
1146
1165
|
/* Execute a hook if present */
|
|
1147
1166
|
_executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
|
|
1148
|
-
const
|
|
1149
|
-
attributes
|
|
1150
|
-
} = currentNode;
|
|
1167
|
+
const attributes = currentNode.attributes;
|
|
1151
1168
|
/* Check if we have attributes; if not we might have a text node */
|
|
1152
1169
|
if (!attributes || _isClobbered(currentNode)) {
|
|
1153
1170
|
return;
|
|
@@ -1163,11 +1180,9 @@ function createDOMPurify() {
|
|
|
1163
1180
|
/* Go backwards over all attributes; safely remove bad ones */
|
|
1164
1181
|
while (l--) {
|
|
1165
1182
|
const attr = attributes[l];
|
|
1166
|
-
const
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
value: attrValue
|
|
1170
|
-
} = attr;
|
|
1183
|
+
const name = attr.name,
|
|
1184
|
+
namespaceURI = attr.namespaceURI,
|
|
1185
|
+
attrValue = attr.value;
|
|
1171
1186
|
const lcName = transformCaseFunc(name);
|
|
1172
1187
|
const initValue = attrValue;
|
|
1173
1188
|
let value = name === 'value' ? initValue : stringTrim(initValue);
|
|
@@ -1215,7 +1230,7 @@ function createDOMPurify() {
|
|
|
1215
1230
|
}
|
|
1216
1231
|
/* Sanitize attribute content to be template-safe */
|
|
1217
1232
|
if (SAFE_FOR_TEMPLATES) {
|
|
1218
|
-
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
1233
|
+
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
|
|
1219
1234
|
value = stringReplace(value, expr, ' ');
|
|
1220
1235
|
});
|
|
1221
1236
|
}
|
|
@@ -1289,6 +1304,49 @@ function createDOMPurify() {
|
|
|
1289
1304
|
/* Execute a hook if present */
|
|
1290
1305
|
_executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
|
|
1291
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
|
+
};
|
|
1292
1350
|
// eslint-disable-next-line complexity
|
|
1293
1351
|
DOMPurify.sanitize = function (dirty) {
|
|
1294
1352
|
let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
@@ -1333,6 +1391,9 @@ function createDOMPurify() {
|
|
|
1333
1391
|
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
1334
1392
|
}
|
|
1335
1393
|
}
|
|
1394
|
+
/* Sanitize attached shadow roots before the main iterator runs.
|
|
1395
|
+
The iterator does not descend into shadow trees. */
|
|
1396
|
+
_sanitizeAttachedShadowRoots2(dirty);
|
|
1336
1397
|
} else if (dirty instanceof Node) {
|
|
1337
1398
|
/* If dirty is a DOM element, append to an empty document to avoid
|
|
1338
1399
|
elements being stripped by the parser */
|
|
@@ -1347,6 +1408,10 @@ function createDOMPurify() {
|
|
|
1347
1408
|
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
1348
1409
|
body.appendChild(importedNode);
|
|
1349
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);
|
|
1350
1415
|
} else {
|
|
1351
1416
|
/* Exit directly if we have nothing to do */
|
|
1352
1417
|
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
@@ -1387,7 +1452,7 @@ function createDOMPurify() {
|
|
|
1387
1452
|
if (SAFE_FOR_TEMPLATES) {
|
|
1388
1453
|
body.normalize();
|
|
1389
1454
|
let html = body.innerHTML;
|
|
1390
|
-
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
1455
|
+
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
|
|
1391
1456
|
html = stringReplace(html, expr, ' ');
|
|
1392
1457
|
});
|
|
1393
1458
|
body.innerHTML = html;
|
|
@@ -1420,7 +1485,7 @@ function createDOMPurify() {
|
|
|
1420
1485
|
}
|
|
1421
1486
|
/* Sanitize final string template-safe */
|
|
1422
1487
|
if (SAFE_FOR_TEMPLATES) {
|
|
1423
|
-
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
1488
|
+
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
|
|
1424
1489
|
serializedHTML = stringReplace(serializedHTML, expr, ' ');
|
|
1425
1490
|
});
|
|
1426
1491
|
}
|