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