dompurify 3.4.0 → 3.4.2

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.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 */
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 */
2
2
 
3
3
  'use strict';
4
4
 
@@ -49,13 +49,19 @@ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
49
49
  const arrayPop = unapply(Array.prototype.pop);
50
50
  const arrayPush = unapply(Array.prototype.push);
51
51
  const arraySplice = unapply(Array.prototype.splice);
52
+ const arrayIsArray = Array.isArray;
52
53
  const stringToLowerCase = unapply(String.prototype.toLowerCase);
53
54
  const stringToString = unapply(String.prototype.toString);
54
55
  const stringMatch = unapply(String.prototype.match);
55
56
  const stringReplace = unapply(String.prototype.replace);
56
57
  const stringIndexOf = unapply(String.prototype.indexOf);
57
58
  const stringTrim = unapply(String.prototype.trim);
59
+ const numberToString = unapply(Number.prototype.toString);
60
+ const booleanToString = unapply(Boolean.prototype.toString);
61
+ const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
62
+ const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
58
63
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
64
+ const objectToString = unapply(Object.prototype.toString);
59
65
  const regExpTest = unapply(RegExp.prototype.test);
60
66
  const typeErrorCreate = unconstruct(TypeError);
61
67
  /**
@@ -105,6 +111,9 @@ function addToSet(set, array) {
105
111
  // Prevent prototype setters from intercepting set as a this value.
106
112
  setPrototypeOf(set, null);
107
113
  }
114
+ if (!arrayIsArray(array)) {
115
+ return set;
116
+ }
108
117
  let l = array.length;
109
118
  while (l--) {
110
119
  let element = array[l];
@@ -148,7 +157,7 @@ function clone(object) {
148
157
  for (const [property, value] of entries(object)) {
149
158
  const isPropertyExist = objectHasOwnProperty(object, property);
150
159
  if (isPropertyExist) {
151
- if (Array.isArray(value)) {
160
+ if (arrayIsArray(value)) {
152
161
  newObject[property] = cleanArray(value);
153
162
  } else if (value && typeof value === 'object' && value.constructor === Object) {
154
163
  newObject[property] = clone(value);
@@ -159,6 +168,58 @@ function clone(object) {
159
168
  }
160
169
  return newObject;
161
170
  }
171
+ /**
172
+ * Convert non-node values into strings without depending on direct property access.
173
+ *
174
+ * @param value - The value to stringify.
175
+ * @returns A string representation of the provided value.
176
+ */
177
+ function stringifyValue(value) {
178
+ switch (typeof value) {
179
+ case 'string':
180
+ {
181
+ return value;
182
+ }
183
+ case 'number':
184
+ {
185
+ return numberToString(value);
186
+ }
187
+ case 'boolean':
188
+ {
189
+ return booleanToString(value);
190
+ }
191
+ case 'bigint':
192
+ {
193
+ return bigintToString ? bigintToString(value) : '0';
194
+ }
195
+ case 'symbol':
196
+ {
197
+ return symbolToString ? symbolToString(value) : 'Symbol()';
198
+ }
199
+ case 'undefined':
200
+ {
201
+ return objectToString(value);
202
+ }
203
+ case 'function':
204
+ case 'object':
205
+ {
206
+ if (value === null) {
207
+ return objectToString(value);
208
+ }
209
+ const valueAsRecord = value;
210
+ const valueToString = lookupGetter(valueAsRecord, 'toString');
211
+ if (typeof valueToString === 'function') {
212
+ const stringified = valueToString(valueAsRecord);
213
+ return typeof stringified === 'string' ? stringified : objectToString(stringified);
214
+ }
215
+ return objectToString(value);
216
+ }
217
+ default:
218
+ {
219
+ return objectToString(value);
220
+ }
221
+ }
222
+ }
162
223
  /**
163
224
  * This method automatically checks if the prop is function or getter and behaves accordingly.
164
225
  *
@@ -184,6 +245,14 @@ function lookupGetter(object, prop) {
184
245
  }
185
246
  return fallbackValue;
186
247
  }
248
+ function isRegex(value) {
249
+ try {
250
+ regExpTest(value, '');
251
+ return true;
252
+ } catch (_unused) {
253
+ return false;
254
+ }
255
+ }
187
256
 
188
257
  const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
189
258
  const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
@@ -199,7 +268,7 @@ const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mgly
199
268
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
200
269
  const text = freeze(['#text']);
201
270
 
202
- 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']);
271
+ 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']);
203
272
  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']);
204
273
  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']);
205
274
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
@@ -219,17 +288,17 @@ const DOCTYPE_NAME = seal(/^html$/i);
219
288
  const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
220
289
 
221
290
  var EXPRESSIONS = /*#__PURE__*/Object.freeze({
222
- __proto__: null,
223
- ARIA_ATTR: ARIA_ATTR,
224
- ATTR_WHITESPACE: ATTR_WHITESPACE,
225
- CUSTOM_ELEMENT: CUSTOM_ELEMENT,
226
- DATA_ATTR: DATA_ATTR,
227
- DOCTYPE_NAME: DOCTYPE_NAME,
228
- ERB_EXPR: ERB_EXPR,
229
- IS_ALLOWED_URI: IS_ALLOWED_URI,
230
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
231
- MUSTACHE_EXPR: MUSTACHE_EXPR,
232
- TMPLIT_EXPR: TMPLIT_EXPR
291
+ __proto__: null,
292
+ ARIA_ATTR: ARIA_ATTR,
293
+ ATTR_WHITESPACE: ATTR_WHITESPACE,
294
+ CUSTOM_ELEMENT: CUSTOM_ELEMENT,
295
+ DATA_ATTR: DATA_ATTR,
296
+ DOCTYPE_NAME: DOCTYPE_NAME,
297
+ ERB_EXPR: ERB_EXPR,
298
+ IS_ALLOWED_URI: IS_ALLOWED_URI,
299
+ IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
300
+ MUSTACHE_EXPR: MUSTACHE_EXPR,
301
+ TMPLIT_EXPR: TMPLIT_EXPR
233
302
  });
