dompurify 3.1.2 → 3.1.4

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,4 +1,4 @@
1
- /*! @license DOMPurify 3.1.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.1.2/LICENSE */
1
+ /*! @license DOMPurify 3.1.4 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.1.4/LICENSE */
2
2
 
3
3
  const {
4
4
  entries,
@@ -48,6 +48,10 @@ const stringTrim = unapply(String.prototype.trim);
48
48
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
49
49
  const regExpTest = unapply(RegExp.prototype.test);
50
50
  const typeErrorCreate = unconstruct(TypeError);
51
+ function numberIsNaN(x) {
52
+ // eslint-disable-next-line unicorn/prefer-number-properties
53
+ return typeof x === 'number' && isNaN(x);
54
+ }
51
55
 
52
56
  /**
53
57
  * Creates a new function that calls the given function with a specified thisArg and arguments.
@@ -196,7 +200,7 @@ const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mgly
196
200
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
197
201
  const text = freeze(['#text']);
198
202
 
199
- 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', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', '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', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
203
+ 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', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', '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', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
200
204
  const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', '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', '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', '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', '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', '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', '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']);
201
205
  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']);
202
206
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
@@ -231,6 +235,24 @@ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
231
235
  CUSTOM_ELEMENT: CUSTOM_ELEMENT
232
236
  });
233
237
 
238
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
239
+ const NODE_TYPE = {
240
+ element: 1,
241
+ attribute: 2,
242
+ text: 3,
243
+ cdataSection: 4,
244
+ entityReference: 5,
245
+ // Deprecated
246
+ entityNode: 6,
247
+ // Deprecated
248
+ progressingInstruction: 7,
249
+ comment: 8,
250
+ document: 9,
251
+ documentType: 10,
252
+ documentFragment: 11,
253
+ notation: 12 // Deprecated
254
+ };
255
+
234
256
  const getGlobal = function getGlobal() {
235
257
  return typeof window === 'undefined' ? null : window;
236
258
  };
@@ -282,14 +304,14 @@ function createDOMPurify() {
282
304
  * Version label, exposed for easier checks
283
305
  * if DOMPurify is up to date or not
284
306
  */
285
- DOMPurify.version = '3.1.2';
307
+ DOMPurify.version = '3.1.4';
286
308
 
287
309
  /**
288
310
  * Array of elements that DOMPurify removed during sanitation.
289
311
  * Empty if nothing was removed.
290
312
  */
291
313
  DOMPurify.removed = [];
292
- if (!window || !window.document || window.document.nodeType !== 9) {
314
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
293
315
  // Not running in a browser, provide a factory function
294
316
  // so that you can pass your own Window
295
317
  DOMPurify.isSupported = false;
@@ -1000,13 +1022,13 @@ function createDOMPurify() {
1000
1022
  }
1001
1023
 
1002
1024
  /* Remove any ocurrence of processing instructions */
1003
- if (currentNode.nodeType === 7) {
1025
+ if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
1004
1026
  _forceRemove(currentNode);
1005
1027
  return true;
1006
1028
  }
1007
1029
 
1008
1030
  /* Remove any kind of possibly harmful comments */
1009
- if (SAFE_FOR_XML && currentNode.nodeType === 8 && regExpTest(/<[/\w]/g, currentNode.data)) {
1031
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
1010
1032
  _forceRemove(currentNode);
1011
1033
  return true;
1012
1034
  }
@@ -1053,7 +1075,7 @@ function createDOMPurify() {
1053
1075
  }
1054
1076
 
1055
1077
  /* Sanitize element content to be template-safe */
1056
- if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
1078
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
1057
1079
  /* Get the element's text content */
1058
1080
  content = currentNode.textContent;
1059
1081
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
@@ -1083,7 +1105,7 @@ function createDOMPurify() {
1083
1105
  // eslint-disable-next-line complexity
1084
1106
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1085
1107
  /* Make sure attribute cannot clobber */
1086
- if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1108
+ if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement || value === '__depth' || value === '__removalCount')) {
1087
1109
  return false;
1088
1110
  }
1089
1111
 
@@ -1187,6 +1209,12 @@ function createDOMPurify() {
1187
1209
  continue;
1188
1210
  }
1189
1211
 
1212
+ /* Work around a security issue with comments inside attributes */
1213
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
1214
+ _removeAttribute(name, currentNode);
1215
+ continue;
1216
+ }
1217
+
1190
1218
  /* Sanitize attribute content to be template-safe */
