dompurify 1.0.9 → 1.0.10

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
@@ -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 1.0.9.
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 1.0.10.
10
10
 
11
11
  DOMPurify is written in JavaScript and works in all modern browsers (Safari, 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 [22 different browsers](https://github.com/cure53/DOMPurify/blob/master/test/karma.custom-launchers.config.js#L5) right now, more to come. We also cover Node.js v6.0.0, v8.0.0, v9.0.0 and v10.0.0, running DOMPurify on [jsdom](https://github.com/tmpvar/jsdom).
13
+ Our automated tests cover [25 different browsers](https://github.com/cure53/DOMPurify/blob/master/test/karma.custom-launchers.config.js#L5) right now, more to come. We also cover Node.js v8.0.0, v9.0.0 and v10.0.0, running DOMPurify on [jsdom](https://github.com/tmpvar/jsdom).
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
 
@@ -110,7 +110,24 @@ DOMPurify offers a fall-back behavior for older MSIE browsers. It uses the MSIE-
110
110
 
111
111
  If not even `toStaticHTML` is supported, DOMPurify does nothing at all. It simply returns exactly the string that you fed it.
112
112
 
113
- ## Can I configure it?
113
+ ## What about DOMPurify and Trusted Types?
114
+
115
+ In version 1.0.9, support for [Trusted Types API](https://github.com/WICG/trusted-types) was added to DOMPurify.
116
+
117
+ When `DOMPurify.sanitize` is used in the environment where the Trusted Types API is available (this happens e.g. in Chrome `chrome://flags/#enable-experimental-web-platform-features`), it returns a `TrustedHTML` value instead of a string (the behavior for `RETURN_DOM`, `RETURN_DOM_FRAGMENT`, and `RETURN_DOM_IMPORT` config options does not change).
118
+
119
+ That return value is implicitly casted to a string when needed, returning the actual sanitized HTML snippet. In particular, you can directly use it with DOM sinks like `innerHTML`, or concatenate it with other strings. For most use cases, the API change does not introduce any visible change.
120
+
121
+ That said, `TrustedHTML` values are intentionally immutable, and don't inherit from `String.prototype`. In rare cases where you expect the value to implement String prototype functions (e.g. if you want to `String.replace` the sanitized output), cast the value to a string like so:
122
+
123
+ ```javascript
124
+ const sanitizedAsString = (DOMPurify.sanitize(foo) + '');
125
+ sanitizedAsString.replace(...)
126
+ ```
127
+
128
+ Please note, that if that change breaks your application, you *might* be doing something wrong. The sanitized HTML snippet should not be modified, as it might introduce XSS vulnerabilities.
129
+
130
+ ## Can I configure DOMPurify?
114
131
 
115
132
  Yes. The included default configuration values are pretty good already - but you can of course override them. Check out the [`/demos`](https://github.com/cure53/DOMPurify/tree/master/demos) folder to see a bunch of examples on how you can [customize DOMPurify](https://github.com/cure53/DOMPurify/tree/master/demos#what-is-this).
116
133
 
@@ -119,6 +136,9 @@ Yes. The included default configuration values are pretty good already - but you
119
136
  var clean = DOMPurify.sanitize(dirty, {SAFE_FOR_JQUERY: true});
120
137
 
121
138
  // strip {{ ... }} and <% ... %> to make output safe for template systems
139
+ // be careful please, this mode is not recommended for production usage.
140
+ // allowing template parsing in user-controlled HTML is not advised at all.
141
+ // only use this mode if there is really no alternative.
122
142
  var clean = DOMPurify.sanitize(dirty, {SAFE_FOR_TEMPLATES: true});
123
143
 
124
144
  // allow only <b>
@@ -271,7 +291,7 @@ Several people need to be listed here!
271
291
 
272
292
  Big thanks also go to [@ydaniv](https://github.com/ydaniv), [@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 [@fhemberger](https://twitter.com/fhemberger)!
273
293
 
274
- Further, thanks [@neilj](https://twitter.com/neilj) and [@0xsobky](https://twitter.com/0xsobky) for their code reviews and countless small optimizations, fixes and beautifications.
294
+ Further, thanks [@neilj](https://twitter.com/neilj) and [@0xsobky](https://twitter.com/0xsobky) for their code reviews and countless small optimizations, fixes and beautifications. Thanks also go out to [@kkotowicz](https://twitter.com/kkotowicz) for his Trusted Types implementation and the connected section on our README page.
275
295
 
276
296
  Big thanks also go to [@tdeekens](https://twitter.com/tdeekens) for doing all the hard work and getting us on track with Travis CI and BrowserStack. And thanks to [@Joris-van-der-Wel](https://github.com/Joris-van-der-Wel) for setting up DOMPurify for jsdom and creating the additional test suite. And again [@tdeekens](https://twitter.com/tdeekens) for his [incredible efforts](https://github.com/cure53/DOMPurify/pull/206) and contribution to refactor DOMPurify into using ES201x, proper build tools, better test coverage and much more!
277
297
 
@@ -21,7 +21,7 @@ var freeze$2 = Object.freeze || function (x) {
21
21
 
22
22
  var html$1 = freeze$2(['accept', 'action', 'align', 'alt', 'autocomplete', 'background', 'bgcolor', 'border', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'coords', 'crossorigin', 'datetime', 'default', 'dir', 'disabled', 'download', 'enctype', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'integrity', 'ismap', 'label', 'lang', 'list', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'multiple', 'name', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', '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', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns']);
23
23
 
24
- var svg$1 = freeze$2(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', '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', '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', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'tabindex', 'targetx', 'targety', 'transform', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', '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']);
24
+ var svg$1 = freeze$2(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', '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', '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', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', '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']);
25
25
 
26
26
  var mathMl$1 = freeze$2(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', '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']);
27
27
 
@@ -47,30 +47,39 @@ function addToSet(set, array) {
47
47
  // Prevent prototype setters from intercepting set as a this value.
48
48
  setPrototypeOf(set, null);
49
49
  }
50
+
50
51
  var l = array.length;
51
52
  while (l--) {
52
53
  var element = array[l];
53
54
  if (typeof element === 'string') {
54
55
  var lcElement = element.toLowerCase();
55
56
  if (lcElement !== element) {
56
- array[l] = lcElement;
57
+ // Config presets (e.g. tags.js, attrs.js) are immutable.
58
+ if (!Object.isFrozen(array)) {
59
+ array[l] = lcElement;
60
+ }
61
+
57
62
  element = lcElement;
58
63
  }
59
64
  }
65
+
60
66
  set[element] = true;
61
67
  }
68
+
62
69
  return set;
63
70
  }
64
71
 
65
72
  /* Shallow clone an object */
66
73
  function clone(object) {
67
74
  var newObject = {};
75
+
68
76
  var property = void 0;
69
77
  for (property in object) {
70
78
  if (apply$1(hasOwnProperty, object, [property])) {
71
79
  newObject[property] = object[property];
72
80
  }
73
81
  }
82
+
74
83
  return newObject;
75
84
  }
76
85
 
@@ -138,7 +147,7 @@ var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes,
138
147
  return html$$1;
139
148
  }
140
149
  });
141
- } catch (e) {
150
+ } catch (error) {
142
151
  // Policy creation failed (most likely another DOMPurify script has
143
152
  // already run). Skip creating the policy, as this will only cause errors
144
153
  // if TT are enforced.
@@ -158,7 +167,7 @@ function createDOMPurify() {
158
167
  * Version label, exposed for easier checks
159
168
  * if DOMPurify is up to date or not
160
169
  */
161
- DOMPurify.version = '1.0.9';
170
+ DOMPurify.version = '1.0.10';
162
171
 
163
172
  /**
164
173
  * Array of elements that DOMPurify removed during sanitation.
@@ -337,6 +346,7 @@ function createDOMPurify() {
337
346
  if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
338
347
  cfg = {};
339
348
  }
349
+
340
350
  /* Set configuration parameters */
341
351
  ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
342
352
  ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
@@ -375,16 +385,19 @@ function createDOMPurify() {
375
385
  addToSet(ALLOWED_TAGS, html);
376
386
  addToSet(ALLOWED_ATTR, html$1);
377
387
  }
388
+
378
389
  if (USE_PROFILES.svg === true) {
379
390
  addToSet(ALLOWED_TAGS, svg);
380
391
  addToSet(ALLOWED_ATTR, svg$1);
381
392
  addToSet(ALLOWED_ATTR, xml);
382
393
  }
394
+
383
395
  if (USE_PROFILES.svgFilters === true) {
384
396
  addToSet(ALLOWED_TAGS, svgFilters);
385
397
  addToSet(ALLOWED_ATTR, svg$1);
386
398
  addToSet(ALLOWED_ATTR, xml);
387
399
  }
400
+
388
401
  if (USE_PROFILES.mathMl === true) {
389
402
  addToSet(ALLOWED_TAGS, mathMl);
390
403
  addToSet(ALLOWED_ATTR, mathMl$1);
@@ -397,14 +410,18 @@ function createDOMPurify() {
397
410
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
398
411
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
399
412
  }
413
+
400
414
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
401
415
  }
416
+
402
417
  if (cfg.ADD_ATTR) {
403
418
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
404
419
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
405
420
  }
421
+
406
422
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
407
423
  }
424
+
408
425
  if (cfg.ADD_URI_SAFE_ATTR) {
409
426
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
410
427
  }
@@ -442,7 +459,7 @@ function createDOMPurify() {
442
459
  DOMPurify.removed.push({ element: node });
443
460
  try {
444
461
  node.parentNode.removeChild(node);
445
- } catch (err) {
462
+ } catch (error) {
446
463
  node.outerHTML = emptyHTML;
447
464
  }
448
465
  };
@@ -459,12 +476,13 @@ function createDOMPurify() {
459
476
  attribute: node.getAttributeNode(name),
460
477
  from: node
461
478
  });
462
- } catch (err) {
479
+ } catch (error) {
463
480
  DOMPurify.removed.push({
464
481
  attribute: null,
465
482
  from: node
466
483
  });
467
484
  }
485
+
468
486
  node.removeAttribute(name);
469
487
  };
470
488
 
@@ -494,7 +512,7 @@ function createDOMPurify() {
494
512
  if (useDOMParser) {
495
513
  try {
496
514
  doc = new DOMParser().parseFromString(dirty, 'text/html');
497
- } catch (err) {}
515
+ } catch (error) {}
498
516
  }
499
517
 
500
518
  /* Remove title to fix a mXSS bug in older MS Edge */
@@ -537,15 +555,16 @@ function createDOMPurify() {
537
555
  if (doc.querySelector('svg img')) {
538
556
  useDOMParser = true;
539
557
  }
540
- } catch (err) {}
558
+ } catch (error) {}
541
559
  })();
