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 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.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.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.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
- '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
  ```
@@ -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
 
@@ -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.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.0';
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', 'desc', 'title', 'annotation-xml']);
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 && (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
  }
@@ -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