dompurify 2.4.9 → 2.5.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/LICENSE +1 -1
- package/README.md +2 -2
- package/dist/purify.cjs.js +272 -420
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.js +272 -420
- package/dist/purify.es.js.map +1 -1
- package/dist/purify.js +272 -420
- 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 +1 -1
package/dist/purify.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @license DOMPurify 2.
|
|
1
|
+
/*! @license DOMPurify 2.5.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.5.1/LICENSE */
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
@@ -11,21 +11,17 @@ function _typeof(obj) {
|
|
|
11
11
|
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
12
12
|
}, _typeof(obj);
|
|
13
13
|
}
|
|
14
|
-
|
|
15
14
|
function _setPrototypeOf(o, p) {
|
|
16
15
|
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
|
|
17
16
|
o.__proto__ = p;
|
|
18
17
|
return o;
|
|
19
18
|
};
|
|
20
|
-
|
|
21
19
|
return _setPrototypeOf(o, p);
|
|
22
20
|
}
|
|
23
|
-
|
|
24
21
|
function _isNativeReflectConstruct() {
|
|
25
22
|
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
|
|
26
23
|
if (Reflect.construct.sham) return false;
|
|
27
24
|
if (typeof Proxy === "function") return true;
|
|
28
|
-
|
|
29
25
|
try {
|
|
30
26
|
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
|
|
31
27
|
return true;
|
|
@@ -33,7 +29,6 @@ function _isNativeReflectConstruct() {
|
|
|
33
29
|
return false;
|
|
34
30
|
}
|
|
35
31
|
}
|
|
36
|
-
|
|
37
32
|
function _construct(Parent, args, Class) {
|
|
38
33
|
if (_isNativeReflectConstruct()) {
|
|
39
34
|
_construct = Reflect.construct;
|
|
@@ -47,22 +42,17 @@ function _construct(Parent, args, Class) {
|
|
|
47
42
|
return instance;
|
|
48
43
|
};
|
|
49
44
|
}
|
|
50
|
-
|
|
51
45
|
return _construct.apply(null, arguments);
|
|
52
46
|
}
|
|
53
|
-
|
|
54
47
|
function _toConsumableArray(arr) {
|
|
55
48
|
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
|
|
56
49
|
}
|
|
57
|
-
|
|
58
50
|
function _arrayWithoutHoles(arr) {
|
|
59
51
|
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
|
|
60
52
|
}
|
|
61
|
-
|
|
62
53
|
function _iterableToArray(iter) {
|
|
63
54
|
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
64
55
|
}
|
|
65
|
-
|
|
66
56
|
function _unsupportedIterableToArray(o, minLen) {
|
|
67
57
|
if (!o) return;
|
|
68
58
|
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
@@ -71,56 +61,46 @@ function _unsupportedIterableToArray(o, minLen) {
|
|
|
71
61
|
if (n === "Map" || n === "Set") return Array.from(o);
|
|
72
62
|
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
|
73
63
|
}
|
|
74
|
-
|
|
75
64
|
function _arrayLikeToArray(arr, len) {
|
|
76
65
|
if (len == null || len > arr.length) len = arr.length;
|
|
77
|
-
|
|
78
66
|
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
|
79
|
-
|
|
80
67
|
return arr2;
|
|
81
68
|
}
|
|
82
|
-
|
|
83
69
|
function _nonIterableSpread() {
|
|
84
70
|
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
71
|
}
|
|
86
72
|
|
|
87
73
|
var hasOwnProperty = Object.hasOwnProperty,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
74
|
+
setPrototypeOf = Object.setPrototypeOf,
|
|
75
|
+
isFrozen = Object.isFrozen,
|
|
76
|
+
getPrototypeOf = Object.getPrototypeOf,
|
|
77
|
+
getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
92
78
|
var freeze = Object.freeze,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
79
|
+
seal = Object.seal,
|
|
80
|
+
create = Object.create; // eslint-disable-line import/no-mutable-exports
|
|
96
81
|
var _ref = typeof Reflect !== 'undefined' && Reflect,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
82
|
+
apply = _ref.apply,
|
|
83
|
+
construct = _ref.construct;
|
|
100
84
|
if (!apply) {
|
|
101
85
|
apply = function apply(fun, thisValue, args) {
|
|
102
86
|
return fun.apply(thisValue, args);
|
|
103
87
|
};
|
|
104
88
|
}
|
|
105
|
-
|
|
106
89
|
if (!freeze) {
|
|
107
90
|
freeze = function freeze(x) {
|
|
108
91
|
return x;
|
|
109
92
|
};
|
|
110
93
|
}
|
|
111
|
-
|
|
112
94
|
if (!seal) {
|
|
113
95
|
seal = function seal(x) {
|
|
114
96
|
return x;
|
|
115
97
|
};
|
|
116
98
|
}
|
|
117
|
-
|
|
118
99
|
if (!construct) {
|
|
119
100
|
construct = function construct(Func, args) {
|
|
120
101
|
return _construct(Func, _toConsumableArray(args));
|
|
121
102
|
};
|
|
122
103
|
}
|
|
123
|
-
|
|
124
104
|
var arrayForEach = unapply(Array.prototype.forEach);
|
|
125
105
|
var arrayPop = unapply(Array.prototype.pop);
|
|
126
106
|
var arrayPush = unapply(Array.prototype.push);
|
|
@@ -137,7 +117,6 @@ function unapply(func) {
|
|
|
137
117
|
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
138
118
|
args[_key - 1] = arguments[_key];
|
|
139
119
|
}
|
|
140
|
-
|
|
141
120
|
return apply(func, thisArg, args);
|
|
142
121
|
};
|
|
143
122
|
}
|
|
@@ -146,103 +125,89 @@ function unconstruct(func) {
|
|
|
146
125
|
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
147
126
|
args[_key2] = arguments[_key2];
|
|
148
127
|
}
|
|
149
|
-
|
|
150
128
|
return construct(func, args);
|
|
151
129
|
};
|
|
152
130
|
}
|
|
153
|
-
/* Add properties to a lookup table */
|
|
154
131
|
|
|
132
|
+
/* Add properties to a lookup table */
|
|
155
133
|
function addToSet(set, array, transformCaseFunc) {
|
|
156
134
|
var _transformCaseFunc;
|
|
157
|
-
|
|
158
135
|
transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase;
|
|
159
|
-
|
|
160
136
|
if (setPrototypeOf) {
|
|
161
137
|
// Make 'in' and truthy checks like Boolean(set.constructor)
|
|
162
138
|
// independent of any properties defined on Object.prototype.
|
|
163
139
|
// Prevent prototype setters from intercepting set as a this value.
|
|
164
140
|
setPrototypeOf(set, null);
|
|
165
141
|
}
|
|
166
|
-
|
|
167
142
|
var l = array.length;
|
|
168
|
-
|
|
169
143
|
while (l--) {
|
|
170
144
|
var element = array[l];
|
|
171
|
-
|
|
172
145
|
if (typeof element === 'string') {
|
|
173
146
|
var lcElement = transformCaseFunc(element);
|
|
174
|
-
|
|
175
147
|
if (lcElement !== element) {
|
|
176
148
|
// Config presets (e.g. tags.js, attrs.js) are immutable.
|
|
177
149
|
if (!isFrozen(array)) {
|
|
178
150
|
array[l] = lcElement;
|
|
179
151
|
}
|
|
180
|
-
|
|
181
152
|
element = lcElement;
|
|
182
153
|
}
|
|
183
154
|
}
|
|
184
|
-
|
|
185
155
|
set[element] = true;
|
|
186
156
|
}
|
|
187
|
-
|
|
188
157
|
return set;
|
|
189
158
|
}
|
|
190
|
-
/* Shallow clone an object */
|
|
191
159
|
|
|
160
|
+
/* Shallow clone an object */
|
|
192
161
|
function clone(object) {
|
|
193
162
|
var newObject = create(null);
|
|
194
163
|
var property;
|
|
195
|
-
|
|
196
164
|
for (property in object) {
|
|
197
165
|
if (apply(hasOwnProperty, object, [property]) === true) {
|
|
198
166
|
newObject[property] = object[property];
|
|
199
167
|
}
|
|
200
168
|
}
|
|
201
|
-
|
|
202
169
|
return newObject;
|
|
203
170
|
}
|
|
171
|
+
|
|
204
172
|
/* IE10 doesn't support __lookupGetter__ so lets'
|
|
205
173
|
* simulate it. It also automatically checks
|
|
206
174
|
* if the prop is function or getter and behaves
|
|
207
175
|
* accordingly. */
|
|
208
|
-
|
|
209
176
|
function lookupGetter(object, prop) {
|
|
210
177
|
while (object !== null) {
|
|
211
178
|
var desc = getOwnPropertyDescriptor(object, prop);
|
|
212
|
-
|
|
213
179
|
if (desc) {
|
|
214
180
|
if (desc.get) {
|
|
215
181
|
return unapply(desc.get);
|
|
216
182
|
}
|
|
217
|
-
|
|
218
183
|
if (typeof desc.value === 'function') {
|
|
219
184
|
return unapply(desc.value);
|
|
220
185
|
}
|
|
221
186
|
}
|
|
222
|
-
|
|
223
187
|
object = getPrototypeOf(object);
|
|
224
188
|
}
|
|
225
|
-
|
|
226
189
|
function fallbackValue(element) {
|
|
227
190
|
console.warn('fallback value for', element);
|
|
228
191
|
return null;
|
|
229
192
|
}
|
|
230
|
-
|
|
231
193
|
return fallbackValue;
|
|
232
194
|
}
|
|
233
195
|
|
|
234
|
-
var html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
|
|
196
|
+
var html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
|
|
235
197
|
|
|
198
|
+
// SVG
|
|
236
199
|
var svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
|
|
237
|
-
var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
|
|
200
|
+
var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
|
|
201
|
+
|
|
202
|
+
// List of SVG elements that are disallowed by default.
|
|
238
203
|
// We still need to know them so that we can do namespace
|
|
239
204
|
// checks properly in case one wants to add them to
|
|
240
205
|
// allow-list.
|
|
241
|
-
|
|
242
206
|
var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
|
|
243
|
-
var mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover']);
|
|
244
|
-
// even those that we disallow by default.
|
|
207
|
+
var mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover']);
|
|
245
208
|
|
|
209
|
+
// Similarly to SVG, we want to know all MathML elements,
|
|
210
|
+
// even those that we disallow by default.
|
|
246
211
|
var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
|
|
247
212
|
var text = freeze(['#text']);
|
|
248
213
|
|
|
@@ -251,14 +216,12 @@ var svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline
|
|
|
251
216
|
var mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
|
|
252
217
|
var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
253
218
|
|
|
219
|
+
// eslint-disable-next-line unicorn/better-regex
|
|
254
220
|
var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
|
|
255
|
-
|
|
256
221
|
var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
|
|
257
222
|
var TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
|
|
258
223
|
var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
|
|
259
|
-
|
|
260
224
|
var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
261
|
-
|
|
262
225
|
var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
|
263
226
|
);
|
|
264
227
|
var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
|
@@ -270,6 +233,7 @@ var CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
|
|
|
270
233
|
var getGlobal = function getGlobal() {
|
|
271
234
|
return typeof window === 'undefined' ? null : window;
|
|
272
235
|
};
|
|
236
|
+
|
|
273
237
|
/**
|
|
274
238
|
* Creates a no-op policy for internal use only.
|
|
275
239
|
* Don't export this function outside this module!
|
|
@@ -278,25 +242,20 @@ var getGlobal = function getGlobal() {
|
|
|
278
242
|
* @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
|
|
279
243
|
* are not supported).
|
|
280
244
|
*/
|
|
281
|
-
|
|
282
|
-
|
|
283
245
|
var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
|
|
284
246
|
if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
|
|
285
247
|
return null;
|
|
286
|
-
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Allow the callers to control the unique policy name
|
|
287
251
|
// by adding a data-tt-policy-suffix to the script element with the DOMPurify.
|
|
288
252
|
// Policy creation with duplicate names throws in Trusted Types.
|
|
289
|
-
|
|
290
|
-
|
|
291
253
|
var suffix = null;
|
|
292
254
|
var ATTR_NAME = 'data-tt-policy-suffix';
|
|
293
|
-
|
|
294
255
|
if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
|
|
295
256
|
suffix = document.currentScript.getAttribute(ATTR_NAME);
|
|
296
257
|
}
|
|
297
|
-
|
|
298
258
|
var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
|
|
299
|
-
|
|
300
259
|
try {
|
|
301
260
|
return trustedTypes.createPolicy(policyName, {
|
|
302
261
|
createHTML: function createHTML(html) {
|
|
@@ -314,115 +273,106 @@ var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes,
|
|
|
314
273
|
return null;
|
|
315
274
|
}
|
|
316
275
|
};
|
|
317
|
-
|
|
318
276
|
function createDOMPurify() {
|
|
319
277
|
var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
320
|
-
|
|
321
278
|
var DOMPurify = function DOMPurify(root) {
|
|
322
279
|
return createDOMPurify(root);
|
|
323
280
|
};
|
|
281
|
+
|
|
324
282
|
/**
|
|
325
283
|
* Version label, exposed for easier checks
|
|
326
284
|
* if DOMPurify is up to date or not
|
|
327
285
|
*/
|
|
286
|
+
DOMPurify.version = '2.5.1';
|
|
328
287
|
|
|
329
|
-
|
|
330
|
-
DOMPurify.version = '2.4.9';
|
|
331
288
|
/**
|
|
332
289
|
* Array of elements that DOMPurify removed during sanitation.
|
|
333
290
|
* Empty if nothing was removed.
|
|
334
291
|
*/
|
|
335
|
-
|
|
336
292
|
DOMPurify.removed = [];
|
|
337
|
-
|
|
338
293
|
if (!window || !window.document || window.document.nodeType !== 9) {
|
|
339
294
|
// Not running in a browser, provide a factory function
|
|
340
295
|
// so that you can pass your own Window
|
|
341
296
|
DOMPurify.isSupported = false;
|
|
342
297
|
return DOMPurify;
|
|
343
298
|
}
|
|
344
|
-
|
|
345
299
|
var originalDocument = window.document;
|
|
346
300
|
var document = window.document;
|
|
347
301
|
var DocumentFragment = window.DocumentFragment,
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
302
|
+
HTMLTemplateElement = window.HTMLTemplateElement,
|
|
303
|
+
Node = window.Node,
|
|
304
|
+
Element = window.Element,
|
|
305
|
+
NodeFilter = window.NodeFilter,
|
|
306
|
+
_window$NamedNodeMap = window.NamedNodeMap,
|
|
307
|
+
NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
|
|
308
|
+
HTMLFormElement = window.HTMLFormElement,
|
|
309
|
+
DOMParser = window.DOMParser,
|
|
310
|
+
trustedTypes = window.trustedTypes;
|
|
357
311
|
var ElementPrototype = Element.prototype;
|
|
358
312
|
var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
359
313
|
var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
360
314
|
var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
361
|
-
var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
315
|
+
var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
316
|
+
|
|
317
|
+
// As per issue #47, the web-components registry is inherited by a
|
|
362
318
|
// new document created via createHTMLDocument. As per the spec
|
|
363
319
|
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
|
|
364
320
|
// a new empty registry is used when creating a template contents owner
|
|
365
321
|
// document, so we use that as our parent document to ensure nothing
|
|
366
322
|
// is inherited.
|
|
367
|
-
|
|
368
323
|
if (typeof HTMLTemplateElement === 'function') {
|
|
369
324
|
var template = document.createElement('template');
|
|
370
|
-
|
|
371
325
|
if (template.content && template.content.ownerDocument) {
|
|
372
326
|
document = template.content.ownerDocument;
|
|
373
327
|
}
|
|
374
328
|
}
|
|
375
|
-
|
|
376
329
|
var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
|
|
377
|
-
|
|
378
330
|
var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
|
|
379
331
|
var _document = document,
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
332
|
+
implementation = _document.implementation,
|
|
333
|
+
createNodeIterator = _document.createNodeIterator,
|
|
334
|
+
createDocumentFragment = _document.createDocumentFragment,
|
|
335
|
+
getElementsByTagName = _document.getElementsByTagName;
|
|
384
336
|
var importNode = originalDocument.importNode;
|
|
385
337
|
var documentMode = {};
|
|
386
|
-
|
|
387
338
|
try {
|
|
388
339
|
documentMode = clone(document).documentMode ? document.documentMode : {};
|
|
389
340
|
} catch (_) {}
|
|
390
|
-
|
|
391
341
|
var hooks = {};
|
|
342
|
+
|
|
392
343
|
/**
|
|
393
344
|
* Expose whether this browser supports running the full DOMPurify.
|
|
394
345
|
*/
|
|
395
|
-
|
|
396
346
|
DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined && documentMode !== 9;
|
|
397
347
|
var MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
348
|
+
ERB_EXPR$1 = ERB_EXPR,
|
|
349
|
+
TMPLIT_EXPR$1 = TMPLIT_EXPR,
|
|
350
|
+
DATA_ATTR$1 = DATA_ATTR,
|
|
351
|
+
ARIA_ATTR$1 = ARIA_ATTR,
|
|
352
|
+
IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
|
|
353
|
+
ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
|
|
354
|
+
CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
|
|
405
355
|
var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
|
|
356
|
+
|
|
406
357
|
/**
|
|
407
358
|
* We consider the elements and attributes below to be safe. Ideally
|
|
408
359
|
* don't add any new ones but feel free to remove unwanted ones.
|
|
409
360
|
*/
|
|
410
361
|
|
|
411
362
|
/* allowed element names */
|
|
412
|
-
|
|
413
363
|
var ALLOWED_TAGS = null;
|
|
414
364
|
var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));
|
|
415
|
-
/* Allowed attribute names */
|
|
416
365
|
|
|
366
|
+
/* Allowed attribute names */
|
|
417
367
|
var ALLOWED_ATTR = null;
|
|
418
368
|
var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));
|
|
369
|
+
|
|
419
370
|
/*
|
|
420
371
|
* Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
|
|
421
372
|
* @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
|
|
422
373
|
* @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
|
|
423
374
|
* @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
|
|
424
375
|
*/
|
|
425
|
-
|
|
426
376
|
var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
|
|
427
377
|
tagNameCheck: {
|
|
428
378
|
writable: true,
|
|
@@ -443,59 +393,65 @@ function createDOMPurify() {
|
|
|
443
393
|
value: false
|
|
444
394
|
}
|
|
445
395
|
}));
|
|
446
|
-
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
|
|
447
396
|
|
|
397
|
+
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
|
|
448
398
|
var FORBID_TAGS = null;
|
|
449
|
-
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
|
|
450
399
|
|
|
400
|
+
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
|
|
451
401
|
var FORBID_ATTR = null;
|
|
452
|
-
/* Decide if ARIA attributes are okay */
|
|
453
402
|
|
|
403
|
+
/* Decide if ARIA attributes are okay */
|
|
454
404
|
var ALLOW_ARIA_ATTR = true;
|
|
455
|
-
/* Decide if custom data attributes are okay */
|
|
456
405
|
|
|
406
|
+
/* Decide if custom data attributes are okay */
|
|
457
407
|
var ALLOW_DATA_ATTR = true;
|
|
458
|
-
/* Decide if unknown protocols are okay */
|
|
459
408
|
|
|
409
|
+
/* Decide if unknown protocols are okay */
|
|
460
410
|
var ALLOW_UNKNOWN_PROTOCOLS = false;
|
|
411
|
+
|
|
461
412
|
/* Decide if self-closing tags in attributes are allowed.
|
|
462
413
|
* Usually removed due to a mXSS issue in jQuery 3.0 */
|
|
463
|
-
|
|
464
414
|
var ALLOW_SELF_CLOSE_IN_ATTR = true;
|
|
415
|
+
|
|
465
416
|
/* Output should be safe for common template engines.
|
|
466
417
|
* This means, DOMPurify removes data attributes, mustaches and ERB
|
|
467
418
|
*/
|
|
468
|
-
|
|
469
419
|
var SAFE_FOR_TEMPLATES = false;
|
|
470
|
-
/* Decide if document with <html>... should be returned */
|
|
471
420
|
|
|
421
|
+
/* Output should be safe even for XML used within HTML and alike.
|
|
422
|
+
* This means, DOMPurify removes comments when containing risky content.
|
|
423
|
+
*/
|
|
424
|
+
var SAFE_FOR_XML = true;
|
|
425
|
+
|
|
426
|
+
/* Decide if document with <html>... should be returned */
|
|
472
427
|
var WHOLE_DOCUMENT = false;
|
|
473
|
-
/* Track whether config is already set on this instance of DOMPurify. */
|
|
474
428
|
|
|
429
|
+
/* Track whether config is already set on this instance of DOMPurify. */
|
|
475
430
|
var SET_CONFIG = false;
|
|
431
|
+
|
|
476
432
|
/* Decide if all elements (e.g. style, script) must be children of
|
|
477
433
|
* document.body. By default, browsers might move them to document.head */
|
|
478
|
-
|
|
479
434
|
var FORCE_BODY = false;
|
|
435
|
+
|
|
480
436
|
/* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
|
|
481
437
|
* string (or a TrustedHTML object if Trusted Types are supported).
|
|
482
438
|
* If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
|
|
483
439
|
*/
|
|
484
|
-
|
|
485
440
|
var RETURN_DOM = false;
|
|
441
|
+
|
|
486
442
|
/* Decide if a DOM `DocumentFragment` should be returned, instead of a html
|
|
487
443
|
* string (or a TrustedHTML object if Trusted Types are supported) */
|
|
488
|
-
|
|
489
444
|
var RETURN_DOM_FRAGMENT = false;
|
|
445
|
+
|
|
490
446
|
/* Try to return a Trusted Type object instead of a string, return a string in
|
|
491
447
|
* case Trusted Types are not supported */
|
|
492
|
-
|
|
493
448
|
var RETURN_TRUSTED_TYPE = false;
|
|
449
|
+
|
|
494
450
|
/* Output should be free from DOM clobbering attacks?
|
|
495
451
|
* This sanitizes markups named with colliding, clobberable built-in DOM APIs.
|
|
496
452
|
*/
|
|
497
|
-
|
|
498
453
|
var SANITIZE_DOM = true;
|
|
454
|
+
|
|
499
455
|
/* Achieve full DOM Clobbering protection by isolating the namespace of named
|
|
500
456
|
* properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
|
|
501
457
|
*
|
|
@@ -509,98 +465,101 @@ function createDOMPurify() {
|
|
|
509
465
|
* Namespace isolation is implemented by prefixing `id` and `name` attributes
|
|
510
466
|
* with a constant string, i.e., `user-content-`
|
|
511
467
|
*/
|
|
512
|
-
|
|
513
468
|
var SANITIZE_NAMED_PROPS = false;
|
|
514
469
|
var SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
|
|
515
|
-
/* Keep element content when removing element? */
|
|
516
470
|
|
|
471
|
+
/* Keep element content when removing element? */
|
|
517
472
|
var KEEP_CONTENT = true;
|
|
473
|
+
|
|
518
474
|
/* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
|
|
519
475
|
* of importing it into a new Document and returning a sanitized copy */
|
|
520
|
-
|
|
521
476
|
var IN_PLACE = false;
|
|
522
|
-
/* Allow usage of profiles like html, svg and mathMl */
|
|
523
477
|
|
|
478
|
+
/* Allow usage of profiles like html, svg and mathMl */
|
|
524
479
|
var USE_PROFILES = {};
|
|
525
|
-
/* Tags to ignore content of when KEEP_CONTENT is true */
|
|
526
480
|
|
|
481
|
+
/* Tags to ignore content of when KEEP_CONTENT is true */
|
|
527
482
|
var FORBID_CONTENTS = null;
|
|
528
483
|
var DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
|
|
529
|
-
/* Tags that are safe for data: URIs */
|
|
530
484
|
|
|
485
|
+
/* Tags that are safe for data: URIs */
|
|
531
486
|
var DATA_URI_TAGS = null;
|
|
532
487
|
var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
|
|
533
|
-
/* Attributes safe for values like "javascript:" */
|
|
534
488
|
|
|
489
|
+
/* Attributes safe for values like "javascript:" */
|
|
535
490
|
var URI_SAFE_ATTRIBUTES = null;
|
|
536
491
|
var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
|
|
537
492
|
var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
|
|
538
493
|
var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
539
494
|
var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
540
495
|
/* Document namespace */
|
|
541
|
-
|
|
542
496
|
var NAMESPACE = HTML_NAMESPACE;
|
|
543
497
|
var IS_EMPTY_INPUT = false;
|
|
544
|
-
/* Allowed XHTML+XML namespaces */
|
|
545
498
|
|
|
499
|
+
/* Allowed XHTML+XML namespaces */
|
|
546
500
|
var ALLOWED_NAMESPACES = null;
|
|
547
501
|
var DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
|
|
548
|
-
/* Parsing of strict XHTML documents */
|
|
549
502
|
|
|
503
|
+
/* Parsing of strict XHTML documents */
|
|
550
504
|
var PARSER_MEDIA_TYPE;
|
|
551
505
|
var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
|
|
552
506
|
var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
|
|
553
507
|
var transformCaseFunc;
|
|
554
|
-
/* Keep a reference to config to pass to hooks */
|
|
555
508
|
|
|
509
|
+
/* Keep a reference to config to pass to hooks */
|
|
556
510
|
var CONFIG = null;
|
|
557
|
-
/* Ideally, do not touch anything below this line */
|
|
558
511
|
|
|
512
|
+
/* Specify the maximum element nesting depth to prevent mXSS */
|
|
513
|
+
var MAX_NESTING_DEPTH = 255;
|
|
514
|
+
|
|
515
|
+
/* Ideally, do not touch anything below this line */
|
|
559
516
|
/* ______________________________________________ */
|
|
560
517
|
|
|
561
518
|
var formElement = document.createElement('form');
|
|
562
|
-
|
|
563
519
|
var isRegexOrFunction = function isRegexOrFunction(testValue) {
|
|
564
520
|
return testValue instanceof RegExp || testValue instanceof Function;
|
|
565
521
|
};
|
|
522
|
+
|
|
566
523
|
/**
|
|
567
524
|
* _parseConfig
|
|
568
525
|
*
|
|
569
526
|
* @param {Object} cfg optional config literal
|
|
570
527
|
*/
|
|
571
528
|
// eslint-disable-next-line complexity
|
|
572
|
-
|
|
573
|
-
|
|
574
529
|
var _parseConfig = function _parseConfig(cfg) {
|
|
575
530
|
if (CONFIG && CONFIG === cfg) {
|
|
576
531
|
return;
|
|
577
532
|
}
|
|
578
|
-
/* Shield configuration object from tampering */
|
|
579
|
-
|
|
580
533
|
|
|
534
|
+
/* Shield configuration object from tampering */
|
|
581
535
|
if (!cfg || _typeof(cfg) !== 'object') {
|
|
582
536
|
cfg = {};
|
|
583
537
|
}
|
|
584
|
-
/* Shield configuration object from prototype pollution */
|
|
585
|
-
|
|
586
538
|
|
|
539
|
+
/* Shield configuration object from prototype pollution */
|
|
587
540
|
cfg = clone(cfg);
|
|
588
|
-
PARSER_MEDIA_TYPE =
|
|
589
|
-
|
|
541
|
+
PARSER_MEDIA_TYPE =
|
|
542
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
543
|
+
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
|
|
590
544
|
|
|
545
|
+
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
591
546
|
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
|
|
592
|
-
/* Set configuration parameters */
|
|
593
547
|
|
|
548
|
+
/* Set configuration parameters */
|
|
594
549
|
ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
595
550
|
ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
596
551
|
ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
|
|
597
|
-
URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
|
|
598
|
-
|
|
552
|
+
URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
|
|
553
|
+
// eslint-disable-line indent
|
|
554
|
+
cfg.ADD_URI_SAFE_ATTR,
|
|
555
|
+
// eslint-disable-line indent
|
|
599
556
|
transformCaseFunc // eslint-disable-line indent
|
|
600
557
|
) // eslint-disable-line indent
|
|
601
558
|
: DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
602
|
-
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
|
|
603
|
-
|
|
559
|
+
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
|
|
560
|
+
// eslint-disable-line indent
|
|
561
|
+
cfg.ADD_DATA_URI_TAGS,
|
|
562
|
+
// eslint-disable-line indent
|
|
604
563
|
transformCaseFunc // eslint-disable-line indent
|
|
605
564
|
) // eslint-disable-line indent
|
|
606
565
|
: DEFAULT_DATA_URI_TAGS;
|
|
@@ -609,161 +568,128 @@ function createDOMPurify() {
|
|
|
609
568
|
FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
|
|
610
569
|
USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
|
|
611
570
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
612
|
-
|
|
613
571
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
614
|
-
|
|
615
572
|
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
|
|
616
|
-
|
|
617
573
|
ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
|
|
618
|
-
|
|
619
574
|
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
|
|
620
|
-
|
|
575
|
+
SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
|
|
621
576
|
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
|
|
622
|
-
|
|
623
577
|
RETURN_DOM = cfg.RETURN_DOM || false; // Default false
|
|
624
|
-
|
|
625
578
|
RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
|
|
626
|
-
|
|
627
579
|
RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
|
|
628
|
-
|
|
629
580
|
FORCE_BODY = cfg.FORCE_BODY || false; // Default false
|
|
630
|
-
|
|
631
581
|
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
|
|
632
|
-
|
|
633
582
|
SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
|
|
634
|
-
|
|
635
583
|
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
|
|
636
|
-
|
|
637
584
|
IN_PLACE = cfg.IN_PLACE || false; // Default false
|
|
638
|
-
|
|
639
585
|
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
|
|
640
586
|
NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
|
|
641
587
|
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
|
|
642
|
-
|
|
643
588
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
|
|
644
589
|
CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
|
|
645
590
|
}
|
|
646
|
-
|
|
647
591
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
|
|
648
592
|
CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
|
|
649
593
|
}
|
|
650
|
-
|
|
651
594
|
if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
|
|
652
595
|
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
|
|
653
596
|
}
|
|
654
|
-
|
|
655
597
|
if (SAFE_FOR_TEMPLATES) {
|
|
656
598
|
ALLOW_DATA_ATTR = false;
|
|
657
599
|
}
|
|
658
|
-
|
|
659
600
|
if (RETURN_DOM_FRAGMENT) {
|
|
660
601
|
RETURN_DOM = true;
|
|
661
602
|
}
|
|
662
|
-
/* Parse profile info */
|
|
663
|
-
|
|
664
603
|
|
|
604
|
+
/* Parse profile info */
|
|
665
605
|
if (USE_PROFILES) {
|
|
666
606
|
ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));
|
|
667
607
|
ALLOWED_ATTR = [];
|
|
668
|
-
|
|
669
608
|
if (USE_PROFILES.html === true) {
|
|
670
609
|
addToSet(ALLOWED_TAGS, html$1);
|
|
671
610
|
addToSet(ALLOWED_ATTR, html);
|
|
672
611
|
}
|
|
673
|
-
|
|
674
612
|
if (USE_PROFILES.svg === true) {
|
|
675
613
|
addToSet(ALLOWED_TAGS, svg$1);
|
|
676
614
|
addToSet(ALLOWED_ATTR, svg);
|
|
677
615
|
addToSet(ALLOWED_ATTR, xml);
|
|
678
616
|
}
|
|
679
|
-
|
|
680
617
|
if (USE_PROFILES.svgFilters === true) {
|
|
681
618
|
addToSet(ALLOWED_TAGS, svgFilters);
|
|
682
619
|
addToSet(ALLOWED_ATTR, svg);
|
|
683
620
|
addToSet(ALLOWED_ATTR, xml);
|
|
684
621
|
}
|
|
685
|
-
|
|
686
622
|
if (USE_PROFILES.mathMl === true) {
|
|
687
623
|
addToSet(ALLOWED_TAGS, mathMl$1);
|
|
688
624
|
addToSet(ALLOWED_ATTR, mathMl);
|
|
689
625
|
addToSet(ALLOWED_ATTR, xml);
|
|
690
626
|
}
|
|
691
627
|
}
|
|
692
|
-
/* Merge configuration parameters */
|
|
693
|
-
|
|
694
628
|
|
|
629
|
+
/* Merge configuration parameters */
|
|
695
630
|
if (cfg.ADD_TAGS) {
|
|
696
631
|
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
697
632
|
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
698
633
|
}
|
|
699
|
-
|
|
700
634
|
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
701
635
|
}
|
|
702
|
-
|
|
703
636
|
if (cfg.ADD_ATTR) {
|
|
704
637
|
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
705
638
|
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
706
639
|
}
|
|
707
|
-
|
|
708
640
|
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
709
641
|
}
|
|
710
|
-
|
|
711
642
|
if (cfg.ADD_URI_SAFE_ATTR) {
|
|
712
643
|
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
713
644
|
}
|
|
714
|
-
|
|
715
645
|
if (cfg.FORBID_CONTENTS) {
|
|
716
646
|
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
|
|
717
647
|
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
718
648
|
}
|
|
719
|
-
|
|
720
649
|
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
721
650
|
}
|
|
722
|
-
/* Add #text in case KEEP_CONTENT is set to true */
|
|
723
|
-
|
|
724
651
|
|
|
652
|
+
/* Add #text in case KEEP_CONTENT is set to true */
|
|
725
653
|
if (KEEP_CONTENT) {
|
|
726
654
|
ALLOWED_TAGS['#text'] = true;
|
|
727
655
|
}
|
|
728
|
-
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
729
|
-
|
|
730
656
|
|
|
657
|
+
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
731
658
|
if (WHOLE_DOCUMENT) {
|
|
732
659
|
addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
|
|
733
660
|
}
|
|
734
|
-
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
735
|
-
|
|
736
661
|
|
|
662
|
+
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
737
663
|
if (ALLOWED_TAGS.table) {
|
|
738
664
|
addToSet(ALLOWED_TAGS, ['tbody']);
|
|
739
665
|
delete FORBID_TAGS.tbody;
|
|
740
|
-
}
|
|
741
|
-
// Not available in IE8, Safari 5, etc.
|
|
742
|
-
|
|
666
|
+
}
|
|
743
667
|
|
|
668
|
+
// Prevent further manipulation of configuration.
|
|
669
|
+
// Not available in IE8, Safari 5, etc.
|
|
744
670
|
if (freeze) {
|
|
745
671
|
freeze(cfg);
|
|
746
672
|
}
|
|
747
|
-
|
|
748
673
|
CONFIG = cfg;
|
|
749
674
|
};
|
|
750
|
-
|
|
751
675
|
var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
752
|
-
var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
|
|
676
|
+
var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
|
|
677
|
+
|
|
678
|
+
// Certain elements are allowed in both SVG and HTML
|
|
753
679
|
// namespace. We need to specify them explicitly
|
|
754
680
|
// so that they don't get erroneously deleted from
|
|
755
681
|
// HTML namespace.
|
|
756
|
-
|
|
757
682
|
var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
|
|
683
|
+
|
|
758
684
|
/* Keep track of all possible SVG and MathML tags
|
|
759
685
|
* so that we can perform the namespace checks
|
|
760
686
|
* correctly. */
|
|
761
|
-
|
|
762
687
|
var ALL_SVG_TAGS = addToSet({}, svg$1);
|
|
763
688
|
addToSet(ALL_SVG_TAGS, svgFilters);
|
|
764
689
|
addToSet(ALL_SVG_TAGS, svgDisallowed);
|
|
765
690
|
var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
|
|
766
691
|
addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
|
|
692
|
+
|
|
767
693
|
/**
|
|
768
694
|
*
|
|
769
695
|
*
|
|
@@ -772,64 +698,59 @@ function createDOMPurify() {
|
|
|
772
698
|
* namespace that a spec-compliant parser would never
|
|
773
699
|
* return. Return true otherwise.
|
|
774
700
|
*/
|
|
775
|
-
|
|
776
701
|
var _checkValidNamespace = function _checkValidNamespace(element) {
|
|
777
|
-
var parent = getParentNode(element);
|
|
778
|
-
// can be null. We just simulate parent in this case.
|
|
702
|
+
var parent = getParentNode(element);
|
|
779
703
|
|
|
704
|
+
// In JSDOM, if we're inside shadow DOM, then parentNode
|
|
705
|
+
// can be null. We just simulate parent in this case.
|
|
780
706
|
if (!parent || !parent.tagName) {
|
|
781
707
|
parent = {
|
|
782
708
|
namespaceURI: NAMESPACE,
|
|
783
709
|
tagName: 'template'
|
|
784
710
|
};
|
|
785
711
|
}
|
|
786
|
-
|
|
787
712
|
var tagName = stringToLowerCase(element.tagName);
|
|
788
713
|
var parentTagName = stringToLowerCase(parent.tagName);
|
|
789
|
-
|
|
790
714
|
if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
791
715
|
return false;
|
|
792
716
|
}
|
|
793
|
-
|
|
794
717
|
if (element.namespaceURI === SVG_NAMESPACE) {
|
|
795
718
|
// The only way to switch from HTML namespace to SVG
|
|
796
719
|
// is via <svg>. If it happens via any other tag, then
|
|
797
720
|
// it should be killed.
|
|
798
721
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
799
722
|
return tagName === 'svg';
|
|
800
|
-
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// The only way to switch from MathML to SVG is via`
|
|
801
726
|
// svg if parent is either <annotation-xml> or MathML
|
|
802
727
|
// text integration points.
|
|
803
|
-
|
|
804
|
-
|
|
805
728
|
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
806
729
|
return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
|
|
807
|
-
}
|
|
808
|
-
// spec. All others are disallowed in SVG namespace.
|
|
809
|
-
|
|
730
|
+
}
|
|
810
731
|
|
|
732
|
+
// We only allow elements that are defined in SVG
|
|
733
|
+
// spec. All others are disallowed in SVG namespace.
|
|
811
734
|
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
812
735
|
}
|
|
813
|
-
|
|
814
736
|
if (element.namespaceURI === MATHML_NAMESPACE) {
|
|
815
737
|
// The only way to switch from HTML namespace to MathML
|
|
816
738
|
// is via <math>. If it happens via any other tag, then
|
|
817
739
|
// it should be killed.
|
|
818
740
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
819
741
|
return tagName === 'math';
|
|
820
|
-
}
|
|
821
|
-
// <math> and HTML integration points
|
|
822
|
-
|
|
742
|
+
}
|
|
823
743
|
|
|
744
|
+
// The only way to switch from SVG to MathML is via
|
|
745
|
+
// <math> and HTML integration points
|
|
824
746
|
if (parent.namespaceURI === SVG_NAMESPACE) {
|
|
825
747
|
return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
|
|
826
|
-
}
|
|
827
|
-
// spec. All others are disallowed in MathML namespace.
|
|
828
|
-
|
|
748
|
+
}
|
|
829
749
|
|
|
750
|
+
// We only allow elements that are defined in MathML
|
|
751
|
+
// spec. All others are disallowed in MathML namespace.
|
|
830
752
|
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
831
753
|
}
|
|
832
|
-
|
|
833
754
|
if (element.namespaceURI === HTML_NAMESPACE) {
|
|
834
755
|
// The only way to switch from SVG to HTML is via
|
|
835
756
|
// HTML integration points, and from MathML to HTML
|
|
@@ -837,39 +758,36 @@ function createDOMPurify() {
|
|
|
837
758
|
if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
|
|
838
759
|
return false;
|
|
839
760
|
}
|
|
840
|
-
|
|
841
761
|
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
|
|
842
762
|
return false;
|
|
843
|
-
}
|
|
844
|
-
// or SVG and should never appear in HTML namespace
|
|
845
|
-
|
|
763
|
+
}
|
|
846
764
|
|
|
765
|
+
// We disallow tags that are specific for MathML
|
|
766
|
+
// or SVG and should never appear in HTML namespace
|
|
847
767
|
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
848
|
-
}
|
|
849
|
-
|
|
768
|
+
}
|
|
850
769
|
|
|
770
|
+
// For XHTML and XML documents that support custom namespaces
|
|
851
771
|
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
852
772
|
return true;
|
|
853
|
-
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// The code should never reach this place (this means
|
|
854
776
|
// that the element somehow got namespace that is not
|
|
855
777
|
// HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
|
|
856
778
|
// Return false just in case.
|
|
857
|
-
|
|
858
|
-
|
|
859
779
|
return false;
|
|
860
780
|
};
|
|
781
|
+
|
|
861
782
|
/**
|
|
862
783
|
* _forceRemove
|
|
863
784
|
*
|
|
864
785
|
* @param {Node} node a DOM node
|
|
865
786
|
*/
|
|
866
|
-
|
|
867
|
-
|
|
868
787
|
var _forceRemove = function _forceRemove(node) {
|
|
869
788
|
arrayPush(DOMPurify.removed, {
|
|
870
789
|
element: node
|
|
871
790
|
});
|
|
872
|
-
|
|
873
791
|
try {
|
|
874
792
|
// eslint-disable-next-line unicorn/prefer-dom-node-remove
|
|
875
793
|
node.parentNode.removeChild(node);
|
|
@@ -881,14 +799,13 @@ function createDOMPurify() {
|
|
|
881
799
|
}
|
|
882
800
|
}
|
|
883
801
|
};
|
|
802
|
+
|
|
884
803
|
/**
|
|
885
804
|
* _removeAttribute
|
|
886
805
|
*
|
|
887
806
|
* @param {String} name an Attribute name
|
|
888
807
|
* @param {Node} node a DOM node
|
|
889
808
|
*/
|
|
890
|
-
|
|
891
|
-
|
|
892
809
|
var _removeAttribute = function _removeAttribute(name, node) {
|
|
893
810
|
try {
|
|
894
811
|
arrayPush(DOMPurify.removed, {
|
|
@@ -901,9 +818,9 @@ function createDOMPurify() {
|
|
|
901
818
|
from: node
|
|
902
819
|
});
|
|
903
820
|
}
|
|
821
|
+
node.removeAttribute(name);
|
|
904
822
|
|
|
905
|
-
|
|
906
|
-
|
|
823
|
+
// We void attribute values for unremovable "is"" attributes
|
|
907
824
|
if (name === 'is' && !ALLOWED_ATTR[name]) {
|
|
908
825
|
if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
|
|
909
826
|
try {
|
|
@@ -916,19 +833,17 @@ function createDOMPurify() {
|
|
|
916
833
|
}
|
|
917
834
|
}
|
|
918
835
|
};
|
|
836
|
+
|
|
919
837
|
/**
|
|
920
838
|
* _initDocument
|
|
921
839
|
*
|
|
922
840
|
* @param {String} dirty a string of dirty markup
|
|
923
841
|
* @return {Document} a DOM, filled with the dirty markup
|
|
924
842
|
*/
|
|
925
|
-
|
|
926
|
-
|
|
927
843
|
var _initDocument = function _initDocument(dirty) {
|
|
928
844
|
/* Create a HTML document */
|
|
929
845
|
var doc;
|
|
930
846
|
var leadingWhitespace;
|
|
931
|
-
|
|
932
847
|
if (FORCE_BODY) {
|
|
933
848
|
dirty = '<remove></remove>' + dirty;
|
|
934
849
|
} else {
|
|
@@ -936,83 +851,74 @@ function createDOMPurify() {
|
|
|
936
851
|
var matches = stringMatch(dirty, /^[\r\n\t ]+/);
|
|
937
852
|
leadingWhitespace = matches && matches[0];
|
|
938
853
|
}
|
|
939
|
-
|
|
940
854
|
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
|
|
941
855
|
// Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
|
|
942
856
|
dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
|
|
943
857
|
}
|
|
944
|
-
|
|
945
858
|
var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
946
859
|
/*
|
|
947
860
|
* Use the DOMParser API by default, fallback later if needs be
|
|
948
861
|
* DOMParser not work for svg when has multiple root element.
|
|
949
862
|
*/
|
|
950
|
-
|
|
951
863
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
952
864
|
try {
|
|
953
865
|
doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
|
|
954
866
|
} catch (_) {}
|
|
955
867
|
}
|
|
956
|
-
/* Use createHTMLDocument in case DOMParser is not available */
|
|
957
|
-
|
|
958
868
|
|
|
869
|
+
/* Use createHTMLDocument in case DOMParser is not available */
|
|
959
870
|
if (!doc || !doc.documentElement) {
|
|
960
871
|
doc = implementation.createDocument(NAMESPACE, 'template', null);
|
|
961
|
-
|
|
962
872
|
try {
|
|
963
873
|
doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
|
|
964
|
-
} catch (_) {
|
|
874
|
+
} catch (_) {
|
|
875
|
+
// Syntax error if dirtyPayload is invalid xml
|
|
965
876
|
}
|
|
966
877
|
}
|
|
967
|
-
|
|
968
878
|
var body = doc.body || doc.documentElement;
|
|
969
|
-
|
|
970
879
|
if (dirty && leadingWhitespace) {
|
|
971
880
|
body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
|
|
972
881
|
}
|
|
973
|
-
/* Work on whole document or just its body */
|
|
974
|
-
|
|
975
882
|
|
|
883
|
+
/* Work on whole document or just its body */
|
|
976
884
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
977
885
|
return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
|
|
978
886
|
}
|
|
979
|
-
|
|
980
887
|
return WHOLE_DOCUMENT ? doc.documentElement : body;
|
|
981
888
|
};
|
|
889
|
+
|
|
982
890
|
/**
|
|
983
891
|
* _createIterator
|
|
984
892
|
*
|
|
985
893
|
* @param {Document} root document/fragment to create iterator for
|
|
986
894
|
* @return {Iterator} iterator instance
|
|
987
895
|
*/
|
|
988
|
-
|
|
989
|
-
|
|
990
896
|
var _createIterator = function _createIterator(root) {
|
|
991
|
-
return createNodeIterator.call(root.ownerDocument || root, root,
|
|
897
|
+
return createNodeIterator.call(root.ownerDocument || root, root,
|
|
898
|
+
// eslint-disable-next-line no-bitwise
|
|
992
899
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null, false);
|
|
993
900
|
};
|
|
901
|
+
|
|
994
902
|
/**
|
|
995
903
|
* _isClobbered
|
|
996
904
|
*
|
|
997
905
|
* @param {Node} elm element to check for clobbering attacks
|
|
998
906
|
* @return {Boolean} true if clobbered, false if safe
|
|
999
907
|
*/
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
908
|
var _isClobbered = function _isClobbered(elm) {
|
|
1003
|
-
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');
|
|
909
|
+
return elm instanceof HTMLFormElement && (typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' || 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');
|
|
1004
910
|
};
|
|
911
|
+
|
|
1005
912
|
/**
|
|
1006
913
|
* _isNode
|
|
1007
914
|
*
|
|
1008
915
|
* @param {Node} obj object to check whether it's a DOM node
|
|
1009
916
|
* @return {Boolean} true is object is a DOM node
|
|
1010
917
|
*/
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
918
|
var _isNode = function _isNode(object) {
|
|
1014
919
|
return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
|
|
1015
920
|
};
|
|
921
|
+
|
|
1016
922
|
/**
|
|
1017
923
|
* _executeHook
|
|
1018
924
|
* Execute user configurable hooks
|
|
@@ -1021,17 +927,15 @@ function createDOMPurify() {
|
|
|
1021
927
|
* @param {Node} currentNode node to work on with the hook
|
|
1022
928
|
* @param {Object} data additional hook parameters
|
|
1023
929
|
*/
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
930
|
var _executeHook = function _executeHook(entryPoint, currentNode, data) {
|
|
1027
931
|
if (!hooks[entryPoint]) {
|
|
1028
932
|
return;
|
|
1029
933
|
}
|
|
1030
|
-
|
|
1031
934
|
arrayForEach(hooks[entryPoint], function (hook) {
|
|
1032
935
|
hook.call(DOMPurify, currentNode, data, CONFIG);
|
|
1033
936
|
});
|
|
1034
937
|
};
|
|
938
|
+
|
|
1035
939
|
/**
|
|
1036
940
|
* _sanitizeElements
|
|
1037
941
|
*
|
|
@@ -1042,118 +946,101 @@ function createDOMPurify() {
|
|
|
1042
946
|
* @param {Node} currentNode to check for permission to exist
|
|
1043
947
|
* @return {Boolean} true if node was killed, false if left alive
|
|
1044
948
|
*/
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
949
|
var _sanitizeElements = function _sanitizeElements(currentNode) {
|
|
1048
950
|
var content;
|
|
1049
|
-
/* Execute a hook if present */
|
|
1050
951
|
|
|
952
|
+
/* Execute a hook if present */
|
|
1051
953
|
_executeHook('beforeSanitizeElements', currentNode, null);
|
|
1052
|
-
/* Check if element is clobbered or can clobber */
|
|
1053
|
-
|
|
1054
954
|
|
|
955
|
+
/* Check if element is clobbered or can clobber */
|
|
1055
956
|
if (_isClobbered(currentNode)) {
|
|
1056
957
|
_forceRemove(currentNode);
|
|
1057
|
-
|
|
1058
958
|
return true;
|
|
1059
959
|
}
|
|
1060
|
-
/* Check if tagname contains Unicode */
|
|
1061
|
-
|
|
1062
960
|
|
|
961
|
+
/* Check if tagname contains Unicode */
|
|
1063
962
|
if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) {
|
|
1064
963
|
_forceRemove(currentNode);
|
|
1065
|
-
|
|
1066
964
|
return true;
|
|
1067
965
|
}
|
|
1068
|
-
/* Now let's check the element's type and name */
|
|
1069
|
-
|
|
1070
966
|
|
|
967
|
+
/* Now let's check the element's type and name */
|
|
1071
968
|
var tagName = transformCaseFunc(currentNode.nodeName);
|
|
1072
|
-
/* Execute a hook if present */
|
|
1073
969
|
|
|
970
|
+
/* Execute a hook if present */
|
|
1074
971
|
_executeHook('uponSanitizeElement', currentNode, {
|
|
1075
972
|
tagName: tagName,
|
|
1076
973
|
allowedTags: ALLOWED_TAGS
|
|
1077
974
|
});
|
|
1078
|
-
/* Detect mXSS attempts abusing namespace confusion */
|
|
1079
|
-
|
|
1080
975
|
|
|
976
|
+
/* Detect mXSS attempts abusing namespace confusion */
|
|
1081
977
|
if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
|
|
1082
978
|
_forceRemove(currentNode);
|
|
1083
|
-
|
|
1084
979
|
return true;
|
|
1085
980
|
}
|
|
1086
|
-
/* Mitigate a problem with templates inside select */
|
|
1087
|
-
|
|
1088
981
|
|
|
982
|
+
/* Mitigate a problem with templates inside select */
|
|
1089
983
|
if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
|
|
1090
984
|
_forceRemove(currentNode);
|
|
1091
|
-
|
|
1092
985
|
return true;
|
|
1093
986
|
}
|
|
1094
|
-
/* Remove any ocurrence of processing instructions */
|
|
1095
|
-
|
|
1096
987
|
|
|
988
|
+
/* Remove any ocurrence of processing instructions */
|
|
1097
989
|
if (currentNode.nodeType === 7) {
|
|
1098
990
|
_forceRemove(currentNode);
|
|
1099
|
-
|
|
1100
991
|
return true;
|
|
1101
992
|
}
|
|
1102
|
-
/* Remove element if anything forbids its presence */
|
|
1103
993
|
|
|
994
|
+
/* Remove any kind of possibly harmful comments */
|
|
995
|
+
if (SAFE_FOR_XML && currentNode.nodeType === 8 && regExpTest(/<[/\w]/g, currentNode.data)) {
|
|
996
|
+
_forceRemove(currentNode);
|
|
997
|
+
return true;
|
|
998
|
+
}
|
|
1104
999
|
|
|
1000
|
+
/* Remove element if anything forbids its presence */
|
|
1105
1001
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1106
1002
|
/* Check if we have a custom element to handle */
|
|
1107
1003
|
if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
|
|
1108
1004
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
1109
1005
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
|
|
1110
1006
|
}
|
|
1111
|
-
/* Keep content except for bad-listed elements */
|
|
1112
|
-
|
|
1113
1007
|
|
|
1008
|
+
/* Keep content except for bad-listed elements */
|
|
1114
1009
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
1115
1010
|
var parentNode = getParentNode(currentNode) || currentNode.parentNode;
|
|
1116
1011
|
var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
|
|
1117
|
-
|
|
1118
1012
|
if (childNodes && parentNode) {
|
|
1119
1013
|
var childCount = childNodes.length;
|
|
1120
|
-
|
|
1121
1014
|
for (var i = childCount - 1; i >= 0; --i) {
|
|
1122
|
-
|
|
1015
|
+
var childClone = cloneNode(childNodes[i], true);
|
|
1016
|
+
childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
|
|
1017
|
+
parentNode.insertBefore(childClone, getNextSibling(currentNode));
|
|
1123
1018
|
}
|
|
1124
1019
|
}
|
|
1125
1020
|
}
|
|
1126
|
-
|
|
1127
1021
|
_forceRemove(currentNode);
|
|
1128
|
-
|
|
1129
1022
|
return true;
|
|
1130
1023
|
}
|
|
1131
|
-
/* Check whether element has a valid namespace */
|
|
1132
|
-
|
|
1133
1024
|
|
|
1025
|
+
/* Check whether element has a valid namespace */
|
|
1134
1026
|
if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
|
|
1135
1027
|
_forceRemove(currentNode);
|
|
1136
|
-
|
|
1137
1028
|
return true;
|
|
1138
1029
|
}
|
|
1139
|
-
/* Make sure that older browsers don't get fallback-tag mXSS */
|
|
1140
|
-
|
|
1141
1030
|
|
|
1031
|
+
/* Make sure that older browsers don't get fallback-tag mXSS */
|
|
1142
1032
|
if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
|
|
1143
1033
|
_forceRemove(currentNode);
|
|
1144
|
-
|
|
1145
1034
|
return true;
|
|
1146
1035
|
}
|
|
1147
|
-
/* Sanitize element content to be template-safe */
|
|
1148
|
-
|
|
1149
1036
|
|
|
1037
|
+
/* Sanitize element content to be template-safe */
|
|
1150
1038
|
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
|
|
1151
1039
|
/* Get the element's text content */
|
|
1152
1040
|
content = currentNode.textContent;
|
|
1153
1041
|
content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
|
|
1154
1042
|
content = stringReplace(content, ERB_EXPR$1, ' ');
|
|
1155
1043
|
content = stringReplace(content, TMPLIT_EXPR$1, ' ');
|
|
1156
|
-
|
|
1157
1044
|
if (currentNode.textContent !== content) {
|
|
1158
1045
|
arrayPush(DOMPurify.removed, {
|
|
1159
1046
|
element: currentNode.cloneNode()
|
|
@@ -1161,13 +1048,12 @@ function createDOMPurify() {
|
|
|
1161
1048
|
currentNode.textContent = content;
|
|
1162
1049
|
}
|
|
1163
1050
|
}
|
|
1164
|
-
/* Execute a hook if present */
|
|
1165
|
-
|
|
1166
1051
|
|
|
1052
|
+
/* Execute a hook if present */
|
|
1167
1053
|
_executeHook('afterSanitizeElements', currentNode, null);
|
|
1168
|
-
|
|
1169
1054
|
return false;
|
|
1170
1055
|
};
|
|
1056
|
+
|
|
1171
1057
|
/**
|
|
1172
1058
|
* _isValidAttribute
|
|
1173
1059
|
*
|
|
@@ -1177,47 +1063,44 @@ function createDOMPurify() {
|
|
|
1177
1063
|
* @return {Boolean} Returns true if `value` is valid, otherwise false.
|
|
1178
1064
|
*/
|
|
1179
1065
|
// eslint-disable-next-line complexity
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
1066
|
var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
1183
1067
|
/* Make sure attribute cannot clobber */
|
|
1184
1068
|
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
1185
1069
|
return false;
|
|
1186
1070
|
}
|
|
1071
|
+
|
|
1187
1072
|
/* Allow valid data-* attributes: At least one character after "-"
|
|
1188
1073
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
1189
1074
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
1190
1075
|
We don't need to check the value; it's always URI safe. */
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
1076
|
if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
|
|
1194
|
-
if (
|
|
1077
|
+
if (
|
|
1078
|
+
// First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
1195
1079
|
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1196
1080
|
// and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
|
|
1197
|
-
_basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) ||
|
|
1081
|
+
_basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) ||
|
|
1082
|
+
// Alternative, second condition checks if it's an `is`-attribute, AND
|
|
1198
1083
|
// the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1199
1084
|
lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {
|
|
1200
1085
|
return false;
|
|
1201
1086
|
}
|
|
1202
1087
|
/* Check value is safe. First, is attr inert? If so, is safe */
|
|
1203
|
-
|
|
1204
1088
|
} else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (value) {
|
|
1205
1089
|
return false;
|
|
1206
1090
|
} else ;
|
|
1207
|
-
|
|
1208
1091
|
return true;
|
|
1209
1092
|
};
|
|
1093
|
+
|
|
1210
1094
|
/**
|
|
1211
1095
|
* _basicCustomElementCheck
|
|
1212
1096
|
* checks if at least one dash is included in tagName, and it's not the first char
|
|
1213
1097
|
* for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
|
|
1214
1098
|
* @param {string} tagName name of the tag of the node to sanitize
|
|
1215
1099
|
*/
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
1100
|
var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
|
|
1219
1101
|
return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT$1);
|
|
1220
1102
|
};
|
|
1103
|
+
|
|
1221
1104
|
/**
|
|
1222
1105
|
* _sanitizeAttributes
|
|
1223
1106
|
*
|
|
@@ -1228,24 +1111,19 @@ function createDOMPurify() {
|
|
|
1228
1111
|
*
|
|
1229
1112
|
* @param {Node} currentNode to sanitize
|
|
1230
1113
|
*/
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
1114
|
var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
1234
1115
|
var attr;
|
|
1235
1116
|
var value;
|
|
1236
1117
|
var lcName;
|
|
1237
1118
|
var l;
|
|
1238
1119
|
/* Execute a hook if present */
|
|
1239
|
-
|
|
1240
1120
|
_executeHook('beforeSanitizeAttributes', currentNode, null);
|
|
1241
|
-
|
|
1242
1121
|
var attributes = currentNode.attributes;
|
|
1243
|
-
/* Check if we have attributes; if not we might have a text node */
|
|
1244
1122
|
|
|
1123
|
+
/* Check if we have attributes; if not we might have a text node */
|
|
1245
1124
|
if (!attributes) {
|
|
1246
1125
|
return;
|
|
1247
1126
|
}
|
|
1248
|
-
|
|
1249
1127
|
var hookEvent = {
|
|
1250
1128
|
attrName: '',
|
|
1251
1129
|
attrValue: '',
|
|
@@ -1253,79 +1131,67 @@ function createDOMPurify() {
|
|
|
1253
1131
|
allowedAttributes: ALLOWED_ATTR
|
|
1254
1132
|
};
|
|
1255
1133
|
l = attributes.length;
|
|
1256
|
-
/* Go backwards over all attributes; safely remove bad ones */
|
|
1257
1134
|
|
|
1135
|
+
/* Go backwards over all attributes; safely remove bad ones */
|
|
1258
1136
|
while (l--) {
|
|
1259
1137
|
attr = attributes[l];
|
|
1260
1138
|
var _attr = attr,
|
|
1261
|
-
|
|
1262
|
-
|
|
1139
|
+
name = _attr.name,
|
|
1140
|
+
namespaceURI = _attr.namespaceURI;
|
|
1263
1141
|
value = name === 'value' ? attr.value : stringTrim(attr.value);
|
|
1264
1142
|
lcName = transformCaseFunc(name);
|
|
1265
|
-
/* Execute a hook if present */
|
|
1266
1143
|
|
|
1144
|
+
/* Execute a hook if present */
|
|
1267
1145
|
hookEvent.attrName = lcName;
|
|
1268
1146
|
hookEvent.attrValue = value;
|
|
1269
1147
|
hookEvent.keepAttr = true;
|
|
1270
1148
|
hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
|
|
1271
|
-
|
|
1272
1149
|
_executeHook('uponSanitizeAttribute', currentNode, hookEvent);
|
|
1273
|
-
|
|
1274
1150
|
value = hookEvent.attrValue;
|
|
1275
1151
|
/* Did the hooks approve of the attribute? */
|
|
1276
|
-
|
|
1277
1152
|
if (hookEvent.forceKeepAttr) {
|
|
1278
1153
|
continue;
|
|
1279
1154
|
}
|
|
1280
|
-
/* Remove attribute */
|
|
1281
|
-
|
|
1282
1155
|
|
|
1156
|
+
/* Remove attribute */
|
|
1283
1157
|
_removeAttribute(name, currentNode);
|
|
1284
|
-
/* Did the hooks approve of the attribute? */
|
|
1285
|
-
|
|
1286
1158
|
|
|
1159
|
+
/* Did the hooks approve of the attribute? */
|
|
1287
1160
|
if (!hookEvent.keepAttr) {
|
|
1288
1161
|
continue;
|
|
1289
1162
|
}
|
|
1290
|
-
/* Work around a security issue in jQuery 3.0 */
|
|
1291
|
-
|
|
1292
1163
|
|
|
1164
|
+
/* Work around a security issue in jQuery 3.0 */
|
|
1293
1165
|
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
|
|
1294
1166
|
_removeAttribute(name, currentNode);
|
|
1295
|
-
|
|
1296
1167
|
continue;
|
|
1297
1168
|
}
|
|
1298
|
-
/* Sanitize attribute content to be template-safe */
|
|
1299
|
-
|
|
1300
1169
|
|
|
1170
|
+
/* Sanitize attribute content to be template-safe */
|
|
1301
1171
|
if (SAFE_FOR_TEMPLATES) {
|
|
1302
1172
|
value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
|
|
1303
1173
|
value = stringReplace(value, ERB_EXPR$1, ' ');
|
|
1304
1174
|
value = stringReplace(value, TMPLIT_EXPR$1, ' ');
|
|
1305
1175
|
}
|
|
1306
|
-
/* Is `value` valid for this attribute? */
|
|
1307
|
-
|
|
1308
1176
|
|
|
1177
|
+
/* Is `value` valid for this attribute? */
|
|
1309
1178
|
var lcTag = transformCaseFunc(currentNode.nodeName);
|
|
1310
|
-
|
|
1311
1179
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
1312
1180
|
continue;
|
|
1313
1181
|
}
|
|
1182
|
+
|
|
1314
1183
|
/* Full DOM Clobbering protection via namespace isolation,
|
|
1315
1184
|
* Prefix id and name attributes with `user-content-`
|
|
1316
1185
|
*/
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
1186
|
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
|
|
1320
1187
|
// Remove the attribute with this value
|
|
1321
|
-
_removeAttribute(name, currentNode);
|
|
1322
|
-
|
|
1188
|
+
_removeAttribute(name, currentNode);
|
|
1323
1189
|
|
|
1190
|
+
// Prefix the value and later re-create the attribute with the sanitized value
|
|
1324
1191
|
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
1325
1192
|
}
|
|
1326
|
-
/* Handle attributes that require Trusted Types */
|
|
1327
|
-
|
|
1328
1193
|
|
|
1194
|
+
/* Handle attributes that require Trusted Types */
|
|
1329
1195
|
if (trustedTypesPolicy && _typeof(trustedTypes) === 'object' && typeof trustedTypes.getAttributeType === 'function') {
|
|
1330
1196
|
if (namespaceURI) ; else {
|
|
1331
1197
|
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
|
|
@@ -1334,7 +1200,6 @@ function createDOMPurify() {
|
|
|
1334
1200
|
value = trustedTypesPolicy.createHTML(value);
|
|
1335
1201
|
break;
|
|
1336
1202
|
}
|
|
1337
|
-
|
|
1338
1203
|
case 'TrustedScriptURL':
|
|
1339
1204
|
{
|
|
1340
1205
|
value = trustedTypesPolicy.createScriptURL(value);
|
|
@@ -1343,9 +1208,8 @@ function createDOMPurify() {
|
|
|
1343
1208
|
}
|
|
1344
1209
|
}
|
|
1345
1210
|
}
|
|
1346
|
-
/* Handle invalid data-* attribute set by try-catching it */
|
|
1347
|
-
|
|
1348
1211
|
|
|
1212
|
+
/* Handle invalid data-* attribute set by try-catching it */
|
|
1349
1213
|
try {
|
|
1350
1214
|
if (namespaceURI) {
|
|
1351
1215
|
currentNode.setAttributeNS(namespaceURI, name, value);
|
|
@@ -1353,56 +1217,66 @@ function createDOMPurify() {
|
|
|
1353
1217
|
/* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
|
|
1354
1218
|
currentNode.setAttribute(name, value);
|
|
1355
1219
|
}
|
|
1356
|
-
|
|
1357
1220
|
arrayPop(DOMPurify.removed);
|
|
1358
1221
|
} catch (_) {}
|
|
1359
1222
|
}
|
|
1360
|
-
/* Execute a hook if present */
|
|
1361
|
-
|
|
1362
1223
|
|
|
1224
|
+
/* Execute a hook if present */
|
|
1363
1225
|
_executeHook('afterSanitizeAttributes', currentNode, null);
|
|
1364
1226
|
};
|
|
1227
|
+
|
|
1365
1228
|
/**
|
|
1366
1229
|
* _sanitizeShadowDOM
|
|
1367
1230
|
*
|
|
1368
1231
|
* @param {DocumentFragment} fragment to iterate over recursively
|
|
1369
1232
|
*/
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
1233
|
var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
|
|
1373
1234
|
var shadowNode;
|
|
1374
|
-
|
|
1375
1235
|
var shadowIterator = _createIterator(fragment);
|
|
1376
|
-
/* Execute a hook if present */
|
|
1377
|
-
|
|
1378
1236
|
|
|
1237
|
+
/* Execute a hook if present */
|
|
1379
1238
|
_executeHook('beforeSanitizeShadowDOM', fragment, null);
|
|
1380
|
-
|
|
1381
1239
|
while (shadowNode = shadowIterator.nextNode()) {
|
|
1382
1240
|
/* Execute a hook if present */
|
|
1383
1241
|
_executeHook('uponSanitizeShadowNode', shadowNode, null);
|
|
1384
|
-
/* Sanitize tags and elements */
|
|
1385
|
-
|
|
1386
1242
|
|
|
1243
|
+
/* Sanitize tags and elements */
|
|
1387
1244
|
if (_sanitizeElements(shadowNode)) {
|
|
1388
1245
|
continue;
|
|
1389
1246
|
}
|
|
1390
|
-
/* Deep shadow DOM detected */
|
|
1391
1247
|
|
|
1248
|
+
/* Set the nesting depth of an element */
|
|
1249
|
+
if (shadowNode.nodeType === 1) {
|
|
1250
|
+
if (shadowNode.parentNode && shadowNode.parentNode.__depth) {
|
|
1251
|
+
/*
|
|
1252
|
+
We want the depth of the node in the original tree, which can
|
|
1253
|
+
change when it's removed from its parent.
|
|
1254
|
+
*/
|
|
1255
|
+
shadowNode.__depth = (shadowNode.__removalCount || 0) + shadowNode.parentNode.__depth + 1;
|
|
1256
|
+
} else {
|
|
1257
|
+
shadowNode.__depth = 1;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
/* Remove an element if nested too deeply to avoid mXSS */
|
|
1262
|
+
if (shadowNode.__depth >= MAX_NESTING_DEPTH) {
|
|
1263
|
+
_forceRemove(shadowNode);
|
|
1264
|
+
}
|
|
1392
1265
|
|
|
1266
|
+
/* Deep shadow DOM detected */
|
|
1393
1267
|
if (shadowNode.content instanceof DocumentFragment) {
|
|
1268
|
+
shadowNode.content.__depth = shadowNode.__depth;
|
|
1394
1269
|
_sanitizeShadowDOM(shadowNode.content);
|
|
1395
1270
|
}
|
|
1396
|
-
/* Check attributes, sanitize if necessary */
|
|
1397
|
-
|
|
1398
1271
|
|
|
1272
|
+
/* Check attributes, sanitize if necessary */
|
|
1399
1273
|
_sanitizeAttributes(shadowNode);
|
|
1400
1274
|
}
|
|
1401
|
-
/* Execute a hook if present */
|
|
1402
|
-
|
|
1403
1275
|
|
|
1276
|
+
/* Execute a hook if present */
|
|
1404
1277
|
_executeHook('afterSanitizeShadowDOM', fragment, null);
|
|
1405
1278
|
};
|
|
1279
|
+
|
|
1406
1280
|
/**
|
|
1407
1281
|
* Sanitize
|
|
1408
1282
|
* Public method providing core sanitation functionality
|
|
@@ -1411,8 +1285,6 @@ function createDOMPurify() {
|
|
|
1411
1285
|
* @param {Object} configuration object
|
|
1412
1286
|
*/
|
|
1413
1287
|
// eslint-disable-next-line complexity
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
1288
|
DOMPurify.sanitize = function (dirty) {
|
|
1417
1289
|
var cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1418
1290
|
var body;
|
|
@@ -1423,19 +1295,15 @@ function createDOMPurify() {
|
|
|
1423
1295
|
/* Make sure we have a string to sanitize.
|
|
1424
1296
|
DO NOT return early, as this will return the wrong type if
|
|
1425
1297
|
the user has requested a DOM object rather than a string */
|
|
1426
|
-
|
|
1427
1298
|
IS_EMPTY_INPUT = !dirty;
|
|
1428
|
-
|
|
1429
1299
|
if (IS_EMPTY_INPUT) {
|
|
1430
1300
|
dirty = '<!-->';
|
|
1431
1301
|
}
|
|
1432
|
-
/* Stringify, in case dirty is an object */
|
|
1433
|
-
|
|
1434
1302
|
|
|
1303
|
+
/* Stringify, in case dirty is an object */
|
|
1435
1304
|
if (typeof dirty !== 'string' && !_isNode(dirty)) {
|
|
1436
1305
|
if (typeof dirty.toString === 'function') {
|
|
1437
1306
|
dirty = dirty.toString();
|
|
1438
|
-
|
|
1439
1307
|
if (typeof dirty !== 'string') {
|
|
1440
1308
|
throw typeErrorCreate('dirty is not a string, aborting');
|
|
1441
1309
|
}
|
|
@@ -1443,43 +1311,36 @@ function createDOMPurify() {
|
|
|
1443
1311
|
throw typeErrorCreate('toString is not a function');
|
|
1444
1312
|
}
|
|
1445
1313
|
}
|
|
1446
|
-
/* Check we can run. Otherwise fall back or ignore */
|
|
1447
|
-
|
|
1448
1314
|
|
|
1315
|
+
/* Check we can run. Otherwise fall back or ignore */
|
|
1449
1316
|
if (!DOMPurify.isSupported) {
|
|
1450
1317
|
if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
|
|
1451
1318
|
if (typeof dirty === 'string') {
|
|
1452
1319
|
return window.toStaticHTML(dirty);
|
|
1453
1320
|
}
|
|
1454
|
-
|
|
1455
1321
|
if (_isNode(dirty)) {
|
|
1456
1322
|
return window.toStaticHTML(dirty.outerHTML);
|
|
1457
1323
|
}
|
|
1458
1324
|
}
|
|
1459
|
-
|
|
1460
1325
|
return dirty;
|
|
1461
1326
|
}
|
|
1462
|
-
/* Assign config vars */
|
|
1463
|
-
|
|
1464
1327
|
|
|
1328
|
+
/* Assign config vars */
|
|
1465
1329
|
if (!SET_CONFIG) {
|
|
1466
1330
|
_parseConfig(cfg);
|
|
1467
1331
|
}
|
|
1468
|
-
/* Clean up removed elements */
|
|
1469
|
-
|
|
1470
1332
|
|
|
1333
|
+
/* Clean up removed elements */
|
|
1471
1334
|
DOMPurify.removed = [];
|
|
1472
|
-
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1473
1335
|
|
|
1336
|
+
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1474
1337
|
if (typeof dirty === 'string') {
|
|
1475
1338
|
IN_PLACE = false;
|
|
1476
1339
|
}
|
|
1477
|
-
|
|
1478
1340
|
if (IN_PLACE) {
|
|
1479
1341
|
/* Do some early pre-sanitization to avoid unsafe root nodes */
|
|
1480
1342
|
if (dirty.nodeName) {
|
|
1481
1343
|
var tagName = transformCaseFunc(dirty.nodeName);
|
|
1482
|
-
|
|
1483
1344
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1484
1345
|
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
1485
1346
|
}
|
|
@@ -1489,7 +1350,6 @@ function createDOMPurify() {
|
|
|
1489
1350
|
elements being stripped by the parser */
|
|
1490
1351
|
body = _initDocument('<!---->');
|
|
1491
1352
|
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
1492
|
-
|
|
1493
1353
|
if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
|
|
1494
1354
|
/* Node is already a body, use as is */
|
|
1495
1355
|
body = importedNode;
|
|
@@ -1501,71 +1361,80 @@ function createDOMPurify() {
|
|
|
1501
1361
|
}
|
|
1502
1362
|
} else {
|
|
1503
1363
|
/* Exit directly if we have nothing to do */
|
|
1504
|
-
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
1364
|
+
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
1365
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
1505
1366
|
dirty.indexOf('<') === -1) {
|
|
1506
1367
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
1507
1368
|
}
|
|
1508
|
-
/* Initialize the document to work on */
|
|
1509
|
-
|
|
1510
1369
|
|
|
1370
|
+
/* Initialize the document to work on */
|
|
1511
1371
|
body = _initDocument(dirty);
|
|
1512
|
-
/* Check we have a DOM node from the data */
|
|
1513
1372
|
|
|
1373
|
+
/* Check we have a DOM node from the data */
|
|
1514
1374
|
if (!body) {
|
|
1515
1375
|
return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
|
|
1516
1376
|
}
|
|
1517
1377
|
}
|
|
1518
|
-
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1519
|
-
|
|
1520
1378
|
|
|
1379
|
+
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1521
1380
|
if (body && FORCE_BODY) {
|
|
1522
1381
|
_forceRemove(body.firstChild);
|
|
1523
1382
|
}
|
|
1524
|
-
/* Get node iterator */
|
|
1525
|
-
|
|
1526
1383
|
|
|
1384
|
+
/* Get node iterator */
|
|
1527
1385
|
var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
|
|
1528
|
-
/* Now start iterating over the created document */
|
|
1529
|
-
|
|
1530
1386
|
|
|
1387
|
+
/* Now start iterating over the created document */
|
|
1531
1388
|
while (currentNode = nodeIterator.nextNode()) {
|
|
1532
1389
|
/* Fix IE's strange behavior with manipulated textNodes #89 */
|
|
1533
1390
|
if (currentNode.nodeType === 3 && currentNode === oldNode) {
|
|
1534
1391
|
continue;
|
|
1535
1392
|
}
|
|
1536
|
-
/* Sanitize tags and elements */
|
|
1537
|
-
|
|
1538
1393
|
|
|
1394
|
+
/* Sanitize tags and elements */
|
|
1539
1395
|
if (_sanitizeElements(currentNode)) {
|
|
1540
1396
|
continue;
|
|
1541
1397
|
}
|
|
1542
|
-
/* Shadow DOM detected, sanitize it */
|
|
1543
1398
|
|
|
1399
|
+
/* Set the nesting depth of an element */
|
|
1400
|
+
if (currentNode.nodeType === 1) {
|
|
1401
|
+
if (currentNode.parentNode && currentNode.parentNode.__depth) {
|
|
1402
|
+
/*
|
|
1403
|
+
We want the depth of the node in the original tree, which can
|
|
1404
|
+
change when it's removed from its parent.
|
|
1405
|
+
*/
|
|
1406
|
+
currentNode.__depth = (currentNode.__removalCount || 0) + currentNode.parentNode.__depth + 1;
|
|
1407
|
+
} else {
|
|
1408
|
+
currentNode.__depth = 1;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/* Remove an element if nested too deeply to avoid mXSS */
|
|
1413
|
+
if (currentNode.__depth >= MAX_NESTING_DEPTH) {
|
|
1414
|
+
_forceRemove(currentNode);
|
|
1415
|
+
}
|
|
1544
1416
|
|
|
1417
|
+
/* Shadow DOM detected, sanitize it */
|
|
1545
1418
|
if (currentNode.content instanceof DocumentFragment) {
|
|
1419
|
+
currentNode.content.__depth = currentNode.__depth;
|
|
1546
1420
|
_sanitizeShadowDOM(currentNode.content);
|
|
1547
1421
|
}
|
|
1548
|
-
/* Check attributes, sanitize if necessary */
|
|
1549
|
-
|
|
1550
1422
|
|
|
1423
|
+
/* Check attributes, sanitize if necessary */
|
|
1551
1424
|
_sanitizeAttributes(currentNode);
|
|
1552
|
-
|
|
1553
1425
|
oldNode = currentNode;
|
|
1554
1426
|
}
|
|
1555
|
-
|
|
1556
1427
|
oldNode = null;
|
|
1557
|
-
/* If we sanitized `dirty` in-place, return it. */
|
|
1558
1428
|
|
|
1429
|
+
/* If we sanitized `dirty` in-place, return it. */
|
|
1559
1430
|
if (IN_PLACE) {
|
|
1560
1431
|
return dirty;
|
|
1561
1432
|
}
|
|
1562
|
-
/* Return sanitized string or DOM */
|
|
1563
|
-
|
|
1564
1433
|
|
|
1434
|
+
/* Return sanitized string or DOM */
|
|
1565
1435
|
if (RETURN_DOM) {
|
|
1566
1436
|
if (RETURN_DOM_FRAGMENT) {
|
|
1567
1437
|
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
1568
|
-
|
|
1569
1438
|
while (body.firstChild) {
|
|
1570
1439
|
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
1571
1440
|
returnNode.appendChild(body.firstChild);
|
|
@@ -1573,7 +1442,6 @@ function createDOMPurify() {
|
|
|
1573
1442
|
} else {
|
|
1574
1443
|
returnNode = body;
|
|
1575
1444
|
}
|
|
1576
|
-
|
|
1577
1445
|
if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
|
|
1578
1446
|
/*
|
|
1579
1447
|
AdoptNode() is not used because internal state is not reset
|
|
@@ -1584,51 +1452,45 @@ function createDOMPurify() {
|
|
|
1584
1452
|
*/
|
|
1585
1453
|
returnNode = importNode.call(originalDocument, returnNode, true);
|
|
1586
1454
|
}
|
|
1587
|
-
|
|
1588
1455
|
return returnNode;
|
|
1589
1456
|
}
|
|
1590
|
-
|
|
1591
1457
|
var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
|
|
1592
|
-
/* Serialize doctype if allowed */
|
|
1593
1458
|
|
|
1459
|
+
/* Serialize doctype if allowed */
|
|
1594
1460
|
if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
|
|
1595
1461
|
serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
|
|
1596
1462
|
}
|
|
1597
|
-
/* Sanitize final string template-safe */
|
|
1598
|
-
|
|
1599
1463
|
|
|
1464
|
+
/* Sanitize final string template-safe */
|
|
1600
1465
|
if (SAFE_FOR_TEMPLATES) {
|
|
1601
1466
|
serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
|
|
1602
1467
|
serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
|
|
1603
1468
|
serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR$1, ' ');
|
|
1604
1469
|
}
|
|
1605
|
-
|
|
1606
1470
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
|
|
1607
1471
|
};
|
|
1472
|
+
|
|
1608
1473
|
/**
|
|
1609
1474
|
* Public method to set the configuration once
|
|
1610
1475
|
* setConfig
|
|
1611
1476
|
*
|
|
1612
1477
|
* @param {Object} cfg configuration object
|
|
1613
1478
|
*/
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
1479
|
DOMPurify.setConfig = function (cfg) {
|
|
1617
1480
|
_parseConfig(cfg);
|
|
1618
|
-
|
|
1619
1481
|
SET_CONFIG = true;
|
|
1620
1482
|
};
|
|
1483
|
+
|
|
1621
1484
|
/**
|
|
1622
1485
|
* Public method to remove the configuration
|
|
1623
1486
|
* clearConfig
|
|
1624
1487
|
*
|
|
1625
1488
|
*/
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
1489
|
DOMPurify.clearConfig = function () {
|
|
1629
1490
|
CONFIG = null;
|
|
1630
1491
|
SET_CONFIG = false;
|
|
1631
1492
|
};
|
|
1493
|
+
|
|
1632
1494
|
/**
|
|
1633
1495
|
* Public method to check if an attribute value is valid.
|
|
1634
1496
|
* Uses last set config, if any. Otherwise, uses config defaults.
|
|
@@ -1639,18 +1501,16 @@ function createDOMPurify() {
|
|
|
1639
1501
|
* @param {string} value Attribute value.
|
|
1640
1502
|
* @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
|
|
1641
1503
|
*/
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
1504
|
DOMPurify.isValidAttribute = function (tag, attr, value) {
|
|
1645
1505
|
/* Initialize shared config vars if necessary. */
|
|
1646
1506
|
if (!CONFIG) {
|
|
1647
1507
|
_parseConfig({});
|
|
1648
1508
|
}
|
|
1649
|
-
|
|
1650
1509
|
var lcTag = transformCaseFunc(tag);
|
|
1651
1510
|
var lcName = transformCaseFunc(attr);
|
|
1652
1511
|
return _isValidAttribute(lcTag, lcName, value);
|
|
1653
1512
|
};
|
|
1513
|
+
|
|
1654
1514
|
/**
|
|
1655
1515
|
* AddHook
|
|
1656
1516
|
* Public method to add DOMPurify hooks
|
|
@@ -1658,16 +1518,14 @@ function createDOMPurify() {
|
|
|
1658
1518
|
* @param {String} entryPoint entry point for the hook to add
|
|
1659
1519
|
* @param {Function} hookFunction function to execute
|
|
1660
1520
|
*/
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
1521
|
DOMPurify.addHook = function (entryPoint, hookFunction) {
|
|
1664
1522
|
if (typeof hookFunction !== 'function') {
|
|
1665
1523
|
return;
|
|
1666
1524
|
}
|
|
1667
|
-
|
|
1668
1525
|
hooks[entryPoint] = hooks[entryPoint] || [];
|
|
1669
1526
|
arrayPush(hooks[entryPoint], hookFunction);
|
|
1670
1527
|
};
|
|
1528
|
+
|
|
1671
1529
|
/**
|
|
1672
1530
|
* RemoveHook
|
|
1673
1531
|
* Public method to remove a DOMPurify hook at a given entryPoint
|
|
@@ -1676,40 +1534,34 @@ function createDOMPurify() {
|
|
|
1676
1534
|
* @param {String} entryPoint entry point for the hook to remove
|
|
1677
1535
|
* @return {Function} removed(popped) hook
|
|
1678
1536
|
*/
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
1537
|
DOMPurify.removeHook = function (entryPoint) {
|
|
1682
1538
|
if (hooks[entryPoint]) {
|
|
1683
1539
|
return arrayPop(hooks[entryPoint]);
|
|
1684
1540
|
}
|
|
1685
1541
|
};
|
|
1542
|
+
|
|
1686
1543
|
/**
|
|
1687
1544
|
* RemoveHooks
|
|
1688
1545
|
* Public method to remove all DOMPurify hooks at a given entryPoint
|
|
1689
1546
|
*
|
|
1690
1547
|
* @param {String} entryPoint entry point for the hooks to remove
|
|
1691
1548
|
*/
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
1549
|
DOMPurify.removeHooks = function (entryPoint) {
|
|
1695
1550
|
if (hooks[entryPoint]) {
|
|
1696
1551
|
hooks[entryPoint] = [];
|
|
1697
1552
|
}
|
|
1698
1553
|
};
|
|
1554
|
+
|
|
1699
1555
|
/**
|
|
1700
1556
|
* RemoveAllHooks
|
|
1701
1557
|
* Public method to remove all DOMPurify hooks
|
|
1702
1558
|
*
|
|
1703
1559
|
*/
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
1560
|
DOMPurify.removeAllHooks = function () {
|
|
1707
1561
|
hooks = {};
|
|
1708
1562
|
};
|
|
1709
|
-
|
|
1710
1563
|
return DOMPurify;
|
|
1711
1564
|
}
|
|
1712
|
-
|
|
1713
1565
|
var purify = createDOMPurify();
|
|
1714
1566
|
|
|
1715
1567
|
module.exports = purify;
|