dompurify 3.1.7 → 3.2.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/README.md +2 -6
- package/dist/purify.cjs.d.ts +402 -0
- package/dist/purify.cjs.js +93 -315
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.d.mts +399 -0
- package/dist/purify.es.mjs +93 -315
- package/dist/purify.es.mjs.map +1 -1
- package/dist/purify.js +93 -315
- 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 +49 -13
package/dist/purify.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @license DOMPurify 3.1
|
|
1
|
+
/*! @license DOMPurify 3.2.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.2.1/LICENSE */
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
@@ -50,12 +50,11 @@ const stringTrim = unapply(String.prototype.trim);
|
|
|
50
50
|
const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
|
|
51
51
|
const regExpTest = unapply(RegExp.prototype.test);
|
|
52
52
|
const typeErrorCreate = unconstruct(TypeError);
|
|
53
|
-
|
|
54
53
|
/**
|
|
55
54
|
* Creates a new function that calls the given function with a specified thisArg and arguments.
|
|
56
55
|
*
|
|
57
|
-
* @param
|
|
58
|
-
* @returns
|
|
56
|
+
* @param func - The function to be wrapped and called.
|
|
57
|
+
* @returns A new function that calls the given function with a specified thisArg and arguments.
|
|
59
58
|
*/
|
|
60
59
|
function unapply(func) {
|
|
61
60
|
return function (thisArg) {
|
|
@@ -65,12 +64,11 @@ function unapply(func) {
|
|
|
65
64
|
return apply(func, thisArg, args);
|
|
66
65
|
};
|
|
67
66
|
}
|
|
68
|
-
|
|
69
67
|
/**
|
|
70
68
|
* Creates a new function that constructs an instance of the given constructor function with the provided arguments.
|
|
71
69
|
*
|
|
72
|
-
* @param
|
|
73
|
-
* @returns
|
|
70
|
+
* @param func - The constructor function to be wrapped and called.
|
|
71
|
+
* @returns A new function that constructs an instance of the given constructor function with the provided arguments.
|
|
74
72
|
*/
|
|
75
73
|
function unconstruct(func) {
|
|
76
74
|
return function () {
|
|
@@ -80,14 +78,13 @@ function unconstruct(func) {
|
|
|
80
78
|
return construct(func, args);
|
|
81
79
|
};
|
|
82
80
|
}
|
|
83
|
-
|
|
84
81
|
/**
|
|
85
82
|
* Add properties to a lookup table
|
|
86
83
|
*
|
|
87
|
-
* @param
|
|
88
|
-
* @param
|
|
89
|
-
* @param
|
|
90
|
-
* @returns
|
|
84
|
+
* @param set - The set to which elements will be added.
|
|
85
|
+
* @param array - The array containing elements to be added to the set.
|
|
86
|
+
* @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
|
|
87
|
+
* @returns The modified set with added elements.
|
|
91
88
|
*/
|
|
92
89
|
function addToSet(set, array) {
|
|
93
90
|
let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
|
|
@@ -114,12 +111,11 @@ function addToSet(set, array) {
|
|
|
114
111
|
}
|
|
115
112
|
return set;
|
|
116
113
|
}
|
|
117
|
-
|
|
118
114
|
/**
|
|
119
115
|
* Clean up an array to harden against CSPP
|
|
120
116
|
*
|
|
121
|
-
* @param
|
|
122
|
-
* @returns
|
|
117
|
+
* @param array - The array to be cleaned.
|
|
118
|
+
* @returns The cleaned version of the array
|
|
123
119
|
*/
|
|
124
120
|
function cleanArray(array) {
|
|
125
121
|
for (let index = 0; index < array.length; index++) {
|
|
@@ -130,12 +126,11 @@ function cleanArray(array) {
|
|
|
130
126
|
}
|
|
131
127
|
return array;
|
|
132
128
|
}
|
|
133
|
-
|
|
134
129
|
/**
|
|
135
130
|
* Shallow clone an object
|
|
136
131
|
*
|
|
137
|
-
* @param
|
|
138
|
-
* @returns
|
|
132
|
+
* @param object - The object to be cloned.
|
|
133
|
+
* @returns A new object that copies the original.
|
|
139
134
|
*/
|
|
140
135
|
function clone(object) {
|
|
141
136
|
const newObject = create(null);
|
|
@@ -153,13 +148,12 @@ function clone(object) {
|
|
|
153
148
|
}
|
|
154
149
|
return newObject;
|
|
155
150
|
}
|
|
156
|
-
|
|
157
151
|
/**
|
|
158
152
|
* This method automatically checks if the prop is function or getter and behaves accordingly.
|
|
159
153
|
*
|
|
160
|
-
* @param
|
|
161
|
-
* @param
|
|
162
|
-
* @returns
|
|
154
|
+
* @param object - The object to look up the getter function in its prototype chain.
|
|
155
|
+
* @param prop - The property name for which to find the getter function.
|
|
156
|
+
* @returns The getter function found in the prototype chain or a fallback function.
|
|
163
157
|
*/
|
|
164
158
|
function lookupGetter(object, prop) {
|
|
165
159
|
while (object !== null) {
|
|
@@ -181,18 +175,15 @@ function lookupGetter(object, prop) {
|
|
|
181
175
|
}
|
|
182
176
|
|
|
183
177
|
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', 'section', 'select', 'shadow', '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']);
|
|
184
|
-
|
|
185
178
|
// SVG
|
|
186
179
|
const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
|
|
187
180
|
const svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
|
|
188
|
-
|
|
189
181
|
// List of SVG elements that are disallowed by default.
|
|
190
182
|
// We still need to know them so that we can do namespace
|
|
191
183
|
// checks properly in case one wants to add them to
|
|
192
184
|
// allow-list.
|
|
193
185
|
const svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
|
|
194
186
|
const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover', 'mprescripts']);
|
|
195
|
-
|
|
196
187
|
// Similarly to SVG, we want to know all MathML elements,
|
|
197
188
|
// even those that we disallow by default.
|
|
198
189
|
const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
|
|
@@ -219,18 +210,19 @@ const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
|
|
|
219
210
|
|
|
220
211
|
var EXPRESSIONS = /*#__PURE__*/Object.freeze({
|
|
221
212
|
__proto__: null,
|
|
222
|
-
MUSTACHE_EXPR: MUSTACHE_EXPR,
|
|
223
|
-
ERB_EXPR: ERB_EXPR,
|
|
224
|
-
TMPLIT_EXPR: TMPLIT_EXPR,
|
|
225
|
-
DATA_ATTR: DATA_ATTR,
|
|
226
213
|
ARIA_ATTR: ARIA_ATTR,
|
|
227
|
-
IS_ALLOWED_URI: IS_ALLOWED_URI,
|
|
228
|
-
IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
|
|
229
214
|
ATTR_WHITESPACE: ATTR_WHITESPACE,
|
|
215
|
+
CUSTOM_ELEMENT: CUSTOM_ELEMENT,
|
|
216
|
+
DATA_ATTR: DATA_ATTR,
|
|
230
217
|
DOCTYPE_NAME: DOCTYPE_NAME,
|
|
231
|
-
|
|
218
|
+
ERB_EXPR: ERB_EXPR,
|
|
219
|
+
IS_ALLOWED_URI: IS_ALLOWED_URI,
|
|
220
|
+
IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
|
|
221
|
+
MUSTACHE_EXPR: MUSTACHE_EXPR,
|
|
222
|
+
TMPLIT_EXPR: TMPLIT_EXPR
|
|
232
223
|
});
|
|
233
224
|
|
|
225
|
+
/* eslint-disable @typescript-eslint/indent */
|
|
234
226
|
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
|
|
235
227
|
const NODE_TYPE = {
|
|
236
228
|
element: 1,
|
|
@@ -251,20 +243,18 @@ const NODE_TYPE = {
|
|
|
251
243
|
const getGlobal = function getGlobal() {
|
|
252
244
|
return typeof window === 'undefined' ? null : window;
|
|
253
245
|
};
|
|
254
|
-
|
|
255
246
|
/**
|
|
256
247
|
* Creates a no-op policy for internal use only.
|
|
257
248
|
* Don't export this function outside this module!
|
|
258
|
-
* @param
|
|
259
|
-
* @param
|
|
260
|
-
* @return
|
|
249
|
+
* @param trustedTypes The policy factory.
|
|
250
|
+
* @param purifyHostElement The Script element used to load DOMPurify (to determine policy name suffix).
|
|
251
|
+
* @return The policy created (or null, if Trusted Types
|
|
261
252
|
* are not supported or creating the policy failed).
|
|
262
253
|
*/
|
|
263
254
|
const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, purifyHostElement) {
|
|
264
255
|
if (typeof trustedTypes !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
|
|
265
256
|
return null;
|
|
266
257
|
}
|
|
267
|
-
|
|
268
258
|
// Allow the callers to control the unique policy name
|
|
269
259
|
// by adding a data-tt-policy-suffix to the script element with the DOMPurify.
|
|
270
260
|
// Policy creation with duplicate names throws in Trusted Types.
|
|
@@ -294,17 +284,7 @@ const _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedType
|
|
|
294
284
|
function createDOMPurify() {
|
|
295
285
|
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
296
286
|
const DOMPurify = root => createDOMPurify(root);
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Version label, exposed for easier checks
|
|
300
|
-
* if DOMPurify is up to date or not
|
|
301
|
-
*/
|
|
302
|
-
DOMPurify.version = '3.1.7';
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Array of elements that DOMPurify removed during sanitation.
|
|
306
|
-
* Empty if nothing was removed.
|
|
307
|
-
*/
|
|
287
|
+
DOMPurify.version = '3.2.1';
|
|
308
288
|
DOMPurify.removed = [];
|
|
309
289
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
|
|
310
290
|
// Not running in a browser, provide a factory function
|
|
@@ -334,7 +314,6 @@ function createDOMPurify() {
|
|
|
334
314
|
const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
335
315
|
const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
336
316
|
const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
337
|
-
|
|
338
317
|
// As per issue #47, the web-components registry is inherited by a
|
|
339
318
|
// new document created via createHTMLDocument. As per the spec
|
|
340
319
|
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
|
|
@@ -359,7 +338,6 @@ function createDOMPurify() {
|
|
|
359
338
|
importNode
|
|
360
339
|
} = originalDocument;
|
|
361
340
|
let hooks = {};
|
|
362
|
-
|
|
363
341
|
/**
|
|
364
342
|
* Expose whether this browser supports running the full DOMPurify.
|
|
365
343
|
*/
|
|
@@ -377,22 +355,18 @@ function createDOMPurify() {
|
|
|
377
355
|
let {
|
|
378
356
|
IS_ALLOWED_URI: IS_ALLOWED_URI$1
|
|
379
357
|
} = EXPRESSIONS;
|
|
380
|
-
|
|
381
358
|
/**
|
|
382
359
|
* We consider the elements and attributes below to be safe. Ideally
|
|
383
360
|
* don't add any new ones but feel free to remove unwanted ones.
|
|
384
361
|
*/
|
|
385
|
-
|
|
386
362
|
/* allowed element names */
|
|
387
363
|
let ALLOWED_TAGS = null;
|
|
388
364
|
const DEFAULT_ALLOWED_TAGS = addToSet({}, [...html$1, ...svg$1, ...svgFilters, ...mathMl$1, ...text]);
|
|
389
|
-
|
|
390
365
|
/* Allowed attribute names */
|
|
391
366
|
let ALLOWED_ATTR = null;
|
|
392
367
|
const DEFAULT_ALLOWED_ATTR = addToSet({}, [...html, ...svg, ...mathMl, ...xml]);
|
|
393
|
-
|
|
394
368
|
/*
|
|
395
|
-
* Configure how
|
|
369
|
+
* Configure how DOMPurify should handle custom elements and their attributes as well as customized built-in elements.
|
|
396
370
|
* @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
|
|
397
371
|
* @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
|
|
398
372
|
* @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
|
|
@@ -417,65 +391,49 @@ function createDOMPurify() {
|
|
|
417
391
|
value: false
|
|
418
392
|
}
|
|
419
393
|
}));
|
|
420
|
-
|
|
421
394
|
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
|
|
422
395
|
let FORBID_TAGS = null;
|
|
423
|
-
|
|
424
396
|
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
|
|
425
397
|
let FORBID_ATTR = null;
|
|
426
|
-
|
|
427
398
|
/* Decide if ARIA attributes are okay */
|
|
428
399
|
let ALLOW_ARIA_ATTR = true;
|
|
429
|
-
|
|
430
400
|
/* Decide if custom data attributes are okay */
|
|
431
401
|
let ALLOW_DATA_ATTR = true;
|
|
432
|
-
|
|
433
402
|
/* Decide if unknown protocols are okay */
|
|
434
403
|
let ALLOW_UNKNOWN_PROTOCOLS = false;
|
|
435
|
-
|
|
436
404
|
/* Decide if self-closing tags in attributes are allowed.
|
|
437
405
|
* Usually removed due to a mXSS issue in jQuery 3.0 */
|
|
438
406
|
let ALLOW_SELF_CLOSE_IN_ATTR = true;
|
|
439
|
-
|
|
440
407
|
/* Output should be safe for common template engines.
|
|
441
408
|
* This means, DOMPurify removes data attributes, mustaches and ERB
|
|
442
409
|
*/
|
|
443
410
|
let SAFE_FOR_TEMPLATES = false;
|
|
444
|
-
|
|
445
411
|
/* Output should be safe even for XML used within HTML and alike.
|
|
446
412
|
* This means, DOMPurify removes comments when containing risky content.
|
|
447
413
|
*/
|
|
448
414
|
let SAFE_FOR_XML = true;
|
|
449
|
-
|
|
450
415
|
/* Decide if document with <html>... should be returned */
|
|
451
416
|
let WHOLE_DOCUMENT = false;
|
|
452
|
-
|
|
453
417
|
/* Track whether config is already set on this instance of DOMPurify. */
|
|
454
418
|
let SET_CONFIG = false;
|
|
455
|
-
|
|
456
419
|
/* Decide if all elements (e.g. style, script) must be children of
|
|
457
420
|
* document.body. By default, browsers might move them to document.head */
|
|
458
421
|
let FORCE_BODY = false;
|
|
459
|
-
|
|
460
422
|
/* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
|
|
461
423
|
* string (or a TrustedHTML object if Trusted Types are supported).
|
|
462
424
|
* If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
|
|
463
425
|
*/
|
|
464
426
|
let RETURN_DOM = false;
|
|
465
|
-
|
|
466
427
|
/* Decide if a DOM `DocumentFragment` should be returned, instead of a html
|
|
467
428
|
* string (or a TrustedHTML object if Trusted Types are supported) */
|
|
468
429
|
let RETURN_DOM_FRAGMENT = false;
|
|
469
|
-
|
|
470
430
|
/* Try to return a Trusted Type object instead of a string, return a string in
|
|
471
431
|
* case Trusted Types are not supported */
|
|
472
432
|
let RETURN_TRUSTED_TYPE = false;
|
|
473
|
-
|
|
474
433
|
/* Output should be free from DOM clobbering attacks?
|
|
475
434
|
* This sanitizes markups named with colliding, clobberable built-in DOM APIs.
|
|
476
435
|
*/
|
|
477
436
|
let SANITIZE_DOM = true;
|
|
478
|
-
|
|
479
437
|
/* Achieve full DOM Clobbering protection by isolating the namespace of named
|
|
480
438
|
* properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
|
|
481
439
|
*
|
|
@@ -491,25 +449,19 @@ function createDOMPurify() {
|
|
|
491
449
|
*/
|
|
492
450
|
let SANITIZE_NAMED_PROPS = false;
|
|
493
451
|
const SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
|
|
494
|
-
|
|
495
452
|
/* Keep element content when removing element? */
|
|
496
453
|
let KEEP_CONTENT = true;
|
|
497
|
-
|
|
498
454
|
/* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
|
|
499
455
|
* of importing it into a new Document and returning a sanitized copy */
|
|
500
456
|
let IN_PLACE = false;
|
|
501
|
-
|
|
502
457
|
/* Allow usage of profiles like html, svg and mathMl */
|
|
503
458
|
let USE_PROFILES = {};
|
|
504
|
-
|
|
505
459
|
/* Tags to ignore content of when KEEP_CONTENT is true */
|
|
506
460
|
let FORBID_CONTENTS = null;
|
|
507
461
|
const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
|
|
508
|
-
|
|
509
462
|
/* Tags that are safe for data: URIs */
|
|
510
463
|
let DATA_URI_TAGS = null;
|
|
511
464
|
const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
|
|
512
|
-
|
|
513
465
|
/* Attributes safe for values like "javascript:" */
|
|
514
466
|
let URI_SAFE_ATTRIBUTES = null;
|
|
515
467
|
const DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
|
|
@@ -519,32 +471,33 @@ function createDOMPurify() {
|
|
|
519
471
|
/* Document namespace */
|
|
520
472
|
let NAMESPACE = HTML_NAMESPACE;
|
|
521
473
|
let IS_EMPTY_INPUT = false;
|
|
522
|
-
|
|
523
474
|
/* Allowed XHTML+XML namespaces */
|
|
524
475
|
let ALLOWED_NAMESPACES = null;
|
|
525
476
|
const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
|
|
526
|
-
|
|
477
|
+
let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
478
|
+
let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
|
|
479
|
+
// Certain elements are allowed in both SVG and HTML
|
|
480
|
+
// namespace. We need to specify them explicitly
|
|
481
|
+
// so that they don't get erroneously deleted from
|
|
482
|
+
// HTML namespace.
|
|
483
|
+
const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
|
|
527
484
|
/* Parsing of strict XHTML documents */
|
|
528
485
|
let PARSER_MEDIA_TYPE = null;
|
|
529
486
|
const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
|
|
530
487
|
const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
|
|
531
488
|
let transformCaseFunc = null;
|
|
532
|
-
|
|
533
489
|
/* Keep a reference to config to pass to hooks */
|
|
534
490
|
let CONFIG = null;
|
|
535
|
-
|
|
536
491
|
/* Ideally, do not touch anything below this line */
|
|
537
492
|
/* ______________________________________________ */
|
|
538
|
-
|
|
539
493
|
const formElement = document.createElement('form');
|
|
540
494
|
const isRegexOrFunction = function isRegexOrFunction(testValue) {
|
|
541
495
|
return testValue instanceof RegExp || testValue instanceof Function;
|
|
542
496
|
};
|
|
543
|
-
|
|
544
497
|
/**
|
|
545
498
|
* _parseConfig
|
|
546
499
|
*
|
|
547
|
-
* @param
|
|
500
|
+
* @param cfg optional config literal
|
|
548
501
|
*/
|
|
549
502
|
// eslint-disable-next-line complexity
|
|
550
503
|
const _parseConfig = function _parseConfig() {
|
|
@@ -552,39 +505,23 @@ function createDOMPurify() {
|
|
|
552
505
|
if (CONFIG && CONFIG === cfg) {
|
|
553
506
|
return;
|
|
554
507
|
}
|
|
555
|
-
|
|
556
508
|
/* Shield configuration object from tampering */
|
|
557
509
|
if (!cfg || typeof cfg !== 'object') {
|
|
558
510
|
cfg = {};
|
|
559
511
|
}
|
|
560
|
-
|
|
561
512
|
/* Shield configuration object from prototype pollution */
|
|
562
513
|
cfg = clone(cfg);
|
|
563
514
|
PARSER_MEDIA_TYPE =
|
|
564
515
|
// eslint-disable-next-line unicorn/prefer-includes
|
|
565
516
|
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
|
|
566
|
-
|
|
567
517
|
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
568
518
|
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
|
|
569
|
-
|
|
570
519
|
/* Set configuration parameters */
|
|
571
520
|
ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
572
521
|
ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
573
522
|
ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
|
|
574
|
-
URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
|
|
575
|
-
|
|
576
|
-
cfg.ADD_URI_SAFE_ATTR,
|
|
577
|
-
// eslint-disable-line indent
|
|
578
|
-
transformCaseFunc // eslint-disable-line indent
|
|
579
|
-
) // eslint-disable-line indent
|
|
580
|
-
: DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
581
|
-
DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
|
|
582
|
-
// eslint-disable-line indent
|
|
583
|
-
cfg.ADD_DATA_URI_TAGS,
|
|
584
|
-
// eslint-disable-line indent
|
|
585
|
-
transformCaseFunc // eslint-disable-line indent
|
|
586
|
-
) // eslint-disable-line indent
|
|
587
|
-
: DEFAULT_DATA_URI_TAGS;
|
|
523
|
+
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;
|
|
524
|
+
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;
|
|
588
525
|
FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
|
|
589
526
|
FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
|
|
590
527
|
FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
|
|
@@ -606,6 +543,8 @@ function createDOMPurify() {
|
|
|
606
543
|
IN_PLACE = cfg.IN_PLACE || false; // Default false
|
|
607
544
|
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
|
|
608
545
|
NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
|
|
546
|
+
MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
|
|
547
|
+
HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
|
|
609
548
|
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
|
|
610
549
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
|
|
611
550
|
CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
|
|
@@ -622,7 +561,6 @@ function createDOMPurify() {
|
|
|
622
561
|
if (RETURN_DOM_FRAGMENT) {
|
|
623
562
|
RETURN_DOM = true;
|
|
624
563
|
}
|
|
625
|
-
|
|
626
564
|
/* Parse profile info */
|
|
627
565
|
if (USE_PROFILES) {
|
|
628
566
|
ALLOWED_TAGS = addToSet({}, text);
|
|
@@ -647,7 +585,6 @@ function createDOMPurify() {
|
|
|
647
585
|
addToSet(ALLOWED_ATTR, xml);
|
|
648
586
|
}
|
|
649
587
|
}
|
|
650
|
-
|
|
651
588
|
/* Merge configuration parameters */
|
|
652
589
|
if (cfg.ADD_TAGS) {
|
|
653
590
|
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
@@ -670,17 +607,14 @@ function createDOMPurify() {
|
|
|
670
607
|
}
|
|
671
608
|
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
672
609
|
}
|
|
673
|
-
|
|
674
610
|
/* Add #text in case KEEP_CONTENT is set to true */
|
|
675
611
|
if (KEEP_CONTENT) {
|
|
676
612
|
ALLOWED_TAGS['#text'] = true;
|
|
677
613
|
}
|
|
678
|
-
|
|
679
614
|
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
680
615
|
if (WHOLE_DOCUMENT) {
|
|
681
616
|
addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
|
|
682
617
|
}
|
|
683
|
-
|
|
684
618
|
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
685
619
|
if (ALLOWED_TAGS.table) {
|
|
686
620
|
addToSet(ALLOWED_TAGS, ['tbody']);
|
|
@@ -693,10 +627,8 @@ function createDOMPurify() {
|
|
|
693
627
|
if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
|
|
694
628
|
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
|
|
695
629
|
}
|
|
696
|
-
|
|
697
630
|
// Overwrite existing TrustedTypes policy.
|
|
698
631
|
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
|
|
699
|
-
|
|
700
632
|
// Sign local variables required by `sanitize`.
|
|
701
633
|
emptyHTML = trustedTypesPolicy.createHTML('');
|
|
702
634
|
} else {
|
|
@@ -704,13 +636,11 @@ function createDOMPurify() {
|
|
|
704
636
|
if (trustedTypesPolicy === undefined) {
|
|
705
637
|
trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
|
|
706
638
|
}
|
|
707
|
-
|
|
708
639
|
// If creating the internal policy succeeded sign internal variables.
|
|
709
640
|
if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
|
|
710
641
|
emptyHTML = trustedTypesPolicy.createHTML('');
|
|
711
642
|
}
|
|
712
643
|
}
|
|
713
|
-
|
|
714
644
|
// Prevent further manipulation of configuration.
|
|
715
645
|
// Not available in IE8, Safari 5, etc.
|
|
716
646
|
if (freeze) {
|
|
@@ -718,30 +648,19 @@ function createDOMPurify() {
|
|
|
718
648
|
}
|
|
719
649
|
CONFIG = cfg;
|
|
720
650
|
};
|
|
721
|
-
const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
722
|
-
const HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
|
|
723
|
-
|
|
724
|
-
// Certain elements are allowed in both SVG and HTML
|
|
725
|
-
// namespace. We need to specify them explicitly
|
|
726
|
-
// so that they don't get erroneously deleted from
|
|
727
|
-
// HTML namespace.
|
|
728
|
-
const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
|
|
729
|
-
|
|
730
651
|
/* Keep track of all possible SVG and MathML tags
|
|
731
652
|
* so that we can perform the namespace checks
|
|
732
653
|
* correctly. */
|
|
733
654
|
const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
|
|
734
655
|
const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
|
|
735
|
-
|
|
736
656
|
/**
|
|
737
|
-
* @param
|
|
738
|
-
* @returns
|
|
657
|
+
* @param element a DOM element whose namespace is being checked
|
|
658
|
+
* @returns Return false if the element has a
|
|
739
659
|
* namespace that a spec-compliant parser would never
|
|
740
660
|
* return. Return true otherwise.
|
|
741
661
|
*/
|
|
742
662
|
const _checkValidNamespace = function _checkValidNamespace(element) {
|
|
743
663
|
let parent = getParentNode(element);
|
|
744
|
-
|
|
745
664
|
// In JSDOM, if we're inside shadow DOM, then parentNode
|
|
746
665
|
// can be null. We just simulate parent in this case.
|
|
747
666
|
if (!parent || !parent.tagName) {
|
|
@@ -762,14 +681,12 @@ function createDOMPurify() {
|
|
|
762
681
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
763
682
|
return tagName === 'svg';
|
|
764
683
|
}
|
|
765
|
-
|
|
766
684
|
// The only way to switch from MathML to SVG is via`
|
|
767
685
|
// svg if parent is either <annotation-xml> or MathML
|
|
768
686
|
// text integration points.
|
|
769
687
|
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
770
688
|
return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
|
|
771
689
|
}
|
|
772
|
-
|
|
773
690
|
// We only allow elements that are defined in SVG
|
|
774
691
|
// spec. All others are disallowed in SVG namespace.
|
|
775
692
|
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
@@ -781,13 +698,11 @@ function createDOMPurify() {
|
|
|
781
698
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
782
699
|
return tagName === 'math';
|
|
783
700
|
}
|
|
784
|
-
|
|
785
701
|
// The only way to switch from SVG to MathML is via
|
|
786
702
|
// <math> and HTML integration points
|
|
787
703
|
if (parent.namespaceURI === SVG_NAMESPACE) {
|
|
788
704
|
return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
|
|
789
705
|
}
|
|
790
|
-
|
|
791
706
|
// We only allow elements that are defined in MathML
|
|
792
707
|
// spec. All others are disallowed in MathML namespace.
|
|
793
708
|
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
@@ -802,28 +717,24 @@ function createDOMPurify() {
|
|
|
802
717
|
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
|
|
803
718
|
return false;
|
|
804
719
|
}
|
|
805
|
-
|
|
806
720
|
// We disallow tags that are specific for MathML
|
|
807
721
|
// or SVG and should never appear in HTML namespace
|
|
808
722
|
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
809
723
|
}
|
|
810
|
-
|
|
811
724
|
// For XHTML and XML documents that support custom namespaces
|
|
812
725
|
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
813
726
|
return true;
|
|
814
727
|
}
|
|
815
|
-
|
|
816
728
|
// The code should never reach this place (this means
|
|
817
729
|
// that the element somehow got namespace that is not
|
|
818
730
|
// HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
|
|
819
731
|
// Return false just in case.
|
|
820
732
|
return false;
|
|
821
733
|
};
|
|
822
|
-
|
|
823
734
|
/**
|
|
824
735
|
* _forceRemove
|
|
825
736
|
*
|
|
826
|
-
* @param
|
|
737
|
+
* @param node a DOM node
|
|
827
738
|
*/
|
|
828
739
|
const _forceRemove = function _forceRemove(node) {
|
|
829
740
|
arrayPush(DOMPurify.removed, {
|
|
@@ -836,46 +747,43 @@ function createDOMPurify() {
|
|
|
836
747
|
remove(node);
|
|
837
748
|
}
|
|
838
749
|
};
|
|
839
|
-
|
|
840
750
|
/**
|
|
841
751
|
* _removeAttribute
|
|
842
752
|
*
|
|
843
|
-
* @param
|
|
844
|
-
* @param
|
|
753
|
+
* @param name an Attribute name
|
|
754
|
+
* @param element a DOM node
|
|
845
755
|
*/
|
|
846
|
-
const _removeAttribute = function _removeAttribute(name,
|
|
756
|
+
const _removeAttribute = function _removeAttribute(name, element) {
|
|
847
757
|
try {
|
|
848
758
|
arrayPush(DOMPurify.removed, {
|
|
849
|
-
attribute:
|
|
850
|
-
from:
|
|
759
|
+
attribute: element.getAttributeNode(name),
|
|
760
|
+
from: element
|
|
851
761
|
});
|
|
852
762
|
} catch (_) {
|
|
853
763
|
arrayPush(DOMPurify.removed, {
|
|
854
764
|
attribute: null,
|
|
855
|
-
from:
|
|
765
|
+
from: element
|
|
856
766
|
});
|
|
857
767
|
}
|
|
858
|
-
|
|
859
|
-
|
|
768
|
+
element.removeAttribute(name);
|
|
860
769
|
// We void attribute values for unremovable "is"" attributes
|
|
861
770
|
if (name === 'is' && !ALLOWED_ATTR[name]) {
|
|
862
771
|
if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
|
|
863
772
|
try {
|
|
864
|
-
_forceRemove(
|
|
773
|
+
_forceRemove(element);
|
|
865
774
|
} catch (_) {}
|
|
866
775
|
} else {
|
|
867
776
|
try {
|
|
868
|
-
|
|
777
|
+
element.setAttribute(name, '');
|
|
869
778
|
} catch (_) {}
|
|
870
779
|
}
|
|
871
780
|
}
|
|
872
781
|
};
|
|
873
|
-
|
|
874
782
|
/**
|
|
875
783
|
* _initDocument
|
|
876
784
|
*
|
|
877
|
-
* @param
|
|
878
|
-
* @return
|
|
785
|
+
* @param dirty - a string of dirty markup
|
|
786
|
+
* @return a DOM, filled with the dirty markup
|
|
879
787
|
*/
|
|
880
788
|
const _initDocument = function _initDocument(dirty) {
|
|
881
789
|
/* Create a HTML document */
|
|
@@ -902,7 +810,6 @@ function createDOMPurify() {
|
|
|
902
810
|
doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
|
|
903
811
|
} catch (_) {}
|
|
904
812
|
}
|
|
905
|
-
|
|
906
813
|
/* Use createHTMLDocument in case DOMParser is not available */
|
|
907
814
|
if (!doc || !doc.documentElement) {
|
|
908
815
|
doc = implementation.createDocument(NAMESPACE, 'template', null);
|
|
@@ -916,112 +823,89 @@ function createDOMPurify() {
|
|
|
916
823
|
if (dirty && leadingWhitespace) {
|
|
917
824
|
body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
|
|
918
825
|
}
|
|
919
|
-
|
|
920
826
|
/* Work on whole document or just its body */
|
|
921
827
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
922
828
|
return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
|
|
923
829
|
}
|
|
924
830
|
return WHOLE_DOCUMENT ? doc.documentElement : body;
|
|
925
831
|
};
|
|
926
|
-
|
|
927
832
|
/**
|
|
928
833
|
* Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
|
|
929
834
|
*
|
|
930
|
-
* @param
|
|
931
|
-
* @return
|
|
835
|
+
* @param root The root element or node to start traversing on.
|
|
836
|
+
* @return The created NodeIterator
|
|
932
837
|
*/
|
|
933
838
|
const _createNodeIterator = function _createNodeIterator(root) {
|
|
934
839
|
return createNodeIterator.call(root.ownerDocument || root, root,
|
|
935
840
|
// eslint-disable-next-line no-bitwise
|
|
936
841
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
|
|
937
842
|
};
|
|
938
|
-
|
|
939
843
|
/**
|
|
940
844
|
* _isClobbered
|
|
941
845
|
*
|
|
942
|
-
* @param
|
|
943
|
-
* @return
|
|
846
|
+
* @param element element to check for clobbering attacks
|
|
847
|
+
* @return true if clobbered, false if safe
|
|
944
848
|
*/
|
|
945
|
-
const _isClobbered = function _isClobbered(
|
|
946
|
-
return
|
|
849
|
+
const _isClobbered = function _isClobbered(element) {
|
|
850
|
+
return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');
|
|
947
851
|
};
|
|
948
|
-
|
|
949
852
|
/**
|
|
950
853
|
* Checks whether the given object is a DOM node.
|
|
951
854
|
*
|
|
952
|
-
* @param
|
|
953
|
-
* @return
|
|
855
|
+
* @param value object to check whether it's a DOM node
|
|
856
|
+
* @return true is object is a DOM node
|
|
954
857
|
*/
|
|
955
|
-
const _isNode = function _isNode(
|
|
956
|
-
return typeof Node === 'function' &&
|
|
858
|
+
const _isNode = function _isNode(value) {
|
|
859
|
+
return typeof Node === 'function' && value instanceof Node;
|
|
957
860
|
};
|
|
958
|
-
|
|
959
|
-
/**
|
|
960
|
-
* _executeHook
|
|
961
|
-
* Execute user configurable hooks
|
|
962
|
-
*
|
|
963
|
-
* @param {String} entryPoint Name of the hook's entry point
|
|
964
|
-
* @param {Node} currentNode node to work on with the hook
|
|
965
|
-
* @param {Object} data additional hook parameters
|
|
966
|
-
*/
|
|
967
|
-
const _executeHook = function _executeHook(entryPoint, currentNode, data) {
|
|
861
|
+
function _executeHook(entryPoint, currentNode, data) {
|
|
968
862
|
if (!hooks[entryPoint]) {
|
|
969
863
|
return;
|
|
970
864
|
}
|
|
971
865
|
arrayForEach(hooks[entryPoint], hook => {
|
|
972
866
|
hook.call(DOMPurify, currentNode, data, CONFIG);
|
|
973
867
|
});
|
|
974
|
-
}
|
|
975
|
-
|
|
868
|
+
}
|
|
976
869
|
/**
|
|
977
870
|
* _sanitizeElements
|
|
978
871
|
*
|
|
979
872
|
* @protect nodeName
|
|
980
873
|
* @protect textContent
|
|
981
874
|
* @protect removeChild
|
|
982
|
-
*
|
|
983
|
-
* @
|
|
984
|
-
* @return {Boolean} true if node was killed, false if left alive
|
|
875
|
+
* @param currentNode to check for permission to exist
|
|
876
|
+
* @return true if node was killed, false if left alive
|
|
985
877
|
*/
|
|
986
878
|
const _sanitizeElements = function _sanitizeElements(currentNode) {
|
|
987
879
|
let content = null;
|
|
988
|
-
|
|
989
880
|
/* Execute a hook if present */
|
|
990
881
|
_executeHook('beforeSanitizeElements', currentNode, null);
|
|
991
|
-
|
|
992
882
|
/* Check if element is clobbered or can clobber */
|
|
993
883
|
if (_isClobbered(currentNode)) {
|
|
994
884
|
_forceRemove(currentNode);
|
|
995
885
|
return true;
|
|
996
886
|
}
|
|
997
|
-
|
|
998
887
|
/* Now let's check the element's type and name */
|
|
999
888
|
const tagName = transformCaseFunc(currentNode.nodeName);
|
|
1000
|
-
|
|
1001
889
|
/* Execute a hook if present */
|
|
1002
890
|
_executeHook('uponSanitizeElement', currentNode, {
|
|
1003
891
|
tagName,
|
|
1004
892
|
allowedTags: ALLOWED_TAGS
|
|
1005
893
|
});
|
|
1006
|
-
|
|
1007
894
|
/* Detect mXSS attempts abusing namespace confusion */
|
|
1008
895
|
if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
|
|
1009
896
|
_forceRemove(currentNode);
|
|
1010
897
|
return true;
|
|
1011
898
|
}
|
|
1012
|
-
|
|
1013
899
|
/* Remove any occurrence of processing instructions */
|
|
1014
900
|
if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
|
|
1015
901
|
_forceRemove(currentNode);
|
|
1016
902
|
return true;
|
|
1017
903
|
}
|
|
1018
|
-
|
|
1019
904
|
/* Remove any kind of possibly harmful comments */
|
|
1020
905
|
if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
|
|
1021
906
|
_forceRemove(currentNode);
|
|
1022
907
|
return true;
|
|
1023
908
|
}
|
|
1024
|
-
|
|
1025
909
|
/* Remove element if anything forbids its presence */
|
|
1026
910
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1027
911
|
/* Check if we have a custom element to handle */
|
|
@@ -1033,7 +917,6 @@ function createDOMPurify() {
|
|
|
1033
917
|
return false;
|
|
1034
918
|
}
|
|
1035
919
|
}
|
|
1036
|
-
|
|
1037
920
|
/* Keep content except for bad-listed elements */
|
|
1038
921
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
1039
922
|
const parentNode = getParentNode(currentNode) || currentNode.parentNode;
|
|
@@ -1050,19 +933,16 @@ function createDOMPurify() {
|
|
|
1050
933
|
_forceRemove(currentNode);
|
|
1051
934
|
return true;
|
|
1052
935
|
}
|
|
1053
|
-
|
|
1054
936
|
/* Check whether element has a valid namespace */
|
|
1055
937
|
if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
|
|
1056
938
|
_forceRemove(currentNode);
|
|
1057
939
|
return true;
|
|
1058
940
|
}
|
|
1059
|
-
|
|
1060
941
|
/* Make sure that older browsers don't get fallback-tag mXSS */
|
|
1061
942
|
if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
|
|
1062
943
|
_forceRemove(currentNode);
|
|
1063
944
|
return true;
|
|
1064
945
|
}
|
|
1065
|
-
|
|
1066
946
|
/* Sanitize element content to be template-safe */
|
|
1067
947
|
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
|
|
1068
948
|
/* Get the element's text content */
|
|
@@ -1077,19 +957,17 @@ function createDOMPurify() {
|
|
|
1077
957
|
currentNode.textContent = content;
|
|
1078
958
|
}
|
|
1079
959
|
}
|
|
1080
|
-
|
|
1081
960
|
/* Execute a hook if present */
|
|
1082
961
|
_executeHook('afterSanitizeElements', currentNode, null);
|
|
1083
962
|
return false;
|
|
1084
963
|
};
|
|
1085
|
-
|
|
1086
964
|
/**
|
|
1087
965
|
* _isValidAttribute
|
|
1088
966
|
*
|
|
1089
|
-
* @param
|
|
1090
|
-
* @param
|
|
1091
|
-
* @param
|
|
1092
|
-
* @return
|
|
967
|
+
* @param lcTag Lowercase tag name of containing element.
|
|
968
|
+
* @param lcName Lowercase attribute name.
|
|
969
|
+
* @param value Attribute value.
|
|
970
|
+
* @return Returns true if `value` is valid, otherwise false.
|
|
1093
971
|
*/
|
|
1094
972
|
// eslint-disable-next-line complexity
|
|
1095
973
|
const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
@@ -1097,7 +975,6 @@ function createDOMPurify() {
|
|
|
1097
975
|
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
1098
976
|
return false;
|
|
1099
977
|
}
|
|
1100
|
-
|
|
1101
978
|
/* Allow valid data-* attributes: At least one character after "-"
|
|
1102
979
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
1103
980
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
@@ -1119,19 +996,17 @@ function createDOMPurify() {
|
|
|
1119
996
|
} else ;
|
|
1120
997
|
return true;
|
|
1121
998
|
};
|
|
1122
|
-
|
|
1123
999
|
/**
|
|
1124
1000
|
* _isBasicCustomElement
|
|
1125
1001
|
* checks if at least one dash is included in tagName, and it's not the first char
|
|
1126
1002
|
* for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
|
|
1127
1003
|
*
|
|
1128
|
-
* @param
|
|
1129
|
-
* @returns
|
|
1004
|
+
* @param tagName name of the tag of the node to sanitize
|
|
1005
|
+
* @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
|
|
1130
1006
|
*/
|
|
1131
1007
|
const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
|
|
1132
1008
|
return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
|
|
1133
1009
|
};
|
|
1134
|
-
|
|
1135
1010
|
/**
|
|
1136
1011
|
* _sanitizeAttributes
|
|
1137
1012
|
*
|
|
@@ -1140,7 +1015,7 @@ function createDOMPurify() {
|
|
|
1140
1015
|
* @protect removeAttribute
|
|
1141
1016
|
* @protect setAttribute
|
|
1142
1017
|
*
|
|
1143
|
-
* @param
|
|
1018
|
+
* @param currentNode to sanitize
|
|
1144
1019
|
*/
|
|
1145
1020
|
const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
1146
1021
|
/* Execute a hook if present */
|
|
@@ -1148,7 +1023,6 @@ function createDOMPurify() {
|
|
|
1148
1023
|
const {
|
|
1149
1024
|
attributes
|
|
1150
1025
|
} = currentNode;
|
|
1151
|
-
|
|
1152
1026
|
/* Check if we have attributes; if not we might have a text node */
|
|
1153
1027
|
if (!attributes) {
|
|
1154
1028
|
return;
|
|
@@ -1157,10 +1031,10 @@ function createDOMPurify() {
|
|
|
1157
1031
|
attrName: '',
|
|
1158
1032
|
attrValue: '',
|
|
1159
1033
|
keepAttr: true,
|
|
1160
|
-
allowedAttributes: ALLOWED_ATTR
|
|
1034
|
+
allowedAttributes: ALLOWED_ATTR,
|
|
1035
|
+
forceKeepAttr: undefined
|
|
1161
1036
|
};
|
|
1162
1037
|
let l = attributes.length;
|
|
1163
|
-
|
|
1164
1038
|
/* Go backwards over all attributes; safely remove bad ones */
|
|
1165
1039
|
while (l--) {
|
|
1166
1040
|
const attr = attributes[l];
|
|
@@ -1171,7 +1045,6 @@ function createDOMPurify() {
|
|
|
1171
1045
|
} = attr;
|
|
1172
1046
|
const lcName = transformCaseFunc(name);
|
|
1173
1047
|
let value = name === 'value' ? attrValue : stringTrim(attrValue);
|
|
1174
|
-
|
|
1175
1048
|
/* Execute a hook if present */
|
|
1176
1049
|
hookEvent.attrName = lcName;
|
|
1177
1050
|
hookEvent.attrValue = value;
|
|
@@ -1179,56 +1052,46 @@ function createDOMPurify() {
|
|
|
1179
1052
|
hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
|
|
1180
1053
|
_executeHook('uponSanitizeAttribute', currentNode, hookEvent);
|
|
1181
1054
|
value = hookEvent.attrValue;
|
|
1182
|
-
|
|
1055
|
+
/* Full DOM Clobbering protection via namespace isolation,
|
|
1056
|
+
* Prefix id and name attributes with `user-content-`
|
|
1057
|
+
*/
|
|
1058
|
+
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
|
|
1059
|
+
// Remove the attribute with this value
|
|
1060
|
+
_removeAttribute(name, currentNode);
|
|
1061
|
+
// Prefix the value and later re-create the attribute with the sanitized value
|
|
1062
|
+
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
1063
|
+
}
|
|
1064
|
+
/* Work around a security issue with comments inside attributes */
|
|
1065
|
+
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
|
|
1066
|
+
_removeAttribute(name, currentNode);
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1183
1069
|
/* Did the hooks approve of the attribute? */
|
|
1184
1070
|
if (hookEvent.forceKeepAttr) {
|
|
1185
1071
|
continue;
|
|
1186
1072
|
}
|
|
1187
|
-
|
|
1188
1073
|
/* Remove attribute */
|
|
1189
1074
|
_removeAttribute(name, currentNode);
|
|
1190
|
-
|
|
1191
1075
|
/* Did the hooks approve of the attribute? */
|
|
1192
1076
|
if (!hookEvent.keepAttr) {
|
|
1193
1077
|
continue;
|
|
1194
1078
|
}
|
|
1195
|
-
|
|
1196
1079
|
/* Work around a security issue in jQuery 3.0 */
|
|
1197
1080
|
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
|
|
1198
1081
|
_removeAttribute(name, currentNode);
|
|
1199
1082
|
continue;
|
|
1200
1083
|
}
|
|
1201
|
-
|
|
1202
1084
|
/* Sanitize attribute content to be template-safe */
|
|
1203
1085
|
if (SAFE_FOR_TEMPLATES) {
|
|
1204
1086
|
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
1205
1087
|
value = stringReplace(value, expr, ' ');
|
|
1206
1088
|
});
|
|
1207
1089
|
}
|
|
1208
|
-
|
|
1209
1090
|
/* Is `value` valid for this attribute? */
|
|
1210
1091
|
const lcTag = transformCaseFunc(currentNode.nodeName);
|
|
1211
1092
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
1212
1093
|
continue;
|
|
1213
1094
|
}
|
|
1214
|
-
|
|
1215
|
-
/* Full DOM Clobbering protection via namespace isolation,
|
|
1216
|
-
* Prefix id and name attributes with `user-content-`
|
|
1217
|
-
*/
|
|
1218
|
-
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
|
|
1219
|
-
// Remove the attribute with this value
|
|
1220
|
-
_removeAttribute(name, currentNode);
|
|
1221
|
-
|
|
1222
|
-
// Prefix the value and later re-create the attribute with the sanitized value
|
|
1223
|
-
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
/* Work around a security issue with comments inside attributes */
|
|
1227
|
-
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
|
|
1228
|
-
_removeAttribute(name, currentNode);
|
|
1229
|
-
continue;
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
1095
|
/* Handle attributes that require Trusted Types */
|
|
1233
1096
|
if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
|
|
1234
1097
|
if (namespaceURI) ; else {
|
|
@@ -1246,7 +1109,6 @@ function createDOMPurify() {
|
|
|
1246
1109
|
}
|
|
1247
1110
|
}
|
|
1248
1111
|
}
|
|
1249
|
-
|
|
1250
1112
|
/* Handle invalid data-* attribute set by try-catching it */
|
|
1251
1113
|
try {
|
|
1252
1114
|
if (namespaceURI) {
|
|
@@ -1262,51 +1124,36 @@ function createDOMPurify() {
|
|
|
1262
1124
|
}
|
|
1263
1125
|
} catch (_) {}
|
|
1264
1126
|
}
|
|
1265
|
-
|
|
1266
1127
|
/* Execute a hook if present */
|
|
1267
1128
|
_executeHook('afterSanitizeAttributes', currentNode, null);
|
|
1268
1129
|
};
|
|
1269
|
-
|
|
1270
1130
|
/**
|
|
1271
1131
|
* _sanitizeShadowDOM
|
|
1272
1132
|
*
|
|
1273
|
-
* @param
|
|
1133
|
+
* @param fragment to iterate over recursively
|
|
1274
1134
|
*/
|
|
1275
1135
|
const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
|
|
1276
1136
|
let shadowNode = null;
|
|
1277
1137
|
const shadowIterator = _createNodeIterator(fragment);
|
|
1278
|
-
|
|
1279
1138
|
/* Execute a hook if present */
|
|
1280
1139
|
_executeHook('beforeSanitizeShadowDOM', fragment, null);
|
|
1281
1140
|
while (shadowNode = shadowIterator.nextNode()) {
|
|
1282
1141
|
/* Execute a hook if present */
|
|
1283
1142
|
_executeHook('uponSanitizeShadowNode', shadowNode, null);
|
|
1284
|
-
|
|
1285
1143
|
/* Sanitize tags and elements */
|
|
1286
1144
|
if (_sanitizeElements(shadowNode)) {
|
|
1287
1145
|
continue;
|
|
1288
1146
|
}
|
|
1289
|
-
|
|
1290
1147
|
/* Deep shadow DOM detected */
|
|
1291
1148
|
if (shadowNode.content instanceof DocumentFragment) {
|
|
1292
1149
|
_sanitizeShadowDOM(shadowNode.content);
|
|
1293
1150
|
}
|
|
1294
|
-
|
|
1295
1151
|
/* Check attributes, sanitize if necessary */
|
|
1296
1152
|
_sanitizeAttributes(shadowNode);
|
|
1297
1153
|
}
|
|
1298
|
-
|
|
1299
1154
|
/* Execute a hook if present */
|
|
1300
1155
|
_executeHook('afterSanitizeShadowDOM', fragment, null);
|
|
1301
1156
|
};
|
|
1302
|
-
|
|
1303
|
-
/**
|
|
1304
|
-
* Sanitize
|
|
1305
|
-
* Public method providing core sanitation functionality
|
|
1306
|
-
*
|
|
1307
|
-
* @param {String|Node} dirty string or DOM node
|
|
1308
|
-
* @param {Object} cfg object
|
|
1309
|
-
*/
|
|
1310
1157
|
// eslint-disable-next-line complexity
|
|
1311
1158
|
DOMPurify.sanitize = function (dirty) {
|
|
1312
1159
|
let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
@@ -1321,7 +1168,6 @@ function createDOMPurify() {
|
|
|
1321
1168
|
if (IS_EMPTY_INPUT) {
|
|
1322
1169
|
dirty = '<!-->';
|
|
1323
1170
|
}
|
|
1324
|
-
|
|
1325
1171
|
/* Stringify, in case dirty is an object */
|
|
1326
1172
|
if (typeof dirty !== 'string' && !_isNode(dirty)) {
|
|
1327
1173
|
if (typeof dirty.toString === 'function') {
|
|
@@ -1333,20 +1179,16 @@ function createDOMPurify() {
|
|
|
1333
1179
|
throw typeErrorCreate('toString is not a function');
|
|
1334
1180
|
}
|
|
1335
1181
|
}
|
|
1336
|
-
|
|
1337
1182
|
/* Return dirty HTML if DOMPurify cannot run */
|
|
1338
1183
|
if (!DOMPurify.isSupported) {
|
|
1339
1184
|
return dirty;
|
|
1340
1185
|
}
|
|
1341
|
-
|
|
1342
1186
|
/* Assign config vars */
|
|
1343
1187
|
if (!SET_CONFIG) {
|
|
1344
1188
|
_parseConfig(cfg);
|
|
1345
1189
|
}
|
|
1346
|
-
|
|
1347
1190
|
/* Clean up removed elements */
|
|
1348
1191
|
DOMPurify.removed = [];
|
|
1349
|
-
|
|
1350
1192
|
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1351
1193
|
if (typeof dirty === 'string') {
|
|
1352
1194
|
IN_PLACE = false;
|
|
@@ -1380,45 +1222,36 @@ function createDOMPurify() {
|
|
|
1380
1222
|
dirty.indexOf('<') === -1) {
|
|
1381
1223
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
1382
1224
|
}
|
|
1383
|
-
|
|
1384
1225
|
/* Initialize the document to work on */
|
|
1385
1226
|
body = _initDocument(dirty);
|
|
1386
|
-
|
|
1387
1227
|
/* Check we have a DOM node from the data */
|
|
1388
1228
|
if (!body) {
|
|
1389
1229
|
return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
|
|
1390
1230
|
}
|
|
1391
1231
|
}
|
|
1392
|
-
|
|
1393
1232
|
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1394
1233
|
if (body && FORCE_BODY) {
|
|
1395
1234
|
_forceRemove(body.firstChild);
|
|
1396
1235
|
}
|
|
1397
|
-
|
|
1398
1236
|
/* Get node iterator */
|
|
1399
1237
|
const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
|
|
1400
|
-
|
|
1401
1238
|
/* Now start iterating over the created document */
|
|
1402
1239
|
while (currentNode = nodeIterator.nextNode()) {
|
|
1403
1240
|
/* Sanitize tags and elements */
|
|
1404
1241
|
if (_sanitizeElements(currentNode)) {
|
|
1405
1242
|
continue;
|
|
1406
1243
|
}
|
|
1407
|
-
|
|
1408
1244
|
/* Shadow DOM detected, sanitize it */
|
|
1409
1245
|
if (currentNode.content instanceof DocumentFragment) {
|
|
1410
1246
|
_sanitizeShadowDOM(currentNode.content);
|
|
1411
1247
|
}
|
|
1412
|
-
|
|
1413
1248
|
/* Check attributes, sanitize if necessary */
|
|
1414
1249
|
_sanitizeAttributes(currentNode);
|
|
1415
1250
|
}
|
|
1416
|
-
|
|
1417
1251
|
/* If we sanitized `dirty` in-place, return it. */
|
|
1418
1252
|
if (IN_PLACE) {
|
|
1419
1253
|
return dirty;
|
|
1420
1254
|
}
|
|
1421
|
-
|
|
1422
1255
|
/* Return sanitized string or DOM */
|
|
1423
1256
|
if (RETURN_DOM) {
|
|
1424
1257
|
if (RETURN_DOM_FRAGMENT) {
|
|
@@ -1443,12 +1276,10 @@ function createDOMPurify() {
|
|
|
1443
1276
|
return returnNode;
|
|
1444
1277
|
}
|
|
1445
1278
|
let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
|
|
1446
|
-
|
|
1447
1279
|
/* Serialize doctype if allowed */
|
|
1448
1280
|
if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
|
|
1449
1281
|
serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
|
|
1450
1282
|
}
|
|
1451
|
-
|
|
1452
1283
|
/* Sanitize final string template-safe */
|
|
1453
1284
|
if (SAFE_FOR_TEMPLATES) {
|
|
1454
1285
|
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
@@ -1457,39 +1288,15 @@ function createDOMPurify() {
|
|
|
1457
1288
|
}
|
|
1458
1289
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
|
|
1459
1290
|
};
|
|
1460
|
-
|
|
1461
|
-
/**
|
|
1462
|
-
* Public method to set the configuration once
|
|
1463
|
-
* setConfig
|
|
1464
|
-
*
|
|
1465
|
-
* @param {Object} cfg configuration object
|
|
1466
|
-
*/
|
|
1467
1291
|
DOMPurify.setConfig = function () {
|
|
1468
1292
|
let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
1469
1293
|
_parseConfig(cfg);
|
|
1470
1294
|
SET_CONFIG = true;
|
|
1471
1295
|
};
|
|
1472
|
-
|
|
1473
|
-
/**
|
|
1474
|
-
* Public method to remove the configuration
|
|
1475
|
-
* clearConfig
|
|
1476
|
-
*
|
|
1477
|
-
*/
|
|
1478
1296
|
DOMPurify.clearConfig = function () {
|
|
1479
1297
|
CONFIG = null;
|
|
1480
1298
|
SET_CONFIG = false;
|
|
1481
1299
|
};
|
|
1482
|
-
|
|
1483
|
-
/**
|
|
1484
|
-
* Public method to check if an attribute value is valid.
|
|
1485
|
-
* Uses last set config, if any. Otherwise, uses config defaults.
|
|
1486
|
-
* isValidAttribute
|
|
1487
|
-
*
|
|
1488
|
-
* @param {String} tag Tag name of containing element.
|
|
1489
|
-
* @param {String} attr Attribute name.
|
|
1490
|
-
* @param {String} value Attribute value.
|
|
1491
|
-
* @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
|
|
1492
|
-
*/
|
|
1493
1300
|
DOMPurify.isValidAttribute = function (tag, attr, value) {
|
|
1494
1301
|
/* Initialize shared config vars if necessary. */
|
|
1495
1302
|
if (!CONFIG) {
|
|
@@ -1499,14 +1306,6 @@ function createDOMPurify() {
|
|
|
1499
1306
|
const lcName = transformCaseFunc(attr);
|
|
1500
1307
|
return _isValidAttribute(lcTag, lcName, value);
|
|
1501
1308
|
};
|
|
1502
|
-
|
|
1503
|
-
/**
|
|
1504
|
-
* AddHook
|
|
1505
|
-
* Public method to add DOMPurify hooks
|
|
1506
|
-
*
|
|
1507
|
-
* @param {String} entryPoint entry point for the hook to add
|
|
1508
|
-
* @param {Function} hookFunction function to execute
|
|
1509
|
-
*/
|
|
1510
1309
|
DOMPurify.addHook = function (entryPoint, hookFunction) {
|
|
1511
1310
|
if (typeof hookFunction !== 'function') {
|
|
1512
1311
|
return;
|
|
@@ -1514,37 +1313,16 @@ function createDOMPurify() {
|
|
|
1514
1313
|
hooks[entryPoint] = hooks[entryPoint] || [];
|
|
1515
1314
|
arrayPush(hooks[entryPoint], hookFunction);
|
|
1516
1315
|
};
|
|
1517
|
-
|
|
1518
|
-
/**
|
|
1519
|
-
* RemoveHook
|
|
1520
|
-
* Public method to remove a DOMPurify hook at a given entryPoint
|
|
1521
|
-
* (pops it from the stack of hooks if more are present)
|
|
1522
|
-
*
|
|
1523
|
-
* @param {String} entryPoint entry point for the hook to remove
|
|
1524
|
-
* @return {Function} removed(popped) hook
|
|
1525
|
-
*/
|
|
1526
1316
|
DOMPurify.removeHook = function (entryPoint) {
|
|
1527
1317
|
if (hooks[entryPoint]) {
|
|
1528
1318
|
return arrayPop(hooks[entryPoint]);
|
|
1529
1319
|
}
|
|
1530
1320
|
};
|
|
1531
|
-
|
|
1532
|
-
/**
|
|
1533
|
-
* RemoveHooks
|
|
1534
|
-
* Public method to remove all DOMPurify hooks at a given entryPoint
|
|
1535
|
-
*
|
|
1536
|
-
* @param {String} entryPoint entry point for the hooks to remove
|
|
1537
|
-
*/
|
|
1538
1321
|
DOMPurify.removeHooks = function (entryPoint) {
|
|
1539
1322
|
if (hooks[entryPoint]) {
|
|
1540
1323
|
hooks[entryPoint] = [];
|
|
1541
1324
|
}
|
|
1542
1325
|
};
|
|
1543
|
-
|
|
1544
|
-
/**
|
|
1545
|
-
* RemoveAllHooks
|
|
1546
|
-
* Public method to remove all DOMPurify hooks
|
|
1547
|
-
*/
|
|
1548
1326
|
DOMPurify.removeAllHooks = function () {
|
|
1549
1327
|
hooks = {};
|
|
1550
1328
|
};
|