234
303
 
235
304
  /* eslint-disable @typescript-eslint/indent */
@@ -298,7 +367,7 @@ const _createHooksMap = function _createHooksMap() {
298
367
  function createDOMPurify() {
299
368
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
300
369
  const DOMPurify = root => createDOMPurify(root);
301
- DOMPurify.version = '3.4.0';
370
+ DOMPurify.version = '3.4.2';
302
371
  DOMPurify.removed = [];
303
372
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
304
373
  // Not running in a browser, provide a factory function
@@ -546,15 +615,15 @@ function createDOMPurify() {
546
615
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
547
616
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
548
617
  /* Set configuration parameters */
549
- ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
550
- ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
551
- ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
552
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
553
- DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
554
- FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
555
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
556
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
557
- USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
618
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
619
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
620
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
621
+ URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR) ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
622
+ DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') && arrayIsArray(cfg.ADD_DATA_URI_TAGS) ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
623
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
624
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
625
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
626
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
558
627
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
559
628
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
560
629
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
@@ -570,19 +639,20 @@ function createDOMPurify() {
570
639
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
571
640
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
572
641
  IN_PLACE = cfg.IN_PLACE || false; // Default false
573
- IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
574
- NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
575
- MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
576
- HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
577
- CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || create(null);
578
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
579
- CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
642
+ IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
643
+ NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
644
+ MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); // Default built-in map
645
+ HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, ['annotation-xml']); // Default built-in map
646
+ const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
647
+ CUSTOM_ELEMENT_HANDLING = create(null);
648
+ if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
649
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
580
650
  }
581
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
582
- CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
651
+ if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
652
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
583
653
  }
584
- if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
585
- CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
654
+ if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
655
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
586
656
  }
587
657
  if (SAFE_FOR_TEMPLATES) {
588
658
  ALLOW_DATA_ATTR = false;
@@ -619,36 +689,36 @@ function createDOMPurify() {
619
689
  EXTRA_ELEMENT_HANDLING.tagCheck = null;
620
690
  EXTRA_ELEMENT_HANDLING.attributeCheck = null;
621
691
  /* Merge configuration parameters */
622
- if (cfg.ADD_TAGS) {
692
+ if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
623
693
  if (typeof cfg.ADD_TAGS === 'function') {
624
694
  EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
625
- } else {
695
+ } else if (arrayIsArray(cfg.ADD_TAGS)) {
626
696
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
627
697
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
628
698
  }
629
699
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
630
700
  }
631
701
  }
632
- if (cfg.ADD_ATTR) {
702
+ if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
633
703
  if (typeof cfg.ADD_ATTR === 'function') {
634
704
  EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
635
- } else {
705
+ } else if (arrayIsArray(cfg.ADD_ATTR)) {
636
706
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
637
707
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
638
708
  }
639
709
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
640
710
  }
641
711
  }
