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.es.mjs
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
|
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 (
|
|
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'
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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.
|
|
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
|
|
572
|
-
NAMESPACE = cfg.NAMESPACE
|
|
573
|
-
MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS
|
|
574
|
-
HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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 (
|
|
580
|
-
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 (
|
|
583
|
-
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
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
|
-
|
|
1258
|
-
|
|
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
|
}
|