560
+
542
561
  (function () {
543
562
  try {
544
563
  var doc = _initDocument('<x/><title>&lt;/title&gt;&lt;img&gt;');
545
564
  if (doc.querySelector('title').innerHTML.match(/<\/title/)) {
546
565
  removeTitle = true;
547
566
  }
548
- } catch (err) {}
567
+ } catch (error) {}
549
568
  })();
550
569
  }
551
570
 
@@ -571,9 +590,11 @@ function createDOMPurify() {
571
590
  if (elm instanceof Text || elm instanceof Comment) {
572
591
  return false;
573
592
  }
593
+
574
594
  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') {
575
595
  return true;
576
596
  }
597
+
577
598
  return false;
578
599
  };
579
600
 
@@ -615,6 +636,7 @@ function createDOMPurify() {
615
636
  * @param {Node} currentNode to check for permission to exist
616
637
  * @return {Boolean} true if node was killed, false if left alive
617
638
  */
639
+ // eslint-disable-next-line complexity
618
640
  var _sanitizeElements = function _sanitizeElements(currentNode) {
619
641
  var content = void 0;
620
642
 
@@ -643,8 +665,20 @@ function createDOMPurify() {
643
665
  try {
644
666
  var htmlToInsert = currentNode.innerHTML;
645
667
  currentNode.insertAdjacentHTML('AfterEnd', trustedTypesPolicy ? trustedTypesPolicy.createHTML(htmlToInsert) : htmlToInsert);
646
- } catch (err) {}
668
+ } catch (error) {}
647
669
  }
670
+
671
+ _forceRemove(currentNode);
672
+ return true;
673
+ }
674
+
675
+ /* Remove in case a noscript/noembed XSS is suspected */
676
+ if (tagName === 'noscript' && currentNode.innerHTML.match(/<\/noscript/i)) {
677
+ _forceRemove(currentNode);
678
+ return true;
679
+ }
680
+
681
+ if (tagName === 'noembed' && currentNode.innerHTML.match(/<\/noembed/i)) {
648
682
  _forceRemove(currentNode);
649
683
  return true;
650
684
  }
