dompurify 2.2.2 → 2.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -7
- package/dist/purify.cjs.js +178 -21
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.js +178 -21
- package/dist/purify.es.js.map +1 -1
- package/dist/purify.js +178 -21
- 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 +4 -4
package/README.md
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG.
|
|
8
8
|
|
|
9
|
-
It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version 2.2.
|
|
9
|
+
It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version 2.2.6.
|
|
10
10
|
|
|
11
11
|
DOMPurify is written in JavaScript and works in all modern browsers (Safari (10+), Opera (15+), Internet Explorer (10+), Edge, Firefox and Chrome - as well as almost anything else using Blink or WebKit). It doesn't break on MSIE6 or other legacy browsers. It either uses [a fall-back](#what-about-older-browsers-like-msie8) or simply does nothing.
|
|
12
12
|
|
|
13
|
-
Our automated tests cover [15 different browsers](https://github.com/cure53/DOMPurify/blob/main/test/karma.custom-launchers.config.js#L5) right now, more to come. We also cover Node.js
|
|
13
|
+
Our automated tests cover [15 different browsers](https://github.com/cure53/DOMPurify/blob/main/test/karma.custom-launchers.config.js#L5) right now, more to come. We also cover Node.js v14.15.1, v15.4.0, running DOMPurify on [jsdom](https://github.com/tmpvar/jsdom). Older Node.js versions are known to work as well.
|
|
14
14
|
|
|
15
15
|
DOMPurify is written by security people who have vast background in web attacks and XSS. Fear not. For more details please also read about our [Security Goals & Threat Model](https://github.com/cure53/DOMPurify/wiki/Security-Goals-&-Threat-Model). Please, read it. Like, really.
|
|
16
16
|
|
|
@@ -37,10 +37,15 @@ It's easy. Just include DOMPurify on your website.
|
|
|
37
37
|
Afterwards you can sanitize strings by executing the following code:
|
|
38
38
|
|
|
39
39
|
```js
|
|
40
|
-
|
|
40
|
+
let clean = DOMPurify.sanitize( dirty );
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
The resulting HTML can be written into a DOM element using `innerHTML` or the DOM using `document.write()`. That is fully up to you.
|
|
43
|
+
The resulting HTML can be written into a DOM element using `innerHTML` or the DOM using `document.write()`. That is fully up to you.
|
|
44
|
+
Note that by default, we permit HTML, SVG **and** MathML. If you only need HTML, which might be a very common use-case, you can easily set that up as well:
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
let clean = DOMPurify.sanitize( dirty , {USE_PROFILES: {html: true}} );
|
|
48
|
+
```
|
|
44
49
|
|
|
45
50
|
### Is there any foot-gun potential?
|
|
46
51
|
|
|
@@ -230,7 +235,7 @@ var clean = DOMPurify.sanitize(dirty, {WHOLE_DOCUMENT: true});
|
|
|
230
235
|
// disable DOM Clobbering protection on output (default is true, handle with care, minor XSS risks here)
|
|
231
236
|
var clean = DOMPurify.sanitize(dirty, {SANITIZE_DOM: false});
|
|
232
237
|
|
|
233
|
-
// keep an element's content when the element is removed (default is true
|
|
238
|
+
// keep an element's content when the element is removed (default is true)
|
|
234
239
|
var clean = DOMPurify.sanitize(dirty, {KEEP_CONTENT: false});
|
|
235
240
|
|
|
236
241
|
// glue elements like style, script or others to document.body and prevent unintuitive browser behavior in several edge-cases (default is false)
|
|
@@ -283,7 +288,7 @@ DOMPurify.addHook('beforeSanitizeElements', function (
|
|
|
283
288
|
|
|
284
289
|
## Continuous Integration
|
|
285
290
|
|
|
286
|
-
We are currently using
|
|
291
|
+
We are currently using Github Actions in combination with BrowserStack. This gives us the possibility to confirm for each and every commit that all is going according to plan in all supported browsers. Check out the build logs here: https://github.com/cure53/DOMPurify/actions
|
|
287
292
|
|
|
288
293
|
You can further run local tests by executing `npm test`. The tests work fine with Node.js v0.6.2 and jsdom@8.5.0.
|
|
289
294
|
|
|
@@ -327,7 +332,7 @@ Feature releases will not be announced to this list.
|
|
|
327
332
|
|
|
328
333
|
Many people helped and help DOMPurify become what it is and need to be acknowledged here!
|
|
329
334
|
|
|
330
|
-
[oreoshake 💸](https://github.com/oreoshake), [dcramer 💸](https://github.com/dcramer),[tdeekens ❤️](https://github.com/tdeekens), [peernohell ❤️](https://github.com/peernohell), [neilj](https://github.com/neilj), [fhemberger](https://github.com/fhemberger), [Joris-van-der-Wel](https://github.com/Joris-van-der-Wel), [ydaniv](https://github.com/ydaniv), [terjanq](https://twitter.com/terjanq), [filedescriptor](https://github.com/filedescriptor), [ConradIrwin](https://github.com/ConradIrwin), [gibson042](https://github.com/gibson042), [choumx](https://github.com/choumx), [0xSobky](https://github.com/0xSobky), [styfle](https://github.com/styfle), [koto](https://github.com/koto), [tlau88](https://github.com/tlau88), [strugee](https://github.com/strugee), [oparoz](https://github.com/oparoz), [mathiasbynens](https://github.com/mathiasbynens), [edg2s](https://github.com/edg2s), [dnkolegov](https://github.com/dnkolegov), [dhardtke](https://github.com/dhardtke), [wirehead](https://github.com/wirehead), [thorn0](https://github.com/thorn0), [styu](https://github.com/styu), [mozfreddyb](https://github.com/mozfreddyb), [mikesamuel](https://github.com/mikesamuel), [jorangreef](https://github.com/jorangreef), [jimmyhchan](https://github.com/jimmyhchan), [jameydeorio](https://github.com/jameydeorio), [jameskraus](https://github.com/jameskraus), [hyderali](https://github.com/hyderali), [hansottowirtz](https://github.com/hansottowirtz), [hackvertor](https://github.com/hackvertor), [freddyb](https://github.com/freddyb), [flavorjones](https://github.com/flavorjones), [djfarrelly](https://github.com/djfarrelly), [devd](https://github.com/devd), [camerondunford](https://github.com/camerondunford), [buu700](https://github.com/buu700), [buildog](https://github.com/buildog), [alabiaga](https://github.com/alabiaga), [Vector919](https://github.com/Vector919), [Robbert](https://github.com/Robbert), [GreLI](https://github.com/GreLI), [FuzzySockets](https://github.com/FuzzySockets), [ArtemBernatskyy](https://github.com/ArtemBernatskyy), [@garethheyes](https://twitter.com/garethheyes), [@shafigullin](https://twitter.com/shafigullin), [@mmrupp](https://twitter.com/mmrupp), [@irsdl](https://twitter.com/irsdl),[ShikariSenpai](https://github.com/ShikariSenpai), [ansjdnakjdnajkd](https://github.com/ansjdnakjdnajkd), [@asutherland](https://twitter.com/asutherland), [@mathias](https://twitter.com/mathias), [@cgvwzq](https://twitter.com/cgvwzq), [@robbertatwork](https://twitter.com/robbertatwork), [@giutro](https://twitter.com/giutro) and especially [@masatokinugawa](https://twitter.com/masatokinugawa)
|
|
335
|
+
[granlem 💸](https://twitter.com/MaximeVeit), [oreoshake 💸](https://github.com/oreoshake), [dcramer 💸](https://github.com/dcramer),[tdeekens ❤️](https://github.com/tdeekens), [peernohell ❤️](https://github.com/peernohell), [neilj](https://github.com/neilj), [fhemberger](https://github.com/fhemberger), [Joris-van-der-Wel](https://github.com/Joris-van-der-Wel), [ydaniv](https://github.com/ydaniv), [terjanq](https://twitter.com/terjanq), [filedescriptor](https://github.com/filedescriptor), [ConradIrwin](https://github.com/ConradIrwin), [gibson042](https://github.com/gibson042), [choumx](https://github.com/choumx), [0xSobky](https://github.com/0xSobky), [styfle](https://github.com/styfle), [koto](https://github.com/koto), [tlau88](https://github.com/tlau88), [strugee](https://github.com/strugee), [oparoz](https://github.com/oparoz), [mathiasbynens](https://github.com/mathiasbynens), [edg2s](https://github.com/edg2s), [dnkolegov](https://github.com/dnkolegov), [dhardtke](https://github.com/dhardtke), [wirehead](https://github.com/wirehead), [thorn0](https://github.com/thorn0), [styu](https://github.com/styu), [mozfreddyb](https://github.com/mozfreddyb), [mikesamuel](https://github.com/mikesamuel), [jorangreef](https://github.com/jorangreef), [jimmyhchan](https://github.com/jimmyhchan), [jameydeorio](https://github.com/jameydeorio), [jameskraus](https://github.com/jameskraus), [hyderali](https://github.com/hyderali), [hansottowirtz](https://github.com/hansottowirtz), [hackvertor](https://github.com/hackvertor), [freddyb](https://github.com/freddyb), [flavorjones](https://github.com/flavorjones), [djfarrelly](https://github.com/djfarrelly), [devd](https://github.com/devd), [camerondunford](https://github.com/camerondunford), [buu700](https://github.com/buu700), [buildog](https://github.com/buildog), [alabiaga](https://github.com/alabiaga), [Vector919](https://github.com/Vector919), [Robbert](https://github.com/Robbert), [GreLI](https://github.com/GreLI), [FuzzySockets](https://github.com/FuzzySockets), [ArtemBernatskyy](https://github.com/ArtemBernatskyy), [@garethheyes](https://twitter.com/garethheyes), [@shafigullin](https://twitter.com/shafigullin), [@mmrupp](https://twitter.com/mmrupp), [@irsdl](https://twitter.com/irsdl),[ShikariSenpai](https://github.com/ShikariSenpai), [ansjdnakjdnajkd](https://github.com/ansjdnakjdnajkd), [@asutherland](https://twitter.com/asutherland), [@mathias](https://twitter.com/mathias), [@cgvwzq](https://twitter.com/cgvwzq), [@robbertatwork](https://twitter.com/robbertatwork), [@giutro](https://twitter.com/giutro) and especially [@securitymb ❤️](https://twitter.com/securitymb) & [@masatokinugawa ❤️](https://twitter.com/masatokinugawa)
|
|
331
336
|
|
|
332
337
|
## Testing powered by
|
|
333
338
|
<a target="_blank" href="https://www.browserstack.com/"><img width="200" src="https://www.browserstack.com/images/layout/browserstack-logo-600x315.png"></a><br>
|
package/dist/purify.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.
|
|
1
|
+
/*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.2.2/LICENSE */
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
@@ -6,7 +6,9 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr
|
|
|
6
6
|
|
|
7
7
|
var hasOwnProperty = Object.hasOwnProperty,
|
|
8
8
|
setPrototypeOf = Object.setPrototypeOf,
|
|
9
|
-
isFrozen = Object.isFrozen
|
|
9
|
+
isFrozen = Object.isFrozen,
|
|
10
|
+
getPrototypeOf = Object.getPrototypeOf,
|
|
11
|
+
getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
10
12
|
var freeze = Object.freeze,
|
|
11
13
|
seal = Object.seal,
|
|
12
14
|
create = Object.create; // eslint-disable-line import/no-mutable-exports
|
|
@@ -117,15 +119,48 @@ function clone(object) {
|
|
|
117
119
|
return newObject;
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
/* IE10 doesn't support __lookupGetter__ so lets'
|
|
123
|
+
* simulate it. It also automatically checks
|
|
124
|
+
* if the prop is function or getter and behaves
|
|
125
|
+
* accordingly. */
|
|
126
|
+
function lookupGetter(object, prop) {
|
|
127
|
+
while (object !== null) {
|
|
128
|
+
var desc = getOwnPropertyDescriptor(object, prop);
|
|
129
|
+
if (desc) {
|
|
130
|
+
if (desc.get) {
|
|
131
|
+
return unapply(desc.get);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (typeof desc.value === 'function') {
|
|
135
|
+
return unapply(desc.value);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
object = getPrototypeOf(object);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
120
145
|
var html = 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']);
|
|
121
146
|
|
|
122
147
|
// SVG
|
|
123
|
-
var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', '
|
|
148
|
+
var svg = 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']);
|
|
124
149
|
|
|
125
150
|
var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
|
|
126
151
|
|
|
152
|
+
// List of SVG elements that are disallowed by default.
|
|
153
|
+
// We still need to know them so that we can do namespace
|
|
154
|
+
// checks properly in case one wants to add them to
|
|
155
|
+
// allow-list.
|
|
156
|
+
var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'feimage', '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']);
|
|
157
|
+
|
|
127
158
|
var mathMl = 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']);
|
|
128
159
|
|
|
160
|
+
// Similarly to SVG, we want to know all MathML elements,
|
|
161
|
+
// even those that we disallow by default.
|
|
162
|
+
var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
|
|
163
|
+
|
|
129
164
|
var text = freeze(['#text']);
|
|
130
165
|
|
|
131
166
|
var html$1 = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns']);
|
|
@@ -205,7 +240,7 @@ function createDOMPurify() {
|
|
|
205
240
|
* Version label, exposed for easier checks
|
|
206
241
|
* if DOMPurify is up to date or not
|
|
207
242
|
*/
|
|
208
|
-
DOMPurify.version = '2.2.
|
|
243
|
+
DOMPurify.version = '2.2.6';
|
|
209
244
|
|
|
210
245
|
/**
|
|
211
246
|
* Array of elements that DOMPurify removed during sanitation.
|
|
@@ -227,6 +262,7 @@ function createDOMPurify() {
|
|
|
227
262
|
var DocumentFragment = window.DocumentFragment,
|
|
228
263
|
HTMLTemplateElement = window.HTMLTemplateElement,
|
|
229
264
|
Node = window.Node,
|
|
265
|
+
Element = window.Element,
|
|
230
266
|
NodeFilter = window.NodeFilter,
|
|
231
267
|
_window$NamedNodeMap = window.NamedNodeMap,
|
|
232
268
|
NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
|
|
@@ -235,13 +271,20 @@ function createDOMPurify() {
|
|
|
235
271
|
DOMParser = window.DOMParser,
|
|
236
272
|
trustedTypes = window.trustedTypes;
|
|
237
273
|
|
|
274
|
+
|
|
275
|
+
var ElementPrototype = Element.prototype;
|
|
276
|
+
|
|
277
|
+
var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
278
|
+
var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
279
|
+
var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
280
|
+
var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
281
|
+
|
|
238
282
|
// As per issue #47, the web-components registry is inherited by a
|
|
239
283
|
// new document created via createHTMLDocument. As per the spec
|
|
240
284
|
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
|
|
241
285
|
// a new empty registry is used when creating a template contents owner
|
|
242
286
|
// document, so we use that as our parent document to ensure nothing
|
|
243
287
|
// is inherited.
|
|
244
|
-
|
|
245
288
|
if (typeof HTMLTemplateElement === 'function') {
|
|
246
289
|
var template = document.createElement('template');
|
|
247
290
|
if (template.content && template.content.ownerDocument) {
|
|
@@ -363,7 +406,7 @@ function createDOMPurify() {
|
|
|
363
406
|
var USE_PROFILES = {};
|
|
364
407
|
|
|
365
408
|
/* Tags to ignore content of when KEEP_CONTENT is true */
|
|
366
|
-
var FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
|
|
409
|
+
var 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']);
|
|
367
410
|
|
|
368
411
|
/* Tags that are safe for data: URIs */
|
|
369
412
|
var DATA_URI_TAGS = null;
|
|
@@ -504,6 +547,115 @@ function createDOMPurify() {
|
|
|
504
547
|
CONFIG = cfg;
|
|
505
548
|
};
|
|
506
549
|
|
|
550
|
+
var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
551
|
+
|
|
552
|
+
var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
|
|
553
|
+
|
|
554
|
+
/* Keep track of all possible SVG and MathML tags
|
|
555
|
+
* so that we can perform the namespace checks
|
|
556
|
+
* correctly. */
|
|
557
|
+
var ALL_SVG_TAGS = addToSet({}, svg);
|
|
558
|
+
addToSet(ALL_SVG_TAGS, svgFilters);
|
|
559
|
+
addToSet(ALL_SVG_TAGS, svgDisallowed);
|
|
560
|
+
|
|
561
|
+
var ALL_MATHML_TAGS = addToSet({}, mathMl);
|
|
562
|
+
addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
|
|
563
|
+
|
|
564
|
+
var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
|
|
565
|
+
var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
566
|
+
var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
*
|
|
570
|
+
*
|
|
571
|
+
* @param {Element} element a DOM element whose namespace is being checked
|
|
572
|
+
* @returns {boolean} Return false if the element has a
|
|
573
|
+
* namespace that a spec-compliant parser would never
|
|
574
|
+
* return. Return true otherwise.
|
|
575
|
+
*/
|
|
576
|
+
var _checkValidNamespace = function _checkValidNamespace(element) {
|
|
577
|
+
var parent = getParentNode(element);
|
|
578
|
+
|
|
579
|
+
// In JSDOM, if we're inside shadow DOM, then parentNode
|
|
580
|
+
// can be null. We just simulate parent in this case.
|
|
581
|
+
if (!parent || !parent.tagName) {
|
|
582
|
+
parent = {
|
|
583
|
+
namespaceURI: HTML_NAMESPACE,
|
|
584
|
+
tagName: 'template'
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
var tagName = stringToLowerCase(element.tagName);
|
|
589
|
+
var parentTagName = stringToLowerCase(parent.tagName);
|
|
590
|
+
|
|
591
|
+
if (element.namespaceURI === SVG_NAMESPACE) {
|
|
592
|
+
// The only way to switch from HTML namespace to SVG
|
|
593
|
+
// is via <svg>. If it happens via any other tag, then
|
|
594
|
+
// it should be killed.
|
|
595
|
+
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
596
|
+
return tagName === 'svg';
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// The only way to switch from MathML to SVG is via
|
|
600
|
+
// svg if parent is either <annotation-xml> or MathML
|
|
601
|
+
// text integration points.
|
|
602
|
+
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
603
|
+
return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// We only allow elements that are defined in SVG
|
|
607
|
+
// spec. All others are disallowed in SVG namespace.
|
|
608
|
+
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (element.namespaceURI === MATHML_NAMESPACE) {
|
|
612
|
+
// The only way to switch from HTML namespace to MathML
|
|
613
|
+
// is via <math>. If it happens via any other tag, then
|
|
614
|
+
// it should be killed.
|
|
615
|
+
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
616
|
+
return tagName === 'math';
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// The only way to switch from SVG to MathML is via
|
|
620
|
+
// <math> and HTML integration points
|
|
621
|
+
if (parent.namespaceURI === SVG_NAMESPACE) {
|
|
622
|
+
return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// We only allow elements that are defined in MathML
|
|
626
|
+
// spec. All others are disallowed in MathML namespace.
|
|
627
|
+
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (element.namespaceURI === HTML_NAMESPACE) {
|
|
631
|
+
// The only way to switch from SVG to HTML is via
|
|
632
|
+
// HTML integration points, and from MathML to HTML
|
|
633
|
+
// is via MathML text integration points
|
|
634
|
+
if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Certain elements are allowed in both SVG and HTML
|
|
643
|
+
// namespace. We need to specify them explicitly
|
|
644
|
+
// so that they don't get erronously deleted from
|
|
645
|
+
// HTML namespace.
|
|
646
|
+
var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
|
|
647
|
+
|
|
648
|
+
// We disallow tags that are specific for MathML
|
|
649
|
+
// or SVG and should never appear in HTML namespace
|
|
650
|
+
return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// The code should never reach this place (this means
|
|
654
|
+
// that the element somehow got namespace that is not
|
|
655
|
+
// HTML, SVG or MathML). Return false just in case.
|
|
656
|
+
return false;
|
|
657
|
+
};
|
|
658
|
+
|
|
507
659
|
/**
|
|
508
660
|
* _forceRemove
|
|
509
661
|
*
|
|
@@ -514,7 +666,11 @@ function createDOMPurify() {
|
|
|
514
666
|
try {
|
|
515
667
|
node.parentNode.removeChild(node);
|
|
516
668
|
} catch (_) {
|
|
517
|
-
|
|
669
|
+
try {
|
|
670
|
+
node.outerHTML = emptyHTML;
|
|
671
|
+
} catch (_) {
|
|
672
|
+
node.remove();
|
|
673
|
+
}
|
|
518
674
|
}
|
|
519
675
|
};
|
|
520
676
|
|
|
@@ -606,7 +762,7 @@ function createDOMPurify() {
|
|
|
606
762
|
return false;
|
|
607
763
|
}
|
|
608
764
|
|
|
609
|
-
if (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') {
|
|
765
|
+
if (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') {
|
|
610
766
|
return true;
|
|
611
767
|
}
|
|
612
768
|
|
|
@@ -678,14 +834,8 @@ function createDOMPurify() {
|
|
|
678
834
|
allowedTags: ALLOWED_TAGS
|
|
679
835
|
});
|
|
680
836
|
|
|
681
|
-
/* Take care of an mXSS pattern using p, br inside svg, math */
|
|
682
|
-
if ((tagName === 'svg' || tagName === 'math') && currentNode.querySelectorAll('p, br, form, table').length !== 0) {
|
|
683
|
-
_forceRemove(currentNode);
|
|
684
|
-
return true;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
837
|
/* Detect mXSS attempts abusing namespace confusion */
|
|
688
|
-
if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[
|
|
838
|
+
if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
|
|
689
839
|
_forceRemove(currentNode);
|
|
690
840
|
return true;
|
|
691
841
|
}
|
|
@@ -693,18 +843,25 @@ function createDOMPurify() {
|
|
|
693
843
|
/* Remove element if anything forbids its presence */
|
|
694
844
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
695
845
|
/* Keep content except for bad-listed elements */
|
|
696
|
-
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
846
|
+
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
847
|
+
var parentNode = getParentNode(currentNode);
|
|
848
|
+
var childNodes = getChildNodes(currentNode);
|
|
849
|
+
var childCount = childNodes.length;
|
|
850
|
+
for (var i = childCount - 1; i >= 0; --i) {
|
|
851
|
+
parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
|
|
852
|
+
}
|
|
701
853
|
}
|
|
702
854
|
|
|
703
855
|
_forceRemove(currentNode);
|
|
704
856
|
return true;
|
|
705
857
|
}
|
|
706
858
|
|
|
707
|
-
/*
|
|
859
|
+
/* Check whether element has a valid namespace */
|
|
860
|
+
if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
|
|
861
|
+
_forceRemove(currentNode);
|
|
862
|
+
return true;
|
|
863
|
+
}
|
|
864
|
+
|
|
708
865
|
if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
|
|
709
866
|
_forceRemove(currentNode);
|
|
710
867
|
return true;
|