1191
1219
  if (SAFE_FOR_TEMPLATES) {
1192
1220
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
@@ -1237,7 +1265,11 @@ function createDOMPurify() {
1237
1265
  /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1238
1266
  currentNode.setAttribute(name, value);
1239
1267
  }
1240
- arrayPop(DOMPurify.removed);
1268
+ if (_isClobbered(currentNode)) {
1269
+ _forceRemove(currentNode);
1270
+ } else {
1271
+ arrayPop(DOMPurify.removed);
1272
+ }
1241
1273
  } catch (_) {}
1242
1274
  }
1243
1275
 
@@ -1267,7 +1299,7 @@ function createDOMPurify() {
1267
1299
  const parentNode = getParentNode(shadowNode);
1268
1300
 
1269
1301
  /* Set the nesting depth of an element */
1270
- if (shadowNode.nodeType === 1) {
1302
+ if (shadowNode.nodeType === NODE_TYPE.element) {
1271
1303
  if (parentNode && parentNode.__depth) {
1272
1304
  /*
1273
1305
  We want the depth of the node in the original tree, which can
@@ -1279,8 +1311,11 @@ function createDOMPurify() {
1279
1311
  }
1280
1312
  }
1281
1313
 
1282
- /* Remove an element if nested too deeply to avoid mXSS */
1283
- if (shadowNode.__depth >= MAX_NESTING_DEPTH) {
1314
+ /*
1315
+ * Remove an element if nested too deeply to avoid mXSS
1316
+ * or if the __depth might have been tampered with
1317
+ */
1318
+ if (shadowNode.__depth >= MAX_NESTING_DEPTH || shadowNode.__depth < 0 || numberIsNaN(shadowNode.__depth)) {
1284
1319
  _forceRemove(shadowNode);
1285
1320
  }
1286
1321
 
@@ -1362,7 +1397,7 @@ function createDOMPurify() {
1362
1397
  elements being stripped by the parser */
1363
1398
  body = _initDocument('<!---->');
1364
1399
  importedNode = body.ownerDocument.importNode(dirty, true);
1365
- if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1400
+ if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
1366
1401
  /* Node is already a body, use as is */
1367
1402
  body = importedNode;
1368
1403
  } else if (importedNode.nodeName === 'HTML') {
@@ -1405,7 +1440,7 @@ function createDOMPurify() {
1405
1440
  const parentNode = getParentNode(currentNode);
1406
1441
 
1407
1442
  /* Set the nesting depth of an element */
1408
- if (currentNode.nodeType === 1) {
1443
+ if (currentNode.nodeType === NODE_TYPE.element) {
1409
1444
  if (parentNode && parentNode.__depth) {
1410
1445
  /*
1411
1446
  We want the depth of the node in the original tree, which can
@@ -1417,8 +1452,11 @@ function createDOMPurify() {
1417
1452
  }
1418
1453
  }
1419
1454
 
1420
- /* Remove an element if nested too deeply to avoid mXSS */
1421
- if (currentNode.__depth >= MAX_NESTING_DEPTH) {
1455
+ /*
1456
+ * Remove an element if nested too deeply to avoid mXSS
1457
+ * or if the __depth might have been tampered with
1458
+ */
1459
+ if (currentNode.__depth >= MAX_NESTING_DEPTH || currentNode.__depth < 0 || numberIsNaN(currentNode.__depth)) {
1422
1460
  _forceRemove(currentNode);
1423
1461
  }
1424
1462