dompurify 3.1.1 → 3.1.3

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.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.3**.
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.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).**
13
+ **Note that [DOMPurify v2.5.3](https://github.com/cure53/DOMPurify/releases/tag/2.5.3) 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
 
@@ -424,7 +424,7 @@ Feature releases will not be announced to this list.
424
424
 
425
425
  Many people helped and help DOMPurify become what it is and need to be acknowledged here!
426
426
 
427
- [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
+ [hash_kitten ❤️](https://twitter.com/hash_kitten), [kevin_mizu ❤️](https://twitter.com/kevin_mizu), [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), [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)
428
428
 
429
429
  ## Testing powered by
430
430
 
@@ -1,4 +1,4 @@
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 */
1
+ /*! @license DOMPurify 3.1.3 | (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.3/LICENSE */
2
2
 
3
3
  'use strict';
4
4
 
@@ -50,6 +50,7 @@ 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
+ const numberIsNaN = unapply(Number.isNaN);
53
54
 
54
55
  /**
55
56
  * Creates a new function that calls the given function with a specified thisArg and arguments.
@@ -233,6 +234,24 @@ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
233
234
  CUSTOM_ELEMENT: CUSTOM_ELEMENT
234
235
  });
235
236
 
237
+ // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
238
+ const NODE_TYPE = {
239
+ element: 1,
240
+ attribute: 2,
241
+ text: 3,
242
+ cdataSection: 4,
243
+ entityReference: 5,
244
+ // Deprecated
245
+ entityNode: 6,
246
+ // Deprecated
247
+ progressingInstruction: 7,
248
+ comment: 8,
249
+ document: 9,
250
+ documentType: 10,
251
+ documentFragment: 11,
252
+ notation: 12 // Deprecated
253
+ };
254
+
236
255
  const getGlobal = function getGlobal() {
237
256
  return typeof window === 'undefined' ? null : window;
238
257
  };
@@ -284,14 +303,14 @@ function createDOMPurify() {
284
303
  * Version label, exposed for easier checks
285
304
  * if DOMPurify is up to date or not
286
305
  */
287
- DOMPurify.version = '3.1.1';
306
+ DOMPurify.version = '3.1.3';
288
307
 
289
308
  /**
290
309
  * Array of elements that DOMPurify removed during sanitation.
291
310
  * Empty if nothing was removed.
292
311
  */
293
312
  DOMPurify.removed = [];
294
- if (!window || !window.document || window.document.nodeType !== 9) {
313
+ if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document) {
295
314
  // Not running in a browser, provide a factory function
296
315
  // so that you can pass your own Window
297
316
  DOMPurify.isSupported = false;
@@ -706,7 +725,7 @@ function createDOMPurify() {
706
725
  CONFIG = cfg;
707
726
  };
708
727
  const MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
709
- const HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
728
+ const HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'annotation-xml']);
710
729
 
711
730
  // Certain elements are allowed in both SVG and HTML
712
731
  // namespace. We need to specify them explicitly
@@ -1002,13 +1021,13 @@ function createDOMPurify() {
1002
1021
  }
1003
1022
 
1004
1023
  /* Remove any ocurrence of processing instructions */
1005
- if (currentNode.nodeType === 7) {
1024
+ if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
1006
1025
  _forceRemove(currentNode);
1007
1026
  return true;
1008
1027
  }
1009
1028
 
1010
1029
  /* Remove any kind of possibly harmful comments */
1011
- if (SAFE_FOR_XML && currentNode.nodeType === 8 && regExpTest(/<[/\w]/g, currentNode.data)) {
1030
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
1012
1031
  _forceRemove(currentNode);
1013
1032
  return true;
1014
1033
  }
@@ -1055,7 +1074,7 @@ function createDOMPurify() {
1055
1074
  }
1056
1075
 
1057
1076
  /* Sanitize element content to be template-safe */
1058
- if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
1077
+ if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
1059
1078
  /* Get the element's text content */
1060
1079
  content = currentNode.textContent;
1061
1080
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
@@ -1085,7 +1104,7 @@ function createDOMPurify() {
1085
1104
  // eslint-disable-next-line complexity
1086
1105
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1087
1106
  /* Make sure attribute cannot clobber */
1088
- if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
1107
+ if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement || value === '__depth' || value === '__removalCount')) {
1089
1108
  return false;
1090
1109
  }
1091
1110
 
@@ -1189,6 +1208,12 @@ function createDOMPurify() {
1189
1208
  continue;
1190
1209
  }
1191
1210
 
1211
+ /* Work around a security issue with comments inside attributes */
1212
+ if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|title)/i, value)) {
1213
+ _removeAttribute(name, currentNode);
1214
+ continue;
1215
+ }
1216
+
1192
1217
  /* Sanitize attribute content to be template-safe */