@@ -685,18 +719,13 @@ function createDOMPurify() {
685
719
  * @param {string} value Attribute value.
686
720
  * @return {Boolean} Returns true if `value` is valid, otherwise false.
687
721
  */
722
+ // eslint-disable-next-line complexity
688
723
  var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
689
724
  /* Make sure attribute cannot clobber */
690
725
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
691
726
  return false;
692
727
  }
693
728
 
694
- /* Sanitize attribute content to be template-safe */
695
- if (SAFE_FOR_TEMPLATES) {
696
- value = value.replace(MUSTACHE_EXPR$$1, ' ');
697
- value = value.replace(ERB_EXPR$$1, ' ');
698
- }
699
-
700
729
  /* Allow valid data-* attributes: At least one character after "-"
701
730
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
702
731
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
@@ -733,6 +762,7 @@ function createDOMPurify() {
733
762
  } else {
734
763
  return false;
735
764
  }
765
+
736
766
  return true;
737
767
  };
738
768
 
@@ -744,9 +774,8 @@ function createDOMPurify() {
744
774
  * @protect removeAttribute
745
775
  * @protect setAttribute
746
776
  *
747
- * @param {Node} node to sanitize
777
+ * @param {Node} currentNode to sanitize
748
778
  */
749
- // eslint-disable-next-line complexity
750
779
  var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
