dompurify 3.3.3 → 3.4.1
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 +163 -119
- package/dist/purify.cjs.d.ts +5 -4
- package/dist/purify.cjs.js +149 -76
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.d.mts +5 -4
- package/dist/purify.es.mjs +149 -76
- package/dist/purify.es.mjs.map +1 -1
- package/dist/purify.js +1392 -1319
- 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 +25 -33
package/dist/purify.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @license DOMPurify 3.
|
|
1
|
+
/*! @license DOMPurify 3.4.1 | (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.1/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,9 +268,9 @@ 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
|
-
const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', '
|
|
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']);
|
|
206
275
|
|
|
207
276
|
// eslint-disable-next-line unicorn/better-regex
|
|
@@ -219,37 +288,28 @@ 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 */
|
|
236
305
|
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
|
|
237
306
|
const NODE_TYPE = {
|
|
238
307
|
element: 1,
|
|
239
|
-
attribute: 2,
|
|
240
308
|
text: 3,
|
|
241
|
-
cdataSection: 4,
|
|
242
|
-
entityReference: 5,
|
|
243
|
-
// Deprecated
|
|
244
|
-
entityNode: 6,
|
|
245
309
|
// Deprecated
|
|
246
310
|
progressingInstruction: 7,
|
|
247
311
|
comment: 8,
|
|
248
|
-
document: 9
|
|
249
|
-
documentType: 10,
|
|
250
|
-
documentFragment: 11,
|
|
251
|
-
notation: 12 // Deprecated
|
|
252
|
-
};
|
|
312
|
+
document: 9};
|
|
253
313
|
const getGlobal = function getGlobal() {
|
|
254
314
|
return typeof window === 'undefined' ? null : window;
|
|
255
315
|
};
|
|
@@ -307,7 +367,7 @@ const _createHooksMap = function _createHooksMap() {
|
|
|
307
367
|
function createDOMPurify() {
|
|
308
368
|
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
309
369
|
const DOMPurify = root => createDOMPurify(root);
|
|
310
|
-
DOMPurify.version = '3.
|
|
370
|
+
DOMPurify.version = '3.4.1';
|
|
311
371
|
DOMPurify.removed = [];
|
|
312
372
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
313
373
|
// Not running in a browser, provide a factory function
|
|
@@ -555,15 +615,15 @@ function createDOMPurify() {
|
|
|
555
615
|
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
556
616
|
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
|
|
557
617
|
/* Set configuration parameters */
|
|
558
|
-
ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
559
|
-
ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
560
|
-
ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
|
|
561
|
-
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;
|
|
562
|
-
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;
|
|
563
|
-
FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
|
|
564
|
-
FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
|
|
565
|
-
FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
|
|
566
|
-
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;
|
|
567
627
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
568
628
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
569
629
|
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
|
|
@@ -579,19 +639,20 @@ function createDOMPurify() {
|
|
|
579
639
|
SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
|
|
580
640
|
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
|
|
581
641
|
IN_PLACE = cfg.IN_PLACE || false; // Default false
|
|
582
|
-
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP
|
|
583
|
-
NAMESPACE = cfg.NAMESPACE
|
|
584
|
-
MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS
|
|
585
|
-
HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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
|
|
589
650
|
}
|
|
590
|
-
if (
|
|
591
|
-
CUSTOM_ELEMENT_HANDLING.attributeNameCheck =
|
|
651
|
+
if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
|
|
652
|
+
CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
|
|
592
653
|
}
|
|
593
|
-
if (
|
|
594
|
-
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements =
|
|
654
|
+
if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
|
|
655
|
+
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
|
|
595
656
|
}
|
|
596
657
|
if (SAFE_FOR_TEMPLATES) {
|
|
597
658
|
ALLOW_DATA_ATTR = false;
|
|
@@ -623,44 +684,41 @@ function createDOMPurify() {
|
|
|
623
684
|
addToSet(ALLOWED_ATTR, xml);
|
|
624
685
|
}
|
|
625
686
|
}
|
|
626
|
-
/*
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
if (!objectHasOwnProperty(cfg, 'ADD_ATTR')) {
|
|
631
|
-
EXTRA_ELEMENT_HANDLING.attributeCheck = null;
|
|
632
|
-
}
|
|
687
|
+
/* Always reset function-based ADD_TAGS / ADD_ATTR checks to prevent
|
|
688
|
+
* leaking across calls when switching from function to array config */
|
|
689
|
+
EXTRA_ELEMENT_HANDLING.tagCheck = null;
|
|
690
|
+
EXTRA_ELEMENT_HANDLING.attributeCheck = null;
|
|
633
691
|
/* Merge configuration parameters */
|
|
634
|
-
if (cfg
|
|
692
|
+
if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
|
|
635
693
|
if (typeof cfg.ADD_TAGS === 'function') {
|
|
636
694
|
EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
|
|
637
|
-
} else {
|
|
695
|
+
} else if (arrayIsArray(cfg.ADD_TAGS)) {
|
|
638
696
|
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
639
697
|
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
640
698
|
}
|
|
641
699
|
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
642
700
|
}
|
|
643
701
|
}
|
|
644
|
-
if (cfg
|
|
702
|
+
if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
|
|
645
703
|
if (typeof cfg.ADD_ATTR === 'function') {
|
|
646
704
|
EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
|
|
647
|
-
} else {
|
|
705
|
+
} else if (arrayIsArray(cfg.ADD_ATTR)) {
|
|
648
706
|
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
649
707
|
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
650
708
|
}
|
|
651
709
|
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
652
710
|
}
|
|
653
711
|
}
|
|
654
|
-
if (cfg.ADD_URI_SAFE_ATTR) {
|
|
712
|
+
if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
|
|
655
713
|
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
656
714
|
}
|
|
657
|
-
if (cfg.FORBID_CONTENTS) {
|
|
715
|
+
if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
|
|
658
716
|
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
|
|
659
717
|
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
660
718
|
}
|
|
661
719
|
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
662
720
|
}
|
|
663
|
-
if (cfg.ADD_FORBID_CONTENTS) {
|
|
721
|
+
if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
|
|
664
722
|
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
|
|
665
723
|
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
666
724
|
}
|
|
@@ -952,6 +1010,11 @@ function createDOMPurify() {
|
|
|
952
1010
|
_forceRemove(currentNode);
|
|
953
1011
|
return true;
|
|
954
1012
|
}
|
|
1013
|
+
/* Remove risky CSS construction leading to mXSS */
|
|
1014
|
+
if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
|
|
1015
|
+
_forceRemove(currentNode);
|
|
1016
|
+
return true;
|
|
1017
|
+
}
|
|
955
1018
|
/* Remove any occurrence of processing instructions */
|
|
956
1019
|
if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
|
|
957
1020
|
_forceRemove(currentNode);
|
|
@@ -963,7 +1026,7 @@ function createDOMPurify() {
|
|
|
963
1026
|
return true;
|
|
964
1027
|
}
|
|
965
1028
|
/* Remove element if anything forbids its presence */
|
|
966
|
-
if (!(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) &&
|
|
1029
|
+
if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
|
|
967
1030
|
/* Check if we have a custom element to handle */
|
|
968
1031
|
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
|
|
969
1032
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
|
|
@@ -981,7 +1044,6 @@ function createDOMPurify() {
|
|
|
981
1044
|
const childCount = childNodes.length;
|
|
982
1045
|
for (let i = childCount - 1; i >= 0; --i) {
|
|
983
1046
|
const childClone = cloneNode(childNodes[i], true);
|
|
984
|
-
childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
|
|
985
1047
|
parentNode.insertBefore(childClone, getNextSibling(currentNode));
|
|
986
1048
|
}
|
|
987
1049
|
}
|
|
@@ -1056,6 +1118,10 @@ function createDOMPurify() {
|
|
|
1056
1118
|
} else ;
|
|
1057
1119
|
return true;
|
|
1058
1120
|
};
|
|
1121
|
+
/* Names the HTML spec reserves from valid-custom-element-name; these must
|
|
1122
|
+
* never be treated as basic custom elements even when a permissive
|
|
1123
|
+
* CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
|
|
1124
|
+
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']);
|
|
1059
1125
|
/**
|
|
1060
1126
|
* _isBasicCustomElement
|
|
1061
1127
|
* checks if at least one dash is included in tagName, and it's not the first char
|
|
@@ -1065,7 +1131,7 @@ function createDOMPurify() {
|
|
|
1065
1131
|
* @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
|
|
1066
1132
|
*/
|
|
1067
1133
|
const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
|
|
1068
|
-
return tagName
|
|
1134
|
+
return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT, tagName);
|
|
1069
1135
|
};
|
|
1070
1136
|
/**
|
|
1071
1137
|
* _sanitizeAttributes
|
|
@@ -1116,12 +1182,14 @@ function createDOMPurify() {
|
|
|
1116
1182
|
/* Full DOM Clobbering protection via namespace isolation,
|
|
1117
1183
|
* Prefix id and name attributes with `user-content-`
|
|
1118
1184
|
*/
|
|
1119
|
-
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
|
|
1185
|
+
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
|
|
1120
1186
|
// Remove the attribute with this value
|
|
1121
1187
|
_removeAttribute(name, currentNode);
|
|
1122
1188
|
// Prefix the value and later re-create the attribute with the sanitized value
|
|
1123
1189
|
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
1124
1190
|
}
|
|
1191
|
+
// Else: already prefixed, leave the attribute alone — the prefix is
|
|
1192
|
+
// itself the clobbering protection, and re-applying it is incorrect.
|
|
1125
1193
|
/* Work around a security issue with comments inside attributes */
|
|
1126
1194
|
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
|
|
1127
1195
|
_removeAttribute(name, currentNode);
|
|
@@ -1202,7 +1270,7 @@ function createDOMPurify() {
|
|
|
1202
1270
|
*
|
|
1203
1271
|
* @param fragment to iterate over recursively
|
|
1204
1272
|
*/
|
|
1205
|
-
const
|
|
1273
|
+
const _sanitizeShadowDOM2 = function _sanitizeShadowDOM(fragment) {
|
|
1206
1274
|
let shadowNode = null;
|
|
1207
1275
|
const shadowIterator = _createNodeIterator(fragment);
|
|
1208
1276
|
/* Execute a hook if present */
|
|
@@ -1216,7 +1284,7 @@ function createDOMPurify() {
|
|
|
1216
1284
|
_sanitizeAttributes(shadowNode);
|
|
1217
1285
|
/* Deep shadow DOM detected */
|
|
1218
1286
|
if (shadowNode.content instanceof DocumentFragment) {
|
|
1219
|
-
|
|
1287
|
+
_sanitizeShadowDOM2(shadowNode.content);
|
|
1220
1288
|
}
|
|
1221
1289
|
}
|
|
1222
1290
|
/* Execute a hook if present */
|
|
@@ -1238,13 +1306,9 @@ function createDOMPurify() {
|
|
|
1238
1306
|
}
|
|
1239
1307
|
/* Stringify, in case dirty is an object */
|
|
1240
1308
|
if (typeof dirty !== 'string' && !_isNode(dirty)) {
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
throw typeErrorCreate('dirty is not a string, aborting');
|
|
1245
|
-
}
|
|
1246
|
-
} else {
|
|
1247
|
-
throw typeErrorCreate('toString is not a function');
|
|
1309
|
+
dirty = stringifyValue(dirty);
|
|
1310
|
+
if (typeof dirty !== 'string') {
|
|
1311
|
+
throw typeErrorCreate('dirty is not a string, aborting');
|
|
1248
1312
|
}
|
|
1249
1313
|
}
|
|
1250
1314
|
/* Return dirty HTML if DOMPurify cannot run */
|
|
@@ -1263,8 +1327,9 @@ function createDOMPurify() {
|
|
|
1263
1327
|
}
|
|
1264
1328
|
if (IN_PLACE) {
|
|
1265
1329
|
/* Do some early pre-sanitization to avoid unsafe root nodes */
|
|
1266
|
-
|
|
1267
|
-
|
|
1330
|
+
const nn = dirty.nodeName;
|
|
1331
|
+
if (typeof nn === 'string') {
|
|
1332
|
+
const tagName = transformCaseFunc(nn);
|
|
1268
1333
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1269
1334
|
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
1270
1335
|
}
|
|
@@ -1311,7 +1376,7 @@ function createDOMPurify() {
|
|
|
1311
1376
|
_sanitizeAttributes(currentNode);
|
|
1312
1377
|
/* Shadow DOM detected, sanitize it */
|
|
1313
1378
|
if (currentNode.content instanceof DocumentFragment) {
|
|
1314
|
-
|
|
1379
|
+
_sanitizeShadowDOM2(currentNode.content);
|
|
1315
1380
|
}
|
|
1316
1381
|
}
|
|
1317
1382
|
/* If we sanitized `dirty` in-place, return it. */
|
|
@@ -1320,6 +1385,14 @@ function createDOMPurify() {
|
|
|
1320
1385
|
}
|
|
1321
1386
|
/* Return sanitized string or DOM */
|
|
1322
1387
|
if (RETURN_DOM) {
|
|
1388
|
+
if (SAFE_FOR_TEMPLATES) {
|
|
1389
|
+
body.normalize();
|
|
1390
|
+
let html = body.innerHTML;
|
|
1391
|
+
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
1392
|
+
html = stringReplace(html, expr, ' ');
|
|
1393
|
+
});
|
|
1394
|
+
body.innerHTML = html;
|
|
1395
|
+
}
|
|
1323
1396
|
if (RETURN_DOM_FRAGMENT) {
|
|
1324
1397
|
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
1325
1398
|
while (body.firstChild) {
|