mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 00:32:42 +01:00
Enforce upload size limits and transport exceptions with appropriate response encoding
Summary: - When a user uploads an oversized file, throw an exception. - When an uncaught exception occurs during a Conduit request, return a Conduit response. - When an uncaught exception occurs during a non-workflow Ajax request, return an Ajax response. Test Plan: - Uploaded overlarge files. - Hit an exception page with ?__ajax__=1 and ?__conduit__=1 Reviewers: btrahan, vrana, jungejason Reviewed By: btrahan CC: aran Maniphest Tasks: T875, T788 Differential Revision: https://secure.phabricator.com/D2385
This commit is contained in:
parent
b80ea9e0e8
commit
7b5f47b17d
9 changed files with 67 additions and 5 deletions
2
externals/javelin
vendored
2
externals/javelin
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 748eac2b2fb3f210dc68506d2bc36f96a6b0d77f
|
||||
Subproject commit 1cdedbfc00a32f3167aa3bc14dd917cf886014ff
|
|
@ -455,10 +455,32 @@ class AphrontDefaultApplicationConfiguration
|
|||
}
|
||||
|
||||
public function handleException(Exception $ex) {
|
||||
$request = $this->getRequest();
|
||||
|
||||
// For Conduit requests, return a Conduit response.
|
||||
if ($request->isConduit()) {
|
||||
$response = new ConduitAPIResponse();
|
||||
$response->setErrorCode(get_class($ex));
|
||||
$response->setErrorInfo($ex->getMessage());
|
||||
|
||||
return id(new AphrontJSONResponse())
|
||||
->setContent($response->toDictionary());
|
||||
}
|
||||
|
||||
// For non-workflow requests, return a Ajax response.
|
||||
if ($request->isAjax() && !$request->isJavelinWorkflow()) {
|
||||
$response = new AphrontAjaxResponse();
|
||||
$response->setError(
|
||||
array(
|
||||
'code' => get_class($ex),
|
||||
'info' => $ex->getMessage(),
|
||||
));
|
||||
return $response;
|
||||
}
|
||||
|
||||
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
||||
|
||||
$user = $this->getRequest()->getUser();
|
||||
$user = $request->getUser();
|
||||
if (!$user) {
|
||||
// If we hit an exception very early, we won't have a user.
|
||||
$user = new PhabricatorUser();
|
||||
|
|
|
@ -10,9 +10,11 @@ phutil_require_module('phabricator', 'aphront/applicationconfiguration');
|
|||
phutil_require_module('phabricator', 'aphront/request');
|
||||
phutil_require_module('phabricator', 'aphront/response/ajax');
|
||||
phutil_require_module('phabricator', 'aphront/response/dialog');
|
||||
phutil_require_module('phabricator', 'aphront/response/json');
|
||||
phutil_require_module('phabricator', 'aphront/response/webpage');
|
||||
phutil_require_module('phabricator', 'applications/base/controller/404');
|
||||
phutil_require_module('phabricator', 'applications/base/controller/redirect');
|
||||
phutil_require_module('phabricator', 'applications/conduit/protocol/response');
|
||||
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||
phutil_require_module('phabricator', 'infrastructure/env');
|
||||
phutil_require_module('phabricator', 'view/control/table');
|
||||
|
|
|
@ -24,9 +24,15 @@
|
|||
*/
|
||||
final class AphrontRequest {
|
||||
|
||||
// NOTE: These magic request-type parameters are automatically included in
|
||||
// certain requests (e.g., by phabricator_render_form(), JX.Request,
|
||||
// JX.Workflow, and ConduitClient) and help us figure out what sort of
|
||||
// response the client expects.
|
||||
|
||||
const TYPE_AJAX = '__ajax__';
|
||||
const TYPE_FORM = '__form__';
|
||||
const TYPE_CONDUIT = '__conduit__';
|
||||
const TYPE_WORKFLOW = '__wflow__';
|
||||
|
||||
private $host;
|
||||
private $path;
|
||||
|
@ -171,6 +177,10 @@ final class AphrontRequest {
|
|||
return $this->getExists(self::TYPE_AJAX);
|
||||
}
|
||||
|
||||
final public function isJavelinWorkflow() {
|
||||
return $this->getExists(self::TYPE_WORKFLOW);
|
||||
}
|
||||
|
||||
final public function isConduit() {
|
||||
return $this->getExists(self::TYPE_CONDUIT);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,11 @@ final class AphrontAjaxResponse extends AphrontResponse {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setError($error) {
|
||||
$this->error = $error;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function buildResponseString() {
|
||||
$response = CelerityAPI::getStaticResourceResponse();
|
||||
$object = $response->buildAjaxResponse(
|
||||
|
|
|
@ -29,7 +29,7 @@ final class PhabricatorFileDropUploadController
|
|||
$data = file_get_contents('php://input');
|
||||
$name = $request->getStr('name');
|
||||
|
||||
$file = PhabricatorFile::newFromFileData(
|
||||
$file = PhabricatorFile::newFromXHRUpload(
|
||||
$data,
|
||||
array(
|
||||
'name' => $request->getStr('name'),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -35,6 +35,10 @@ final class PhabricatorFileUploadException extends Exception {
|
|||
"Unable to write file: failed to write to temporary directory.",
|
||||
UPLOAD_ERR_EXTENSION =>
|
||||
"Unable to upload: a PHP extension stopped the upload.",
|
||||
|
||||
-1000 =>
|
||||
"Uploaded file exceeds limit in Phabricator ".
|
||||
"'storage.upload-size-limit' configuration.",
|
||||
);
|
||||
|
||||
$message = idx($map, $code, "Upload failed: unknown error.");
|
||||
|
|
|
@ -66,6 +66,8 @@ final class PhabricatorFile extends PhabricatorFileDAO {
|
|||
throw new Exception("File size disagrees with uploaded size.");
|
||||
}
|
||||
|
||||
self::validateFileSize(strlen($file_data));
|
||||
|
||||
return $file_data;
|
||||
}
|
||||
|
||||
|
@ -82,8 +84,24 @@ final class PhabricatorFile extends PhabricatorFileDAO {
|
|||
return self::newFromFileData($file_data, $params);
|
||||
}
|
||||
|
||||
public static function newFromFileData($data, array $params = array()) {
|
||||
public static function newFromXHRUpload($data, array $params = array()) {
|
||||
self::validateFileSize(strlen($data));
|
||||
return self::newFromFileData($data, $params);
|
||||
}
|
||||
|
||||
private static function validateFileSize($size) {
|
||||
$limit = PhabricatorEnv::getEnvConfig('storage.upload-size-limit');
|
||||
if (!$limit) {
|
||||
return;
|
||||
}
|
||||
|
||||
$limit = phabricator_parse_bytes($limit);
|
||||
if ($size > $limit) {
|
||||
throw new PhabricatorFileUploadException(-1000);
|
||||
}
|
||||
}
|
||||
|
||||
public static function newFromFileData($data, array $params = array()) {
|
||||
$selector = PhabricatorEnv::newObjectFromConfig('storage.engine-selector');
|
||||
|
||||
$engines = $selector->selectStorageEngines($data, $params);
|
||||
|
|
|
@ -13,6 +13,7 @@ phutil_require_module('phabricator', 'applications/phid/constants');
|
|||
phutil_require_module('phabricator', 'applications/phid/storage/phid');
|
||||
phutil_require_module('phabricator', 'infrastructure/env');
|
||||
phutil_require_module('phabricator', 'infrastructure/util/hash');
|
||||
phutil_require_module('phabricator', 'view/utils');
|
||||
|
||||
phutil_require_module('phutil', 'error');
|
||||
phutil_require_module('phutil', 'error/aggregate');
|
||||
|
|
Loading…
Reference in a new issue