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
  const {
4
4
  entries,
@@ -47,13 +47,19 @@ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
47
47
  const arrayPop = unapply(Array.prototype.pop);
48
48
  const arrayPush = unapply(Array.prototype.push);
49
49
  const arraySplice = unapply(Array.prototype.splice);
50
+ const arrayIsArray = Array.isArray;
50
51
  const stringToLowerCase = unapply(String.prototype.toLowerCase);
51
52
  const stringToString = unapply(String.prototype.toString);
52
53
  const stringMatch = unapply(String.prototype.match);
53
54
  const stringReplace = unapply(String.prototype.replace);
54
55
  const stringIndexOf = unapply(String.prototype.indexOf);
55
56
  const stringTrim = unapply(String.prototype.trim);
57
+ const numberToString = unapply(Number.prototype.toString);
58
+ const booleanToString = unapply(Boolean.prototype.toString);
59
+ const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
60
+ const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
56
61
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
62
+ const objectToString = unapply(Object.prototype.toString);
57
63
  const regExpTest = unapply(RegExp.prototype.test);
58
64
  const typeErrorCreate = unconstruct(TypeError);
59
65
  /**
@@ -103,6 +109,9 @@ function addToSet(set, array) {
103
109
  // Prevent prototype setters from intercepting set as a this value.
104
110
  setPrototypeOf(set, null);
105
111
  }
112
+ if (!arrayIsArray(array)) {
113
+ return set;
114
+ }
106
115
  let l = array.length;
107
116
  while (l--) {
108
117
  let element = array[l];
@@ -146,7 +155,7 @@ function clone(object) {
146
155
  for (const [property, value] of entries(object)) {
147
156
  const isPropertyExist = objectHasOwnProperty(object, property);
148
157
  if (isPropertyExist) {
149
- if (Array.isArray(value)) {
158
+ if (arrayIsArray(value)) {
150
159
  newObject[property] = cleanArray(value);
151
160
  } else if (value && typeof value === 'object' && value.constructor === Object) {
152
161
  newObject[property] = clone(value);
@@ -157,6 +166,58 @@ function clone(object) {
157
166
  }
158
167
  return newObject;
159
168
  }
169
+ /**
170
+ * Convert non-node values into strings without depending on direct property access.
171
+ *
172
+ * @param value - The value to stringify.
173
+ * @returns A string representation of the provided value.
174
+ */
175
+ function stringifyValue(value) {
176
+ switch (typeof value) {
177
+ case 'string':
178
+ {
179
+ return value;
180
+ }
181
+ case 'number':
182
+ {
183
+ return numberToString(value);
184
+ }
185
+ case 'boolean':
186
+ {
187
+ return booleanToString(value);
188
+ }
189
+ case 'bigint':
190
+ {
191
+ return bigintToString ? bigintToString(value) : '0';
192
+ }
193
+ case 'symbol':
194
+ {
195
+ return symbolToString ? symbolToString(value) : 'Symbol()';
196
+ }
197
+ case 'undefined':
198
+ {
199
+ return objectToString(value);
200
+ }
201
+ case 'function':
202
+ case 'object':
203
+ {
204
+ if (value === null) {
205
+ return objectToString(value);
206
+ }
207
+ const valueAsRecord = value;
208
+ const valueToString = lookupGetter(valueAsRecord, 'toString');
209
+ if (typeof valueToString === 'function') {
210
+ const stringified = valueToString(valueAsRecord);
211
+ return typeof stringified === 'string' ? stringified : objectToString(stringified);
212
+ }
213
+ return objectToString(value);
214
+ }
215
+ default:
216
+ {
217
+ return objectToString(value);
218
+ }
219
+ }
220
+ }
160
221
  /**
161
222
  * This method automatically checks if the prop is function or getter and behaves accordingly.
162
223
  *
@@ -182,6 +243,14 @@ function lookupGetter(object, prop) {
182
243
  }
183
244
  return fallbackValue;
184
245
  }
246
+ function isRegex(value) {
247
+ try {
248
+ regExpTest(value, '');
249
+ return true;
250
+ } catch (_unused) {
251
+ return false;
252
+ }
253
+ }
185
254
 
186
255
  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']);
187
256
  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']);
@@ -197,7 +266,7 @@ const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mgly
197
266
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
198
267
  const text = freeze(['#text']);
199
268
 
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']);
269
+ 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']);
201
270
  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
271
  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
272
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
@@ -217,17 +286,17 @@ const DOCTYPE_NAME = seal(/^html$/i);
217
286
  const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
218
287
 
219
288
  var EXPRESSIONS = /*#__PURE__*/Object.freeze({
220
- __proto__: null,
221
- ARIA_ATTR: ARIA_ATTR,
222
- ATTR_WHITESPACE: ATTR_WHITESPACE,
223
- CUSTOM_ELEMENT: CUSTOM_ELEMENT,
224
- DATA_ATTR: DATA_ATTR,
225
- DOCTYPE_NAME: DOCTYPE_NAME,
226
- ERB_EXPR: ERB_EXPR,
227
- IS_ALLOWED_URI: IS_ALLOWED_URI,
228
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
229
- MUSTACHE_EXPR: MUSTACHE_EXPR,
230
- TMPLIT_EXPR: TMPLIT_EXPR
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
231
300
  });
232
301
 
233
302
  /* eslint-disable @typescript-eslint/indent */
@@ -296,7 +365,7 @@ const _createHooksMap = function _createHooksMap() {
296
365
  function createDOMPurify() {
297
366
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
298
367
  const DOMPurify = root => createDOMPurify(root);
299
- DOMPurify.version = '3.4.0';
368
+ DOMPurify.version = '3.4.2';
300
369
  DOMPurify.removed = [];
301
370
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
302
371
  // Not running in a browser, provide a factory function
@@ -544,15 +613,15 @@ function createDOMPurify() {
544
613
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
545
614
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
546
615
  /* Set configuration parameters */
547
- ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
548
- ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
549
- ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
550
- 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;
551
- 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;
552
- FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
553
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
554
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
555
- USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
616
+ ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
617
+ ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
618
+ ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
619
+ 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;
620
+ 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;
621
+ FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
622
+ FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
623
+ FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
624
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
556
625
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
557
626
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
558
627
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
@@ -568,19 +637,20 @@ function createDOMPurify() {
568
637
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
569
638
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
570
639
  IN_PLACE = cfg.IN_PLACE || false; // Default false
571
- IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
572
- NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
573
- MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
574
- HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
575
- CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || create(null);
576
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
577
- CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
640
+ IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
641
+ NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
642
+ 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
643
+ 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
644
+ const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
645
+ CUSTOM_ELEMENT_HANDLING = create(null);
646
+ if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
647
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
578
648
  }
579
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
580
- CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
649
+ if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
650
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
581
651
  }
582
- if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
583
- CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
652
+ if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
653
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
584
654
  }
585
655
  if (SAFE_FOR_TEMPLATES) {
586
656
  ALLOW_DATA_ATTR = false;
@@ -617,36 +687,36 @@ function createDOMPurify() {
617
687
  EXTRA_ELEMENT_HANDLING.tagCheck = null;
618
688
  EXTRA_ELEMENT_HANDLING.attributeCheck = null;
619
689
  /* Merge configuration parameters */
620
- if (cfg.ADD_TAGS) {
690
+ if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
621
691
  if (typeof cfg.ADD_TAGS === 'function') {
622
692
  EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
623
- } else {
693
+ } else if (arrayIsArray(cfg.ADD_TAGS)) {
624
694
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
625
695
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
626
696
  }
627
697
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
628
698
  }
629
699
  }
630
- if (cfg.ADD_ATTR) {
700
+ if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
631
701
  if (typeof cfg.ADD_ATTR === 'function') {
632
702
  EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
633
- } else {
703
+ } else if (arrayIsArray(cfg.ADD_ATTR)) {
634
704
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
635
705
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
636
706
  }
637
707
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
638
708
  }
639
709
  }
640
- if (cfg.ADD_URI_SAFE_ATTR) {
710
+ if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
641
711
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
642
712
  }
643
- if (cfg.FORBID_CONTENTS) {
713
+ if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
644
714
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
645
715
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
646
716
  }
647
717
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
648
718
  }
649
- if (cfg.ADD_FORBID_CONTENTS) {
719
+ if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
650
720
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
651
721
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
652
722
  }
@@ -972,7 +1042,6 @@ function createDOMPurify() {
972
1042
  const childCount = childNodes.length;
973
1043
  for (let i = childCount - 1; i >= 0; --i) {
974
1044
  const childClone = cloneNode(childNodes[i], true);
975
- childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
976
1045
  parentNode.insertBefore(childClone, getNextSibling(currentNode));
977
1046
  }
978
1047
  }
@@ -1026,11 +1095,12 @@ function createDOMPurify() {
1026
1095
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1027
1096
  return false;
1028
1097
  }
1098
+ const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
1029
1099
  /* Allow valid data-* attributes: At least one character after "-"
1030
1100
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1031
1101
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1032
1102
  We don't need to check the value; it's always URI safe. */
1033
- 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]) {
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]) {
1034
1104
  if (
1035
1105
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1036
1106
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -1047,6 +1117,10 @@ function createDOMPurify() {
1047
1117
  } else ;
1048
1118
  return true;
1049
1119
  };
1120
+ /* Names the HTML spec reserves from valid-custom-element-name; these must
1121
+ * never be treated as basic custom elements even when a permissive
1122
+ * CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
1123
+ 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']);
1050
1124
  /**
1051
1125
  * _isBasicCustomElement
1052
1126
  * checks if at least one dash is included in tagName, and it's not the first char
@@ -1056,7 +1130,7 @@ function createDOMPurify() {
1056
1130
  * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1057
1131
  */
1058
1132
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
1059
- return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
1133
+ return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT, tagName);
1060
1134
  };
