dompurify 3.1.0 → 3.1.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 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.0**.
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.1**.
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.0](https://github.com/cure53/DOMPurify/releases/tag/2.5.0) 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).**
13
+ **Note that [DOMPurify v2.5.1](https://github.com/cure53/DOMPurify/releases/tag/2.5.1) 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
- 'beforeSanitizeElements',
372
+ 'uponSanitizeAttribute',
364
373
  function (currentNode, hookEvent, config) {
365
- // Do something with the current node and return it
366
- // You can also mutate hookEvent (i.e. set hookEvent.forceKeepAttr = true)
367
- return currentNode;
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
  ```
@@ -1,4 +1,4 @@
1
- /*! @license DOMPurify 3.1.0 | (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.0/LICENSE */
1
+ /*! @license DOMPurify 3.1.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.1.1/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.0';
287
+ DOMPurify.version = '3.1.1';
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
 
@@ -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 && (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');
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
- parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
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
  }
@@ -1258,8 +1267,27 @@ function createDOMPurify() {
1258
1267
  continue;
1259
1268
  }
1260
1269
 
1270
+ /* Set the nesting depth of an element */
1271
+ if (shadowNode.nodeType === 1) {
1272
+ if (shadowNode.parentNode && shadowNode.parentNode.__depth) {
1273
+ /*
1274
+ We want the depth of the node in the original tree, which can
1275
+ change when it's removed from its parent.
1276
+ */
1277
+ shadowNode.__depth = (shadowNode.__removalCount || 0) + shadowNode.parentNode.__depth + 1;
1278
+ } else {
1279
+ shadowNode.__depth = 1;
1280
+ }
1281
+ }
1282
+
1283
+ /* Remove an element if nested too deeply to avoid mXSS */
1284
+ if (shadowNode.__depth >= MAX_NESTING_DEPTH) {
1285
+ _forceRemove(shadowNode);
1286
+ }
1287
+
1261
1288
  /* Deep shadow DOM detected */
1262
1289
  if (shadowNode.content instanceof DocumentFragment) {
1290
+ shadowNode.content.__depth = shadowNode.__depth;
1263
1291
  _sanitizeShadowDOM(shadowNode.content);
1264
1292
  }
1265
1293
 
@@ -1376,8 +1404,27 @@ function createDOMPurify() {
1376
1404
  continue;
1377
1405
  }
1378
1406
 
1407
+ /* Set the nesting depth of an element */
1408
+ if (currentNode.nodeType === 1) {
1409
+ if (currentNode.parentNode && currentNode.parentNode.__depth) {
1410
+ /*
1411
+ We want the depth of the node in the original tree, which can
1412
+ change when it's removed from its parent.
1413
+ */
1414
+ currentNode.__depth = (currentNode.__removalCount || 0) + currentNode.parentNode.__depth + 1;
1415
+ } else {
1416
+ currentNode.__depth = 1;
1417
+ }
1418
+ }
1419
+
1420
+ /* Remove an element if nested too deeply to avoid mXSS */
1421
+ if (currentNode.__depth >= MAX_NESTING_DEPTH) {
1422
+ _forceRemove(currentNode);
1423
+ }
1424
+
1379
1425
  /* Shadow DOM detected, sanitize it */
1380
1426
  if (currentNode.content instanceof DocumentFragment) {
1427
+ currentNode.content.__depth = currentNode.__depth;
1381
1428
  _sanitizeShadowDOM(currentNode.content);
1382
1429
  }
1383
1430