dompurify 2.0.3 → 2.0.7

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,7 +6,7 @@
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.0.3.
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.0.7.
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
 
@@ -83,9 +83,11 @@ const clean = DOMPurify.sanitize(dirty);
83
83
 
84
84
  Of course there is a demo! [Play with DOMPurify](https://cure53.de/purify)
85
85
 
86
- ## What if I find a bypass?
86
+ ## What if I find a _security_ bug?
87
87
 
88
- If that happens, you probably qualify for a juicy bug bounty! The fine folks over at [FastMail](https://www.fastmail.com/) use DOMPurify for their services and added our library to their bug bounty scope. So, if you find a way to bypass or weaken DOMPurify, please have a look at their website and the [bug bounty info](https://www.fastmail.com/about/bugbounty.html).
88
+ First of all, please immediately contact us via [email](mailto:mario@cure53.de) so we can work on a fix. [PGP key](https://keyserver.ubuntu.com/pks/lookup?op=vindex&search=0xC26C858090F70ADA)
89
+
90
+ Also, you probably qualify for a bug bounty! The fine folks over at [FastMail](https://www.fastmail.com/) use DOMPurify for their services and added our library to their bug bounty scope. So, if you find a way to bypass or weaken DOMPurify, please also have a look at their website and the [bug bounty info](https://www.fastmail.com/about/bugbounty.html).
89
91
 
90
92
  ## Some purification samples please?
91
93
 
@@ -278,14 +280,8 @@ Feature releases will not be announced to this list.
278
280
 
279
281
  ## Who contributed?
280
282
 
281
- Several people need to be listed here!
282
-
283
- [@garethheyes](https://twitter.com/garethheyes) and [@filedescriptor](https://twitter.com/filedescriptor) for invaluable help, [@shafigullin](https://twitter.com/shafigullin) for breaking the library multiple times and thereby strengthening it in the early days, [@mmrupp](https://twitter.com/mmrupp) and [@irsdl](https://twitter.com/irsdl) for doing the same. And lastly, thanks to @ShikariSenpai and @ansjdnakjdnajkd for spotting a [massive Safari 10.1 bug](https://github.com/cure53/DOMPurify/releases/tag/0.8.6) early on and reporting it.
284
-
285
- 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)! Further, we would like to thank @masatokinugawa for his continuous and extremely valuable help in the past and present.
286
-
287
- Further, thanks [@neilj](https://twitter.com/neilj) and [@0xsobky](https://twitter.com/0xsobky) for their early 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.
283
+ Many people helped DOMPurify become what it is and need to be acknowledged here!
288
284
 
289
- 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!
285
+ @tdeekens, @neilj, @fhemberger, @Joris-van-der-Wel, @ydaniv, @filedescriptor, @ConradIrwin, @gibson042, @choumx, @0xSobky, @styfle, @koto, @tlau88, @strugee, @oparoz, @mathiasbynens, @edg2s, @dnkolegov, @dhardtke, @wirehead, @thorn0, @styu, @mozfreddyb, @mikesamuel, @jorangreef, @jimmyhchan, @jameydeorio, @jameskraus, @hyderali, @hansottowirtz, @hackvertor, @freddyb, @flavorjones, @djfarrelly, @devd, @camerondunford, @buu700, @buildog, @alabiaga, @Vector919, @Robbert, @GreLI, @FuzzySockets, @ArtemBernatskyy, [@garethheyes](https://twitter.com/garethheyes), [@filedescriptor](https://twitter.com/filedescriptor), [@shafigullin](https://twitter.com/shafigullin), [@mmrupp](https://twitter.com/mmrupp), [@irsdl](https://twitter.com/irsdl), @ShikariSenpai, @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
290
286
 
291
- And last but not least, thanks to [BrowserStack](https://browserstack.com) for supporting this project with their services for free and delivering excellent, dedicated and very professional support on top of that.
287
+ And last but not least, thanks to [BrowserStack](https://browserstack.com) for supporting this project with their services for free and delivering excellent, dedicated and very professional support on top of that.
@@ -23,7 +23,7 @@ var html$1 = freeze$2(['accept', 'action', 'align', 'alt', 'autocomplete', 'back
23
23
 
24
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', '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', '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
- 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']);
26
+ var mathMl$1 = freeze$2(['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']);
27
27
 
28
28
  var xml = freeze$2(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
29
29
 
@@ -167,7 +167,7 @@ function createDOMPurify() {
167
167
  * Version label, exposed for easier checks
168
168
  * if DOMPurify is up to date or not
169
169
  */
170
- DOMPurify.version = '2.0.3';
170
+ DOMPurify.version = '2.0.7';
171
171
 
172
172
  /**
173
173
  * Array of elements that DOMPurify removed during sanitation.
@@ -185,7 +185,6 @@ function createDOMPurify() {
185
185
 
186
186
  var originalDocument = window.document;
187
187
  var useDOMParser = false;
188
- var removeSVGAttr = false;
189
188
  var removeTitle = false;
190
189
 
191
190
  var document = window.document;
@@ -321,7 +320,7 @@ function createDOMPurify() {
321
320
  var USE_PROFILES = {};
322
321
 
323
322
  /* Tags to ignore content of when KEEP_CONTENT is true */
324
- var FORBID_CONTENTS = addToSet({}, ['audio', 'colgroup', 'head', 'math', 'script', 'style', 'template', 'thead', 'svg', 'video']);
323
+ 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']);
325
324
 
326
325
  /* Tags that are safe for data: URIs */
327
326
  var DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image']);
@@ -541,7 +540,7 @@ function createDOMPurify() {
541
540
  body.outerHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
542
541
  }
543
542
 
544
- if (leadingWhitespace) {
543
+ if (dirty && leadingWhitespace) {
545
544
  doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
546
545
  }
547
546
 
@@ -571,20 +570,11 @@ function createDOMPurify() {
571
570
  (function () {
572
571
  try {
573
572
  var doc = _initDocument('<x/><title>&lt;/title&gt;&lt;img&gt;');
574
- if (doc.querySelector('title').innerHTML.match(/<\/title/)) {
573
+ if (/<\/title/.test(doc.querySelector('title').innerHTML)) {
575
574
  removeTitle = true;
576
575
  }
577
576
  } catch (error) {}
578
577
  })();
579
-
580
- (function () {
581
- try {
582
- var doc = _initDocument('<svg></p></svg>');
583
- if (doc.querySelector('svg p')) {
584
- removeSVGAttr = true;
585
- }
586
- } catch (error) {}
587
- })();
588
578
  }
589
579
 
590
580
  /**
@@ -610,7 +600,7 @@ function createDOMPurify() {
610
600
  return false;
611
601
  }
612
602
 
613
- 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') {
603
+ 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') {
614
604
  return true;
615
605
  }
616
606
 
@@ -677,6 +667,12 @@ function createDOMPurify() {
677
667
  allowedTags: ALLOWED_TAGS
678
668
  });
679
669
 
670
+ /* Take care of an mXSS pattern using p, br inside svg, math */
671
+ if ((tagName === 'svg' || tagName === 'math') && currentNode.querySelectorAll('p, br').length !== 0) {
672
+ _forceRemove(currentNode);
673
+ return true;
674
+ }
675
+
680
676
  /* Remove element if anything forbids its presence */
681
677
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
682
678
  /* Keep content except for black-listed elements */
@@ -692,23 +688,12 @@ function createDOMPurify() {
692
688
  }
693
689
 
694
690
  /* Remove in case a noscript/noembed XSS is suspected */
695
- if (tagName === 'noscript' && currentNode.innerHTML.match(/<\/noscript/i)) {
696
- _forceRemove(currentNode);
697
- return true;
698
- }
699
-
700
- if (tagName === 'noembed' && currentNode.innerHTML.match(/<\/noembed/i)) {
701
- _forceRemove(currentNode);
702
- return true;
703
- }
704
-
705
- /* Remove in case an mXSS is suspected */
706
- if (currentNode.namespaceURI && currentNode.namespaceURI.match(/svg|math/i) && currentNode.textContent && currentNode.textContent.match(new RegExp('</' + tagName, 'i'))) {
691
+ if (tagName === 'noscript' && /<\/noscript/i.test(currentNode.innerHTML)) {
707
692
  _forceRemove(currentNode);
708
693
  return true;
709
694
  }
710
695
 
711
- if ((tagName === 'svg' || tagName === 'math') && (currentNode.innerHTML && currentNode.innerHTML.match(/<template/i) || typeof currentNode.innerHTML === 'undefined' && removeSVGAttr)) {
696
+ if (tagName === 'noembed' && /<\/noembed/i.test(currentNode.innerHTML)) {
712
697
  _forceRemove(currentNode);
713
698
  return true;
714
699
  }
@@ -849,11 +834,6 @@ function createDOMPurify() {
849
834
  _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
850
835
  value = hookEvent.attrValue;
851
836
 
852
- /* Check for possible Chrome mXSS */
853
- if (removeSVGAttr && value.match(/<\//)) {
854
- _forceRemove(currentNode);
855
- }
856
-
857
837
  /* Remove attribute */
858
838
  // Safari (iOS + Mac), last tested v8.0.5, crashes if you try to
859
839
  // remove a "name" attribute from an <img> tag that has an "id"
@@ -887,6 +867,12 @@ function createDOMPurify() {
887
867
  continue;
888
868
  }
889
869
 
870
+ /* Take care of an mXSS pattern using namespace switches */
871
+ if (/svg|math/i.test(currentNode.namespaceURI) && new RegExp('</(' + Object.keys(FORBID_CONTENTS).join('|') + ')', 'i').test(value)) {
872
+ _removeAttribute(name, currentNode);
873
+ continue;
874
+ }
875
+
890
876
  /* Sanitize attribute content to be template-safe */
891
877
  if (SAFE_FOR_TEMPLATES) {
892
878
  value = value.replace(MUSTACHE_EXPR$$1, ' ');