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.
- package/LICENSE +2 -2
- package/README.md +160 -119
- package/dist/purify.cjs.d.ts +2 -2
- package/dist/purify.cjs.js +128 -55
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.d.mts +2 -2
- package/dist/purify.es.mjs +128 -55
- package/dist/purify.es.mjs.map +1 -1
- package/dist/purify.js +1391 -1318
- package/dist/purify.js.map +1 -1
- package/dist/purify.min.js +2 -2
- package/dist/purify.min.js.map +1 -1
- package/package.json +21 -28
- package/src/attrs.ts +376 -0
- package/src/config.ts +259 -0
- package/src/license_header +1 -0
- package/src/purify.ts +2184 -0
- package/src/regexp.ts +17 -0
- package/src/tags.ts +285 -0
- package/src/utils.ts +338 -0
package/dist/purify.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @license DOMPurify 3.4.
|
|
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 (
|
|
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'
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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.
|
|
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
|
|
574
|
-
NAMESPACE = cfg.NAMESPACE
|
|
575
|
-
MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS
|
|
576
|
-
HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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 (
|
|
582
|
-
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 (
|
|
585
|
-
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
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
|
-
|
|
1260
|
-
|
|
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
|
}
|