dompurify 2.4.8 → 2.5.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 +1 -1
- package/dist/purify.cjs.js +235 -418
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.js +235 -418
- package/dist/purify.es.js.map +1 -1
- package/dist/purify.js +235 -418
- 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.0 | (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.0/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,25 +216,24 @@ 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);
|
|
265
228
|
var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
|
|
266
229
|
);
|
|
267
230
|
var DOCTYPE_NAME = seal(/^html$/i);
|
|
268
|
-
var CUSTOM_ELEMENT = seal(/^[a-z][
|
|
231
|
+
var CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
|
|
269
232
|
|
|
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.0';
|
|
328
287
|
|
|
329
|
-
|
|
330
|
-
DOMPurify.version = '2.4.8';
|
|
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,98 @@ 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
|
+
/* Ideally, do not touch anything below this line */
|
|
559
513
|
/* ______________________________________________ */
|
|
560
514
|
|
|
561
515
|
var formElement = document.createElement('form');
|
|
562
|
-
|
|
563
516
|
var isRegexOrFunction = function isRegexOrFunction(testValue) {
|
|
564
517
|
return testValue instanceof RegExp || testValue instanceof Function;
|
|
565
518
|
};
|
|
519
|
+
|
|
566
520
|
/**
|
|
567
521
|
* _parseConfig
|
|
568
522
|
*
|
|
569
523
|
* @param {Object} cfg optional config literal
|
|
570
524
|
*/
|
|
571
525
|
// eslint-disable-next-line complexity
|
|
572
|
-
|
|
573
|
-
|
|
574
526
|
var _parseConfig = function _parseConfig(cfg) {
|
|
575
527
|
if (CONFIG && CONFIG === cfg) {
|
|
576
528
|
return;
|
|
577
529
|
}
|
|
578
|
-
/* Shield configuration object from tampering */
|
|
579
|
-
|
|
580
530
|
|
|
531
|
+
/* Shield configuration object from tampering */
|
|
581
532
|
if (!cfg || _typeof(cfg) !== 'object') {
|
|
582
533
|
cfg = {};
|
|
583
534
|
}
|
|
584
|
-
/* Shield configuration object from prototype pollution */
|
|
585
|
-
|
|
586
535
|
|
|
536
|
+
/* Shield configuration object from prototype pollution */
|
|
587
537
|
cfg = clone(cfg);
|
|
588
|
-
PARSER_MEDIA_TYPE =
|
|
589
|
-
|
|
538
|
+
PARSER_MEDIA_TYPE =
|
|
539
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
540
|
+
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
541
|
|
|
542
|
+
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
591
543
|
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
|
|
592
|
-
/* Set configuration parameters */
|
|
593
544
|
|
|
545
|
+
/* Set configuration parameters */
|
|
594
546
|
ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
595
547
|
ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
596
548
|
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
|
-
|
|
549
|
+
URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
|
|
550
|
+
// eslint-disable-line indent
|
|
551
|
+
cfg.ADD_URI_SAFE_ATTR,
|
|
552
|
+
// eslint-disable-line indent
|
|
599
553
|
transformCaseFunc // eslint-disable-line indent
|
|
600
554
|
) // eslint-disable-line indent
|
|
601
555
|
: DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
602
|
-
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
|
|
603
|
-
|
|
556
|
+
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
|
|
557
|
+
// eslint-disable-line indent
|
|
558
|
+
cfg.ADD_DATA_URI_TAGS,
|
|
559
|
+
// eslint-disable-line indent
|
|
604
560
|
transformCaseFunc // eslint-disable-line indent
|
|
605
561
|
) // eslint-disable-line indent
|
|
606
562
|
: DEFAULT_DATA_URI_TAGS;
|
|
@@ -609,161 +565,128 @@ function createDOMPurify() {
|
|
|
609
565
|
FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
|
|
610
566
|
USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
|
|
611
567
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
612
|
-
|
|
613
568
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
614
|
-
|
|
615
569
|
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
|
|
616
|
-
|
|
617
570
|
ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
|
|
618
|
-
|
|
619
571
|
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
|
|
620
|
-
|
|
572
|
+
SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
|
|
621
573
|
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
|
|
622
|
-
|
|
623
574
|
RETURN_DOM = cfg.RETURN_DOM || false; // Default false
|
|
624
|
-
|
|
625
575
|
RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
|
|
626
|
-
|
|
627
576
|
RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
|
|
628
|
-
|
|
629
577
|
FORCE_BODY = cfg.FORCE_BODY || false; // Default false
|
|
630
|
-
|
|
631
578
|
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
|
|
632
|
-
|
|
633
579
|
SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
|
|
634
|
-
|
|
635
580
|
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
|
|
636
|
-
|
|
637
581
|
IN_PLACE = cfg.IN_PLACE || false; // Default false
|
|
638
|
-
|
|
639
582
|
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
|
|
640
583
|
NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
|
|
641
584
|
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
|
|
642
|
-
|
|
643
585
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
|
|
644
586
|
CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
|
|
645
587
|
}
|
|
646
|
-
|
|
647
588
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
|
|
648
589
|
CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
|
|
649
590
|
}
|
|
650
|
-
|
|
651
591
|
if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
|
|
652
592
|
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
|
|
653
593
|
}
|
|
654
|
-
|
|
655
594
|
if (SAFE_FOR_TEMPLATES) {
|
|
656
595
|
ALLOW_DATA_ATTR = false;
|
|
657
596
|
}
|
|
658
|
-
|
|
659
597
|
if (RETURN_DOM_FRAGMENT) {
|
|
660
598
|
RETURN_DOM = true;
|
|
661
599
|
}
|
|
662
|
-
/* Parse profile info */
|
|
663
|
-
|
|
664
600
|
|
|
601
|
+
/* Parse profile info */
|
|
665
602
|
if (USE_PROFILES) {
|
|
666
603
|
ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));
|
|
667
604
|
ALLOWED_ATTR = [];
|
|
668
|
-
|
|
669
605
|
if (USE_PROFILES.html === true) {
|
|
670
606
|
addToSet(ALLOWED_TAGS, html$1);
|
|
671
607
|
addToSet(ALLOWED_ATTR, html);
|
|
672
608
|
}
|
|
673
|
-
|
|
674
609
|
if (USE_PROFILES.svg === true) {
|
|
675
610
|
addToSet(ALLOWED_TAGS, svg$1);
|
|
676
611
|
addToSet(ALLOWED_ATTR, svg);
|
|
677
612
|
addToSet(ALLOWED_ATTR, xml);
|
|
678
613
|
}
|
|
679
|
-
|
|
680
614
|
if (USE_PROFILES.svgFilters === true) {
|
|
681
615
|
addToSet(ALLOWED_TAGS, svgFilters);
|
|
682
616
|
addToSet(ALLOWED_ATTR, svg);
|
|
683
617
|
addToSet(ALLOWED_ATTR, xml);
|
|
684
618
|
}
|
|
685
|
-
|
|
686
619
|
if (USE_PROFILES.mathMl === true) {
|
|
687
620
|
addToSet(ALLOWED_TAGS, mathMl$1);
|
|
688
621
|
addToSet(ALLOWED_ATTR, mathMl);
|
|
689
622
|
addToSet(ALLOWED_ATTR, xml);
|
|
690
623
|
}
|
|
691
624
|
}
|
|
692
|
-
/* Merge configuration parameters */
|
|
693
|
-
|
|
694
625
|
|
|
626
|
+
/* Merge configuration parameters */
|
|
695
627
|
if (cfg.ADD_TAGS) {
|
|
696
628
|
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
697
629
|
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
698
630
|
}
|
|
699
|
-
|
|
700
631
|
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
701
632
|
}
|
|
702
|
-
|
|
703
633
|
if (cfg.ADD_ATTR) {
|
|
704
634
|
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
705
635
|
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
706
636
|
}
|
|
707
|
-
|
|
708
637
|
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
709
638
|
}
|
|
710
|
-
|
|
711
639
|
if (cfg.ADD_URI_SAFE_ATTR) {
|
|
712
640
|
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
713
641
|
}
|
|
714
|
-
|
|
715
642
|
if (cfg.FORBID_CONTENTS) {
|
|
716
643
|
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
|
|
717
644
|
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
718
645
|
}
|
|
719
|
-
|
|
720
646
|
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
721
647
|
}
|
|
722
|
-
/* Add #text in case KEEP_CONTENT is set to true */
|
|
723
|
-
|
|
724
648
|
|
|
649
|
+
/* Add #text in case KEEP_CONTENT is set to true */
|
|
725
650
|
if (KEEP_CONTENT) {
|
|
726
651
|
ALLOWED_TAGS['#text'] = true;
|
|
727
652
|
}
|
|
728
|
-
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
729
|
-
|
|
730
653
|
|
|
654
|
+
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
731
655
|
if (WHOLE_DOCUMENT) {
|
|
732
656
|
addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
|
|
733
657
|
}
|
|
734
|
-
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
735
|
-
|
|
736
658
|
|
|
659
|
+
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
737
660
|
if (ALLOWED_TAGS.table) {
|
|
738
661
|
addToSet(ALLOWED_TAGS, ['tbody']);
|
|
739
662
|
delete FORBID_TAGS.tbody;
|
|
740
|
-
}
|
|
741
|
-
// Not available in IE8, Safari 5, etc.
|
|
742
|
-
|
|
663
|
+
}
|
|
743
664
|
|
|
665
|
+
// Prevent further manipulation of configuration.
|
|
666
|
+
// Not available in IE8, Safari 5, etc.
|
|
744
667
|
if (freeze) {
|
|
745
668
|
freeze(cfg);
|
|
746
669
|
}
|
|
747
|
-
|
|
748
670
|
CONFIG = cfg;
|
|
749
671
|
};
|
|
750
|
-
|
|
751
672
|
var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
752
|
-
var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
|
|
673
|
+
var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
|
|
674
|
+
|
|
675
|
+
// Certain elements are allowed in both SVG and HTML
|
|
753
676
|
// namespace. We need to specify them explicitly
|
|
754
677
|
// so that they don't get erroneously deleted from
|
|
755
678
|
// HTML namespace.
|
|
756
|
-
|
|
757
679
|
var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
|
|
680
|
+
|
|
758
681
|
/* Keep track of all possible SVG and MathML tags
|
|
759
682
|
* so that we can perform the namespace checks
|
|
760
683
|
* correctly. */
|
|
761
|
-
|
|
762
684
|
var ALL_SVG_TAGS = addToSet({}, svg$1);
|
|
763
685
|
addToSet(ALL_SVG_TAGS, svgFilters);
|
|
764
686
|
addToSet(ALL_SVG_TAGS, svgDisallowed);
|
|
765
687
|
var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
|
|
766
688
|
addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
|
|
689
|
+
|
|
767
690
|
/**
|
|
768
691
|
*
|
|
769
692
|
*
|
|
@@ -772,64 +695,59 @@ function createDOMPurify() {
|
|
|
772
695
|
* namespace that a spec-compliant parser would never
|
|
773
696
|
* return. Return true otherwise.
|
|
774
697
|
*/
|
|
775
|
-
|
|
776
698
|
var _checkValidNamespace = function _checkValidNamespace(element) {
|
|
777
|
-
var parent = getParentNode(element);
|
|
778
|
-
// can be null. We just simulate parent in this case.
|
|
699
|
+
var parent = getParentNode(element);
|
|
779
700
|
|
|
701
|
+
// In JSDOM, if we're inside shadow DOM, then parentNode
|
|
702
|
+
// can be null. We just simulate parent in this case.
|
|
780
703
|
if (!parent || !parent.tagName) {
|
|
781
704
|
parent = {
|
|
782
705
|
namespaceURI: NAMESPACE,
|
|
783
706
|
tagName: 'template'
|
|
784
707
|
};
|
|
785
708
|
}
|
|
786
|
-
|
|
787
709
|
var tagName = stringToLowerCase(element.tagName);
|
|
788
710
|
var parentTagName = stringToLowerCase(parent.tagName);
|
|
789
|
-
|
|
790
711
|
if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
791
712
|
return false;
|
|
792
713
|
}
|
|
793
|
-
|
|
794
714
|
if (element.namespaceURI === SVG_NAMESPACE) {
|
|
795
715
|
// The only way to switch from HTML namespace to SVG
|
|
796
716
|
// is via <svg>. If it happens via any other tag, then
|
|
797
717
|
// it should be killed.
|
|
798
718
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
799
719
|
return tagName === 'svg';
|
|
800
|
-
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// The only way to switch from MathML to SVG is via`
|
|
801
723
|
// svg if parent is either <annotation-xml> or MathML
|
|
802
724
|
// text integration points.
|
|
803
|
-
|
|
804
|
-
|
|
805
725
|
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
806
726
|
return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
|
|
807
|
-
}
|
|
808
|
-
// spec. All others are disallowed in SVG namespace.
|
|
809
|
-
|
|
727
|
+
}
|
|
810
728
|
|
|
729
|
+
// We only allow elements that are defined in SVG
|
|
730
|
+
// spec. All others are disallowed in SVG namespace.
|
|
811
731
|
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
812
732
|
}
|
|
813
|
-
|
|
814
733
|
if (element.namespaceURI === MATHML_NAMESPACE) {
|
|
815
734
|
// The only way to switch from HTML namespace to MathML
|
|
816
735
|
// is via <math>. If it happens via any other tag, then
|
|
817
736
|
// it should be killed.
|
|
818
737
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
819
738
|
return tagName === 'math';
|
|
820
|
-
}
|
|
821
|
-
// <math> and HTML integration points
|
|
822
|
-
|
|
739
|
+
}
|
|
823
740
|
|
|
741
|
+
// The only way to switch from SVG to MathML is via
|
|
742
|
+
// <math> and HTML integration points
|
|
824
743
|
if (parent.namespaceURI === SVG_NAMESPACE) {
|
|
825
744
|
return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
|
|
826
|
-
}
|
|
827
|
-
// spec. All others are disallowed in MathML namespace.
|
|
828
|
-
|
|
745
|
+
}
|
|
829
746
|
|
|
747
|
+
// We only allow elements that are defined in MathML
|
|
748
|
+
// spec. All others are disallowed in MathML namespace.
|
|
830
749
|
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
831
750
|
}
|
|
832
|
-
|
|
833
751
|
if (element.namespaceURI === HTML_NAMESPACE) {
|
|
834
752
|
// The only way to switch from SVG to HTML is via
|
|
835
753
|
// HTML integration points, and from MathML to HTML
|
|
@@ -837,39 +755,36 @@ function createDOMPurify() {
|
|
|
837
755
|
if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
|
|
838
756
|
return false;
|
|
839
757
|
}
|
|
840
|
-
|
|
841
758
|
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
|
|
842
759
|
return false;
|
|
843
|
-
}
|
|
844
|
-
// or SVG and should never appear in HTML namespace
|
|
845
|
-
|
|
760
|
+
}
|
|
846
761
|
|
|
762
|
+
// We disallow tags that are specific for MathML
|
|
763
|
+
// or SVG and should never appear in HTML namespace
|
|
847
764
|
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
848
|
-
}
|
|
849
|
-
|
|
765
|
+
}
|
|
850
766
|
|
|
767
|
+
// For XHTML and XML documents that support custom namespaces
|
|
851
768
|
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
852
769
|
return true;
|
|
853
|
-
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// The code should never reach this place (this means
|
|
854
773
|
// that the element somehow got namespace that is not
|
|
855
774
|
// HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
|
|
856
775
|
// Return false just in case.
|
|
857
|
-
|
|
858
|
-
|
|
859
776
|
return false;
|
|
860
777
|
};
|
|
778
|
+
|
|
861
779
|
/**
|
|
862
780
|
* _forceRemove
|
|
863
781
|
*
|
|
864
782
|
* @param {Node} node a DOM node
|
|
865
783
|
*/
|
|
866
|
-
|
|
867
|
-
|
|
868
784
|
var _forceRemove = function _forceRemove(node) {
|
|
869
785
|
arrayPush(DOMPurify.removed, {
|
|
870
786
|
element: node
|
|
871
787
|
});
|
|
872
|
-
|
|
873
788
|
try {
|
|
874
789
|
// eslint-disable-next-line unicorn/prefer-dom-node-remove
|
|
875
790
|
node.parentNode.removeChild(node);
|
|
@@ -881,14 +796,13 @@ function createDOMPurify() {
|
|
|
881
796
|
}
|
|
882
797
|
}
|
|
883
798
|
};
|
|
799
|
+
|
|
884
800
|
/**
|
|
885
801
|
* _removeAttribute
|
|
886
802
|
*
|
|
887
803
|
* @param {String} name an Attribute name
|
|
888
804
|
* @param {Node} node a DOM node
|
|
889
805
|
*/
|
|
890
|
-
|
|
891
|
-
|
|
892
806
|
var _removeAttribute = function _removeAttribute(name, node) {
|
|
893
807
|
try {
|
|
894
808
|
arrayPush(DOMPurify.removed, {
|
|
@@ -901,9 +815,9 @@ function createDOMPurify() {
|
|
|
901
815
|
from: node
|
|
902
816
|
});
|
|
903
817
|
}
|
|
818
|
+
node.removeAttribute(name);
|
|
904
819
|
|
|
905
|
-
|
|
906
|
-
|
|
820
|
+
// We void attribute values for unremovable "is"" attributes
|
|
907
821
|
if (name === 'is' && !ALLOWED_ATTR[name]) {
|
|
908
822
|
if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
|
|
909
823
|
try {
|
|
@@ -916,19 +830,17 @@ function createDOMPurify() {
|
|
|
916
830
|
}
|
|
917
831
|
}
|
|
918
832
|
};
|
|
833
|
+
|
|
919
834
|
/**
|
|
920
835
|
* _initDocument
|
|
921
836
|
*
|
|
922
837
|
* @param {String} dirty a string of dirty markup
|
|
923
838
|
* @return {Document} a DOM, filled with the dirty markup
|
|
924
839
|
*/
|
|
925
|
-
|
|
926
|
-
|
|
927
840
|
var _initDocument = function _initDocument(dirty) {
|
|
928
841
|
/* Create a HTML document */
|
|
929
842
|
var doc;
|
|
930
843
|
var leadingWhitespace;
|
|
931
|
-
|
|
932
844
|
if (FORCE_BODY) {
|
|
933
845
|
dirty = '<remove></remove>' + dirty;
|
|
934
846
|
} else {
|
|
@@ -936,83 +848,74 @@ function createDOMPurify() {
|
|
|
936
848
|
var matches = stringMatch(dirty, /^[\r\n\t ]+/);
|
|
937
849
|
leadingWhitespace = matches && matches[0];
|
|
938
850
|
}
|
|
939
|
-
|
|
940
851
|
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
|
|
941
852
|
// Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
|
|
942
853
|
dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
|
|
943
854
|
}
|
|
944
|
-
|
|
945
855
|
var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
946
856
|
/*
|
|
947
857
|
* Use the DOMParser API by default, fallback later if needs be
|
|
948
858
|
* DOMParser not work for svg when has multiple root element.
|
|
949
859
|
*/
|
|
950
|
-
|
|
951
860
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
952
861
|
try {
|
|
953
862
|
doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
|
|
954
863
|
} catch (_) {}
|
|
955
864
|
}
|
|
956
|
-
/* Use createHTMLDocument in case DOMParser is not available */
|
|
957
|
-
|
|
958
865
|
|
|
866
|
+
/* Use createHTMLDocument in case DOMParser is not available */
|
|
959
867
|
if (!doc || !doc.documentElement) {
|
|
960
868
|
doc = implementation.createDocument(NAMESPACE, 'template', null);
|
|
961
|
-
|
|
962
869
|
try {
|
|
963
870
|
doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
|
|
964
|
-
} catch (_) {
|
|
871
|
+
} catch (_) {
|
|
872
|
+
// Syntax error if dirtyPayload is invalid xml
|
|
965
873
|
}
|
|
966
874
|
}
|
|
967
|
-
|
|
968
875
|
var body = doc.body || doc.documentElement;
|
|
969
|
-
|
|
970
876
|
if (dirty && leadingWhitespace) {
|
|
971
877
|
body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
|
|
972
878
|
}
|
|
973
|
-
/* Work on whole document or just its body */
|
|
974
|
-
|
|
975
879
|
|
|
880
|
+
/* Work on whole document or just its body */
|
|
976
881
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
977
882
|
return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
|
|
978
883
|
}
|
|
979
|
-
|
|
980
884
|
return WHOLE_DOCUMENT ? doc.documentElement : body;
|
|
981
885
|
};
|
|
886
|
+
|
|
982
887
|
/**
|
|
983
888
|
* _createIterator
|
|
984
889
|
*
|
|
985
890
|
* @param {Document} root document/fragment to create iterator for
|
|
986
891
|
* @return {Iterator} iterator instance
|
|
987
892
|
*/
|
|
988
|
-
|
|
989
|
-
|
|
990
893
|
var _createIterator = function _createIterator(root) {
|
|
991
|
-
return createNodeIterator.call(root.ownerDocument || root, root,
|
|
992
|
-
|
|
894
|
+
return createNodeIterator.call(root.ownerDocument || root, root,
|
|
895
|
+
// eslint-disable-next-line no-bitwise
|
|
896
|
+
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null, false);
|
|
993
897
|
};
|
|
898
|
+
|
|
994
899
|
/**
|
|
995
900
|
* _isClobbered
|
|
996
901
|
*
|
|
997
902
|
* @param {Node} elm element to check for clobbering attacks
|
|
998
903
|
* @return {Boolean} true if clobbered, false if safe
|
|
999
904
|
*/
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
905
|
var _isClobbered = function _isClobbered(elm) {
|
|
1003
906
|
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');
|
|
1004
907
|
};
|
|
908
|
+
|
|
1005
909
|
/**
|
|
1006
910
|
* _isNode
|
|
1007
911
|
*
|
|
1008
912
|
* @param {Node} obj object to check whether it's a DOM node
|
|
1009
913
|
* @return {Boolean} true is object is a DOM node
|
|
1010
914
|
*/
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
915
|
var _isNode = function _isNode(object) {
|
|
1014
916
|
return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
|
|
1015
917
|
};
|
|
918
|
+
|
|
1016
919
|
/**
|
|
1017
920
|
* _executeHook
|
|
1018
921
|
* Execute user configurable hooks
|
|
@@ -1021,17 +924,15 @@ function createDOMPurify() {
|
|
|
1021
924
|
* @param {Node} currentNode node to work on with the hook
|
|
1022
925
|
* @param {Object} data additional hook parameters
|
|
1023
926
|
*/
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
927
|
var _executeHook = function _executeHook(entryPoint, currentNode, data) {
|
|
1027
928
|
if (!hooks[entryPoint]) {
|
|
1028
929
|
return;
|
|
1029
930
|
}
|
|
1030
|
-
|
|
1031
931
|
arrayForEach(hooks[entryPoint], function (hook) {
|
|
1032
932
|
hook.call(DOMPurify, currentNode, data, CONFIG);
|
|
1033
933
|
});
|
|
1034
934
|
};
|
|
935
|
+
|
|
1035
936
|
/**
|
|
1036
937
|
* _sanitizeElements
|
|
1037
938
|
*
|
|
@@ -1042,110 +943,99 @@ function createDOMPurify() {
|
|
|
1042
943
|
* @param {Node} currentNode to check for permission to exist
|
|
1043
944
|
* @return {Boolean} true if node was killed, false if left alive
|
|
1044
945
|
*/
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
946
|
var _sanitizeElements = function _sanitizeElements(currentNode) {
|
|
1048
947
|
var content;
|
|
1049
|
-
/* Execute a hook if present */
|
|
1050
948
|
|
|
949
|
+
/* Execute a hook if present */
|
|
1051
950
|
_executeHook('beforeSanitizeElements', currentNode, null);
|
|
1052
|
-
/* Check if element is clobbered or can clobber */
|
|
1053
|
-
|
|
1054
951
|
|
|
952
|
+
/* Check if element is clobbered or can clobber */
|
|
1055
953
|
if (_isClobbered(currentNode)) {
|
|
1056
954
|
_forceRemove(currentNode);
|
|
1057
|
-
|
|
1058
955
|
return true;
|
|
1059
956
|
}
|
|
1060
|
-
/* Check if tagname contains Unicode */
|
|
1061
|
-
|
|
1062
957
|
|
|
958
|
+
/* Check if tagname contains Unicode */
|
|
1063
959
|
if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) {
|
|
1064
960
|
_forceRemove(currentNode);
|
|
1065
|
-
|
|
1066
961
|
return true;
|
|
1067
962
|
}
|
|
1068
|
-
/* Now let's check the element's type and name */
|
|
1069
|
-
|
|
1070
963
|
|
|
964
|
+
/* Now let's check the element's type and name */
|
|
1071
965
|
var tagName = transformCaseFunc(currentNode.nodeName);
|
|
1072
|
-
/* Execute a hook if present */
|
|
1073
966
|
|
|
967
|
+
/* Execute a hook if present */
|
|
1074
968
|
_executeHook('uponSanitizeElement', currentNode, {
|
|
1075
969
|
tagName: tagName,
|
|
1076
970
|
allowedTags: ALLOWED_TAGS
|
|
1077
971
|
});
|
|
1078
|
-
/* Detect mXSS attempts abusing namespace confusion */
|
|
1079
|
-
|
|
1080
972
|
|
|
973
|
+
/* Detect mXSS attempts abusing namespace confusion */
|
|
1081
974
|
if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
|
|
1082
975
|
_forceRemove(currentNode);
|
|
1083
|
-
|
|
1084
976
|
return true;
|
|
1085
977
|
}
|
|
1086
|
-
/* Mitigate a problem with templates inside select */
|
|
1087
|
-
|
|
1088
978
|
|
|
979
|
+
/* Mitigate a problem with templates inside select */
|
|
1089
980
|
if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
|
|
1090
981
|
_forceRemove(currentNode);
|
|
982
|
+
return true;
|
|
983
|
+
}
|
|
1091
984
|
|
|
985
|
+
/* Remove any ocurrence of processing instructions */
|
|
986
|
+
if (currentNode.nodeType === 7) {
|
|
987
|
+
_forceRemove(currentNode);
|
|
1092
988
|
return true;
|
|
1093
989
|
}
|
|
1094
|
-
/* Remove element if anything forbids its presence */
|
|
1095
990
|
|
|
991
|
+
/* Remove any kind of possibly harmful comments */
|
|
992
|
+
if (SAFE_FOR_XML && currentNode.nodeType === 8 && regExpTest(/<[/\w]/g, currentNode.data)) {
|
|
993
|
+
_forceRemove(currentNode);
|
|
994
|
+
return true;
|
|
995
|
+
}
|
|
1096
996
|
|
|
997
|
+
/* Remove element if anything forbids its presence */
|
|
1097
998
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1098
999
|
/* Check if we have a custom element to handle */
|
|
1099
1000
|
if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
|
|
1100
1001
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
1101
1002
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
|
|
1102
1003
|
}
|
|
1103
|
-
/* Keep content except for bad-listed elements */
|
|
1104
|
-
|
|
1105
1004
|
|
|
1005
|
+
/* Keep content except for bad-listed elements */
|
|
1106
1006
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
1107
1007
|
var parentNode = getParentNode(currentNode) || currentNode.parentNode;
|
|
1108
1008
|
var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
|
|
1109
|
-
|
|
1110
1009
|
if (childNodes && parentNode) {
|
|
1111
1010
|
var childCount = childNodes.length;
|
|
1112
|
-
|
|
1113
1011
|
for (var i = childCount - 1; i >= 0; --i) {
|
|
1114
1012
|
parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
|
|
1115
1013
|
}
|
|
1116
1014
|
}
|
|
1117
1015
|
}
|
|
1118
|
-
|
|
1119
1016
|
_forceRemove(currentNode);
|
|
1120
|
-
|
|
1121
1017
|
return true;
|
|
1122
1018
|
}
|
|
1123
|
-
/* Check whether element has a valid namespace */
|
|
1124
|
-
|
|
1125
1019
|
|
|
1020
|
+
/* Check whether element has a valid namespace */
|
|
1126
1021
|
if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
|
|
1127
1022
|
_forceRemove(currentNode);
|
|
1128
|
-
|
|
1129
1023
|
return true;
|
|
1130
1024
|
}
|
|
1131
|
-
/* Make sure that older browsers don't get fallback-tag mXSS */
|
|
1132
|
-
|
|
1133
1025
|
|
|
1026
|
+
/* Make sure that older browsers don't get fallback-tag mXSS */
|
|
1134
1027
|
if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
|
|
1135
1028
|
_forceRemove(currentNode);
|
|
1136
|
-
|
|
1137
1029
|
return true;
|
|
1138
1030
|
}
|
|
1139
|
-
/* Sanitize element content to be template-safe */
|
|
1140
|
-
|
|
1141
1031
|
|
|
1032
|
+
/* Sanitize element content to be template-safe */
|
|
1142
1033
|
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
|
|
1143
1034
|
/* Get the element's text content */
|
|
1144
1035
|
content = currentNode.textContent;
|
|
1145
1036
|
content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
|
|
1146
1037
|
content = stringReplace(content, ERB_EXPR$1, ' ');
|
|
1147
1038
|
content = stringReplace(content, TMPLIT_EXPR$1, ' ');
|
|
1148
|
-
|
|
1149
1039
|
if (currentNode.textContent !== content) {
|
|
1150
1040
|
arrayPush(DOMPurify.removed, {
|
|
1151
1041
|
element: currentNode.cloneNode()
|
|
@@ -1153,13 +1043,12 @@ function createDOMPurify() {
|
|
|
1153
1043
|
currentNode.textContent = content;
|
|
1154
1044
|
}
|
|
1155
1045
|
}
|
|
1156
|
-
/* Execute a hook if present */
|
|
1157
|
-
|
|
1158
1046
|
|
|
1047
|
+
/* Execute a hook if present */
|
|
1159
1048
|
_executeHook('afterSanitizeElements', currentNode, null);
|
|
1160
|
-
|
|
1161
1049
|
return false;
|
|
1162
1050
|
};
|
|
1051
|
+
|
|
1163
1052
|
/**
|
|
1164
1053
|
* _isValidAttribute
|
|
1165
1054
|
*
|
|
@@ -1169,47 +1058,44 @@ function createDOMPurify() {
|
|
|
1169
1058
|
* @return {Boolean} Returns true if `value` is valid, otherwise false.
|
|
1170
1059
|
*/
|
|
1171
1060
|
// eslint-disable-next-line complexity
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
1061
|
var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
1175
1062
|
/* Make sure attribute cannot clobber */
|
|
1176
1063
|
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
1177
1064
|
return false;
|
|
1178
1065
|
}
|
|
1066
|
+
|
|
1179
1067
|
/* Allow valid data-* attributes: At least one character after "-"
|
|
1180
1068
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
1181
1069
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
1182
1070
|
We don't need to check the value; it's always URI safe. */
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
1071
|
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]) {
|
|
1186
|
-
if (
|
|
1072
|
+
if (
|
|
1073
|
+
// First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
1187
1074
|
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1188
1075
|
// and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
|
|
1189
|
-
_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)) ||
|
|
1076
|
+
_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)) ||
|
|
1077
|
+
// Alternative, second condition checks if it's an `is`-attribute, AND
|
|
1190
1078
|
// the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1191
1079
|
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 {
|
|
1192
1080
|
return false;
|
|
1193
1081
|
}
|
|
1194
1082
|
/* Check value is safe. First, is attr inert? If so, is safe */
|
|
1195
|
-
|
|
1196
1083
|
} 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) {
|
|
1197
1084
|
return false;
|
|
1198
1085
|
} else ;
|
|
1199
|
-
|
|
1200
1086
|
return true;
|
|
1201
1087
|
};
|
|
1088
|
+
|
|
1202
1089
|
/**
|
|
1203
1090
|
* _basicCustomElementCheck
|
|
1204
1091
|
* checks if at least one dash is included in tagName, and it's not the first char
|
|
1205
1092
|
* for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
|
|
1206
1093
|
* @param {string} tagName name of the tag of the node to sanitize
|
|
1207
1094
|
*/
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
1095
|
var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
|
|
1211
1096
|
return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT$1);
|
|
1212
1097
|
};
|
|
1098
|
+
|
|
1213
1099
|
/**
|
|
1214
1100
|
* _sanitizeAttributes
|
|
1215
1101
|
*
|
|
@@ -1220,24 +1106,19 @@ function createDOMPurify() {
|
|
|
1220
1106
|
*
|
|
1221
1107
|
* @param {Node} currentNode to sanitize
|
|
1222
1108
|
*/
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
1109
|
var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
1226
1110
|
var attr;
|
|
1227
1111
|
var value;
|
|
1228
1112
|
var lcName;
|
|
1229
1113
|
var l;
|
|
1230
1114
|
/* Execute a hook if present */
|
|
1231
|
-
|
|
1232
1115
|
_executeHook('beforeSanitizeAttributes', currentNode, null);
|
|
1233
|
-
|
|
1234
1116
|
var attributes = currentNode.attributes;
|
|
1235
|
-
/* Check if we have attributes; if not we might have a text node */
|
|
1236
1117
|
|
|
1118
|
+
/* Check if we have attributes; if not we might have a text node */
|
|
1237
1119
|
if (!attributes) {
|
|
1238
1120
|
return;
|
|
1239
1121
|
}
|
|
1240
|
-
|
|
1241
1122
|
var hookEvent = {
|
|
1242
1123
|
attrName: '',
|
|
1243
1124
|
attrValue: '',
|
|
@@ -1245,79 +1126,67 @@ function createDOMPurify() {
|
|
|
1245
1126
|
allowedAttributes: ALLOWED_ATTR
|
|
1246
1127
|
};
|
|
1247
1128
|
l = attributes.length;
|
|
1248
|
-
/* Go backwards over all attributes; safely remove bad ones */
|
|
1249
1129
|
|
|
1130
|
+
/* Go backwards over all attributes; safely remove bad ones */
|
|
1250
1131
|
while (l--) {
|
|
1251
1132
|
attr = attributes[l];
|
|
1252
1133
|
var _attr = attr,
|
|
1253
|
-
|
|
1254
|
-
|
|
1134
|
+
name = _attr.name,
|
|
1135
|
+
namespaceURI = _attr.namespaceURI;
|
|
1255
1136
|
value = name === 'value' ? attr.value : stringTrim(attr.value);
|
|
1256
1137
|
lcName = transformCaseFunc(name);
|
|
1257
|
-
/* Execute a hook if present */
|
|
1258
1138
|
|
|
1139
|
+
/* Execute a hook if present */
|
|
1259
1140
|
hookEvent.attrName = lcName;
|
|
1260
1141
|
hookEvent.attrValue = value;
|
|
1261
1142
|
hookEvent.keepAttr = true;
|
|
1262
1143
|
hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
|
|
1263
|
-
|
|
1264
1144
|
_executeHook('uponSanitizeAttribute', currentNode, hookEvent);
|
|
1265
|
-
|
|
1266
1145
|
value = hookEvent.attrValue;
|
|
1267
1146
|
/* Did the hooks approve of the attribute? */
|
|
1268
|
-
|
|
1269
1147
|
if (hookEvent.forceKeepAttr) {
|
|
1270
1148
|
continue;
|
|
1271
1149
|
}
|
|
1272
|
-
/* Remove attribute */
|
|
1273
|
-
|
|
1274
1150
|
|
|
1151
|
+
/* Remove attribute */
|
|
1275
1152
|
_removeAttribute(name, currentNode);
|
|
1276
|
-
/* Did the hooks approve of the attribute? */
|
|
1277
|
-
|
|
1278
1153
|
|
|
1154
|
+
/* Did the hooks approve of the attribute? */
|
|
1279
1155
|
if (!hookEvent.keepAttr) {
|
|
1280
1156
|
continue;
|
|
1281
1157
|
}
|
|
1282
|
-
/* Work around a security issue in jQuery 3.0 */
|
|
1283
|
-
|
|
1284
1158
|
|
|
1159
|
+
/* Work around a security issue in jQuery 3.0 */
|
|
1285
1160
|
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
|
|
1286
1161
|
_removeAttribute(name, currentNode);
|
|
1287
|
-
|
|
1288
1162
|
continue;
|
|
1289
1163
|
}
|
|
1290
|
-
/* Sanitize attribute content to be template-safe */
|
|
1291
|
-
|
|
1292
1164
|
|
|
1165
|
+
/* Sanitize attribute content to be template-safe */
|
|
1293
1166
|
if (SAFE_FOR_TEMPLATES) {
|
|
1294
1167
|
value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
|
|
1295
1168
|
value = stringReplace(value, ERB_EXPR$1, ' ');
|
|
1296
1169
|
value = stringReplace(value, TMPLIT_EXPR$1, ' ');
|
|
1297
1170
|
}
|
|
1298
|
-
/* Is `value` valid for this attribute? */
|
|
1299
|
-
|
|
1300
1171
|
|
|
1172
|
+
/* Is `value` valid for this attribute? */
|
|
1301
1173
|
var lcTag = transformCaseFunc(currentNode.nodeName);
|
|
1302
|
-
|
|
1303
1174
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
1304
1175
|
continue;
|
|
1305
1176
|
}
|
|
1177
|
+
|
|
1306
1178
|
/* Full DOM Clobbering protection via namespace isolation,
|
|
1307
1179
|
* Prefix id and name attributes with `user-content-`
|
|
1308
1180
|
*/
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
1181
|
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
|
|
1312
1182
|
// Remove the attribute with this value
|
|
1313
|
-
_removeAttribute(name, currentNode);
|
|
1314
|
-
|
|
1183
|
+
_removeAttribute(name, currentNode);
|
|
1315
1184
|
|
|
1185
|
+
// Prefix the value and later re-create the attribute with the sanitized value
|
|
1316
1186
|
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
1317
1187
|
}
|
|
1318
|
-
/* Handle attributes that require Trusted Types */
|
|
1319
|
-
|
|
1320
1188
|
|
|
1189
|
+
/* Handle attributes that require Trusted Types */
|
|
1321
1190
|
if (trustedTypesPolicy && _typeof(trustedTypes) === 'object' && typeof trustedTypes.getAttributeType === 'function') {
|
|
1322
1191
|
if (namespaceURI) ; else {
|
|
1323
1192
|
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
|
|
@@ -1326,7 +1195,6 @@ function createDOMPurify() {
|
|
|
1326
1195
|
value = trustedTypesPolicy.createHTML(value);
|
|
1327
1196
|
break;
|
|
1328
1197
|
}
|
|
1329
|
-
|
|
1330
1198
|
case 'TrustedScriptURL':
|
|
1331
1199
|
{
|
|
1332
1200
|
value = trustedTypesPolicy.createScriptURL(value);
|
|
@@ -1335,9 +1203,8 @@ function createDOMPurify() {
|
|
|
1335
1203
|
}
|
|
1336
1204
|
}
|
|
1337
1205
|
}
|
|
1338
|
-
/* Handle invalid data-* attribute set by try-catching it */
|
|
1339
|
-
|
|
1340
1206
|
|
|
1207
|
+
/* Handle invalid data-* attribute set by try-catching it */
|
|
1341
1208
|
try {
|
|
1342
1209
|
if (namespaceURI) {
|
|
1343
1210
|
currentNode.setAttributeNS(namespaceURI, name, value);
|
|
@@ -1345,56 +1212,47 @@ function createDOMPurify() {
|
|
|
1345
1212
|
/* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
|
|
1346
1213
|
currentNode.setAttribute(name, value);
|
|
1347
1214
|
}
|
|
1348
|
-
|
|
1349
1215
|
arrayPop(DOMPurify.removed);
|
|
1350
1216
|
} catch (_) {}
|
|
1351
1217
|
}
|
|
1352
|
-
/* Execute a hook if present */
|
|
1353
|
-
|
|
1354
1218
|
|
|
1219
|
+
/* Execute a hook if present */
|
|
1355
1220
|
_executeHook('afterSanitizeAttributes', currentNode, null);
|
|
1356
1221
|
};
|
|
1222
|
+
|
|
1357
1223
|
/**
|
|
1358
1224
|
* _sanitizeShadowDOM
|
|
1359
1225
|
*
|
|
1360
1226
|
* @param {DocumentFragment} fragment to iterate over recursively
|
|
1361
1227
|
*/
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
1228
|
var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
|
|
1365
1229
|
var shadowNode;
|
|
1366
|
-
|
|
1367
1230
|
var shadowIterator = _createIterator(fragment);
|
|
1368
|
-
/* Execute a hook if present */
|
|
1369
|
-
|
|
1370
1231
|
|
|
1232
|
+
/* Execute a hook if present */
|
|
1371
1233
|
_executeHook('beforeSanitizeShadowDOM', fragment, null);
|
|
1372
|
-
|
|
1373
1234
|
while (shadowNode = shadowIterator.nextNode()) {
|
|
1374
1235
|
/* Execute a hook if present */
|
|
1375
1236
|
_executeHook('uponSanitizeShadowNode', shadowNode, null);
|
|
1376
|
-
/* Sanitize tags and elements */
|
|
1377
|
-
|
|
1378
1237
|
|
|
1238
|
+
/* Sanitize tags and elements */
|
|
1379
1239
|
if (_sanitizeElements(shadowNode)) {
|
|
1380
1240
|
continue;
|
|
1381
1241
|
}
|
|
1382
|
-
/* Deep shadow DOM detected */
|
|
1383
|
-
|
|
1384
1242
|
|
|
1243
|
+
/* Deep shadow DOM detected */
|
|
1385
1244
|
if (shadowNode.content instanceof DocumentFragment) {
|
|
1386
1245
|
_sanitizeShadowDOM(shadowNode.content);
|
|
1387
1246
|
}
|
|
1388
|
-
/* Check attributes, sanitize if necessary */
|
|
1389
|
-
|
|
1390
1247
|
|
|
1248
|
+
/* Check attributes, sanitize if necessary */
|
|
1391
1249
|
_sanitizeAttributes(shadowNode);
|
|
1392
1250
|
}
|
|
1393
|
-
/* Execute a hook if present */
|
|
1394
|
-
|
|
1395
1251
|
|
|
1252
|
+
/* Execute a hook if present */
|
|
1396
1253
|
_executeHook('afterSanitizeShadowDOM', fragment, null);
|
|
1397
1254
|
};
|
|
1255
|
+
|
|
1398
1256
|
/**
|
|
1399
1257
|
* Sanitize
|
|
1400
1258
|
* Public method providing core sanitation functionality
|
|
@@ -1403,8 +1261,6 @@ function createDOMPurify() {
|
|
|
1403
1261
|
* @param {Object} configuration object
|
|
1404
1262
|
*/
|
|
1405
1263
|
// eslint-disable-next-line complexity
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
1264
|
DOMPurify.sanitize = function (dirty) {
|
|
1409
1265
|
var cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1410
1266
|
var body;
|
|
@@ -1415,19 +1271,15 @@ function createDOMPurify() {
|
|
|
1415
1271
|
/* Make sure we have a string to sanitize.
|
|
1416
1272
|
DO NOT return early, as this will return the wrong type if
|
|
1417
1273
|
the user has requested a DOM object rather than a string */
|
|
1418
|
-
|
|
1419
1274
|
IS_EMPTY_INPUT = !dirty;
|
|
1420
|
-
|
|
1421
1275
|
if (IS_EMPTY_INPUT) {
|
|
1422
1276
|
dirty = '<!-->';
|
|
1423
1277
|
}
|
|
1424
|
-
/* Stringify, in case dirty is an object */
|
|
1425
|
-
|
|
1426
1278
|
|
|
1279
|
+
/* Stringify, in case dirty is an object */
|
|
1427
1280
|
if (typeof dirty !== 'string' && !_isNode(dirty)) {
|
|
1428
1281
|
if (typeof dirty.toString === 'function') {
|
|
1429
1282
|
dirty = dirty.toString();
|
|
1430
|
-
|
|
1431
1283
|
if (typeof dirty !== 'string') {
|
|
1432
1284
|
throw typeErrorCreate('dirty is not a string, aborting');
|
|
1433
1285
|
}
|
|
@@ -1435,43 +1287,36 @@ function createDOMPurify() {
|
|
|
1435
1287
|
throw typeErrorCreate('toString is not a function');
|
|
1436
1288
|
}
|
|
1437
1289
|
}
|
|
1438
|
-
/* Check we can run. Otherwise fall back or ignore */
|
|
1439
|
-
|
|
1440
1290
|
|
|
1291
|
+
/* Check we can run. Otherwise fall back or ignore */
|
|
1441
1292
|
if (!DOMPurify.isSupported) {
|
|
1442
1293
|
if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
|
|
1443
1294
|
if (typeof dirty === 'string') {
|
|
1444
1295
|
return window.toStaticHTML(dirty);
|
|
1445
1296
|
}
|
|
1446
|
-
|
|
1447
1297
|
if (_isNode(dirty)) {
|
|
1448
1298
|
return window.toStaticHTML(dirty.outerHTML);
|
|
1449
1299
|
}
|
|
1450
1300
|
}
|
|
1451
|
-
|
|
1452
1301
|
return dirty;
|
|
1453
1302
|
}
|
|
1454
|
-
/* Assign config vars */
|
|
1455
|
-
|
|
1456
1303
|
|
|
1304
|
+
/* Assign config vars */
|
|
1457
1305
|
if (!SET_CONFIG) {
|
|
1458
1306
|
_parseConfig(cfg);
|
|
1459
1307
|
}
|
|
1460
|
-
/* Clean up removed elements */
|
|
1461
|
-
|
|
1462
1308
|
|
|
1309
|
+
/* Clean up removed elements */
|
|
1463
1310
|
DOMPurify.removed = [];
|
|
1464
|
-
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1465
1311
|
|
|
1312
|
+
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1466
1313
|
if (typeof dirty === 'string') {
|
|
1467
1314
|
IN_PLACE = false;
|
|
1468
1315
|
}
|
|
1469
|
-
|
|
1470
1316
|
if (IN_PLACE) {
|
|
1471
1317
|
/* Do some early pre-sanitization to avoid unsafe root nodes */
|
|
1472
1318
|
if (dirty.nodeName) {
|
|
1473
1319
|
var tagName = transformCaseFunc(dirty.nodeName);
|
|
1474
|
-
|
|
1475
1320
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1476
1321
|
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
1477
1322
|
}
|
|
@@ -1481,7 +1326,6 @@ function createDOMPurify() {
|
|
|
1481
1326
|
elements being stripped by the parser */
|
|
1482
1327
|
body = _initDocument('<!---->');
|
|
1483
1328
|
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
1484
|
-
|
|
1485
1329
|
if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
|
|
1486
1330
|
/* Node is already a body, use as is */
|
|
1487
1331
|
body = importedNode;
|
|
@@ -1493,71 +1337,61 @@ function createDOMPurify() {
|
|
|
1493
1337
|
}
|
|
1494
1338
|
} else {
|
|
1495
1339
|
/* Exit directly if we have nothing to do */
|
|
1496
|
-
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
1340
|
+
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
1341
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
1497
1342
|
dirty.indexOf('<') === -1) {
|
|
1498
1343
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
1499
1344
|
}
|
|
1500
|
-
/* Initialize the document to work on */
|
|
1501
|
-
|
|
1502
1345
|
|
|
1346
|
+
/* Initialize the document to work on */
|
|
1503
1347
|
body = _initDocument(dirty);
|
|
1504
|
-
/* Check we have a DOM node from the data */
|
|
1505
1348
|
|
|
1349
|
+
/* Check we have a DOM node from the data */
|
|
1506
1350
|
if (!body) {
|
|
1507
1351
|
return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
|
|
1508
1352
|
}
|
|
1509
1353
|
}
|
|
1510
|
-
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1511
|
-
|
|
1512
1354
|
|
|
1355
|
+
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1513
1356
|
if (body && FORCE_BODY) {
|
|
1514
1357
|
_forceRemove(body.firstChild);
|
|
1515
1358
|
}
|
|
1516
|
-
/* Get node iterator */
|
|
1517
|
-
|
|
1518
1359
|
|
|
1360
|
+
/* Get node iterator */
|
|
1519
1361
|
var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
|
|
1520
|
-
/* Now start iterating over the created document */
|
|
1521
|
-
|
|
1522
1362
|
|
|
1363
|
+
/* Now start iterating over the created document */
|
|
1523
1364
|
while (currentNode = nodeIterator.nextNode()) {
|
|
1524
1365
|
/* Fix IE's strange behavior with manipulated textNodes #89 */
|
|
1525
1366
|
if (currentNode.nodeType === 3 && currentNode === oldNode) {
|
|
1526
1367
|
continue;
|
|
1527
1368
|
}
|
|
1528
|
-
/* Sanitize tags and elements */
|
|
1529
|
-
|
|
1530
1369
|
|
|
1370
|
+
/* Sanitize tags and elements */
|
|
1531
1371
|
if (_sanitizeElements(currentNode)) {
|
|
1532
1372
|
continue;
|
|
1533
1373
|
}
|
|
1534
|
-
/* Shadow DOM detected, sanitize it */
|
|
1535
|
-
|
|
1536
1374
|
|
|
1375
|
+
/* Shadow DOM detected, sanitize it */
|
|
1537
1376
|
if (currentNode.content instanceof DocumentFragment) {
|
|
1538
1377
|
_sanitizeShadowDOM(currentNode.content);
|
|
1539
1378
|
}
|
|
1540
|
-
/* Check attributes, sanitize if necessary */
|
|
1541
|
-
|
|
1542
1379
|
|
|
1380
|
+
/* Check attributes, sanitize if necessary */
|
|
1543
1381
|
_sanitizeAttributes(currentNode);
|
|
1544
|
-
|
|
1545
1382
|
oldNode = currentNode;
|
|
1546
1383
|
}
|
|
1547
|
-
|
|
1548
1384
|
oldNode = null;
|
|
1549
|
-
/* If we sanitized `dirty` in-place, return it. */
|
|
1550
1385
|
|
|
1386
|
+
/* If we sanitized `dirty` in-place, return it. */
|
|
1551
1387
|
if (IN_PLACE) {
|
|
1552
1388
|
return dirty;
|
|
1553
1389
|
}
|
|
1554
|
-
/* Return sanitized string or DOM */
|
|
1555
|
-
|
|
1556
1390
|
|
|
1391
|
+
/* Return sanitized string or DOM */
|
|
1557
1392
|
if (RETURN_DOM) {
|
|
1558
1393
|
if (RETURN_DOM_FRAGMENT) {
|
|
1559
1394
|
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
1560
|
-
|
|
1561
1395
|
while (body.firstChild) {
|
|
1562
1396
|
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
1563
1397
|
returnNode.appendChild(body.firstChild);
|
|
@@ -1565,7 +1399,6 @@ function createDOMPurify() {
|
|
|
1565
1399
|
} else {
|
|
1566
1400
|
returnNode = body;
|
|
1567
1401
|
}
|
|
1568
|
-
|
|
1569
1402
|
if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
|
|
1570
1403
|
/*
|
|
1571
1404
|
AdoptNode() is not used because internal state is not reset
|
|
@@ -1576,51 +1409,45 @@ function createDOMPurify() {
|
|
|
1576
1409
|
*/
|
|
1577
1410
|
returnNode = importNode.call(originalDocument, returnNode, true);
|
|
1578
1411
|
}
|
|
1579
|
-
|
|
1580
1412
|
return returnNode;
|
|
1581
1413
|
}
|
|
1582
|
-
|
|
1583
1414
|
var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
|
|
1584
|
-
/* Serialize doctype if allowed */
|
|
1585
1415
|
|
|
1416
|
+
/* Serialize doctype if allowed */
|
|
1586
1417
|
if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
|
|
1587
1418
|
serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
|
|
1588
1419
|
}
|
|
1589
|
-
/* Sanitize final string template-safe */
|
|
1590
|
-
|
|
1591
1420
|
|
|
1421
|
+
/* Sanitize final string template-safe */
|
|
1592
1422
|
if (SAFE_FOR_TEMPLATES) {
|
|
1593
1423
|
serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
|
|
1594
1424
|
serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
|
|
1595
1425
|
serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR$1, ' ');
|
|
1596
1426
|
}
|
|
1597
|
-
|
|
1598
1427
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
|
|
1599
1428
|
};
|
|
1429
|
+
|
|
1600
1430
|
/**
|
|
1601
1431
|
* Public method to set the configuration once
|
|
1602
1432
|
* setConfig
|
|
1603
1433
|
*
|
|
1604
1434
|
* @param {Object} cfg configuration object
|
|
1605
1435
|
*/
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
1436
|
DOMPurify.setConfig = function (cfg) {
|
|
1609
1437
|
_parseConfig(cfg);
|
|
1610
|
-
|
|
1611
1438
|
SET_CONFIG = true;
|
|
1612
1439
|
};
|
|
1440
|
+
|
|
1613
1441
|
/**
|
|
1614
1442
|
* Public method to remove the configuration
|
|
1615
1443
|
* clearConfig
|
|
1616
1444
|
*
|
|
1617
1445
|
*/
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
1446
|
DOMPurify.clearConfig = function () {
|
|
1621
1447
|
CONFIG = null;
|
|
1622
1448
|
SET_CONFIG = false;
|
|
1623
1449
|
};
|
|
1450
|
+
|
|
1624
1451
|
/**
|
|
1625
1452
|
* Public method to check if an attribute value is valid.
|
|
1626
1453
|
* Uses last set config, if any. Otherwise, uses config defaults.
|
|
@@ -1631,18 +1458,16 @@ function createDOMPurify() {
|
|
|
1631
1458
|
* @param {string} value Attribute value.
|
|
1632
1459
|
* @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
|
|
1633
1460
|
*/
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
1461
|
DOMPurify.isValidAttribute = function (tag, attr, value) {
|
|
1637
1462
|
/* Initialize shared config vars if necessary. */
|
|
1638
1463
|
if (!CONFIG) {
|
|
1639
1464
|
_parseConfig({});
|
|
1640
1465
|
}
|
|
1641
|
-
|
|
1642
1466
|
var lcTag = transformCaseFunc(tag);
|
|
1643
1467
|
var lcName = transformCaseFunc(attr);
|
|
1644
1468
|
return _isValidAttribute(lcTag, lcName, value);
|
|
1645
1469
|
};
|
|
1470
|
+
|
|
1646
1471
|
/**
|
|
1647
1472
|
* AddHook
|
|
1648
1473
|
* Public method to add DOMPurify hooks
|
|
@@ -1650,16 +1475,14 @@ function createDOMPurify() {
|
|
|
1650
1475
|
* @param {String} entryPoint entry point for the hook to add
|
|
1651
1476
|
* @param {Function} hookFunction function to execute
|
|
1652
1477
|
*/
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
1478
|
DOMPurify.addHook = function (entryPoint, hookFunction) {
|
|
1656
1479
|
if (typeof hookFunction !== 'function') {
|
|
1657
1480
|
return;
|
|
1658
1481
|
}
|
|
1659
|
-
|
|
1660
1482
|
hooks[entryPoint] = hooks[entryPoint] || [];
|
|
1661
1483
|
arrayPush(hooks[entryPoint], hookFunction);
|
|
1662
1484
|
};
|
|
1485
|
+
|
|
1663
1486
|
/**
|
|
1664
1487
|
* RemoveHook
|
|
1665
1488
|
* Public method to remove a DOMPurify hook at a given entryPoint
|
|
@@ -1668,40 +1491,34 @@ function createDOMPurify() {
|
|
|
1668
1491
|
* @param {String} entryPoint entry point for the hook to remove
|
|
1669
1492
|
* @return {Function} removed(popped) hook
|
|
1670
1493
|
*/
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
1494
|
DOMPurify.removeHook = function (entryPoint) {
|
|
1674
1495
|
if (hooks[entryPoint]) {
|
|
1675
1496
|
return arrayPop(hooks[entryPoint]);
|
|
1676
1497
|
}
|
|
1677
1498
|
};
|
|
1499
|
+
|
|
1678
1500
|
/**
|
|
1679
1501
|
* RemoveHooks
|
|
1680
1502
|
* Public method to remove all DOMPurify hooks at a given entryPoint
|
|
1681
1503
|
*
|
|
1682
1504
|
* @param {String} entryPoint entry point for the hooks to remove
|
|
1683
1505
|
*/
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
1506
|
DOMPurify.removeHooks = function (entryPoint) {
|
|
1687
1507
|
if (hooks[entryPoint]) {
|
|
1688
1508
|
hooks[entryPoint] = [];
|
|
1689
1509
|
}
|
|
1690
1510
|
};
|
|
1511
|
+
|
|
1691
1512
|
/**
|
|
1692
1513
|
* RemoveAllHooks
|
|
1693
1514
|
* Public method to remove all DOMPurify hooks
|
|
1694
1515
|
*
|
|
1695
1516
|
*/
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
1517
|
DOMPurify.removeAllHooks = function () {
|
|
1699
1518
|
hooks = {};
|
|
1700
1519
|
};
|
|
1701
|
-
|
|
1702
1520
|
return DOMPurify;
|
|
1703
1521
|
}
|
|
1704
|
-
|
|
1705
1522
|
var purify = createDOMPurify();
|
|
1706
1523
|
|
|
1707
1524
|
module.exports = purify;
|