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.
@@ -1,6 +1,6 @@
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
- import { TrustedTypePolicy, TrustedHTML, TrustedTypesWindow } from 'trusted-types/lib/index.js';
3
+ import { TrustedTypePolicy, TrustedTypesWindow, TrustedHTML } from 'trusted-types/lib/index.js';
4
4
 
5
5
  /**
6
6
  * Configuration to control DOMPurify behavior.
@@ -444,4 +444,5 @@ type WindowLike = Pick<typeof globalThis, 'DocumentFragment' | 'HTMLTemplateElem
444
444
  MozNamedAttrMap?: typeof window.NamedNodeMap;
445
445
  } & Pick<TrustedTypesWindow, 'trustedTypes'>;
446
446
 
447
- export { type Config, type DOMPurify, type DocumentFragmentHook, type ElementHook, type HookName, type NodeHook, type RemovedAttribute, type RemovedElement, type UponSanitizeAttributeHook, type UponSanitizeAttributeHookEvent, type UponSanitizeElementHook, type UponSanitizeElementHookEvent, type WindowLike, _default as default };
447
+ export { _default as default };
448
+ export type { Config, DOMPurify, DocumentFragmentHook, ElementHook, HookName, NodeHook, RemovedAttribute, RemovedElement, UponSanitizeAttributeHook, UponSanitizeAttributeHookEvent, UponSanitizeElementHook, UponSanitizeElementHookEvent, WindowLike };
@@ -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
  const {
4
4
  entries,
@@ -199,7 +199,7 @@ const text = freeze(['#text']);
199
199
 
200
200
  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']);
201
201
  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']);
202
- 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
+ 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']);
203
203
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
204
204
 
205
205
  // eslint-disable-next-line unicorn/better-regex
@@ -234,20 +234,11 @@ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
234
234
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
235
235
  const NODE_TYPE = {
236
236
  element: 1,
237
- attribute: 2,
238
237
  text: 3,
239
- cdataSection: 4,
240
- entityReference: 5,
241
- // Deprecated
242
- entityNode: 6,
243
238
  // Deprecated
244
239
  progressingInstruction: 7,
245
240
  comment: 8,
246
- document: 9,
247
- documentType: 10,
248
- documentFragment: 11,
249
- notation: 12 // Deprecated
250
- };
241
+ document: 9};
251
242
  const getGlobal = function getGlobal() {
252
243
  return typeof window === 'undefined' ? null : window;
253
244
  };
@@ -305,7 +296,7 @@ const _createHooksMap = function _createHooksMap() {
305
296
  function createDOMPurify() {
306
297
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
307
298
  const DOMPurify = root => createDOMPurify(root);
308
- DOMPurify.version = '3.3.3';
299
+ DOMPurify.version = '3.4.0';
309
300
  DOMPurify.removed = [];
310
301
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
311
302
  // Not running in a browser, provide a factory function
@@ -581,7 +572,7 @@ function createDOMPurify() {
581
572
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
582
573
  MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
583
574
  HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
584
- CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
575
+ CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || create(null);
585
576
  if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
586
577
  CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
587
578
  }
@@ -621,13 +612,10 @@ function createDOMPurify() {
621
612
  addToSet(ALLOWED_ATTR, xml);
622
613
  }
623
614
  }
624
- /* Prevent function-based ADD_ATTR / ADD_TAGS from leaking across calls */
625
- if (!objectHasOwnProperty(cfg, 'ADD_TAGS')) {
626
- EXTRA_ELEMENT_HANDLING.tagCheck = null;
627
- }
628
- if (!objectHasOwnProperty(cfg, 'ADD_ATTR')) {
629
- EXTRA_ELEMENT_HANDLING.attributeCheck = null;
630
- }
615
+ /* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
616
+ * leaking across calls when switching from function to array config */
617
+ EXTRA_ELEMENT_HANDLING.tagCheck = null;
618
+ EXTRA_ELEMENT_HANDLING.attributeCheck = null;
631
619
  /* Merge configuration parameters */
632
620
  if (cfg.ADD_TAGS) {
633
621
  if (typeof cfg.ADD_TAGS === 'function') {
@@ -950,6 +938,11 @@ function createDOMPurify() {
950
938
  _forceRemove(currentNode);
951
939
  return true;
952
940
  }
941
+ /* Remove risky CSS construction leading to mXSS */
942
+ if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
943
+ _forceRemove(currentNode);
944
+ return true;
945
+ }
953
946
  /* Remove any occurrence of processing instructions */
954
947
  if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
955
948
  _forceRemove(currentNode);
@@ -961,7 +954,7 @@ function createDOMPurify() {
961
954
  return true;
962
955
  }
963
956
  /* Remove element if anything forbids its presence */
964
- if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName])) {
957
+ if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
965
958
  /* Check if we have a custom element to handle */
966
959
  if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
967
960
  if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
@@ -1200,7 +1193,7 @@ function createDOMPurify() {
1200
1193
  *
1201
1194
  * @param fragment to iterate over recursively
1202
1195
  */
1203
- const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1196
+ const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
1204
1197
  let shadowNode = null;
1205
1198
  const shadowIterator = _createNodeIterator(fragment);
1206
1199
  /* Execute a hook if present */
@@ -1214,7 +1207,7 @@ function createDOMPurify() {
1214
1207
  _sanitizeAttributes(shadowNode);
1215
1208
  /* Deep shadow DOM detected */
1216
1209
  if (shadowNode.content instanceof DocumentFragment) {
1217
- _sanitizeShadowDOM(shadowNode.content);
1210
+ _sanitizeShadowDOM2(shadowNode.content);
1218
1211
  }
1219
1212
  }
1220
1213
  /* Execute a hook if present */
@@ -1309,7 +1302,7 @@ function createDOMPurify() {
1309
1302
  _sanitizeAttributes(currentNode);
1310
1303
  /* Shadow DOM detected, sanitize it */
1311
1304
  if (currentNode.content instanceof DocumentFragment) {
1312
- _sanitizeShadowDOM(currentNode.content);
1305
+ _sanitizeShadowDOM2(currentNode.content);
1313
1306
  }
1314
1307
  }
1315
1308
  /* If we sanitized `dirty` in-place, return it. */
@@ -1318,6 +1311,14 @@ function createDOMPurify() {
1318
1311
  }
1319
1312
  /* Return sanitized string or DOM */
1320
1313
  if (RETURN_DOM) {
1314
+ if (SAFE_FOR_TEMPLATES) {
1315
+ body.normalize();
1316
+ let html = body.innerHTML;
1317
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1318
+ html = stringReplace(html, expr, ' ');
1319
+ });
1320
+ body.innerHTML = html;
1321
+ }
1321
1322
  if (RETURN_DOM_FRAGMENT) {
1322
1323
  returnNode = createDocumentFragment.call(body.ownerDocument);
1323
1324
  while (body.firstChild) {