sanitize-html 1.26.0 → 1.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  ## Changelog
2
2
 
3
+ 1.27.0:
4
+ - Adds the `allowedIframeDomains` option. This works similar to `allowedIframeHostnames`, where you would set it to an array of web domains. It would then permit any hostname on those domains to be used in iframe `src` attributes. Thanks to [Stanislav Kravchenko](https://github.com/StanisLove) for the contribution.
5
+
3
6
  1.26.0:
4
7
  - Adds the `option` element to the default `nonTextTagsArray` of tags with contents that aren't meant to be displayed visually as text. This can be overridden with the `nonTextTags` option.
5
8
 
package/README.md CHANGED
@@ -344,14 +344,15 @@ Note that the text passed to the `textFilter` method is already escaped for safe
344
344
 
345
345
  ### Iframe Filters
346
346
 
347
- If you would like to allow iframe tags but want to control the domains that are allowed through you can provide an array of hostnames that you would like to allow as iframe sources. This hostname is a property in the options object passed as an argument to the `sanitize-html` function.
347
+ If you would like to allow iframe tags but want to control the domains that are allowed through you can provide an array of hostnames and(or) array of domains that you would like to allow as iframe sources. This hostname is a property in the options object passed as an argument to the `sanitize-html` function.
348
348
 
349
- This array will be checked against the html that is passed to the function and return only `src` urls that include the allowed hostnames in the object. The url in the html that is passed must be formatted correctly (valid hostname) as an embedded iframe otherwise the module will strip out the src from the iframe.
349
+ These arrays will be checked against the html that is passed to the function and return only `src` urls that include the allowed hostnames or domains in the object. The url in the html that is passed must be formatted correctly (valid hostname) as an embedded iframe otherwise the module will strip out the src from the iframe.
350
350
 
351
351
  Make sure to pass a valid hostname along with the domain you wish to allow, i.e.:
352
352
 
353
353
  ```javascript
354
- allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com']
354
+ allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'],
355
+ allowedIframeDomains: ['zoom.us']
355
356
  ```
356
357
 
357
358
  You may also specify whether or not to allow relative URLs as iframe sources.
@@ -360,7 +361,7 @@ You may also specify whether or not to allow relative URLs as iframe sources.
360
361
  allowIframeRelativeUrls: true
361
362
  ```
362
363
 
363
- Note that if unspecified, relative URLs will be allowed by default if no hostname filter is provided but removed by default if a hostname filter is provided.
364
+ Note that if unspecified, relative URLs will be allowed by default if no hostname or domain filter is provided but removed by default if a hostname or domain filter is provided.
364
365
 
365
366
  **Remember that the `iframe` tag must be allowed as well as the `src` attribute.**
366
367
 
@@ -411,6 +412,24 @@ clean = sanitizeHtml('<p><iframe src="https://www.vimeo/video/12345"></iframe><p
411
412
 
412
413
  will return an empty iframe tag.
413
414
 
415
+ If you want to allow any subdomain of any level you can provide the domain in `allowedIframeDomains`
416
+
417
+ ```javascript
418
+ clean = sanitizeHtml('<p><iframe src="https://us02web.zoom.us/embed/12345"></iframe><p>', {
419
+ allowedTags: [ 'p', 'em', 'strong', 'iframe' ],
420
+ allowedClasses: {
421
+ 'p': [ 'fancy', 'simple' ],
422
+ },
423
+ allowedAttributes: {
424
+ 'iframe': ['src']
425
+ },
426
+ allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'],
427
+ allowedIframeDomains: ['zoom.us']
428
+ });
429
+ ```
430
+
431
+ will pass through as safe.
432
+
414
433
  ### Allowed CSS Classes
415
434
 
416
435
  If you wish to allow specific CSS classes on a particular element, you can do so with the `allowedClasses` option. Any other CSS classes are discarded.
@@ -20775,13 +20775,19 @@ function sanitizeHtml(html, options, _recursing) {
20775
20775
  parsed = url.parse(value, false, true);
20776
20776
  var isRelativeUrl = parsed && parsed.host === null && parsed.protocol === null;
20777
20777
  if (isRelativeUrl) {
20778
- // default value of allowIframeRelativeUrls is true unless allowIframeHostnames specified
20778
+ // default value of allowIframeRelativeUrls is true
20779
+ // unless allowedIframeHostnames or allowedIframeDomains specified
20779
20780
  allowed = has(options, "allowIframeRelativeUrls")
20780
- ? options.allowIframeRelativeUrls : !options.allowedIframeHostnames;
20781
- } else if (options.allowedIframeHostnames) {
20782
- allowed = options.allowedIframeHostnames.find(function (hostname) {
20781
+ ? options.allowIframeRelativeUrls
20782
+ : (!options.allowedIframeHostnames && !options.allowedIframeDomains);
20783
+ } else if (options.allowedIframeHostnames || options.allowedIframeDomains) {
20784
+ var allowedHostname = (options.allowedIframeHostnames || []).find(function (hostname) {
20783
20785
  return hostname === parsed.hostname;
20784
20786
  });
20787
+ var allowedDomain = (options.allowedIframeDomains || []).find(function(domain) {
20788
+ return parsed.hostname === domain || parsed.hostname.endsWith(`.${domain}`);
20789
+ });
20790
+ allowed = allowedHostname || allowedDomain;
20785
20791
  }
20786
20792
  } catch (e) {
20787
20793
  // Unparseable iframe src
@@ -4915,8 +4915,9 @@ var passedAllowedAttributesMapCheck=false;if(!allowedAttributesMap||has(allowedA
4915
4915
  var splitStrArray=value.split(' ');var _iterator11=_createForOfIteratorHelper(splitStrArray),_step2;try{for(_iterator11.s();!(_step2=_iterator11.n()).done;){var s=_step2.value;if(o.values.indexOf(s)!==-1){if(newValue===''){newValue=s;}else{newValue+=' '+s;}}}}catch(err){_iterator11.e(err);}finally{_iterator11.f();}}else if(o.values.indexOf(value)>=0){// verified an allowed value matches the entire attribute value
4916
4916
  newValue=value;}value=newValue;}}}catch(err){_iterator10.e(err);}finally{_iterator10.f();}}if(passedAllowedAttributesMapCheck){if(options.allowedSchemesAppliedToAttributes.indexOf(a)!==-1){if(naughtyHref(name,value)){delete frame.attribs[a];return;}}if(name==='iframe'&&a==='src'){var allowed=true;try{// naughtyHref is in charge of whether protocol relative URLs
4917
4917
  // are cool. We should just accept them
4918
- parsed=url.parse(value,false,true);var isRelativeUrl=parsed&&parsed.host===null&&parsed.protocol===null;if(isRelativeUrl){// default value of allowIframeRelativeUrls is true unless allowIframeHostnames specified
4919
- allowed=has(options,"allowIframeRelativeUrls")?options.allowIframeRelativeUrls:!options.allowedIframeHostnames;}else if(options.allowedIframeHostnames){allowed=options.allowedIframeHostnames.find(function(hostname){return hostname===parsed.hostname;});}}catch(e){// Unparseable iframe src
4918
+ parsed=url.parse(value,false,true);var isRelativeUrl=parsed&&parsed.host===null&&parsed.protocol===null;if(isRelativeUrl){// default value of allowIframeRelativeUrls is true
4919
+ // unless allowedIframeHostnames or allowedIframeDomains specified
4920
+ allowed=has(options,"allowIframeRelativeUrls")?options.allowIframeRelativeUrls:!options.allowedIframeHostnames&&!options.allowedIframeDomains;}else if(options.allowedIframeHostnames||options.allowedIframeDomains){var allowedHostname=(options.allowedIframeHostnames||[]).find(function(hostname){return hostname===parsed.hostname;});var allowedDomain=(options.allowedIframeDomains||[]).find(function(domain){return parsed.hostname===domain||parsed.hostname.endsWith(".".concat(domain));});allowed=allowedHostname||allowedDomain;}}catch(e){// Unparseable iframe src
4920
4921
  allowed=false;}if(!allowed){delete frame.attribs[a];return;}}if(a==='srcset'){try{parsed=srcset.parse(value);each(parsed,function(value){if(naughtyHref('srcset',value.url)){value.evil=true;}});parsed=filter(parsed,function(v){return!v.evil;});if(!parsed.length){delete frame.attribs[a];return;}else{value=srcset.stringify(filter(parsed,function(v){return!v.evil;}));frame.attribs[a]=value;}}catch(e){// Unparseable srcset
4921
4922
  delete frame.attribs[a];return;}}if(a==='class'){value=filterClasses(value,allowedClassesMap[name]);if(!value.length){delete frame.attribs[a];return;}}if(a==='style'){try{var abstractSyntaxTree=postcss.parse(name+" {"+value+"}");var filteredAST=filterCss(abstractSyntaxTree,options.allowedStyles);value=stringifyStyleAttributes(filteredAST);if(value.length===0){delete frame.attribs[a];return;}}catch(e){delete frame.attribs[a];return;}}result+=' '+a;if(value&&value.length){result+='="'+escapeHtml(value,true)+'"';}}else{delete frame.attribs[a];}});}if(options.selfClosing.indexOf(name)!==-1){result+=" />";}else{result+=">";if(frame.innerText&&!hasText&&!options.textFilter){result+=frame.innerText;}}if(skip){result=tempResult+escapeHtml(result);tempResult='';}},ontext:function ontext(text){if(skipText){return;}var lastFrame=stack[stack.length-1];var tag;if(lastFrame){tag=lastFrame.tag;// If inner text was set by transform function then let's use it
4922
4923
  text=lastFrame.innerText!==undefined?lastFrame.innerText:text;}if(options.disallowedTagsMode==='discard'&&(tag==='script'||tag==='style')){// htmlparser2 gives us these as-is. Escaping them ruins the content. Allowing