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