642
- if (cfg.ADD_URI_SAFE_ATTR) {
712
+ if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
643
713
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
644
714
  }
645
- if (cfg.FORBID_CONTENTS) {
715
+ if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
646
716
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
647
717
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
648
718
  }
649
719
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
650
720
  }
651
- if (cfg.ADD_FORBID_CONTENTS) {
721
+ if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
652
722
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
653
723
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
654
724
  }
@@ -974,7 +1044,6 @@ function createDOMPurify() {
974
1044
  const childCount = childNodes.length;
975
1045
  for (let i = childCount - 1; i >= 0; --i) {
976
1046
  const childClone = cloneNode(childNodes[i], true);
977
- childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
978
1047
  parentNode.insertBefore(childClone, getNextSibling(currentNode));
979
1048
  }
980
1049
  }
@@ -1028,11 +1097,12 @@ function createDOMPurify() {
1028
1097
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1029
1098
  return false;
1030
1099
  }
1100
+ const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
1031
1101
  /* Allow valid data-* attributes: At least one character after "-"
1032
1102
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1033
1103
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1034
1104
  We don't need to check the value; it's always URI safe. */
1035
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
1105
+ 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]) {
1036
1106
  if (
1037
1107
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1038
1108
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -1049,6 +1119,10 @@ function createDOMPurify() {
1049
1119
  } else ;
1050
1120
  return true;
1051
1121
  };
1122
+ /* Names the HTML spec reserves from valid-custom-element-name; these must
1123
+ * never be treated as basic custom elements even when a permissive
1124
+ * CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
1125
+ const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, ['annotation-xml', 'color-profile', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'missing-glyph']);
1052
1126
  /**
1053
1127
  * _isBasicCustomElement
1054
1128
  * checks if at least one dash is included in tagName, and it's not the first char
@@ -1058,7 +1132,7 @@ function createDOMPurify() {
1058
1132
  * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1059
1133
  */
1060
1134
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
1061
- return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
1135
+ return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT, tagName);
1062
1136
  };
1063
1137
  /**
1064
1138
  * _sanitizeAttributes
@@ -1109,12 +1183,14 @@ function createDOMPurify() {
1109
1183
  /* Full DOM Clobbering protection via namespace isolation,
1110
1184
  * Prefix id and name attributes with `user-content-`
1111
1185
  */
1112
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1186
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
1113
1187
  // Remove the attribute with this value
1114
1188
  _removeAttribute(name, currentNode);
1115
1189
  // Prefix the value and later re-create the attribute with the sanitized value
1116
1190
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
1117
1191
  }
1192
+ // Else: already prefixed, leave the attribute alone — the prefix is
1193
+ // itself the clobbering protection, and re-applying it is incorrect.
1118
1194
  /* Work around a security issue with comments inside attributes */
1119
1195
  if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
1120
1196
  _removeAttribute(name, currentNode);
@@ -1231,13 +1307,9 @@ function createDOMPurify() {
1231
1307
  }
1232
1308
  /* Stringify, in case dirty is an object */
1233
1309
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
1234
- if (typeof dirty.toString === 'function') {
1235
- dirty = dirty.toString();
1236
- if (typeof dirty !== 'string') {
1237
- throw typeErrorCreate('dirty is not a string, aborting');
1238
- }
1239
- } else {
1240
- throw typeErrorCreate('toString is not a function');
1310
+ dirty = stringifyValue(dirty);
1311
+ if (typeof dirty !== 'string') {
1312
+ throw typeErrorCreate('dirty is not a string, aborting');
1241
1313
  }
1242
1314
  }
1243
1315
  /* Return dirty HTML if DOMPurify cannot run */
@@ -1256,8 +1328,9 @@ function createDOMPurify() {
1256
1328
  }
1257
1329
  if (IN_PLACE) {
1258
1330
  /* Do some early pre-sanitization to avoid unsafe root nodes */
1259
- if (dirty.nodeName) {
1260
- const tagName = transformCaseFunc(dirty.nodeName);
1331
+ const nn = dirty.nodeName;
1332
+ if (typeof nn === 'string') {
1333
+ const tagName = transformCaseFunc(nn);
1261
1334
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1262
1335
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1263
1336
  }