1193
1218
  if (SAFE_FOR_TEMPLATES) {
1194
1219
  arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
@@ -1239,7 +1264,11 @@ function createDOMPurify() {
1239
1264
  /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1240
1265
  currentNode.setAttribute(name, value);
1241
1266
  }
1242
- arrayPop(DOMPurify.removed);
1267
+ if (_isClobbered(currentNode)) {
1268
+ _forceRemove(currentNode);
1269
+ } else {
1270
+ arrayPop(DOMPurify.removed);
1271
+ }
1243
1272
  } catch (_) {}
1244
1273
  }
1245
1274
 
@@ -1266,22 +1295,26 @@ function createDOMPurify() {
1266
1295
  if (_sanitizeElements(shadowNode)) {
1267
1296
  continue;
1268
1297
  }
1298
+ const parentNode = getParentNode(shadowNode);
1269
1299
 
1270
1300
  /* Set the nesting depth of an element */
1271
- if (shadowNode.nodeType === 1) {
1272
- if (shadowNode.parentNode && shadowNode.parentNode.__depth) {
1301
+ if (shadowNode.nodeType === NODE_TYPE.element) {
1302
+ if (parentNode && parentNode.__depth) {
1273
1303
  /*
1274
1304
  We want the depth of the node in the original tree, which can
1275
1305
  change when it's removed from its parent.
1276
1306
  */
1277
- shadowNode.__depth = (shadowNode.__removalCount || 0) + shadowNode.parentNode.__depth + 1;
1307
+ shadowNode.__depth = (shadowNode.__removalCount || 0) + parentNode.__depth + 1;
1278
1308
  } else {
1279
1309
  shadowNode.__depth = 1;
1280
1310
  }
1281
1311
  }
1282
1312
 
1283
- /* Remove an element if nested too deeply to avoid mXSS */
1284
- if (shadowNode.__depth >= MAX_NESTING_DEPTH) {
1313
+ /*
1314
+ * Remove an element if nested too deeply to avoid mXSS
1315
+ * or if the __depth might have been tampered with
1316
+ */
1317
+ if (shadowNode.__depth >= MAX_NESTING_DEPTH || shadowNode.__depth < 0 || numberIsNaN(shadowNode.__depth)) {
1285
1318
  _forceRemove(shadowNode);
1286
1319
  }
1287
1320
 
@@ -1363,7 +1396,7 @@ function createDOMPurify() {
1363
1396
  elements being stripped by the parser */
1364
1397
  body = _initDocument('<!---->');
1365
1398
  importedNode = body.ownerDocument.importNode(dirty, true);
1366
- if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
1399
+ if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === 'BODY') {
1367
1400
  /* Node is already a body, use as is */
1368
1401
  body = importedNode;
1369
1402
  } else if (importedNode.nodeName === 'HTML') {
@@ -1403,22 +1436,26 @@ function createDOMPurify() {
1403
1436
  if (_sanitizeElements(currentNode)) {
1404
1437
  continue;
1405
1438
  }
1439
+ const parentNode = getParentNode(currentNode);
1406
1440
 
1407
1441
  /* Set the nesting depth of an element */
1408
- if (currentNode.nodeType === 1) {
1409
- if (currentNode.parentNode && currentNode.parentNode.__depth) {
1442
+ if (currentNode.nodeType === NODE_TYPE.element) {
1443
+ if (parentNode && parentNode.__depth) {
1410
1444
  /*
1411
1445
  We want the depth of the node in the original tree, which can
1412
1446
  change when it's removed from its parent.
1413
1447
  */
1414
- currentNode.__depth = (currentNode.__removalCount || 0) + currentNode.parentNode.__depth + 1;
1448
+ currentNode.__depth = (currentNode.__removalCount || 0) + parentNode.__depth + 1;
1415
1449
  } else {
1416
1450
  currentNode.__depth = 1;
1417
1451
  }
1418
1452
  }
1419
1453
 
1420
- /* Remove an element if nested too deeply to avoid mXSS */
1421
- if (currentNode.__depth >= MAX_NESTING_DEPTH) {
1454
+ /*
1455
+ * Remove an element if nested too deeply to avoid mXSS
1456
+ * or if the __depth might have been tampered with
1457
+ */
1458
+ if (currentNode.__depth >= MAX_NESTING_DEPTH || currentNode.__depth < 0 || numberIsNaN(currentNode.__depth)) {
1422
1459
  _forceRemove(currentNode);
1423
1460
  }
1424
1461