751
780
  var attr = void 0;
752
781
  var value = void 0;
@@ -813,6 +842,7 @@ function createDOMPurify() {
813
842
  if (name === 'id') {
814
843
  currentNode.setAttribute(name, '');
815
844
  }
845
+
816
846
  _removeAttribute(name, currentNode);
817
847
  }
818
848
 
@@ -821,6 +851,12 @@ function createDOMPurify() {
821
851
  continue;
822
852
  }
823
853
 
854
+ /* Sanitize attribute content to be template-safe */
855
+ if (SAFE_FOR_TEMPLATES) {
856
+ value = value.replace(MUSTACHE_EXPR$$1, ' ');
857
+ value = value.replace(ERB_EXPR$$1, ' ');
858
+ }
859
+
824
860
  /* Is `value` valid for this attribute? */
825
861
  var lcTag = currentNode.nodeName.toLowerCase();
826
862
  if (!_isValidAttribute(lcTag, lcName, value)) {
@@ -835,8 +871,9 @@ function createDOMPurify() {
835
871
  /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
836
872
  currentNode.setAttribute(name, value);
837
873
  }
874
+
838
875
  DOMPurify.removed.pop();
839
- } catch (err) {}
876
+ } catch (error) {}
840
877
  }
841
878
 
842
879
  /* Execute a hook if present */
@@ -917,10 +954,12 @@ function createDOMPurify() {
917
954
  if (typeof dirty === 'string') {
918
955
  return window.toStaticHTML(dirty);
919
956
  }
957
+
920
958
  if (_isNode(dirty)) {
921
959
  return window.toStaticHTML(dirty.outerHTML);
922
960
  }
923
961
  }
962
+
924
963
  return dirty;
925
964
  }
926
965
 
@@ -943,11 +982,12 @@ function createDOMPurify() {
943
982
  /* Node is already a body, use as is */
944
983
  body = importedNode;
945
984
  } else {
985
+ // eslint-disable-next-line unicorn/prefer-node-append
946
986
  body.appendChild(importedNode);
947
987
  }
948
988
  } else {
949
989
  /* Exit directly if we have nothing to do */
950
- if (!RETURN_DOM && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) {
990
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) {
951
991
  return trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
952
992
  }
953
993
 
@@ -1004,6 +1044,7 @@ function createDOMPurify() {
1004
1044
  returnNode = createDocumentFragment.call(body.ownerDocument);
1005
1045
 
1006
1046
  while (body.firstChild) {
1047
+ // eslint-disable-next-line unicorn/prefer-node-append
1007
1048
  returnNode.appendChild(body.firstChild);
1008
1049
  }
1009
1050
  } else {
@@ -1023,6 +1064,13 @@ function createDOMPurify() {
1023
1064
  }
1024
1065
 
1025
1066
  var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
1067
+
1068
+ /* Sanitize final string template-safe */
1069
+ if (SAFE_FOR_TEMPLATES) {
1070
+ serializedHTML = serializedHTML.replace(MUSTACHE_EXPR$$1, ' ');
1071
+ serializedHTML = serializedHTML.replace(ERB_EXPR$$1, ' ');
1072
+ }
1073
+
1026
1074
  return trustedTypesPolicy ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
1027
1075
  };
1028
1076
 
@@ -1062,6 +1110,7 @@ function createDOMPurify() {
1062
1110
  if (!CONFIG) {
1063
1111
  _parseConfig({});
1064
1112
  }
1113
+
1065
1114
  var lcTag = tag.toLowerCase();
1066
1115
  var lcName = attr.toLowerCase();
1067
1116
  return _isValidAttribute(lcTag, lcName, value);
@@ -1078,6 +1127,7 @@ function createDOMPurify() {
1078
1127
  if (typeof hookFunction !== 'function') {
1079
1128
  return;
1080
1129
  }
1130
+
1081
1131
  hooks[entryPoint] = hooks[entryPoint] || [];
1082
1132
  hooks[entryPoint].push(hookFunction);
1083
1133
  };