dompurify 3.3.3 → 3.4.0

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/dist/purify.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @license DOMPurify 3.3.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.3.3/LICENSE */
1
+ /*! @license DOMPurify 3.4.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.0/LICENSE */
2
2
 
3
3
  (function (global, factory) {
4
4
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -205,7 +205,7 @@
205
205
 
206
206
  const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
207
207
  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']);
208
- 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']);
208
+ 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']);
209
209
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
210
210
 
211
211
  // eslint-disable-next-line unicorn/better-regex
@@ -240,20 +240,11 @@
240
240
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
241
241
  const NODE_TYPE = {
242
242
  element: 1,
243
- attribute: 2,
244
243
  text: 3,
245
- cdataSection: 4,
246
- entityReference: 5,
247
- // Deprecated
248
- entityNode: 6,
249
244
  // Deprecated
250
245
  progressingInstruction: 7,
251
246
  comment: 8,
252
- document: 9,
253
- documentType: 10,
254
- documentFragment: 11,
255
- notation: 12 // Deprecated
256
- };
247
+ document: 9};
257
248
  const getGlobal = function getGlobal() {
258
249
  return typeof window === 'undefined' ? null : window;
259
250
  };
@@ -311,7 +302,7 @@
311
302
  function createDOMPurify() {
312
303
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
313
304
  const DOMPurify = root => createDOMPurify(root);
314
- DOMPurify.version = '3.3.3';
305
+ DOMPurify.version = '3.4.0';
315
306
  DOMPurify.removed = [];
316
307
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
317
308
  // Not running in a browser, provide a factory function
@@ -587,7 +578,7 @@
587
578
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
588
579
  MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
589
580
  HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
590
- CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
581
+ CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || create(null);
591
582
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
592
583
  CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
593
584
  }
@@ -627,13 +618,10 @@
627
618
  addToSet(ALLOWED_ATTR, xml);
628
619
  }
629
620
  }
630
- /* Prevent function-based ADD_ATTR / ADD_TAGS from leaking across calls */
631
- if (!objectHasOwnProperty(cfg, 'ADD_TAGS')) {
632
- EXTRA_ELEMENT_HANDLING.tagCheck = null;
633
- }
634
- if (!objectHasOwnProperty(cfg, 'ADD_ATTR')) {
635
- EXTRA_ELEMENT_HANDLING.attributeCheck = null;
636
- }
621
+ /* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
622
+ * leaking across calls when switching from function to array config */
623
+ EXTRA_ELEMENT_HANDLING.tagCheck = null;
624
+ EXTRA_ELEMENT_HANDLING.attributeCheck = null;
637
625
  /* Merge configuration parameters */
638
626
  if (cfg.ADD_TAGS) {
639
627
  if (typeof cfg.ADD_TAGS === 'function') {
@@ -956,6 +944,11 @@
956
944
  _forceRemove(currentNode);
957
945
  return true;
958
946
  }
947
+ /* Remove risky CSS construction leading to mXSS */
948
+ if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
949
+ _forceRemove(currentNode);
950
+ return true;
951
+ }
959
952
  /* Remove any occurrence of processing instructions */
960
953
  if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
961
954
  _forceRemove(currentNode);
@@ -967,7 +960,7 @@
967
960
  return true;
968
961
  }
969
962
  /* Remove element if anything forbids its presence */
970
- if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {
963
+ if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
971
964
  /* Check if we have a custom element to handle */
972
965
  if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
973
966
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
@@ -1206,7 +1199,7 @@
1206
1199
  *
1207
1200
  * @param fragment to iterate over recursively
1208
1201
  */
1209
- const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1202
+ const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
1210
1203
  let shadowNode = null;
1211
1204
  const shadowIterator = _createNodeIterator(fragment);
1212
1205
  /* Execute a hook if present */
@@ -1220,7 +1213,7 @@
1220
1213
  _sanitizeAttributes(shadowNode);
1221
1214
  /* Deep shadow DOM detected */
1222
1215
  if (shadowNode.content instanceof DocumentFragment) {
1223
- _sanitizeShadowDOM(shadowNode.content);
1216
+ _sanitizeShadowDOM2(shadowNode.content);
1224
1217
  }
1225
1218
  }
1226
1219
  /* Execute a hook if present */
@@ -1315,7 +1308,7 @@
1315
1308
  _sanitizeAttributes(currentNode);
1316
1309
  /* Shadow DOM detected, sanitize it */
1317
1310
  if (currentNode.content instanceof DocumentFragment) {
1318
- _sanitizeShadowDOM(currentNode.content);
1311
+ _sanitizeShadowDOM2(currentNode.content);
1319
1312
  }
1320
1313
  }
1321
1314
  /* If we sanitized `dirty` in-place, return it. */
@@ -1324,6 +1317,14 @@
1324
1317
  }
1325
1318
  /* Return sanitized string or DOM */
1326
1319
  if (RETURN_DOM) {
1320
+ if (SAFE_FOR_TEMPLATES) {
1321
+ body.normalize();
1322
+ let html = body.innerHTML;
1323
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1324
+ html = stringReplace(html, expr, ' ');
1325
+ });
1326
+ body.innerHTML = html;
1327
+ }
1327
1328
  if (RETURN_DOM_FRAGMENT) {
1328
1329
  returnNode = createDocumentFragment.call(body.ownerDocument);
1329
1330
  while (body.firstChild) {