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 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.4**.
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.4](https://github.com/cure53/DOMPurify/releases/tag/2.5.4) 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.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
@@ -1,4 +1,4 @@
1
- /*! @license DOMPurify 3.1.4 | (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.4/LICENSE */
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.4';
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.parentNode.removeChild(node);
834
+ getParentNode(node).removeChild(node);
844
835
  } catch (_) {
845
- node.remove();
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 ocurrence of processing instructions */
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 || value === '__depth' || value === '__removalCount')) {
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