dompurify 2.3.2 → 2.3.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 CHANGED
@@ -1,16 +1,16 @@
1
1
  # DOMPurify
2
2
 
3
- [![npm version](https://badge.fury.io/js/dompurify.svg)](http://badge.fury.io/js/dompurify) ![Build and Test](https://github.com/cure53/DOMPurify/workflows/Build%20and%20Test/badge.svg?branch=main) [![Downloads](https://img.shields.io/npm/dm/dompurify.svg)](https://www.npmjs.com/package/dompurify) [![minified size](https://badgen.net/bundlephobia/min/dompurify?color=green&label=minified)](https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js) [![gzip size](https://badgen.net/bundlephobia/minzip/dompurify?color=green&label=gzipped)](https://packagephobia.now.sh/result?p=dompurify) [![dependents](https://badgen.net/github/dependents-repo/cure53/dompurify?color=green&label=dependents)](https://github.com/cure53/DOMPurify/network/dependents)
3
+ [![npm version](https://badge.fury.io/js/dompurify.svg)](http://badge.fury.io/js/dompurify) ![Build and Test](https://github.com/cure53/DOMPurify/workflows/Build%20and%20Test/badge.svg?branch=main) [![Downloads](https://img.shields.io/npm/dm/dompurify.svg)](https://www.npmjs.com/package/dompurify) [![minified size](https://badgen.net/bundlephobia/min/dompurify?color=green&label=minified)](https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js) [![gzip size](https://badgen.net/bundlephobia/minzip/dompurify?color=green&label=gzipped)](https://packagephobia.now.sh/result?p=dompurify) [![dependents](https://badgen.net/github/dependents-repo/cure53/dompurify?color=green&label=dependents)](https://github.com/cure53/DOMPurify/network/dependents)
4
4
 
5
5
  [![NPM](https://nodei.co/npm/dompurify.png)](https://nodei.co/npm/dompurify/)
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.3.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.3.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 [17 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.
13
+ Our automated tests cover [19 different browsers](https://github.com/cure53/DOMPurify/blob/main/test/karma.custom-launchers.config.js#L5) right now, more to come. We also cover Node.js v14.15.1, v15.4.0, v16.13.0, v17.0.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,14 +37,14 @@ 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
- let clean = DOMPurify.sanitize( dirty );
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
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
45
 
46
46
  ```js
47
- let clean = DOMPurify.sanitize( dirty , {USE_PROFILES: {html: true}} );
47
+ let clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
48
48
  ```
49
49
 
50
50
  ### Is there any foot-gun potential?
@@ -133,7 +133,7 @@ DOMPurify also exposes a property called `isSupported`, which tells you whether
133
133
  In version 1.0.9, support for [Trusted Types API](https://github.com/WICG/trusted-types) was added to DOMPurify.
134
134
  In version 2.0.0, a config flag was added to control DOMPurify's behavior regarding this.
135
135
 
136
- When `DOMPurify.sanitize` is used in an environment where the Trusted Types API is available and `RETURN_TRUSTED_TYPE` is set to `true`, it tries to return a `TrustedHTML` value instead of a string (the behavior for `RETURN_DOM`, `RETURN_DOM_FRAGMENT`, and `RETURN_DOM_IMPORT` config options does not change).
136
+ When `DOMPurify.sanitize` is used in an environment where the Trusted Types API is available and `RETURN_TRUSTED_TYPE` is set to `true`, it tries to return a `TrustedHTML` value instead of a string (the behavior for `RETURN_DOM` and `RETURN_DOM_FRAGMENT` config options does not change).
137
137
 
138
138
  ## Can I configure DOMPurify?
139
139
 
@@ -185,9 +185,58 @@ var clean = DOMPurify.sanitize(dirty, {ADD_TAGS: ['my-tag']});
185
185
  // extend the existing array of allowed attributes and add my-attr to allow-list
186
186
  var clean = DOMPurify.sanitize(dirty, {ADD_ATTR: ['my-attr']});
187
187
 
188
+ // prohibit ARIA attributes, leave other safe HTML as is (default is true)
189
+ var clean = DOMPurify.sanitize(dirty, {ALLOW_ARIA_ATTR: false});
190
+
188
191
  // prohibit HTML5 data attributes, leave other safe HTML as is (default is true)
189
192
  var clean = DOMPurify.sanitize(dirty, {ALLOW_DATA_ATTR: false});
190
193
 
194
+ /**
195
+ * Control behavior relating to Custom Elements
196
+ */
197
+
198
+ // DOMPurify allows to define rules for Custom Elements. When using the CUSTOM_ELEMENT_HANDLING
199
+ // literal, it is possible to define exactly what elements you wish to allow (by default, none are allowed).
200
+ //
201
+ // The same goes for their attributes. By default, the built-in or configured allow.list is used.
202
+ //
203
+ // You can use a RegExp literal to specify what is allowed or a predicate, examples for both can be seen below.
204
+ // The default values are very restrictive to prevent accidental XSS bypasses. Handle with great care!
205
+
206
+
207
+ var clean = DOMPurify.sanitize(
208
+ '<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
209
+ {
210
+ CUSTOM_ELEMENT_HANDLING: {
211
+ tagNameCheck: null, // no custom elements are allowed
212
+ attributeNameCheck: null, // default / standard attribute allow-list is used
213
+ allowCustomizedBuiltInElements: false, // no customized built-ins allowed
214
+ },
215
+ }
216
+ ); // <div is=""></div>
217
+
218
+ var clean = DOMPurify.sanitize(
219
+ '<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
220
+ {
221
+ CUSTOM_ELEMENT_HANDLING: {
222
+ tagNameCheck: /^foo-/, // allow all tags starting with "foo-"
223
+ attributeNameCheck: /baz/, // allow all attributes containing "baz"
224
+ allowCustomizedBuiltInElements: false, // customized built-ins are allowed
225
+ },
226
+ }
227
+ ); // <foo-bar baz="foobar"></foo-bar><div is=""></div>
228
+
229
+ var clean = DOMPurify.sanitize(
230
+ '<foo-bar baz="foobar" forbidden="true"></foo-bar><div is="foo-baz"></div>',
231
+ {
232
+ CUSTOM_ELEMENT_HANDLING: {
233
+ tagNameCheck: (tagName) => tagName.match(/^foo-/), // allow all tags starting with "foo-"
234
+ attributeNameCheck: (attr) => attr.match(/baz/), // allow all containing "baz"
235
+ allowCustomizedBuiltInElements: true, // allow customized built-ins
236
+ },
237
+ }
238
+ ); // <foo-bar baz="foobar"></foo-bar><div is="foo-baz"></div>
239
+
191
240
  /**
192
241
  * Control behavior relating to URI values
193
242
  */
@@ -211,9 +260,6 @@ var clean = DOMPurify.sanitize(dirty, {ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|
211
260
 
212
261
  /**
213
262
  * Influence the return-type
214
- *
215
- * Careful, this setting has foot-gun potential! If you set RETURN_DOM or RETURN_DOM_FRAGMENT to true, don't set RETURN_DOM_IMPORT to false!
216
- * By default, our settings are secure - we believe - but returning a DOM *and* manually setting RETURN_DOM_IMPORT to false will give you XSS in some situations.
217
263
  */
218
264
  // return a DOM HTMLBodyElement instead of an HTML string (default is false)
219
265
  var clean = DOMPurify.sanitize(dirty, {RETURN_DOM: true});
@@ -221,13 +267,6 @@ var clean = DOMPurify.sanitize(dirty, {RETURN_DOM: true});
221
267
  // return a DOM DocumentFragment instead of an HTML string (default is false)
222
268
  var clean = DOMPurify.sanitize(dirty, {RETURN_DOM_FRAGMENT: true});
223
269
 
224
- // return a DOM DocumentFragment instead of an HTML string (default is false)
225
- // also import it into the current document (default is false).
226
- // RETURN_DOM_IMPORT must be set if you would like to append
227
- // the returned node to the current document (default is true)
228
- var clean = DOMPurify.sanitize(dirty, {RETURN_DOM_FRAGMENT: true, RETURN_DOM_IMPORT: true});
229
- document.body.appendChild(clean);
230
-
231
270
  // use the RETURN_TRUSTED_TYPE flag to turn on Trusted Types support if available
232
271
  var clean = DOMPurify.sanitize(dirty, {RETURN_TRUSTED_TYPE: true}); // will return a TrustedHTML object instead of a string if possible
233
272
 
@@ -246,6 +285,9 @@ var clean = DOMPurify.sanitize(dirty, {KEEP_CONTENT: false});
246
285
  // glue elements like style, script or others to document.body and prevent unintuitive browser behavior in several edge-cases (default is false)
247
286
  var clean = DOMPurify.sanitize(dirty, {FORCE_BODY: true});
248
287
 
288
+ // change the parser type so sanitized data is treated as XML and not as HTML, which is the default
289
+ var clean = DOMPurify.sanitize(dirty, {PARSER_MEDIA_TYPE: 'application/xhtml+xml'});
290
+
249
291
  /**
250
292
  * Influence where we sanitize
251
293
  */
@@ -280,15 +322,14 @@ It passes the currently processed DOM node, when needed a literal with verified
280
322
  _Example_:
281
323
 
282
324
  ```js
283
- DOMPurify.addHook('beforeSanitizeElements', function (
284
- currentNode,
285
- hookEvent,
286
- config
287
- ) {
288
- // Do something with the current node and return it
289
- // You can also mutate hookEvent (i.e. set hookEvent.forceKeepAttr = true)
290
- return currentNode;
291
- });
325
+ DOMPurify.addHook(
326
+ 'beforeSanitizeElements',
327
+ function (currentNode, hookEvent, config) {
328
+ // Do something with the current node and return it
329
+ // You can also mutate hookEvent (i.e. set hookEvent.forceKeepAttr = true)
330
+ return currentNode;
331
+ }
332
+ );
292
333
  ```
293
334
 
294
335
  ## Continuous Integration
@@ -301,9 +342,9 @@ All relevant commits will be signed with the key `0x24BB6BF4` for additional sec
301
342
 
302
343
  ### Development and contributing
303
344
 
304
- #### Installation (`yarn i`)
345
+ #### Installation (`npm i`)
305
346
 
306
- We support both `yarn` and `npm@5.2` officially while providing lock-files for either dependency manager to provide reproducible installs and builds on either or. TravisCI itself is configured to install dependencies using `yarn`. When using an older version of `npm` we can not fully ensure the versions of installed dependencies which might lead to unanticipated problems.
347
+ We support `npm` officially. GitHub Actions workflow is configured to install dependencies using `npm`. When using deprected version of `npm` we can not fully ensure the versions of installed dependencies which might lead to unanticipated problems.
307
348
 
308
349
  #### Scripts
309
350
 
@@ -321,7 +362,7 @@ These are our npm scripts:
321
362
  - `npm run build:umd` to only build an unminified UMD module
322
363
  - `npm run build:umd:min` to only build a minified UMD module
323
364
 
324
- Note: all run scripts triggered via `npm run <script>` can also be started using `yarn <script>`.
365
+ Note: all run scripts triggered via `npm run <script>`.
325
366
 
326
367
  There are more npm scripts but they are mainly to integrate with CI or are meant to be "private" for instance to amend build distribution files with every commit.
327
368
 
@@ -337,9 +378,10 @@ Feature releases will not be announced to this list.
337
378
 
338
379
  Many people helped and help DOMPurify become what it is and need to be acknowledged here!
339
380
 
340
- [lowdefy 💸](https://twitter.com/lowdefy), [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), [NateScarlet](https://github.com/NateScarlet), [neilj](https://github.com/neilj), [fhemberger](https://github.com/fhemberger), [Joris-van-der-Wel](https://github.com/Joris-van-der-Wel), [ydaniv](https://github.com/ydaniv), [terjanq](https://twitter.com/terjanq), [filedescriptor](https://github.com/filedescriptor), [ConradIrwin](https://github.com/ConradIrwin), [gibson042](https://github.com/gibson042), [choumx](https://github.com/choumx), [0xSobky](https://github.com/0xSobky), [styfle](https://github.com/styfle), [koto](https://github.com/koto), [tlau88](https://github.com/tlau88), [strugee](https://github.com/strugee), [oparoz](https://github.com/oparoz), [mathiasbynens](https://github.com/mathiasbynens), [edg2s](https://github.com/edg2s), [dnkolegov](https://github.com/dnkolegov), [dhardtke](https://github.com/dhardtke), [wirehead](https://github.com/wirehead), [thorn0](https://github.com/thorn0), [styu](https://github.com/styu), [mozfreddyb](https://github.com/mozfreddyb), [mikesamuel](https://github.com/mikesamuel), [jorangreef](https://github.com/jorangreef), [jimmyhchan](https://github.com/jimmyhchan), [jameydeorio](https://github.com/jameydeorio), [jameskraus](https://github.com/jameskraus), [hyderali](https://github.com/hyderali), [hansottowirtz](https://github.com/hansottowirtz), [hackvertor](https://github.com/hackvertor), [freddyb](https://github.com/freddyb), [flavorjones](https://github.com/flavorjones), [djfarrelly](https://github.com/djfarrelly), [devd](https://github.com/devd), [camerondunford](https://github.com/camerondunford), [buu700](https://github.com/buu700), [buildog](https://github.com/buildog), [alabiaga](https://github.com/alabiaga), [Vector919](https://github.com/Vector919), [Robbert](https://github.com/Robbert), [GreLI](https://github.com/GreLI), [FuzzySockets](https://github.com/FuzzySockets), [ArtemBernatskyy](https://github.com/ArtemBernatskyy), [@garethheyes](https://twitter.com/garethheyes), [@shafigullin](https://twitter.com/shafigullin), [@mmrupp](https://twitter.com/mmrupp), [@irsdl](https://twitter.com/irsdl),[ShikariSenpai](https://github.com/ShikariSenpai), [ansjdnakjdnajkd](https://github.com/ansjdnakjdnajkd), [@asutherland](https://twitter.com/asutherland), [@mathias](https://twitter.com/mathias), [@cgvwzq](https://twitter.com/cgvwzq), [@robbertatwork](https://twitter.com/robbertatwork), [@giutro](https://twitter.com/giutro), [@CmdEngineer_](https://twitter.com/CmdEngineer_), [@avr4mit](https://twitter.com/avr4mit) and especially [@securitymb ❤️](https://twitter.com/securitymb) & [@masatokinugawa ❤️](https://twitter.com/masatokinugawa)
381
+ [jarrodldavis 💸](https://github.com/jarrodldavis), [GrantGryczan 💸](https://github.com/GrantGryczan), [lowdefy 💸](https://twitter.com/lowdefy), [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), [is2ei](https://github.com/is2ei), [franktopel](https://github.com/franktopel), [NateScarlet](https://github.com/NateScarlet), [neilj](https://github.com/neilj), [fhemberger](https://github.com/fhemberger), [Joris-van-der-Wel](https://github.com/Joris-van-der-Wel), [ydaniv](https://github.com/ydaniv), [terjanq](https://twitter.com/terjanq), [filedescriptor](https://github.com/filedescriptor), [ConradIrwin](https://github.com/ConradIrwin), [gibson042](https://github.com/gibson042), [choumx](https://github.com/choumx), [0xSobky](https://github.com/0xSobky), [styfle](https://github.com/styfle), [koto](https://github.com/koto), [tlau88](https://github.com/tlau88), [strugee](https://github.com/strugee), [oparoz](https://github.com/oparoz), [mathiasbynens](https://github.com/mathiasbynens), [edg2s](https://github.com/edg2s), [dnkolegov](https://github.com/dnkolegov), [dhardtke](https://github.com/dhardtke), [wirehead](https://github.com/wirehead), [thorn0](https://github.com/thorn0), [styu](https://github.com/styu), [mozfreddyb](https://github.com/mozfreddyb), [mikesamuel](https://github.com/mikesamuel), [jorangreef](https://github.com/jorangreef), [jimmyhchan](https://github.com/jimmyhchan), [jameydeorio](https://github.com/jameydeorio), [jameskraus](https://github.com/jameskraus), [hyderali](https://github.com/hyderali), [hansottowirtz](https://github.com/hansottowirtz), [hackvertor](https://github.com/hackvertor), [freddyb](https://github.com/freddyb), [flavorjones](https://github.com/flavorjones), [djfarrelly](https://github.com/djfarrelly), [devd](https://github.com/devd), [camerondunford](https://github.com/camerondunford), [buu700](https://github.com/buu700), [buildog](https://github.com/buildog), [alabiaga](https://github.com/alabiaga), [Vector919](https://github.com/Vector919), [Robbert](https://github.com/Robbert), [GreLI](https://github.com/GreLI), [FuzzySockets](https://github.com/FuzzySockets), [ArtemBernatskyy](https://github.com/ArtemBernatskyy), [@garethheyes](https://twitter.com/garethheyes), [@shafigullin](https://twitter.com/shafigullin), [@mmrupp](https://twitter.com/mmrupp), [@irsdl](https://twitter.com/irsdl),[ShikariSenpai](https://github.com/ShikariSenpai), [ansjdnakjdnajkd](https://github.com/ansjdnakjdnajkd), [@asutherland](https://twitter.com/asutherland), [@mathias](https://twitter.com/mathias), [@cgvwzq](https://twitter.com/cgvwzq), [@robbertatwork](https://twitter.com/robbertatwork), [@giutro](https://twitter.com/giutro), [@CmdEngineer\_](https://twitter.com/CmdEngineer_), [@avr4mit](https://twitter.com/avr4mit) and especially [@securitymb ❤️](https://twitter.com/securitymb) & [@masatokinugawa ❤️](https://twitter.com/masatokinugawa)
382
+
383
+ ## Testing powered by
341
384
 
342
- ## Testing powered by
343
385
  <a target="_blank" href="https://www.browserstack.com/"><img width="200" src="https://www.browserstack.com/images/layout/browserstack-logo-600x315.png"></a><br>
344
386
 
345
387
  And last but not least, thanks to [BrowserStack Open-Source Program](https://www.browserstack.com/open-source) for supporting this project with their services for free and delivering excellent, dedicated and very professional support on top of that.
@@ -1,4 +1,4 @@
1
- /*! @license DOMPurify 2.3.2 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.2/LICENSE */
1
+ /*! @license DOMPurify 2.3.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.6/LICENSE */
2
2
 
3
3
  'use strict';
4
4
 
@@ -152,13 +152,13 @@ var html = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside'
152
152
  // SVG
153
153
  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']);
154
154
 
155
- 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']);
155
+ 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']);
156
156
 
157
157
  // List of SVG elements that are disallowed by default.
158
158
  // We still need to know them so that we can do namespace
159
159
  // checks properly in case one wants to add them to
160
160
  // allow-list.
161
- 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']);
161
+ 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']);
162
162
 
163
163
  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']);
164
164
 
@@ -168,9 +168,9 @@ var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv
168
168
 
169
169
  var text = freeze(['#text']);
170
170
 
171
- 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', 'slot']);
171
+ 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', 'nonce', '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', 'slot']);
172
172
 
173
- var svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
173
+ var svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
174
174
 
175
175
  var mathMl$1 = 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']);
176
176
 
@@ -186,6 +186,7 @@ var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-
186
186
  var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
187
187
  var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
188
188
  );
189
+ var DOCTYPE_NAME = seal(/^html$/i);
189
190
 
190
191
  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
191
192
 
@@ -245,7 +246,7 @@ function createDOMPurify() {
245
246
  * Version label, exposed for easier checks
246
247
  * if DOMPurify is up to date or not
247
248
  */
248
- DOMPurify.version = '2.3.2';
249
+ DOMPurify.version = '2.3.6';
249
250
 
250
251
  /**
251
252
  * Array of elements that DOMPurify removed during sanitation.
@@ -271,8 +272,7 @@ function createDOMPurify() {
271
272
  NodeFilter = window.NodeFilter,
272
273
  _window$NamedNodeMap = window.NamedNodeMap,
273
274
  NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
274
- Text = window.Text,
275
- Comment = window.Comment,
275
+ HTMLFormElement = window.HTMLFormElement,
276
276
  DOMParser = window.DOMParser,
277
277
  trustedTypes = window.trustedTypes;
278
278
 
@@ -298,7 +298,7 @@ function createDOMPurify() {
298
298
  }
299
299
 
300
300
  var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
301
- var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : '';
301
+ var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
302
302
 
303
303
  var _document = document,
304
304
  implementation = _document.implementation,
@@ -342,6 +342,33 @@ function createDOMPurify() {
342
342
  var ALLOWED_ATTR = null;
343
343
  var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml)));
344
344
 
345
+ /*
346
+ * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
347
+ * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
348
+ * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
349
+ * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
350
+ */
351
+ var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
352
+ tagNameCheck: {
353
+ writable: true,
354
+ configurable: false,
355
+ enumerable: true,
356
+ value: null
357
+ },
358
+ attributeNameCheck: {
359
+ writable: true,
360
+ configurable: false,
361
+ enumerable: true,
362
+ value: null
363
+ },
364
+ allowCustomizedBuiltInElements: {
365
+ writable: true,
366
+ configurable: false,
367
+ enumerable: true,
368
+ value: false
369
+ }
370
+ }));
371
+
345
372
  /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
346
373
  var FORBID_TAGS = null;
347
374
 
@@ -382,17 +409,6 @@ function createDOMPurify() {
382
409
  * string (or a TrustedHTML object if Trusted Types are supported) */
383
410
  var RETURN_DOM_FRAGMENT = false;
384
411
 
385
- /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM
386
- * `Node` is imported into the current `Document`. If this flag is not enabled the
387
- * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by
388
- * DOMPurify.
389
- *
390
- * This defaults to `true` starting DOMPurify 2.2.0. Note that setting it to `false`
391
- * might cause XSS from attacks hidden in closed shadowroots in case the browser
392
- * supports Declarative Shadow: DOM https://web.dev/declarative-shadow-dom/
393
- */
394
- var RETURN_DOM_IMPORT = true;
395
-
396
412
  /* Try to return a Trusted Type object instead of a string, return a string in
397
413
  * case Trusted Types are not supported */
398
414
  var RETURN_TRUSTED_TYPE = false;
@@ -443,6 +459,10 @@ function createDOMPurify() {
443
459
 
444
460
  var formElement = document.createElement('form');
445
461
 
462
+ var isRegexOrFunction = function isRegexOrFunction(testValue) {
463
+ return testValue instanceof RegExp || testValue instanceof Function;
464
+ };
465
+
446
466
  /**
447
467
  * _parseConfig
448
468
  *
@@ -478,7 +498,6 @@ function createDOMPurify() {
478
498
  WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
479
499
  RETURN_DOM = cfg.RETURN_DOM || false; // Default false
480
500
  RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
481
- RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true
482
501
  RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
483
502
  FORCE_BODY = cfg.FORCE_BODY || false; // Default false
484
503
  SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
@@ -486,7 +505,22 @@ function createDOMPurify() {
486
505
  IN_PLACE = cfg.IN_PLACE || false; // Default false
487
506
  IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
488
507
  NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
489
- PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE in SUPPORTED_PARSER_MEDIA_TYPES ? cfg.PARSER_MEDIA_TYPE : DEFAULT_PARSER_MEDIA_TYPE;
508
+ if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
509
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
510
+ }
511
+
512
+ if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
513
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
514
+ }
515
+
516
+ if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
517
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
518
+ }
519
+
520
+ PARSER_MEDIA_TYPE =
521
+ // eslint-disable-next-line unicorn/prefer-includes
522
+ SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
523
+
490
524
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
491
525
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {
492
526
  return x;
@@ -807,7 +841,9 @@ function createDOMPurify() {
807
841
  * @return {Iterator} iterator instance
808
842
  */
809
843
  var _createIterator = function _createIterator(root) {
810
- return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
844
+ return createNodeIterator.call(root.ownerDocument || root, root,
845
+ // eslint-disable-next-line no-bitwise
846
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
811
847
  };
812
848
 
813
849
  /**
@@ -817,15 +853,7 @@ function createDOMPurify() {
817
853
  * @return {Boolean} true if clobbered, false if safe
818
854
  */
819
855
  var _isClobbered = function _isClobbered(elm) {
820
- if (elm instanceof Text || elm instanceof Comment) {
821
- return false;
822
- }
823
-
824
- 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') {
825
- return true;
826
- }
827
-
828
- return false;
856
+ 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');
829
857
  };
830
858
 
831
859
  /**
@@ -907,6 +935,12 @@ function createDOMPurify() {
907
935
 
908
936
  /* Remove element if anything forbids its presence */
909
937
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
938
+ /* Check if we have a custom element to handle */
939
+ if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
940
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
941
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
942
+ }
943
+
910
944
  /* Keep content except for bad-listed elements */
911
945
  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
912
946
  var parentNode = getParentNode(currentNode) || currentNode.parentNode;
@@ -974,8 +1008,16 @@ function createDOMPurify() {
974
1008
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
975
1009
  We don't need to check the value; it's always URI safe. */
976
1010
  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]) {
977
- return false;
978
-
1011
+ if (
1012
+ // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1013
+ // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1014
+ // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1015
+ _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)) ||
1016
+ // Alternative, second condition checks if it's an `is`-attribute, AND
1017
+ // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1018
+ 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 {
1019
+ return false;
1020
+ }
979
1021
  /* Check value is safe. First, is attr inert? If so, is safe */
980
1022
  } 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) ; else {
981
1023
  return false;
@@ -984,6 +1026,16 @@ function createDOMPurify() {
984
1026
  return true;
985
1027
  };
986
1028
 
1029
+ /**
1030
+ * _basicCustomElementCheck
1031
+ * checks if at least one dash is included in tagName, and it's not the first char
1032
+ * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1033
+ * @param {string} tagName name of the tag of the node to sanitize
1034
+ */
1035
+ var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
1036
+ return tagName.indexOf('-') > 0;
1037
+ };
1038
+
987
1039
  /**
988
1040
  * _sanitizeAttributes
989
1041
  *
@@ -1180,7 +1232,15 @@ function createDOMPurify() {
1180
1232
  IN_PLACE = false;
1181
1233
  }
1182
1234
 
1183
- if (IN_PLACE) ; else if (dirty instanceof Node) {
1235
+ if (IN_PLACE) {
1236
+ /* Do some early pre-sanitization to avoid unsafe root nodes */
1237
+ if (dirty.nodeName) {
1238
+ var tagName = transformCaseFunc(dirty.nodeName);
1239
+ if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1240
+ throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
1241
+ }
1242
+ }
1243
+ } else if (dirty instanceof Node) {
1184
1244
  /* If dirty is a DOM element, append to an empty document to avoid
1185
1245
  elements being stripped by the parser */
1186
1246
  body = _initDocument('<!---->');
@@ -1207,7 +1267,7 @@ function createDOMPurify() {
1207
1267
 
1208
1268
  /* Check we have a DOM node from the data */
1209
1269
  if (!body) {
1210
- return RETURN_DOM ? null : emptyHTML;
1270
+ return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
1211
1271
  }
1212
1272
  }
1213
1273
 
@@ -1262,7 +1322,7 @@ function createDOMPurify() {
1262
1322
  returnNode = body;
1263
1323
  }
1264
1324
 
1265
- if (RETURN_DOM_IMPORT) {
1325
+ if (ALLOWED_ATTR.shadowroot) {
1266
1326
  /*
1267
1327
  AdoptNode() is not used because internal state is not reset
1268
1328
  (e.g. the past names map of a HTMLFormElement), this is safe
@@ -1278,6 +1338,11 @@ function createDOMPurify() {
1278
1338
 
1279
1339
  var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1280
1340
 
1341
+ /* Serialize doctype if allowed */
1342
+ if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
1343
+ serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
1344
+ }
1345
+
1281
1346
  /* Sanitize final string template-safe */
1282
1347
  if (SAFE_FOR_TEMPLATES) {
1283
1348
  serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' ');