Prevent adding/editing of images from blocked sources
authorAlexander Ebert <ebert@woltlab.com>
Fri, 1 Feb 2019 16:14:38 +0000 (17:14 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Fri, 1 Feb 2019 16:14:38 +0000 (17:14 +0100)
com.woltlab.wcf/templates/wysiwyg.tpl
wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabImage.js
wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php
wcfsetup/install/files/style/ui/redactor.scss
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index c944243280e9075623da294ab928e480abda8d9c..1aac1ff3f3c85b57811f2f2e5d74d6d60c5a8633 100644 (file)
@@ -86,6 +86,7 @@
                                'wcf.editor.image.float.left': '{lang}wcf.editor.image.float.left{/lang}',
                                'wcf.editor.image.float.right': '{lang}wcf.editor.image.float.right{/lang}',
                                'wcf.editor.image.source': '{lang}wcf.editor.image.source{/lang}',
+                               'wcf.editor.image.source.error.blocked': '{lang}wcf.editor.image.source.error.blocked{/lang}',
                                'wcf.editor.image.source.error.insecure': '{lang}wcf.editor.image.source.error.insecure{/lang}',
                                'wcf.editor.image.source.error.invalid': '{lang}wcf.editor.image.source.error.invalid{/lang}',
                                
                                        customButtons: customButtons,
                                        forceSecureImages: {if MESSAGE_FORCE_SECURE_IMAGES}true{else}false{/if},
                                        highlighters: highlighters,
+                                       images: {
+                                               external: {if IMAGE_ALLOW_EXTERNAL_SOURCE}true{else}false{/if},
+                                               secureOnly: {if MESSAGE_FORCE_SECURE_IMAGES}true{else}false{/if},
+                                               whitelist: [
+                                                       {implode from=$__wcf->getBBCodeHandler()->getImageExternalSourceWhitelist() item=$hostname}'{$hostname|encodeJS}'{/implode}
+                                               ]
+                                       },
                                        media: {if $__wcf->session->getPermission('admin.content.cms.canUseMedia')}true{else}false{/if},
                                        mediaUrl: '{link controller='Media' id=-123456789 thumbnail='void' forceFrontend=true}{/link}'
                                }
                                        
                                        // set code
                                        redactor.code.start(content);
+                                       redactor.WoltLabImage.validateImages();
                                        
                                        // set value
                                        redactor.core.textarea().val(redactor.clean.onSync(redactor.$editor.html()));
index 4e5029d502766374077111e0eaa695d59838e1c1..4f9aff26ab3f87e55fa3c8e48c86f5f97d6fc52a 100644 (file)
@@ -57,6 +57,9 @@ $.Redactor.prototype.WoltLabImage = function() {
                                        else if (this.opts.woltlab.forceSecureImages && source.indexOf('http://') === 0) {
                                                return showError(sourceInput, WCF.Language.get('wcf.editor.image.source.error.insecure'));
                                        }
+                                       else if (!this.WoltLabImage.isValidSource(source)) {
+                                               return showError(sourceInput, WCF.Language.get('wcf.editor.image.source.error.blocked'));
+                                       }
                                        
                                        // update image source
                                        image.src = source;
@@ -145,6 +148,9 @@ $.Redactor.prototype.WoltLabImage = function() {
                        else if (this.opts.woltlab.forceSecureImages && source.indexOf('http://') === 0) {
                                return showError(sourceInput, WCF.Language.get('wcf.editor.image.source.error.insecure'));
                        }
+                       else if (!this.WoltLabImage.isValidSource(source)) {
+                               return showError(sourceInput, WCF.Language.get('wcf.editor.image.source.error.blocked'));
+                       }
                        
                        // check if link is valid
                        var linkInput = elById('redactor-image-link');
@@ -181,6 +187,59 @@ $.Redactor.prototype.WoltLabImage = function() {
                                        }
                                }).bind(this), 1);
                        }
+               },
+               
+               /**
+                * @param {string} src
+                */
+               isValidSource: function (src) {
+                       var link = elCreate('a');
+                       link.href = src;
+                       // Relative links in IE might not have a hostname.
+                       if (link.hostname === '') {
+                               return true;
+                       }
+                       
+                       
+                       // Check for secure hosts only.
+                       if (this.opts.woltlab.images.secureOnly && link.protocol !== 'https:') {
+                               return false;
+                       }
+                       
+                       if (!this.opts.woltlab.images.external) {
+                               var isValid = false;
+                               var wildcards = [];
+                               this.opts.woltlab.images.whitelist.forEach(function(hostname) {
+                                       if (hostname.indexOf('*.') === 0) {
+                                               wildcards.push(hostname.replace(/^\*\./, ''));
+                                       }
+                                       else if (link.hostname === hostname) {
+                                               isValid = true;
+                                       }
+                               });
+                               
+                               if (!isValid) {
+                                       wildcards.forEach(function(hostname) {
+                                               if (link.hostname.substr(hostname.length * -1) === hostname) {
+                                                       isValid = true;
+                                               }
+                                       });
+                               }
+                               
+                               if (!isValid) {
+                                       return false;
+                               }
+                       }
+                       
+                       return true;
+               },
+               
+               validateImages: function() {
+                       elBySelAll('img:not(.smiley):not(.woltlabAttachment)', this.core.editor()[0], (function(img) {
+                               if (!this.WoltLabImage.isValidSource(img.src)) {
+                                       img.classList.add('editorImageBlocked');
+                               }
+                       }).bind(this));
                }
        };
 };