1061
1135
  /**
1062
1136
  * _sanitizeAttributes
@@ -1107,12 +1181,14 @@ function createDOMPurify() {
1107
1181
  /* Full DOM Clobbering protection via namespace isolation,
1108
1182
  * Prefix id and name attributes with `user-content-`
1109
1183
  */
1110
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
1184
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
1111
1185
  // Remove the attribute with this value
1112
1186
  _removeAttribute(name, currentNode);
1113
1187
  // Prefix the value and later re-create the attribute with the sanitized value
1114
1188
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
1115
1189
  }
1190
+ // Else: already prefixed, leave the attribute alone — the prefix is
1191
+ // itself the clobbering protection, and re-applying it is incorrect.
1116
1192
  /* Work around a security issue with comments inside attributes */
1117
1193
  if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
1118
1194
  _removeAttribute(name, currentNode);
@@ -1229,13 +1305,9 @@ function createDOMPurify() {
1229
1305
  }
1230
1306
  /* Stringify, in case dirty is an object */
1231
1307
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
1232
- if (typeof dirty.toString === 'function') {
1233
- dirty = dirty.toString();
1234
- if (typeof dirty !== 'string') {
1235
- throw typeErrorCreate('dirty is not a string, aborting');
1236
- }
1237
- } else {
1238
- throw typeErrorCreate('toString is not a function');
1308
+ dirty = stringifyValue(dirty);
1309
+ if (typeof dirty !== 'string') {
1310
+ throw typeErrorCreate('dirty is not a string, aborting');
1239
1311
  }
1240
1312
  }
1241
1313
  /* Return dirty HTML if DOMPurify cannot run */
@@ -1254,8 +1326,9 @@ function createDOMPurify() {
1254
1326
  }
1255
1327
  if (IN_PLACE) {
1256
1328
  /* Do some early pre-sanitization to avoid unsafe root nodes */
1257
- if (dirty.nodeName) {
1258
- const tagName = transformCaseFunc(dirty.nodeName);
1329
+ const nn = dirty.nodeName;
1330
+ if (typeof nn === 'string') {
1331
+ const tagName = transformCaseFunc(nn);
1259
1332
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1260
1333
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1261
1334
  }