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.
@@ -1,21 +1,62 @@
1
- /*! @license DOMPurify 3.4.2 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.2/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;
@@ -152,7 +193,10 @@ function cleanArray(array) {
152
193
  */
153
194
  function clone(object) {
154
195
  const newObject = create(null);
155
- 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];
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
- // eslint-disable-next-line unicorn/better-regex
275
- const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
276
- const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
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.2';
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
- DocumentFragment,
383
- HTMLTemplateElement,
384
- Node,
385
- Element,
386
- NodeFilter,
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
- } = document;
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
- MUSTACHE_EXPR,
428
- ERB_EXPR,
429
- TMPLIT_EXPR,
430
- DATA_ATTR,
431
- ARIA_ATTR,
432
- IS_SCRIPT_OR_DATA,
433
- ATTR_WHITESPACE,
434
- CUSTOM_ELEMENT
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
- name,
1168
- namespaceURI,
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
  }