dompurify 3.1.4 → 3.1.6
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 +11 -2
- package/dist/purify.cjs.js +15 -73
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.mjs +15 -73
- package/dist/purify.es.mjs.map +1 -1
- package/dist/purify.js +15 -73
- 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 +1 -1
package/README.md
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG.
|
|
8
8
|
|
|
9
|
-
It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version **v3.1.
|
|
9
|
+
It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version **v3.1.6**.
|
|
10
10
|
|
|
11
11
|
DOMPurify is written in JavaScript and works in all modern browsers (Safari (10+), Opera (15+), Edge, Firefox and Chrome - as well as almost anything else using Blink, Gecko or WebKit). It doesn't break on MSIE or other legacy browsers. It simply does nothing.
|
|
12
12
|
|
|
13
|
-
**Note that [DOMPurify v2.5.
|
|
13
|
+
**Note that [DOMPurify v2.5.6](https://github.com/cure53/DOMPurify/releases/tag/2.5.6) is the latest version supporting MSIE. For important security updates compatible with MSIE, please use the [2.x branch](https://github.com/cure53/DOMPurify/tree/2.x).**
|
|
14
14
|
|
|
15
15
|
Our automated tests cover [19 different browsers](https://github.com/cure53/DOMPurify/blob/main/test/karma.custom-launchers.config.js#L5) right now, more to come. We also cover Node.js v16.x, v17.x, v18.x and v19.x, running DOMPurify on [jsdom](https://github.com/jsdom/jsdom). Older Node versions are known to work as well, but hey... no guarantees.
|
|
16
16
|
|
|
@@ -181,6 +181,9 @@ const clean = DOMPurify.sanitize(dirty, {SAFE_FOR_TEMPLATES: true});
|
|
|
181
181
|
|
|
182
182
|
|
|
183
183
|
// change how e.g. comments containing risky HTML characters are treated.
|
|
184
|
+
// be very careful, this setting should only be set to `false` if you really only handle
|
|
185
|
+
// HTML and nothing else, no SVG, MathML or the like.
|
|
186
|
+
// Otherwise, changing from `true` to `false` will lead to XSS in this or some other way.
|
|
184
187
|
const clean = DOMPurify.sanitize(dirty, {SAFE_FOR_XML: false});
|
|
185
188
|
```
|
|
186
189
|
|
|
@@ -378,6 +381,12 @@ DOMPurify.addHook(
|
|
|
378
381
|
);
|
|
379
382
|
```
|
|
380
383
|
|
|
384
|
+
## Removed Configuration
|
|
385
|
+
|
|
386
|
+
| Option | Since | Note |
|
|
387
|
+
|-----------------|-------|--------------------------|
|
|
388
|
+
| SAFE_FOR_JQUERY | 2.1.0 | No replacement required. |
|
|
389
|
+
|
|
381
390
|
## Continuous Integration
|
|
382
391
|
|
|
383
392
|
We are currently using Github Actions in combination with BrowserStack. This gives us the possibility to confirm for each and every commit that all is going according to plan in all supported browsers. Check out the build logs here: https://github.com/cure53/DOMPurify/actions
|
package/dist/purify.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @license DOMPurify 3.1.
|
|
1
|
+
/*! @license DOMPurify 3.1.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.1.6/LICENSE */
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
@@ -50,10 +50,6 @@ 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
|
-
function numberIsNaN(x) {
|
|
54
|
-
// eslint-disable-next-line unicorn/prefer-number-properties
|
|
55
|
-
return typeof x === 'number' && isNaN(x);
|
|
56
|
-
}
|
|
57
53
|
|
|
58
54
|
/**
|
|
59
55
|
* Creates a new function that calls the given function with a specified thisArg and arguments.
|
|
@@ -215,11 +211,9 @@ const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-
|
|
|
215
211
|
const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
216
212
|
const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
|
217
213
|
);
|
|
218
|
-
|
|
219
214
|
const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
|
220
215
|
const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
|
|
221
216
|
);
|
|
222
|
-
|
|
223
217
|
const DOCTYPE_NAME = seal(/^html$/i);
|
|
224
218
|
const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
|
|
225
219
|
|
|
@@ -254,7 +248,6 @@ const NODE_TYPE = {
|
|
|
254
248
|
documentFragment: 11,
|
|
255
249
|
notation: 12 // Deprecated
|
|
256
250
|
};
|
|
257
|
-
|
|
258
251
|
const getGlobal = function getGlobal() {
|
|
259
252
|
return typeof window === 'undefined' ? null : window;
|
|
260
253
|
};
|
|
@@ -306,7 +299,7 @@ function createDOMPurify() {
|
|
|
306
299
|
* Version label, exposed for easier checks
|
|
307
300
|
* if DOMPurify is up to date or not
|
|
308
301
|
*/
|
|
309
|
-
DOMPurify.version = '3.1.
|
|
302
|
+
DOMPurify.version = '3.1.6';
|
|
310
303
|
|
|
311
304
|
/**
|
|
312
305
|
* Array of elements that DOMPurify removed during sanitation.
|
|
@@ -337,6 +330,7 @@ function createDOMPurify() {
|
|
|
337
330
|
} = window;
|
|
338
331
|
const ElementPrototype = Element.prototype;
|
|
339
332
|
const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
333
|
+
const remove = lookupGetter(ElementPrototype, 'remove');
|
|
340
334
|
const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
341
335
|
const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
342
336
|
const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
@@ -539,9 +533,6 @@ function createDOMPurify() {
|
|
|
539
533
|
/* Keep a reference to config to pass to hooks */
|
|
540
534
|
let CONFIG = null;
|
|
541
535
|
|
|
542
|
-
/* Specify the maximum element nesting depth to prevent mXSS */
|
|
543
|
-
const MAX_NESTING_DEPTH = 255;
|
|
544
|
-
|
|
545
536
|
/* Ideally, do not touch anything below this line */
|
|
546
537
|
/* ______________________________________________ */
|
|
547
538
|
|
|
@@ -840,9 +831,9 @@ function createDOMPurify() {
|
|
|
840
831
|
});
|
|
841
832
|
try {
|
|
842
833
|
// eslint-disable-next-line unicorn/prefer-dom-node-remove
|
|
843
|
-
node.
|
|
834
|
+
getParentNode(node).removeChild(node);
|
|
844
835
|
} catch (_) {
|
|
845
|
-
|
|
836
|
+
remove(node);
|
|
846
837
|
}
|
|
847
838
|
};
|
|
848
839
|
|
|
@@ -952,11 +943,7 @@ function createDOMPurify() {
|
|
|
952
943
|
* @return {Boolean} true if clobbered, false if safe
|
|
953
944
|
*/
|
|
954
945
|
const _isClobbered = function _isClobbered(elm) {
|
|
955
|
-
return elm instanceof HTMLFormElement && (
|
|
956
|
-
// eslint-disable-next-line unicorn/no-typeof-undefined
|
|
957
|
-
typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' ||
|
|
958
|
-
// eslint-disable-next-line unicorn/no-typeof-undefined
|
|
959
|
-
typeof elm.__removalCount !== 'undefined' && typeof elm.__removalCount !== 'number' || typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function');
|
|
946
|
+
return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function');
|
|
960
947
|
};
|
|
961
948
|
|
|
962
949
|
/**
|
|
@@ -1023,7 +1010,7 @@ function createDOMPurify() {
|
|
|
1023
1010
|
return true;
|
|
1024
1011
|
}
|
|
1025
1012
|
|
|
1026
|
-
/* Remove any
|
|
1013
|
+
/* Remove any occurrence of processing instructions */
|
|
1027
1014
|
if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
|
|
1028
1015
|
_forceRemove(currentNode);
|
|
1029
1016
|
return true;
|
|
@@ -1107,7 +1094,7 @@ function createDOMPurify() {
|
|
|
1107
1094
|
// eslint-disable-next-line complexity
|
|
1108
1095
|
const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
1109
1096
|
/* Make sure attribute cannot clobber */
|
|
1110
|
-
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement
|
|
1097
|
+
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
1111
1098
|
return false;
|
|
1112
1099
|
}
|
|
1113
1100
|
|
|
@@ -1192,6 +1179,13 @@ function createDOMPurify() {
|
|
|
1192
1179
|
hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
|
|
1193
1180
|
_executeHook('uponSanitizeAttribute', currentNode, hookEvent);
|
|
1194
1181
|
value = hookEvent.attrValue;
|
|
1182
|
+
|
|
1183
|
+
/* Work around a security issue with comments inside attributes */
|
|
1184
|
+
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
|
|
1185
|
+
_removeAttribute(name, currentNode);
|
|
1186
|
+
continue;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1195
1189
|
/* Did the hooks approve of the attribute? */
|
|
1196
1190
|
if (hookEvent.forceKeepAttr) {
|
|
1197
1191
|
continue;
|
|
@@ -1211,12 +1205,6 @@ function createDOMPurify() {
|
|
|
1211
1205
|
continue;
|
|
1212
1206
|
}
|
|
1213
1207
|
|
|
1214
|
-
/* Work around a security issue with comments inside attributes */
|
|
1215
|
-
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
|
|
1216
|
-
_removeAttribute(name, currentNode);
|
|
1217
|
-
continue;
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
1208
|
/* Sanitize attribute content to be template-safe */
|
|
1221
1209
|
if (SAFE_FOR_TEMPLATES) {
|
|
1222
1210
|
arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
|
|
@@ -1298,32 +1286,9 @@ function createDOMPurify() {
|
|
|
1298
1286
|
if (_sanitizeElements(shadowNode)) {
|
|
1299
1287
|
continue;
|
|
1300
1288
|
}
|
|
1301
|
-
const parentNode = getParentNode(shadowNode);
|
|
1302
|
-
|
|
1303
|
-
/* Set the nesting depth of an element */
|
|
1304
|
-
if (shadowNode.nodeType === NODE_TYPE.element) {
|
|
1305
|
-
if (parentNode && parentNode.__depth) {
|
|
1306
|
-
/*
|
|
1307
|
-
We want the depth of the node in the original tree, which can
|
|
1308
|
-
change when it's removed from its parent.
|
|
1309
|
-
*/
|
|
1310
|
-
shadowNode.__depth = (shadowNode.__removalCount || 0) + parentNode.__depth + 1;
|
|
1311
|
-
} else {
|
|
1312
|
-
shadowNode.__depth = 1;
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
/*
|
|
1317
|
-
* Remove an element if nested too deeply to avoid mXSS
|
|
1318
|
-
* or if the __depth might have been tampered with
|
|
1319
|
-
*/
|
|
1320
|
-
if (shadowNode.__depth >= MAX_NESTING_DEPTH || shadowNode.__depth < 0 || numberIsNaN(shadowNode.__depth)) {
|
|
1321
|
-
_forceRemove(shadowNode);
|
|
1322
|
-
}
|
|
1323
1289
|
|
|
1324
1290
|
/* Deep shadow DOM detected */
|
|
1325
1291
|
if (shadowNode.content instanceof DocumentFragment) {
|
|
1326
|
-
shadowNode.content.__depth = shadowNode.__depth;
|
|
1327
1292
|
_sanitizeShadowDOM(shadowNode.content);
|
|
1328
1293
|
}
|
|
1329
1294
|
|
|
@@ -1439,32 +1404,9 @@ function createDOMPurify() {
|
|
|
1439
1404
|
if (_sanitizeElements(currentNode)) {
|
|
1440
1405
|
continue;
|
|
1441
1406
|
}
|
|
1442
|
-
const parentNode = getParentNode(currentNode);
|
|
1443
|
-
|
|
1444
|
-
/* Set the nesting depth of an element */
|
|
1445
|
-
if (currentNode.nodeType === NODE_TYPE.element) {
|
|
1446
|
-
if (parentNode && parentNode.__depth) {
|
|
1447
|
-
/*
|
|
1448
|
-
We want the depth of the node in the original tree, which can
|
|
1449
|
-
change when it's removed from its parent.
|
|
1450
|
-
*/
|
|
1451
|
-
currentNode.__depth = (currentNode.__removalCount || 0) + parentNode.__depth + 1;
|
|
1452
|
-
} else {
|
|
1453
|
-
currentNode.__depth = 1;
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
/*
|
|
1458
|
-
* Remove an element if nested too deeply to avoid mXSS
|
|
1459
|
-
* or if the __depth might have been tampered with
|
|
1460
|
-
*/
|
|
1461
|
-
if (currentNode.__depth >= MAX_NESTING_DEPTH || currentNode.__depth < 0 || numberIsNaN(currentNode.__depth)) {
|
|
1462
|
-
_forceRemove(currentNode);
|
|
1463
|
-
}
|
|
1464
1407
|
|
|
1465
1408
|
/* Shadow DOM detected, sanitize it */
|
|
1466
1409
|
if (currentNode.content instanceof DocumentFragment) {
|
|
1467
|
-
currentNode.content.__depth = currentNode.__depth;
|
|
1468
1410
|
_sanitizeShadowDOM(currentNode.content);
|
|
1469
1411
|
}
|
|
1470
1412
|
|