index 5883adea9f05e13b675f50e63b6a8dbad68da75f..3a7b47807fea2785d34a7c765e18a395118c8b60 100644 (file)
@@ -2,7 +2,9 @@
 namespace wcf\system\bbcode;
 use wcf\data\bbcode\BBCode;
 use wcf\data\bbcode\BBCodeCache;
+use wcf\system\application\ApplicationHandler;
 use wcf\system\SingletonFactory;
+use wcf\util\ArrayUtil;
 use wcf\util\JSON;
 
 /**
@@ -138,4 +140,24 @@ class BBCodeHandler extends SingletonFactory {
        public function getHighlighters() {
                return array_keys($this->getHighlighterMeta());
        }
+       
+       /**
+        * Returns a list of hostnames that are permitted as image sources.
+        * 
+        * @return string[]
+        * @since 5.2
+        */
+       public function getImageExternalSourceWhitelist() {
+               $hosts = [];
+               // Hide these hosts unless external sources are actually denied.
+               if (!IMAGE_ALLOW_EXTERNAL_SOURCE) {
+                       $hosts = ArrayUtil::trim(explode("\n", IMAGE_EXTERNAL_SOURCE_WHITELIST));
+               }
+               
+               foreach (ApplicationHandler::getInstance()->getApplications() as $application) {
+                       $hosts[] = $application->domainName;
+               }
+               
+               return array_unique($hosts);
+       }
 }
index 344fe092d1a57fc9f98a6acd17aeaea3184e61a6..ebad4252364f02692f071973eccc2a32b3ae9fff 100644 (file)
                min-height: 0 !important;
        }
 }
+
+.editorImageBlocked {
+       filter: brightness(25%);
+       transition: filter .12s linear;
+       
+       &:hover {
+               filter: brightness(75%);
+       }
+}
index 017ad5735f32a49c53ccf0ca4155d45589dfec8a..43da9e05a8578e86dba8aba190c9388807abde88 100644 (file)
@@ -3614,6 +3614,7 @@ Dateianhänge:
                <item name="wcf.editor.image.float.left"><![CDATA[Links]]></item>
                <item name="wcf.editor.image.float.right"><![CDATA[Rechts]]></item>
                <item name="wcf.editor.image.source"><![CDATA[Quelle]]></item>
+               <item name="wcf.editor.image.source.error.blocked"><![CDATA[Das Einbinden von Grafiken von Drittseiten wurde deaktiviert, bitte verwende{if !LANGUAGE_USE_INFORMAL_VARIANT}n Sie{/if} die Dateianhangs-Funktion.]]></item>
                <item name="wcf.editor.image.source.error.insecure"><![CDATA[Unsichere Adressen („http://“) wurden für Bilder deaktiviert, bitte verwende{if !LANGUAGE_USE_INFORMAL_VARIANT}n Sie{/if} nur sichere Adressen („https://“).]]></item>
                <item name="wcf.editor.image.source.error.invalid"><![CDATA[Der eingegebene Link ist ungültig.]]></item>
                <item name="wcf.editor.link.add"><![CDATA[Link einfügen]]></item>
index 6f4d92d0925b1ba9372f4403fd4e7e4ad18ac7f0..0b63e1611d8de6740dd3696c7a54cc7e949218b2 100644 (file)
@@ -3560,6 +3560,7 @@ Attachments:
                <item name="wcf.editor.image.float.left"><![CDATA[Left]]></item>
                <item name="wcf.editor.image.float.right"><![CDATA[Right]]></item>
                <item name="wcf.editor.image.source"><![CDATA[Source]]></item>
+               <item name="wcf.editor.image.source.error.blocked"><![CDATA[Images from external sources have been disabled, please use the file attachment function.]]></item>
                <item name="wcf.editor.image.source.error.insecure"><![CDATA[Insecure sources (“http://”) for images have been disabled, please use secure sources only (“https://”).]]></item>
                <item name="wcf.editor.image.source.error.invalid"><![CDATA[You have entered an invalid link.]]></item>
                <item name="wcf.editor.link.add"><![CDATA[Insert Link]]></item>