diff --git a/conf/default.conf.php b/conf/default.conf.php index 0d13f5e895..0089844e88 100644 --- a/conf/default.conf.php +++ b/conf/default.conf.php @@ -58,6 +58,9 @@ return array( // configuration file to directly set $_SERVER['HTTPS'] to the correct value. 'security.require-https' => false, + // Is Phabricator permitted to make outbound HTTP requests? + 'security.allow-outbound-http' => true, + // -- Internationalization -------------------------------------------------- // diff --git a/src/applications/config/option/PhabricatorSecurityConfigOptions.php b/src/applications/config/option/PhabricatorSecurityConfigOptions.php index 2ad8fe7c7d..a841611c16 100644 --- a/src/applications/config/option/PhabricatorSecurityConfigOptions.php +++ b/src/applications/config/option/PhabricatorSecurityConfigOptions.php @@ -154,6 +154,18 @@ final class PhabricatorSecurityConfigOptions "inline. This has mild security implications (you'll leak ". "referrers to YouTube) and is pretty silly (but sort of ". "awesome).")), + $this->newOption('security.allow-outbound-http', 'bool', true) + ->setBoolOptions( + array( + pht("Allow"), + pht("Disallow"), + )) + ->setSummary( + pht("Allow outbound HTTP requests")) + ->setDescription( + pht( + "If you enable this, you are allowing Phabricator to potentially ". + "make requests to external servers.")), ); } diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index fee670dd66..1ddbb34c7a 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -333,7 +333,12 @@ final class PhabricatorFile extends PhabricatorFileDAO } - public static function newFromFileDownload($uri, array $params) { + public static function newFromFileDownload($uri, array $params = array()) { + // Make sure we're allowed to make a request first + if (!PhabricatorEnv::getEnvConfig('security.allow-outbound-http')) { + throw new Exception("Outbound HTTP requests are disabled!"); + } + $uri = new PhutilURI($uri); $protocol = $uri->getProtocol(); @@ -352,6 +357,10 @@ final class PhabricatorFile extends PhabricatorFileDAO ->setTimeout($timeout) ->resolvex(); + $params = $params + array( + 'name' => basename($uri), + ); + return self::newFromFileData($file_data, $params); } diff --git a/src/applications/macro/controller/PhabricatorMacroEditController.php b/src/applications/macro/controller/PhabricatorMacroEditController.php index e48461a057..19779331bd 100644 --- a/src/applications/macro/controller/PhabricatorMacroEditController.php +++ b/src/applications/macro/controller/PhabricatorMacroEditController.php @@ -24,6 +24,7 @@ final class PhabricatorMacroEditController $e_name = true; $e_file = true; $file = null; + $can_fetch = PhabricatorEnv::getEnvConfig('security.allow-outbound-http'); $request = $this->getRequest(); $user = $request->getUser(); @@ -57,6 +58,17 @@ final class PhabricatorMacroEditController 'name' => $request->getStr('name'), 'authorPHID' => $user->getPHID(), )); + } else if ($request->getStr('url')) { + try { + $file = PhabricatorFile::newFromFileDownload( + $request->getStr('url'), + array( + 'name' => $request->getStr('name'), + 'authorPHID' => $user->getPHID(), + )); + } catch (Exception $ex) { + $errors[] = pht('Could not fetch URL: %s', $ex->getMessage()); + } } else if ($request->getStr('phid')) { $file = id(new PhabricatorFile())->loadOneWhere( 'phid = %s', @@ -167,6 +179,15 @@ final class PhabricatorMacroEditController $other_label = pht('File'); } + if ($can_fetch) { + $form->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('URL')) + ->setName('url') + ->setValue($request->getStr('url')) + ->setError($e_file)); + } + $form->appendChild( id(new AphrontFormFileControl()) ->setLabel($other_label) @@ -221,7 +242,18 @@ final class PhabricatorMacroEditController $upload_form = id(new AphrontFormView()) ->setFlexible(true) ->setEncType('multipart/form-data') - ->setUser($request->getUser()) + ->setUser($request->getUser()); + + if ($can_fetch) { + $upload_form + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('URL')) + ->setName('url') + ->setValue($request->getStr('url'))); + } + + $upload_form ->appendChild( id(new AphrontFormFileControl()) ->setLabel(pht('File')) diff --git a/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php b/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php index a668967dba..e96c78bd51 100644 --- a/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php @@ -205,17 +205,22 @@ final class PhabricatorSettingsPanelProfile ->setLabel('Change Image') ->setName('image') ->setError($e_image) - ->setCaption('Supported formats: '.implode(', ', $supported_formats))) - ->appendChild( + ->setCaption( + 'Supported formats: '.implode(', ', $supported_formats))); + + if (PhabricatorEnv::getEnvConfig('security.allow-outbound-http')) { + $form->appendChild( id(new AphrontFormTextControl()) ->setLabel('Import Gravatar') ->setName('gravatar') ->setError($e_image) - ->setCaption('Enter gravatar email address')) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue('Save') - ->addCancelButton('/p/'.$user->getUsername().'/')); + ->setCaption('Enter gravatar email address')); + } + + $form->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue('Save') + ->addCancelButton('/p/'.$user->getUsername().'/')); $panel = new AphrontPanelView(); $panel->setHeader('Edit Profile Details');