diff --git a/src/applications/files/config/PhabricatorFilesConfigOptions.php b/src/applications/files/config/PhabricatorFilesConfigOptions.php --- a/src/applications/files/config/PhabricatorFilesConfigOptions.php +++ b/src/applications/files/config/PhabricatorFilesConfigOptions.php @@ -212,6 +212,16 @@ 'must be installed and the "%s" binary must be available to '. 'the webserver for this to work.', 'convert')), + $this->newOption('files.maximum-file-size', 'int', 0) + ->setDescription( + pht( + 'You can set a limit for the maximum byte size for files uploaded. '. + 'Attempting to upload a file larger than the specified size will '. + 'cause the upload to fail. Values smaller than or equal to zero '. + 'will be ignored. Specify a value in bytes.')) + ->setSummary(pht('Global cap for files uploaded (bytes).')) + ->addExample(1048576, pht('Limit files to 1 MB')) + ->addExample(10485760, pht('Limit files to 10 MB')), ); } diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -327,12 +327,28 @@ return $file; } + private static function assertNotExceedingSizeLimit($size) { + + $max_file_size = PhabricatorEnv::getEnvConfig('files.maximum-file-size'); + $exceeds_limit = $max_file_size > 0 && $size > $max_file_size; + + if ($exceeds_limit) { + throw new Exception( + pht( + 'The file uploaded is bigger than the configured maximum file size'. + ' (%s).', + phutil_format_bytes($max_file_size))); + } + } + private static function buildFromFileData($data, array $params = array()) { + $size = strlen($data); + self::assertNotExceedingSizeLimit($size); + if (isset($params['storageEngines'])) { $engines = $params['storageEngines']; } else { - $size = strlen($data); $engines = PhabricatorFileStorageEngine::loadStorageEngines($size); if (!$engines) { diff --git a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php --- a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php +++ b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php @@ -603,4 +603,50 @@ pht('newChunkedFile returns a PhabricatorFile')); } + public function newRandomFile(int $size) { + $engine = new PhabricatorTestStorageEngine(); + + $data = Filesystem::readRandomCharacters($size); + $params = array( + 'name' => 'test.dat', + 'storageEngines' => array( + $engine, + ), + ); + + PhabricatorFile::newFromFileData($data, $params); + } + + public function testMaximumFileSize() { + $env = PhabricatorEnv::beginScopedEnv(); + + $env->overrideEnvConfig('files.maximum-file-size', -1024); + $this->tryTestCases( + array( + '512 KB' => 512, + '1 MB' => 1024, + '2 MB' => 2048, + ), + array( + true, + true, + true, + ), + array($this, 'newRandomFile')); + + $env->overrideEnvConfig('files.maximum-file-size', 1024); + $this->tryTestCases( + array( + '512 KB' => 512, + '1 MB' => 1024, + '2 MB' => 2048, + ), + array( + true, + true, + false, + ), + array($this, 'newRandomFile')); + } + }