From 74c960dd0e64a883502426cd105e6e6606e85d8a Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Fri, 1 Feb 2019 17:14:38 +0100 Subject: [PATCH] Prevent adding/editing of images from blocked sources --- com.woltlab.wcf/templates/wysiwyg.tpl | 9 +++ .../redactor2/plugins/WoltLabImage.js | 59 +++++++++++++++++++ .../lib/system/bbcode/BBCodeHandler.class.php | 22 +++++++ wcfsetup/install/files/style/ui/redactor.scss | 9 +++ wcfsetup/install/lang/de.xml | 1 + wcfsetup/install/lang/en.xml | 1 + 6 files changed, 101 insertions(+) diff --git a/com.woltlab.wcf/templates/wysiwyg.tpl b/com.woltlab.wcf/templates/wysiwyg.tpl index c944243280..1aac1ff3f3 100644 --- a/com.woltlab.wcf/templates/wysiwyg.tpl +++ b/com.woltlab.wcf/templates/wysiwyg.tpl @@ -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}', @@ -245,6 +246,13 @@ 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}' } @@ -294,6 +302,7 @@ // set code redactor.code.start(content); + redactor.WoltLabImage.validateImages(); // set value redactor.core.textarea().val(redactor.clean.onSync(redactor.$editor.html())); diff --git a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabImage.js b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabImage.js index 4e5029d502..4f9aff26ab 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabImage.js +++ b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabImage.js @@ -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)); } }; }; diff --git a/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php b/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php index 5883adea9f..3a7b47807f 100644 --- a/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php +++ b/wcfsetup/install/files/lib/system/bbcode/BBCodeHandler.class.php @@ -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); + } } diff --git a/wcfsetup/install/files/style/ui/redactor.scss b/wcfsetup/install/files/style/ui/redactor.scss index 344fe092d1..ebad425236 100644 --- a/wcfsetup/install/files/style/ui/redactor.scss +++ b/wcfsetup/install/files/style/ui/redactor.scss @@ -601,3 +601,12 @@ min-height: 0 !important; } } + +.editorImageBlocked { + filter: brightness(25%); + transition: filter .12s linear; + + &:hover { + filter: brightness(75%); + } +} diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 017ad5735f..43da9e05a8 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -3614,6 +3614,7 @@ Dateianhänge: + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 6f4d92d092..0b63e1611d 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -3560,6 +3560,7 @@ Attachments: + -- 2.20.1