dompurify 2.4.3 → 3.0.0
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/LICENSE +1 -1
- package/README.md +6 -10
- package/dist/purify.cjs.js +132 -63
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.js +132 -63
- package/dist/purify.es.js.map +1 -1
- package/dist/purify.js +132 -63
- 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 +2 -2
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
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
|
|
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 3.0.0.
|
|
10
10
|
|
|
11
|
-
DOMPurify is written in JavaScript and works in all modern browsers (Safari (10+), Opera (15+),
|
|
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.4.
|
|
13
|
+
**Note that [DOMPurify v2.4.4](https://github.com/cure53/DOMPurify/releases/tag/2.4.4) is the final 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
|
-
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
|
|
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
|
|
|
17
17
|
DOMPurify is written by security people who have vast background in web attacks and XSS. Fear not. For more details please also read about our [Security Goals & Threat Model](https://github.com/cure53/DOMPurify/wiki/Security-Goals-&-Threat-Model). Please, read it. Like, really.
|
|
18
18
|
|
|
@@ -145,13 +145,9 @@ DOMPurify.sanitize('<UL><li><A HREF=//google.com>click</UL>'); // becomes <ul><l
|
|
|
145
145
|
|
|
146
146
|
DOMPurify currently supports HTML5, SVG and MathML. DOMPurify per default allows CSS, HTML custom data attributes. DOMPurify also supports the Shadow DOM - and sanitizes DOM templates recursively. DOMPurify also allows you to sanitize HTML for being used with the jQuery `$()` and `elm.html()` API without any known problems.
|
|
147
147
|
|
|
148
|
-
## What about
|
|
148
|
+
## What about legacy browsers like Internet Explorer?
|
|
149
149
|
|
|
150
|
-
DOMPurify
|
|
151
|
-
|
|
152
|
-
If not even `toStaticHTML` is supported, DOMPurify does nothing at all. It simply returns exactly the string that you fed it.
|
|
153
|
-
|
|
154
|
-
DOMPurify also exposes a property called `isSupported`, which tells you whether DOMPurify will be able to do its job.
|
|
150
|
+
DOMPurify does nothing at all. It simply returns exactly the string that you fed it. DOMPurify exposes a property called `isSupported`, which tells you whether it will be able to do its job, so you can come up with your own backup plan.
|
|
155
151
|
|
|
156
152
|
## What about DOMPurify and Trusted Types?
|
|
157
153
|
|
package/dist/purify.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @license DOMPurify
|
|
1
|
+
/*! @license DOMPurify 3.0.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.0.0/LICENSE */
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
@@ -51,6 +51,10 @@ function _construct(Parent, args, Class) {
|
|
|
51
51
|
return _construct.apply(null, arguments);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function _slicedToArray(arr, i) {
|
|
55
|
+
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
|
|
56
|
+
}
|
|
57
|
+
|
|
54
58
|
function _toConsumableArray(arr) {
|
|
55
59
|
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
|
|
56
60
|
}
|
|
@@ -59,10 +63,44 @@ function _arrayWithoutHoles(arr) {
|
|
|
59
63
|
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
|
|
60
64
|
}
|
|
61
65
|
|
|
66
|
+
function _arrayWithHoles(arr) {
|
|
67
|
+
if (Array.isArray(arr)) return arr;
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
function _iterableToArray(iter) {
|
|
63
71
|
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
64
72
|
}
|
|
65
73
|
|
|
74
|
+
function _iterableToArrayLimit(arr, i) {
|
|
75
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
76
|
+
|
|
77
|
+
if (_i == null) return;
|
|
78
|
+
var _arr = [];
|
|
79
|
+
var _n = true;
|
|
80
|
+
var _d = false;
|
|
81
|
+
|
|
82
|
+
var _s, _e;
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
|
|
86
|
+
_arr.push(_s.value);
|
|
87
|
+
|
|
88
|
+
if (i && _arr.length === i) break;
|
|
89
|
+
}
|
|
90
|
+
} catch (err) {
|
|
91
|
+
_d = true;
|
|
92
|
+
_e = err;
|
|
93
|
+
} finally {
|
|
94
|
+
try {
|
|
95
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
96
|
+
} finally {
|
|
97
|
+
if (_d) throw _e;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return _arr;
|
|
102
|
+
}
|
|
103
|
+
|
|
66
104
|
function _unsupportedIterableToArray(o, minLen) {
|
|
67
105
|
if (!o) return;
|
|
68
106
|
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
@@ -84,7 +122,68 @@ function _nonIterableSpread() {
|
|
|
84
122
|
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
85
123
|
}
|
|
86
124
|
|
|
87
|
-
|
|
125
|
+
function _nonIterableRest() {
|
|
126
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function _createForOfIteratorHelper(o, allowArrayLike) {
|
|
130
|
+
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
|
|
131
|
+
|
|
132
|
+
if (!it) {
|
|
133
|
+
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
|
|
134
|
+
if (it) o = it;
|
|
135
|
+
var i = 0;
|
|
136
|
+
|
|
137
|
+
var F = function () {};
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
s: F,
|
|
141
|
+
n: function () {
|
|
142
|
+
if (i >= o.length) return {
|
|
143
|
+
done: true
|
|
144
|
+
};
|
|
145
|
+
return {
|
|
146
|
+
done: false,
|
|
147
|
+
value: o[i++]
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
e: function (e) {
|
|
151
|
+
throw e;
|
|
152
|
+
},
|
|
153
|
+
f: F
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
var normalCompletion = true,
|
|
161
|
+
didErr = false,
|
|
162
|
+
err;
|
|
163
|
+
return {
|
|
164
|
+
s: function () {
|
|
165
|
+
it = it.call(o);
|
|
166
|
+
},
|
|
167
|
+
n: function () {
|
|
168
|
+
var step = it.next();
|
|
169
|
+
normalCompletion = step.done;
|
|
170
|
+
return step;
|
|
171
|
+
},
|
|
172
|
+
e: function (e) {
|
|
173
|
+
didErr = true;
|
|
174
|
+
err = e;
|
|
175
|
+
},
|
|
176
|
+
f: function () {
|
|
177
|
+
try {
|
|
178
|
+
if (!normalCompletion && it.return != null) it.return();
|
|
179
|
+
} finally {
|
|
180
|
+
if (didErr) throw err;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
var entries = Object.entries,
|
|
88
187
|
setPrototypeOf = Object.setPrototypeOf,
|
|
89
188
|
isFrozen = Object.isFrozen,
|
|
90
189
|
getPrototypeOf = Object.getPrototypeOf,
|
|
@@ -189,20 +288,28 @@ function addToSet(set, array, transformCaseFunc) {
|
|
|
189
288
|
|
|
190
289
|
function clone(object) {
|
|
191
290
|
var newObject = create(null);
|
|
192
|
-
var property;
|
|
193
291
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
292
|
+
var _iterator = _createForOfIteratorHelper(entries(object)),
|
|
293
|
+
_step;
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
297
|
+
var _step$value = _slicedToArray(_step.value, 2),
|
|
298
|
+
property = _step$value[0],
|
|
299
|
+
value = _step$value[1];
|
|
300
|
+
|
|
301
|
+
newObject[property] = value;
|
|
197
302
|
}
|
|
303
|
+
} catch (err) {
|
|
304
|
+
_iterator.e(err);
|
|
305
|
+
} finally {
|
|
306
|
+
_iterator.f();
|
|
198
307
|
}
|
|
199
308
|
|
|
200
309
|
return newObject;
|
|
201
310
|
}
|
|
202
|
-
/*
|
|
203
|
-
*
|
|
204
|
-
* if the prop is function or getter and behaves
|
|
205
|
-
* accordingly. */
|
|
311
|
+
/* This method automatically checks if the prop is function
|
|
312
|
+
* or getter and behaves accordingly. */
|
|
206
313
|
|
|
207
314
|
function lookupGetter(object, prop) {
|
|
208
315
|
while (object !== null) {
|
|
@@ -324,7 +431,7 @@ function createDOMPurify() {
|
|
|
324
431
|
*/
|
|
325
432
|
|
|
326
433
|
|
|
327
|
-
DOMPurify.version = '
|
|
434
|
+
DOMPurify.version = '3.0.0';
|
|
328
435
|
/**
|
|
329
436
|
* Array of elements that DOMPurify removed during sanitation.
|
|
330
437
|
* Empty if nothing was removed.
|
|
@@ -379,18 +486,12 @@ function createDOMPurify() {
|
|
|
379
486
|
createDocumentFragment = _document.createDocumentFragment,
|
|
380
487
|
getElementsByTagName = _document.getElementsByTagName;
|
|
381
488
|
var importNode = originalDocument.importNode;
|
|
382
|
-
var documentMode = {};
|
|
383
|
-
|
|
384
|
-
try {
|
|
385
|
-
documentMode = clone(document).documentMode ? document.documentMode : {};
|
|
386
|
-
} catch (_) {}
|
|
387
|
-
|
|
388
489
|
var hooks = {};
|
|
389
490
|
/**
|
|
390
491
|
* Expose whether this browser supports running the full DOMPurify.
|
|
391
492
|
*/
|
|
392
493
|
|
|
393
|
-
DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined'
|
|
494
|
+
DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined';
|
|
394
495
|
var MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
|
|
395
496
|
ERB_EXPR$1 = ERB_EXPR,
|
|
396
497
|
TMPLIT_EXPR$1 = TMPLIT_EXPR,
|
|
@@ -454,6 +555,10 @@ function createDOMPurify() {
|
|
|
454
555
|
/* Decide if unknown protocols are okay */
|
|
455
556
|
|
|
456
557
|
var ALLOW_UNKNOWN_PROTOCOLS = false;
|
|
558
|
+
/* Decide if self-closing tags in attributes are allowed.
|
|
559
|
+
* Usually removed due to a mXSS issue in jQuery 3.0 */
|
|
560
|
+
|
|
561
|
+
var ALLOW_SELF_CLOSE_IN_ATTR = true;
|
|
457
562
|
/* Output should be safe for common template engines.
|
|
458
563
|
* This means, DOMPurify removes data attributes, mustaches and ERB
|
|
459
564
|
*/
|
|
@@ -606,6 +711,8 @@ function createDOMPurify() {
|
|
|
606
711
|
|
|
607
712
|
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
|
|
608
713
|
|
|
714
|
+
ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
|
|
715
|
+
|
|
609
716
|
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
|
|
610
717
|
|
|
611
718
|
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
|
|
@@ -863,11 +970,7 @@ function createDOMPurify() {
|
|
|
863
970
|
// eslint-disable-next-line unicorn/prefer-dom-node-remove
|
|
864
971
|
node.parentNode.removeChild(node);
|
|
865
972
|
} catch (_) {
|
|
866
|
-
|
|
867
|
-
node.outerHTML = emptyHTML;
|
|
868
|
-
} catch (_) {
|
|
869
|
-
node.remove();
|
|
870
|
-
}
|
|
973
|
+
node.remove();
|
|
871
974
|
}
|
|
872
975
|
};
|
|
873
976
|
/**
|
|
@@ -1046,14 +1149,6 @@ function createDOMPurify() {
|
|
|
1046
1149
|
|
|
1047
1150
|
return true;
|
|
1048
1151
|
}
|
|
1049
|
-
/* Check if tagname contains Unicode */
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) {
|
|
1053
|
-
_forceRemove(currentNode);
|
|
1054
|
-
|
|
1055
|
-
return true;
|
|
1056
|
-
}
|
|
1057
1152
|
/* Now let's check the element's type and name */
|
|
1058
1153
|
|
|
1059
1154
|
|
|
@@ -1072,14 +1167,6 @@ function createDOMPurify() {
|
|
|
1072
1167
|
|
|
1073
1168
|
return true;
|
|
1074
1169
|
}
|
|
1075
|
-
/* Mitigate a problem with templates inside select */
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
|
|
1079
|
-
_forceRemove(currentNode);
|
|
1080
|
-
|
|
1081
|
-
return true;
|
|
1082
|
-
}
|
|
1083
1170
|
/* Remove element if anything forbids its presence */
|
|
1084
1171
|
|
|
1085
1172
|
|
|
@@ -1117,6 +1204,8 @@ function createDOMPurify() {
|
|
|
1117
1204
|
|
|
1118
1205
|
return true;
|
|
1119
1206
|
}
|
|
1207
|
+
/* Make sure that older browsers don't get noscript mXSS */
|
|
1208
|
+
|
|
1120
1209
|
|
|
1121
1210
|
if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
|
|
1122
1211
|
_forceRemove(currentNode);
|
|
@@ -1269,7 +1358,7 @@ function createDOMPurify() {
|
|
|
1269
1358
|
/* Work around a security issue in jQuery 3.0 */
|
|
1270
1359
|
|
|
1271
1360
|
|
|
1272
|
-
if (regExpTest(/\/>/i, value)) {
|
|
1361
|
+
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
|
|
1273
1362
|
_removeAttribute(name, currentNode);
|
|
1274
1363
|
|
|
1275
1364
|
continue;
|
|
@@ -1393,7 +1482,6 @@ function createDOMPurify() {
|
|
|
1393
1482
|
var body;
|
|
1394
1483
|
var importedNode;
|
|
1395
1484
|
var currentNode;
|
|
1396
|
-
var oldNode;
|
|
1397
1485
|
var returnNode;
|
|
1398
1486
|
/* Make sure we have a string to sanitize.
|
|
1399
1487
|
DO NOT return early, as this will return the wrong type if
|
|
@@ -1419,20 +1507,10 @@ function createDOMPurify() {
|
|
|
1419
1507
|
}
|
|
1420
1508
|
}
|
|
1421
1509
|
}
|
|
1422
|
-
/*
|
|
1510
|
+
/* Return dirty HTML if DOMPurify cannot run */
|
|
1423
1511
|
|
|
1424
1512
|
|
|
1425
1513
|
if (!DOMPurify.isSupported) {
|
|
1426
|
-
if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
|
|
1427
|
-
if (typeof dirty === 'string') {
|
|
1428
|
-
return window.toStaticHTML(dirty);
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
if (_isNode(dirty)) {
|
|
1432
|
-
return window.toStaticHTML(dirty.outerHTML);
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
1514
|
return dirty;
|
|
1437
1515
|
}
|
|
1438
1516
|
/* Assign config vars */
|
|
@@ -1505,13 +1583,7 @@ function createDOMPurify() {
|
|
|
1505
1583
|
|
|
1506
1584
|
|
|
1507
1585
|
while (currentNode = nodeIterator.nextNode()) {
|
|
1508
|
-
/* Fix IE's strange behavior with manipulated textNodes #89 */
|
|
1509
|
-
if (currentNode.nodeType === 3 && currentNode === oldNode) {
|
|
1510
|
-
continue;
|
|
1511
|
-
}
|
|
1512
1586
|
/* Sanitize tags and elements */
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
1587
|
if (_sanitizeElements(currentNode)) {
|
|
1516
1588
|
continue;
|
|
1517
1589
|
}
|
|
@@ -1525,13 +1597,10 @@ function createDOMPurify() {
|
|
|
1525
1597
|
|
|
1526
1598
|
|
|
1527
1599
|
_sanitizeAttributes(currentNode);
|
|
1528
|
-
|
|
1529
|
-
oldNode = currentNode;
|
|
1530
1600
|
}
|
|
1531
|
-
|
|
1532
|
-
oldNode = null;
|
|
1533
1601
|
/* If we sanitized `dirty` in-place, return it. */
|
|
1534
1602
|
|
|
1603
|
+
|
|
1535
1604
|
if (IN_PLACE) {
|
|
1536
1605
|
return dirty;
|
|
1537
1606
|
}
|
|
@@ -1550,7 +1619,7 @@ function createDOMPurify() {
|
|
|
1550
1619
|
returnNode = body;
|
|
1551
1620
|
}
|
|
1552
1621
|
|
|
1553
|
-
if (ALLOWED_ATTR.shadowroot) {
|
|
1622
|
+
if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
|
|
1554
1623
|
/*
|
|
1555
1624
|
AdoptNode() is not used because internal state is not reset
|
|
1556
1625
|
(e.g. the past names map of a HTMLFormElement), this is safe
|