dompurify 3.1.0 → 3.1.2
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 +17 -8
- package/dist/purify.cjs.js +54 -5
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.mjs +54 -5
- package/dist/purify.es.mjs.map +1 -1
- package/dist/purify.js +54 -5
- 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.2**.
|
|
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.2](https://github.com/cure53/DOMPurify/releases/tag/2.5.2) 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
|
|
|
@@ -73,7 +73,7 @@ After sanitizing your markup, you can also have a look at the property `DOMPurif
|
|
|
73
73
|
|
|
74
74
|
DOMPurify technically also works server-side with Node.js. Our support strives to follow the [Node.js release cycle](https://nodejs.org/en/about/releases/).
|
|
75
75
|
|
|
76
|
-
Running DOMPurify on the server requires a DOM to be present, which is probably no surprise. Usually, [jsdom](https://github.com/jsdom/jsdom) is the tool of choice and we **strongly recommend** to use the latest version of _jsdom_.
|
|
76
|
+
Running DOMPurify on the server requires a DOM to be present, which is probably no surprise. Usually, [jsdom](https://github.com/jsdom/jsdom) is the tool of choice and we **strongly recommend** to use the latest version of _jsdom_.
|
|
77
77
|
|
|
78
78
|
Why? Because older versions of _jsdom_ are known to be buggy in ways that result in XSS _even if_ DOMPurify does everything 100% correctly. There are **known attack vectors** in, e.g. _jsdom v19.0.0_ that are fixed in _jsdom v20.0.0_ - and we really recommend to keep _jsdom_ up to date because of that.
|
|
79
79
|
|
|
@@ -158,6 +158,15 @@ In version 2.0.0, a config flag was added to control DOMPurify's behavior regard
|
|
|
158
158
|
|
|
159
159
|
When `DOMPurify.sanitize` is used in an environment where the Trusted Types API is available and `RETURN_TRUSTED_TYPE` is set to `true`, it tries to return a `TrustedHTML` value instead of a string (the behavior for `RETURN_DOM` and `RETURN_DOM_FRAGMENT` config options does not change).
|
|
160
160
|
|
|
161
|
+
Note that in order to create a policy in `trustedTypes` using DOMPurify, `RETURN_TRUSTED_TYPE: false` is required, as `createHTML` expects a normal string, not `TrustedHTML`. The example below shows this.
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
window.trustedTypes!.createPolicy('default', {
|
|
165
|
+
createHTML: (to_escape) =>
|
|
166
|
+
DOMPurify.sanitize(to_escape, { RETURN_TRUSTED_TYPE: false }),
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
161
170
|
## Can I configure DOMPurify?
|
|
162
171
|
|
|
163
172
|
Yes. The included default configuration values are pretty good already - but you can of course override them. Check out the [`/demos`](https://github.com/cure53/DOMPurify/tree/main/demos) folder to see a bunch of examples on how you can [customize DOMPurify](https://github.com/cure53/DOMPurify/tree/main/demos#what-is-this).
|
|
@@ -360,11 +369,11 @@ _Example_:
|
|
|
360
369
|
|
|
361
370
|
```js
|
|
362
371
|
DOMPurify.addHook(
|
|
363
|
-
'
|
|
372
|
+
'uponSanitizeAttribute',
|
|
364
373
|
function (currentNode, hookEvent, config) {
|
|
365
|
-
// Do something with the current node
|
|
366
|
-
// You can also mutate hookEvent (i.e. set hookEvent.forceKeepAttr = true)
|
|
367
|
-
|
|
374
|
+
// Do something with the current node
|
|
375
|
+
// You can also mutate hookEvent for current node (i.e. set hookEvent.forceKeepAttr = true)
|
|
376
|
+
// For other than 'uponSanitizeAttribute' hook types hookEvent equals to null
|
|
368
377
|
}
|
|
369
378
|
);
|
|
370
379
|
```
|
|
@@ -415,7 +424,7 @@ Feature releases will not be announced to this list.
|
|
|
415
424
|
|
|
416
425
|
Many people helped and help DOMPurify become what it is and need to be acknowledged here!
|
|
417
426
|
|
|
418
|
-
[dcramer 💸](https://github.com/dcramer), [JGraph 💸](https://github.com/jgraph), [baekilda 💸](https://github.com/baekilda), [Healthchecks 💸](https://github.com/healthchecks), [Sentry 💸](https://github.com/getsentry), [jarrodldavis 💸](https://github.com/jarrodldavis), [CynegeticIO](https://github.com/CynegeticIO), [ssi02014 ❤️](https://github.com/ssi02014), [kevin_mizu](https://twitter.com/kevin_mizu), [GrantGryczan](https://github.com/GrantGryczan), [Lowdefy](https://twitter.com/lowdefy), [granlem](https://twitter.com/MaximeVeit), [oreoshake](https://github.com/oreoshake), [tdeekens ❤️](https://github.com/tdeekens), [peernohell ❤️](https://github.com/peernohell), [is2ei](https://github.com/is2ei), [SoheilKhodayari](https://github.com/SoheilKhodayari), [franktopel](https://github.com/franktopel), [NateScarlet](https://github.com/NateScarlet), [neilj](https://github.com/neilj), [fhemberger](https://github.com/fhemberger), [Joris-van-der-Wel](https://github.com/Joris-van-der-Wel), [ydaniv](https://github.com/ydaniv), [terjanq](https://twitter.com/terjanq), [filedescriptor](https://github.com/filedescriptor), [ConradIrwin](https://github.com/ConradIrwin), [gibson042](https://github.com/gibson042), [choumx](https://github.com/choumx), [0xSobky](https://github.com/0xSobky), [styfle](https://github.com/styfle), [koto](https://github.com/koto), [tlau88](https://github.com/tlau88), [strugee](https://github.com/strugee), [oparoz](https://github.com/oparoz), [mathiasbynens](https://github.com/mathiasbynens), [edg2s](https://github.com/edg2s), [dnkolegov](https://github.com/dnkolegov), [dhardtke](https://github.com/dhardtke), [wirehead](https://github.com/wirehead), [thorn0](https://github.com/thorn0), [styu](https://github.com/styu), [mozfreddyb](https://github.com/mozfreddyb), [mikesamuel](https://github.com/mikesamuel), [jorangreef](https://github.com/jorangreef), [jimmyhchan](https://github.com/jimmyhchan), [jameydeorio](https://github.com/jameydeorio), [jameskraus](https://github.com/jameskraus), [hyderali](https://github.com/hyderali), [hansottowirtz](https://github.com/hansottowirtz), [hackvertor](https://github.com/hackvertor), [freddyb](https://github.com/freddyb), [flavorjones](https://github.com/flavorjones), [djfarrelly](https://github.com/djfarrelly), [devd](https://github.com/devd), [camerondunford](https://github.com/camerondunford), [buu700](https://github.com/buu700), [buildog](https://github.com/buildog), [alabiaga](https://github.com/alabiaga), [Vector919](https://github.com/Vector919), [Robbert](https://github.com/Robbert), [GreLI](https://github.com/GreLI), [FuzzySockets](https://github.com/FuzzySockets), [ArtemBernatskyy](https://github.com/ArtemBernatskyy), [@garethheyes](https://twitter.com/garethheyes), [@shafigullin](https://twitter.com/shafigullin), [@mmrupp](https://twitter.com/mmrupp), [@irsdl](https://twitter.com/irsdl),[ShikariSenpai](https://github.com/ShikariSenpai), [ansjdnakjdnajkd](https://github.com/ansjdnakjdnajkd), [@asutherland](https://twitter.com/asutherland), [@mathias](https://twitter.com/mathias), [@cgvwzq](https://twitter.com/cgvwzq), [@robbertatwork](https://twitter.com/robbertatwork), [@giutro](https://twitter.com/giutro), [@CmdEngineer\_](https://twitter.com/CmdEngineer_), [@avr4mit](https://twitter.com/avr4mit) and especially [@securitymb ❤️](https://twitter.com/securitymb) & [@masatokinugawa ❤️](https://twitter.com/masatokinugawa)
|
|
427
|
+
[icesfont ❤️](https://github.com/icesfont) [dcramer 💸](https://github.com/dcramer), [JGraph 💸](https://github.com/jgraph), [baekilda 💸](https://github.com/baekilda), [Healthchecks 💸](https://github.com/healthchecks), [Sentry 💸](https://github.com/getsentry), [jarrodldavis 💸](https://github.com/jarrodldavis), [CynegeticIO](https://github.com/CynegeticIO), [ssi02014 ❤️](https://github.com/ssi02014), [kevin_mizu](https://twitter.com/kevin_mizu), [GrantGryczan](https://github.com/GrantGryczan), [Lowdefy](https://twitter.com/lowdefy), [granlem](https://twitter.com/MaximeVeit), [oreoshake](https://github.com/oreoshake), [tdeekens ❤️](https://github.com/tdeekens), [peernohell ❤️](https://github.com/peernohell), [is2ei](https://github.com/is2ei), [SoheilKhodayari](https://github.com/SoheilKhodayari), [franktopel](https://github.com/franktopel), [NateScarlet](https://github.com/NateScarlet), [neilj](https://github.com/neilj), [fhemberger](https://github.com/fhemberger), [Joris-van-der-Wel](https://github.com/Joris-van-der-Wel), [ydaniv](https://github.com/ydaniv), [terjanq](https://twitter.com/terjanq), [filedescriptor](https://github.com/filedescriptor), [ConradIrwin](https://github.com/ConradIrwin), [gibson042](https://github.com/gibson042), [choumx](https://github.com/choumx), [0xSobky](https://github.com/0xSobky), [styfle](https://github.com/styfle), [koto](https://github.com/koto), [tlau88](https://github.com/tlau88), [strugee](https://github.com/strugee), [oparoz](https://github.com/oparoz), [mathiasbynens](https://github.com/mathiasbynens), [edg2s](https://github.com/edg2s), [dnkolegov](https://github.com/dnkolegov), [dhardtke](https://github.com/dhardtke), [wirehead](https://github.com/wirehead), [thorn0](https://github.com/thorn0), [styu](https://github.com/styu), [mozfreddyb](https://github.com/mozfreddyb), [mikesamuel](https://github.com/mikesamuel), [jorangreef](https://github.com/jorangreef), [jimmyhchan](https://github.com/jimmyhchan), [jameydeorio](https://github.com/jameydeorio), [jameskraus](https://github.com/jameskraus), [hyderali](https://github.com/hyderali), [hansottowirtz](https://github.com/hansottowirtz), [hackvertor](https://github.com/hackvertor), [freddyb](https://github.com/freddyb), [flavorjones](https://github.com/flavorjones), [djfarrelly](https://github.com/djfarrelly), [devd](https://github.com/devd), [camerondunford](https://github.com/camerondunford), [buu700](https://github.com/buu700), [buildog](https://github.com/buildog), [alabiaga](https://github.com/alabiaga), [Vector919](https://github.com/Vector919), [Robbert](https://github.com/Robbert), [GreLI](https://github.com/GreLI), [FuzzySockets](https://github.com/FuzzySockets), [ArtemBernatskyy](https://github.com/ArtemBernatskyy), [@garethheyes](https://twitter.com/garethheyes), [@shafigullin](https://twitter.com/shafigullin), [@mmrupp](https://twitter.com/mmrupp), [@irsdl](https://twitter.com/irsdl),[ShikariSenpai](https://github.com/ShikariSenpai), [ansjdnakjdnajkd](https://github.com/ansjdnakjdnajkd), [@asutherland](https://twitter.com/asutherland), [@mathias](https://twitter.com/mathias), [@cgvwzq](https://twitter.com/cgvwzq), [@robbertatwork](https://twitter.com/robbertatwork), [@giutro](https://twitter.com/giutro), [@CmdEngineer\_](https://twitter.com/CmdEngineer_), [@avr4mit](https://twitter.com/avr4mit) and especially [@securitymb ❤️](https://twitter.com/securitymb) & [@masatokinugawa ❤️](https://twitter.com/masatokinugawa)
|
|
419
428
|
|
|
420
429
|
## Testing powered by
|
|
421
430
|
|
package/dist/purify.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @license DOMPurify 3.1.
|
|
1
|
+
/*! @license DOMPurify 3.1.2 | (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.2/LICENSE */
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
@@ -284,7 +284,7 @@ function createDOMPurify() {
|
|
|
284
284
|
* Version label, exposed for easier checks
|
|
285
285
|
* if DOMPurify is up to date or not
|
|
286
286
|
*/
|
|
287
|
-
DOMPurify.version = '3.1.
|
|
287
|
+
DOMPurify.version = '3.1.2';
|
|
288
288
|
|
|
289
289
|
/**
|
|
290
290
|
* Array of elements that DOMPurify removed during sanitation.
|
|
@@ -517,6 +517,9 @@ function createDOMPurify() {
|
|
|
517
517
|
/* Keep a reference to config to pass to hooks */
|
|
518
518
|
let CONFIG = null;
|
|
519
519
|
|
|
520
|
+
/* Specify the maximum element nesting depth to prevent mXSS */
|
|
521
|
+
const MAX_NESTING_DEPTH = 255;
|
|
522
|
+
|
|
520
523
|
/* Ideally, do not touch anything below this line */
|
|
521
524
|
/* ______________________________________________ */
|
|
522
525
|
|
|
@@ -703,7 +706,7 @@ function createDOMPurify() {
|
|
|
703
706
|
CONFIG = cfg;
|
|
704
707
|
};
|
|
705
708
|
const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
706
|
-
const HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', '
|
|
709
|
+
const HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'annotation-xml']);
|
|
707
710
|
|
|
708
711
|
// Certain elements are allowed in both SVG and HTML
|
|
709
712
|
// namespace. We need to specify them explicitly
|
|
@@ -927,7 +930,11 @@ function createDOMPurify() {
|
|
|
927
930
|
* @return {Boolean} true if clobbered, false if safe
|
|
928
931
|
*/
|
|
929
932
|
const _isClobbered = function _isClobbered(elm) {
|
|
930
|
-
return elm instanceof HTMLFormElement && (
|
|
933
|
+
return elm instanceof HTMLFormElement && (
|
|
934
|
+
// eslint-disable-next-line unicorn/no-typeof-undefined
|
|
935
|
+
typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' ||
|
|
936
|
+
// eslint-disable-next-line unicorn/no-typeof-undefined
|
|
937
|
+
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');
|
|
931
938
|
};
|
|
932
939
|
|
|
933
940
|
/**
|
|
@@ -1025,7 +1032,9 @@ function createDOMPurify() {
|
|
|
1025
1032
|
if (childNodes && parentNode) {
|
|
1026
1033
|
const childCount = childNodes.length;
|
|
1027
1034
|
for (let i = childCount - 1; i >= 0; --i) {
|
|
1028
|
-
|
|
1035
|
+
const childClone = cloneNode(childNodes[i], true);
|
|
1036
|
+
childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
|
|
1037
|
+
parentNode.insertBefore(childClone, getNextSibling(currentNode));
|
|
1029
1038
|
}
|
|
1030
1039
|
}
|
|
1031
1040
|
}
|
|
@@ -1257,9 +1266,29 @@ function createDOMPurify() {
|
|
|
1257
1266
|
if (_sanitizeElements(shadowNode)) {
|
|
1258
1267
|
continue;
|
|
1259
1268
|
}
|
|
1269
|
+
const parentNode = getParentNode(shadowNode);
|
|
1270
|
+
|
|
1271
|
+
/* Set the nesting depth of an element */
|
|
1272
|
+
if (shadowNode.nodeType === 1) {
|
|
1273
|
+
if (parentNode && parentNode.__depth) {
|
|
1274
|
+
/*
|
|
1275
|
+
We want the depth of the node in the original tree, which can
|
|
1276
|
+
change when it's removed from its parent.
|
|
1277
|
+
*/
|
|
1278
|
+
shadowNode.__depth = (shadowNode.__removalCount || 0) + parentNode.__depth + 1;
|
|
1279
|
+
} else {
|
|
1280
|
+
shadowNode.__depth = 1;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
/* Remove an element if nested too deeply to avoid mXSS */
|
|
1285
|
+
if (shadowNode.__depth >= MAX_NESTING_DEPTH) {
|
|
1286
|
+
_forceRemove(shadowNode);
|
|
1287
|
+
}
|
|
1260
1288
|
|
|
1261
1289
|
/* Deep shadow DOM detected */
|
|
1262
1290
|
if (shadowNode.content instanceof DocumentFragment) {
|
|
1291
|
+
shadowNode.content.__depth = shadowNode.__depth;
|
|
1263
1292
|
_sanitizeShadowDOM(shadowNode.content);
|
|
1264
1293
|
}
|
|
1265
1294
|
|
|
@@ -1375,9 +1404,29 @@ function createDOMPurify() {
|
|
|
1375
1404
|
if (_sanitizeElements(currentNode)) {
|
|
1376
1405
|
continue;
|
|
1377
1406
|
}
|
|
1407
|
+
const parentNode = getParentNode(currentNode);
|
|
1408
|
+
|
|
1409
|
+
/* Set the nesting depth of an element */
|
|
1410
|
+
if (currentNode.nodeType === 1) {
|
|
1411
|
+
if (parentNode && parentNode.__depth) {
|
|
1412
|
+
/*
|
|
1413
|
+
We want the depth of the node in the original tree, which can
|
|
1414
|
+
change when it's removed from its parent.
|
|
1415
|
+
*/
|
|
1416
|
+
currentNode.__depth = (currentNode.__removalCount || 0) + parentNode.__depth + 1;
|
|
1417
|
+
} else {
|
|
1418
|
+
currentNode.__depth = 1;
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
/* Remove an element if nested too deeply to avoid mXSS */
|
|
1423
|
+
if (currentNode.__depth >= MAX_NESTING_DEPTH) {
|
|
1424
|
+
_forceRemove(currentNode);
|
|
1425
|
+
}
|
|
1378
1426
|
|
|
1379
1427
|
/* Shadow DOM detected, sanitize it */
|
|
1380
1428
|
if (currentNode.content instanceof DocumentFragment) {
|
|
1429
|
+
currentNode.content.__depth = currentNode.__depth;
|
|
1381
1430
|
_sanitizeShadowDOM(currentNode.content);
|
|
1382
1431
|
}
|
|
1383
1432
|
|