From d263c5a7ca6c1c4750a5744b34046240b9245393 Mon Sep 17 00:00:00 2001 From: Roly Rudy Gutierrez Pinto Date: Tue, 28 Aug 2018 09:34:11 -0400 Subject: [PATCH 1/6] HOR-4492 --- composer.json | 3 +- framework/src/Maveriks/WebApplication.php | 23 ++- gulliver/system/class.g.php | 21 ++- .../translations/english/processmaker.en.po | 18 ++ workflow/engine/controllers/adminProxy.php | 10 +- workflow/engine/controllers/pmTablesProxy.php | 10 ++ workflow/engine/data/mysql/insert.sql | 3 + .../methods/appFolder/appFolderAjax.php | 22 ++- .../methods/enterprise/addonsStoreAction.php | 14 ++ .../methods/enterprise/pluginsImportFile.php | 4 + .../processes/processes_Import_Ajax.php | 10 ++ .../methods/processes/processes_doUpload.php | 25 ++- .../engine/methods/setup/languages_Import.php | 5 + .../methods/setup/pluginsImportFile.php | 4 + workflow/engine/methods/setup/skin_Ajax.php | 4 + .../BusinessModel/Cases/InputDocument.php | 55 +++--- .../BusinessModel/FilesManager.php | 5 - .../src/ProcessMaker/BusinessModel/Light.php | 10 -- .../engine/src/ProcessMaker/Core/System.php | 4 +- .../src/ProcessMaker/Services/Api/Light.php | 9 +- .../Services/Api/Project/FilesManager.php | 19 +- .../ProcessMaker/Validation/Exception429.php | 23 +++ .../src/ProcessMaker/Validation/Rule.php | 137 ++++++++++++++ .../Validation/ValidationUploadedFiles.php | 167 ++++++++++++++++++ .../src/ProcessMaker/Validation/Validator.php | 138 +++++++++++++++ workflow/engine/templates/admin/pmLogo.js | 5 +- workflow/public_html/sysGeneric.php | 7 +- 27 files changed, 658 insertions(+), 97 deletions(-) create mode 100644 workflow/engine/src/ProcessMaker/Validation/Exception429.php create mode 100644 workflow/engine/src/ProcessMaker/Validation/Rule.php create mode 100644 workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php create mode 100644 workflow/engine/src/ProcessMaker/Validation/Validator.php diff --git a/composer.json b/composer.json index 6c2931319..b13829995 100644 --- a/composer.json +++ b/composer.json @@ -87,7 +87,8 @@ "thirdparty/pake/pakeFunction.php", "thirdparty/HTMLPurifier/HTMLPurifier.auto.php", "workflow/engine/classes/class.pmFunctions.php", - "workflow/engine/src/ProcessMaker/Util/helpers.php" + "workflow/engine/src/ProcessMaker/Util/helpers.php", + "framework/src/Maveriks/Extension/Restler/UploadFormat.php" ] }, "autoload-dev": { diff --git a/framework/src/Maveriks/WebApplication.php b/framework/src/Maveriks/WebApplication.php index fe9632e83..9344be9dd 100644 --- a/framework/src/Maveriks/WebApplication.php +++ b/framework/src/Maveriks/WebApplication.php @@ -3,14 +3,16 @@ namespace Maveriks; use Bootstrap; +use G; +use Illuminate\Foundation\Http\Kernel; +use Luracast\Restler\Format\UploadFormat; +use Luracast\Restler\RestException; use Maveriks\Util; use ProcessMaker\Core\System; use ProcessMaker\Plugins\PluginRegistry; use ProcessMaker\Services; use ProcessMaker\Services\Api; -use Luracast\Restler\RestException; -use Illuminate\Foundation\Http\Kernel; -use G; +use ProcessMaker\Validation\ValidationUploadedFiles; /** * Web application bootstrap @@ -252,8 +254,6 @@ class WebApplication */ protected function initRest($uri, $version, $multipart = false) { - require_once $this->rootDir . "/framework/src/Maveriks/Extension/Restler/UploadFormat.php"; - // $servicesDir contains directory where Services Classes are allocated $servicesDir = $this->workflowDir . 'engine' . DS . 'src' . DS . 'ProcessMaker' . DS . 'Services' . DS; // $apiDir - contains directory to scan classes and add them to Restler @@ -331,6 +331,19 @@ class WebApplication $this->rest->setOverridingFormats('JsonFormat', 'UploadFormat'); + UploadFormat::$customValidationFunction = function($target) { + $validator = ValidationUploadedFiles::getValidationUploadedFiles()->runRules([ + 'filename' => $target['name'], + 'path' => $target['tmp_name'] + ]); + if ($validator->fails()) { + if ($validator->getStatus() === 429) { + throw new RestException($validator->getStatus()); + } + } + return true; + }; + // scan all api directory to find api classes $classesList = Util\Common::rglob($apiDir . "/*"); diff --git a/gulliver/system/class.g.php b/gulliver/system/class.g.php index f7bf48ac6..6392a4696 100644 --- a/gulliver/system/class.g.php +++ b/gulliver/system/class.g.php @@ -4,6 +4,7 @@ use ProcessMaker\Core\System; use ProcessMaker\AuditLog\AuditLog; use ProcessMaker\Plugins\PluginRegistry; use ProcessMaker\Services\OAuth2\Server; +use ProcessMaker\Validation\ValidationUploadedFiles; class G { @@ -1183,7 +1184,7 @@ class G \Bootstrap::registerMonologPhpUploadExecution('phpExecution', 200, 'Php Execution', $filename); require_once($filename); } else { - $message = G::LoadTranslation('THE_PHP_FILES_EXECUTION_WAS_DISABLED'); + $message = G::LoadTranslation('ID_THE_PHP_FILES_EXECUTION_WAS_DISABLED'); \Bootstrap::registerMonologPhpUploadExecution('phpExecution', 550, $message, $filename); echo $message; } @@ -5488,6 +5489,16 @@ class G */ public static function verifyInputDocExtension($InpDocAllowedFiles, $fileName, $filesTmpName) { + $error = null; + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) use(&$error) { + $error = new stdclass(); + $error->status = false; + $error->message = $validator->getMessage(); + }); + if (!is_null($error)) { + return $error; + } + // Initialize variables $res = new stdclass(); $res->status = false; @@ -5497,14 +5508,6 @@ class G $aux = pathinfo($fileName); $fileExtension = isset($aux['extension']) ? strtolower($aux['extension']) : ''; - if (\Bootstrap::getDisablePhpUploadExecution() === 1 && $fileExtension === 'php') { - $message = \G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - \Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, $fileName); - $res->status = false; - $res->message = $message; - return $res; - } - // If required extension is *.* don't validate if (in_array('*', $allowedTypes)) { $res->status = true; diff --git a/workflow/engine/content/translations/english/processmaker.en.po b/workflow/engine/content/translations/english/processmaker.en.po index 0114a1a82..68431bc17 100644 --- a/workflow/engine/content/translations/english/processmaker.en.po +++ b/workflow/engine/content/translations/english/processmaker.en.po @@ -24851,12 +24851,24 @@ msgstr "The default configuration was not defined" msgid "The change might cause data loss in the PM table. Do you want to continue?" msgstr "The change might cause data loss in the PM table. Do you want to continue?" +# TRANSLATION +# LABEL/ID_THE_PHP_FILES_EXECUTION_WAS_DISABLED +#: LABEL/ID_THE_PHP_FILES_EXECUTION_WAS_DISABLED +msgid "The PHP files execution was disabled please contact the system administrator." +msgstr "The PHP files execution was disabled please contact the system administrator." + # TRANSLATION # LABEL/ID_THE_REASON_REASSIGN_USER_EMPTY #: LABEL/ID_THE_REASON_REASSIGN_USER_EMPTY msgid "Please complete the reassign reason." msgstr "Please complete the reassign reason." +# TRANSLATION +# LABEL/ID_THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED +#: LABEL/ID_THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED +msgid "The upload of PHP files was disabled please contact the system administrator." +msgstr "The upload of PHP files was disabled please contact the system administrator." + # TRANSLATION # LABEL/ID_THE_USERNAME_EMAIL_IS_INCORRECT #: LABEL/ID_THE_USERNAME_EMAIL_IS_INCORRECT @@ -25181,6 +25193,12 @@ msgstr "Today" msgid "Tools" msgstr "Tools" +# TRANSLATION +# LABEL/ID_TOO_MANY_REQUESTS +#: LABEL/ID_TOO_MANY_REQUESTS +msgid "Too many requests" +msgstr "Too many requests" + # TRANSLATION # LABEL/ID_TOP_MARGIN #: LABEL/ID_TOP_MARGIN diff --git a/workflow/engine/controllers/adminProxy.php b/workflow/engine/controllers/adminProxy.php index 6c86f2c06..4809054bc 100644 --- a/workflow/engine/controllers/adminProxy.php +++ b/workflow/engine/controllers/adminProxy.php @@ -2,6 +2,7 @@ use ProcessMaker\Core\System; use ProcessMaker\Plugins\PluginRegistry; +use ProcessMaker\Validation\ValidationUploadedFiles; /** * adminProxy.php @@ -1025,7 +1026,14 @@ class adminProxy extends HttpProxyController */ public function uploadImage() { - //!dataSystem + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + echo G::json_encode([ + 'success' => true, + 'failed' => true, + 'message' => $validator->getMessage() + ]); + exit(); + }); $filter = new InputFilter(); $_SERVER["REQUEST_URI"] = $filter->xssFilterHard($_SERVER["REQUEST_URI"]); diff --git a/workflow/engine/controllers/pmTablesProxy.php b/workflow/engine/controllers/pmTablesProxy.php index ed62a64a1..4507ea2ff 100644 --- a/workflow/engine/controllers/pmTablesProxy.php +++ b/workflow/engine/controllers/pmTablesProxy.php @@ -8,6 +8,8 @@ */ use ProcessMaker\Core\System; +use ProcessMaker\Validation\Exception429; +use ProcessMaker\Validation\ValidationUploadedFiles; header("Content-type: text/html;charset=utf-8"); require_once 'classes/model/AdditionalTables.php'; @@ -723,6 +725,9 @@ class pmTablesProxy extends HttpProxyController } try { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + throw new Exception429($validator->getMessage()); + }); $result = new stdClass(); $errors = ''; $fromConfirm = false; @@ -891,6 +896,11 @@ class pmTablesProxy extends HttpProxyController } $result->message = $msg; + } catch (Exception429 $e) { + $result = new stdClass(); + $result->success = false; + $result->errorType = 'notice'; + $result->message = $e->getMessage(); } catch (Exception $e) { $result = new stdClass(); $result->fromAdmin = $fromAdmin; diff --git a/workflow/engine/data/mysql/insert.sql b/workflow/engine/data/mysql/insert.sql index b531f3398..346bffb5d 100644 --- a/workflow/engine/data/mysql/insert.sql +++ b/workflow/engine/data/mysql/insert.sql @@ -61035,7 +61035,9 @@ INSERT INTO TRANSLATION (TRN_CATEGORY,TRN_ID,TRN_LANG,TRN_VALUE,TRN_UPDATE_DATE ( 'LABEL','ID_THE_APPLICATION_IS_NOT_CANCELED','en','Error: The application {0} is not canceled.','2016-06-15') , ( 'LABEL','ID_THE_DEFAULT_CONFIGURATION','en','The default configuration was not defined','2016-11-16') , ( 'LABEL','ID_THE_NAME_CHANGE_MAY_CAUSE_DATA_LOSS','en','The change might cause data loss in the PM table. Do you want to continue?','2017-03-30') , +( 'LABEL','ID_THE_PHP_FILES_EXECUTION_WAS_DISABLED','en','The PHP files execution was disabled please contact the system administrator.','2018-04-20') , ( 'LABEL','ID_THE_REASON_REASSIGN_USER_EMPTY','en','Please complete the reassign reason.','2016-10-20') , +( 'LABEL','ID_THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED','en','The upload of PHP files was disabled please contact the system administrator.','2018-04-20') , ( 'LABEL','ID_THE_USERNAME_EMAIL_IS_INCORRECT','en','The username or email is incorrect','2018-01-18') , ( 'LABEL','ID_THIS_MONTH','en','This Month','2014-01-15') , ( 'LABEL','ID_THIS_QUARTER','en','This quarter','2014-01-15') , @@ -61092,6 +61094,7 @@ INSERT INTO TRANSLATION (TRN_CATEGORY,TRN_ID,TRN_LANG,TRN_VALUE,TRN_UPDATE_DATE ( 'LABEL','ID_TO','en','To','2014-01-15') , ( 'LABEL','ID_TODAY','en','Today','2014-01-15') , ( 'LABEL','ID_TOOLS','en','Tools','2014-01-15') , +( 'LABEL','ID_TOO_MANY_REQUESTS','en','Too many requests','2018-04-25') , ( 'LABEL','ID_TOP_MARGIN','en','Top Margin','2014-01-15') , ( 'LABEL','ID_TOTAL_CASES','en','Total Cases','2014-01-15') , ( 'LABEL','ID_TOTAL_CASES_REASSIGNED','en','Total Cases Reassigned','2014-01-15') , diff --git a/workflow/engine/methods/appFolder/appFolderAjax.php b/workflow/engine/methods/appFolder/appFolderAjax.php index b75f038b5..33072d250 100644 --- a/workflow/engine/methods/appFolder/appFolderAjax.php +++ b/workflow/engine/methods/appFolder/appFolderAjax.php @@ -1,6 +1,7 @@ xssFilterHard($_POST); @@ -1445,6 +1446,15 @@ function checkTree($uidOriginFolder, $uidNewFolder) */ function uploadExternalDocument() { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + $response = [ + 'error' => $validator->getMessage(), + 'message' => $validator->getMessage(), + 'success' => false + ]; + print_r(G::json_encode($response)); + die(); + }); $response = []; $response['action'] = $_POST['action'] . " - " . $_POST['option']; $response['error'] = "error"; @@ -1531,18 +1541,6 @@ function uploadExternalDocument() //Read. Instance Document classes if (!empty($quequeUpload)) { - foreach ($quequeUpload as $key => $fileObj) { - $extension = pathinfo($fileObj['fileName'], PATHINFO_EXTENSION); - if (\Bootstrap::getDisablePhpUploadExecution() === 1 && $extension === 'php') { - $message = \G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - \Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, $fileObj['fileName']); - $response['error'] = $message; - $response['message'] = $message; - $response['success'] = false; - print_r(G::json_encode($response)); - exit(); - } - } $docUid = $_POST['docUid']; $appDocUid = isset($_POST['APP_DOC_UID']) ? $_POST['APP_DOC_UID'] : ""; $docVersion = isset($_POST['docVersion']) ? $_POST['docVersion'] : ""; diff --git a/workflow/engine/methods/enterprise/addonsStoreAction.php b/workflow/engine/methods/enterprise/addonsStoreAction.php index 9dc5133af..7197606cb 100644 --- a/workflow/engine/methods/enterprise/addonsStoreAction.php +++ b/workflow/engine/methods/enterprise/addonsStoreAction.php @@ -2,6 +2,8 @@ use ProcessMaker\Core\System; use ProcessMaker\Plugins\PluginRegistry; +use ProcessMaker\Validation\Exception429; +use ProcessMaker\Validation\ValidationUploadedFiles; function runBgProcessmaker($task, $log) { @@ -16,6 +18,9 @@ function runBgProcessmaker($task, $log) } try { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + throw new Exception429($validator->getMessage()); + }); if (isset($_REQUEST["action"])) { $action = $_REQUEST["action"]; } else { @@ -312,6 +317,15 @@ try { $result["addons"] = array(); } G::outRes(G::json_encode($result)); +} catch (Exception429 $e) { + $token = strtotime("now"); + PMException::registerErrorLog($e, $token); + G::outRes( + G::json_encode(array( + "success" => false, + "errors" => $e->getMessage() + )) + ); } catch (Exception $e) { $token = strtotime("now"); PMException::registerErrorLog($e, $token); diff --git a/workflow/engine/methods/enterprise/pluginsImportFile.php b/workflow/engine/methods/enterprise/pluginsImportFile.php index 490d3f204..64939795f 100644 --- a/workflow/engine/methods/enterprise/pluginsImportFile.php +++ b/workflow/engine/methods/enterprise/pluginsImportFile.php @@ -27,6 +27,7 @@ global $RBAC; use ProcessMaker\Plugins\PluginRegistry; +use ProcessMaker\Validation\ValidationUploadedFiles; $RBAC->requirePermissions("PM_SETUP_ADVANCE"); require_once PATH_CORE . 'methods' . PATH_SEP . 'enterprise' . PATH_SEP . 'enterprise.php'; @@ -35,6 +36,9 @@ $response = array(); $status = 1; try { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + throw new Exception($validator->getMessage()); + }); if (!isset($_FILES["form"]["error"]["PLUGIN_FILENAME"]) || $_FILES["form"]["error"]["PLUGIN_FILENAME"] == 1) { $str = "There was an error uploading the file, probably the file size if greater than upload_max_filesize parameter in php.ini, please check this parameter and try again."; diff --git a/workflow/engine/methods/processes/processes_Import_Ajax.php b/workflow/engine/methods/processes/processes_Import_Ajax.php index 870db04eb..0f724bc58 100644 --- a/workflow/engine/methods/processes/processes_Import_Ajax.php +++ b/workflow/engine/methods/processes/processes_Import_Ajax.php @@ -23,6 +23,16 @@ */ use \ProcessMaker\Importer\XmlImporter; +use ProcessMaker\Validation\ValidationUploadedFiles; + +ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + echo G::json_encode([ + 'status' => 'ERROR', + 'success' => true, + 'catchMessage' => $validator->getMessage() + ]); + exit(); +}); ini_set("max_execution_time", 0); $affectedGroups = array(); diff --git a/workflow/engine/methods/processes/processes_doUpload.php b/workflow/engine/methods/processes/processes_doUpload.php index 07cdf06e4..db0077ef9 100644 --- a/workflow/engine/methods/processes/processes_doUpload.php +++ b/workflow/engine/methods/processes/processes_doUpload.php @@ -1,5 +1,7 @@ userCanAccess('PM_FACTORY') == 1) { @@ -25,26 +27,23 @@ if ($RBAC->userCanAccess('PM_FACTORY') == 1) { } } - $fileName = $_FILES['form']['name']; - $canUploadPhpFile = true; - $extension = pathinfo($fileName, PATHINFO_EXTENSION); - if (\Bootstrap::getDisablePhpUploadExecution() === 1 && $extension === 'php') { - $message = \G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - \Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, $fileName); - $canUploadPhpFile = false; - } + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + $response = [ + 'result' => 0, + 'msg' => $validator->getMessage() + ]; + print_r(G::json_encode($response)); + die(); + }); - if ($_FILES['form']['error'] == "0" && $canUploadPhpFile) { + $fileName = $_FILES['form']['name']; + if ($_FILES['form']['error'] == "0") { G::uploadFile($_FILES['form']['tmp_name'], $sDirectory, $fileName); $msg = "Uploaded (" . (round((filesize($sDirectory . $fileName) / 1024) * 10) / 10) . " kb)"; $result = 1; } else { $msg = "Failed"; - if ($canUploadPhpFile === false) { - $msg = $message; - } $result = 0; } - echo "{'result': $result, 'msg':'$msg'}"; } diff --git a/workflow/engine/methods/setup/languages_Import.php b/workflow/engine/methods/setup/languages_Import.php index ce808315b..1a1f3fa55 100644 --- a/workflow/engine/methods/setup/languages_Import.php +++ b/workflow/engine/methods/setup/languages_Import.php @@ -2,6 +2,8 @@ require_once "classes/model/Language.php"; +use ProcessMaker\Validation\ValidationUploadedFiles; + global $RBAC; $access = $RBAC->userCanAccess('PM_SETUP_ADVANCE'); @@ -24,6 +26,9 @@ if ($access != 1) { $result = new stdClass(); try { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + throw new Exception($validator->getMessage()); + }); //if the xmlform path is writeable if (!is_writable(PATH_XMLFORM)) { throw new Exception(G::LoadTranslation('IMPORT_LANGUAGE_ERR_NO_WRITABLE')); diff --git a/workflow/engine/methods/setup/pluginsImportFile.php b/workflow/engine/methods/setup/pluginsImportFile.php index 3cb70b2d0..8ddce45fa 100644 --- a/workflow/engine/methods/setup/pluginsImportFile.php +++ b/workflow/engine/methods/setup/pluginsImportFile.php @@ -26,11 +26,15 @@ use ProcessMaker\Core\System; use ProcessMaker\Plugins\PluginRegistry; +use ProcessMaker\Validation\ValidationUploadedFiles; global $RBAC; $RBAC->requirePermissions('PM_SETUP_ADVANCE'); try { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + throw new Exception($validator->getMessage()); + }); //load the variables if (!isset($_FILES['form']['error']['PLUGIN_FILENAME']) || $_FILES['form']['error']['PLUGIN_FILENAME'] == 1) { throw (new Exception(G::loadTranslation('ID_ERROR_UPLOADING_PLUGIN_FILENAME'))); diff --git a/workflow/engine/methods/setup/skin_Ajax.php b/workflow/engine/methods/setup/skin_Ajax.php index d939fac45..ad47b31bb 100644 --- a/workflow/engine/methods/setup/skin_Ajax.php +++ b/workflow/engine/methods/setup/skin_Ajax.php @@ -1,6 +1,7 @@ dispach(function($validator) { + throw new Exception($validator->getMessage()); + }); if (! isset( $_FILES['uploadedFile'] )) { throw (new Exception( G::LoadTranslation( 'ID_SKIN_FILE_REQUIRED' ) )); } diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/Cases/InputDocument.php b/workflow/engine/src/ProcessMaker/BusinessModel/Cases/InputDocument.php index f8e177c1e..1b73cb143 100644 --- a/workflow/engine/src/ProcessMaker/BusinessModel/Cases/InputDocument.php +++ b/workflow/engine/src/ProcessMaker/BusinessModel/Cases/InputDocument.php @@ -1,28 +1,29 @@ dispach(function($validator) { + G::SendMessageText($validator->getMessage(), "ERROR"); + $url = explode("sys" . config("system.workspace"), $_SERVER['HTTP_REFERER']); + G::header("location: " . "/sys" . config("system.workspace") . $url[1]); + die(); + }); $arrayField = array(); $arrayFileName = array(); $arrayFileTmpName = array(); @@ -1034,16 +1041,6 @@ class InputDocument $aFields = array("APP_UID" => $appUid, "DEL_INDEX" => $delIndex, "USR_UID" => $userUid, "DOC_UID" => -1, "APP_DOC_TYPE" => "ATTACHED", "APP_DOC_CREATE_DATE" => date("Y-m-d H:i:s"), "APP_DOC_COMMENT" => "", "APP_DOC_TITLE" => "", "APP_DOC_FILENAME" => $arrayFileName[$i], "APP_DOC_FIELDNAME" => $fieldName); } - $sExtension = pathinfo($aFields["APP_DOC_FILENAME"]); - if (Bootstrap::getDisablePhpUploadExecution() === 1 && $sExtension["extension"] === 'php') { - $message = G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, 'processmaker.log'); - G::SendMessageText($message, "ERROR"); - $backUrlObj = explode("sys" . config("system.workspace"), $_SERVER['HTTP_REFERER']); - G::header("location: " . "/sys" . config("system.workspace") . $backUrlObj[1]); - die(); - } - $oAppDocument = new AppDocument(); $oAppDocument->create($aFields); diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/FilesManager.php b/workflow/engine/src/ProcessMaker/BusinessModel/FilesManager.php index 2a91bcde0..c1260a5dd 100644 --- a/workflow/engine/src/ProcessMaker/BusinessModel/FilesManager.php +++ b/workflow/engine/src/ProcessMaker/BusinessModel/FilesManager.php @@ -196,11 +196,6 @@ class FilesManager if ($extention == '.exe') { throw new \Exception(\G::LoadTranslation('ID_FILE_UPLOAD_INCORRECT_EXTENSION')); } - if (\Bootstrap::getDisablePhpUploadExecution() === 1 && $extention === '.php' && !$isImport) { - $message = \G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - \Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, $aData['prf_filename']); - throw new \Exception($message); - } break; default: $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectory . $aData['prf_filename']; diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/Light.php b/workflow/engine/src/ProcessMaker/BusinessModel/Light.php index eaeba18ec..fda4a306e 100644 --- a/workflow/engine/src/ProcessMaker/BusinessModel/Light.php +++ b/workflow/engine/src/ProcessMaker/BusinessModel/Light.php @@ -995,16 +995,6 @@ class Light $confEnvSetting = $config->getFormats(); $user = new Users(); foreach ($requestData as $k => $file) { - $ext = pathinfo($file['name'], PATHINFO_EXTENSION); - if (Bootstrap::getDisablePhpUploadExecution() === 1 && $ext === 'php') { - $message = G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, $file['name']); - $response[$k]['error'] = array( - "code" => "400", - "message" => $message - ); - continue; - } $cases = new Cases(); $delIndex = $cases->getCurrentDelegation($appUid, $userUid); $docUid = !empty($file['docUid']) ? $file['docUid'] : -1; diff --git a/workflow/engine/src/ProcessMaker/Core/System.php b/workflow/engine/src/ProcessMaker/Core/System.php index 1a5f7aea3..5e5475ace 100644 --- a/workflow/engine/src/ProcessMaker/Core/System.php +++ b/workflow/engine/src/ProcessMaker/Core/System.php @@ -66,7 +66,9 @@ class System 'logging_level' => 'INFO', 'smtp_timeout' => 20, 'google_map_api_key' => '', - 'google_map_signature' => '' + 'google_map_signature' => '', + 'logging_level' => 'INFO', + 'upload_attempts_limit_per_user' => '60,1' ); /** diff --git a/workflow/engine/src/ProcessMaker/Services/Api/Light.php b/workflow/engine/src/ProcessMaker/Services/Api/Light.php index 11f126b25..7dec46cf7 100644 --- a/workflow/engine/src/ProcessMaker/Services/Api/Light.php +++ b/workflow/engine/src/ProcessMaker/Services/Api/Light.php @@ -23,6 +23,7 @@ use ProcessMaker\Project\Adapter; use ProcessMaker\Services\Api; use ProcessMaker\Services\Api\Project\Activity\Step; use ProcessMaker\Util\DateTime; +use ProcessMaker\Validation\Exception429; use RBAC; use stdclass; use StepPeer; @@ -1466,10 +1467,11 @@ class Light extends Api $userUid = $this->getUserId(); $oMobile = new BusinessModelLight(); $filesUids = $oMobile->postUidUploadFiles($userUid, $app_uid, $request_data); + } catch (Exception429 $e) { + throw new RestException($e->getStatus()); } catch (Exception $e) { - throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); + throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } - return $filesUids; } @@ -1501,10 +1503,11 @@ class Light extends Api $userUid = $this->getUserId(); $oMobile = new BusinessModelLight(); $response = $oMobile->documentUploadFiles($userUid, $app_uid, $app_doc_uid, $request_data); + } catch (Exception429 $e) { + throw new RestException($e->getStatus()); } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } - return $response; } diff --git a/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php b/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php index 043ec9326..876cd15cd 100644 --- a/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php +++ b/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php @@ -1,8 +1,11 @@ getUserId(); $request_data = (array)($request_data); $request_data = array_merge(array('prf_content' => $prf_content ), $request_data); - $filesManager = new \ProcessMaker\BusinessModel\FilesManager(); + $filesManager = new FilesManagerBusinessModel(); $arrayData = $filesManager->addProcessFilesManager($prj_uid, $userUid, $request_data); //Response $response = $arrayData; - } catch (\Exception $e) { + } catch (Exception429 $e) { + throw new RestException($e->getStatus()); + } catch (Exception $e) { //response throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } @@ -85,11 +90,13 @@ class FilesManager extends Api public function doPostProcessFilesManagerUpload($prj_uid, $prf_uid) { try { - $filesManager = new \ProcessMaker\BusinessModel\FilesManager(); + $filesManager = new FilesManagerBusinessModel(); $sData = $filesManager->uploadProcessFilesManager($prj_uid, $prf_uid); //Response $response = $sData; - } catch (\Exception $e) { + } catch (Exception429 $e) { + throw new RestException($e->getStatus()); + } catch (Exception $e) { //response throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } diff --git a/workflow/engine/src/ProcessMaker/Validation/Exception429.php b/workflow/engine/src/ProcessMaker/Validation/Exception429.php new file mode 100644 index 000000000..200844d39 --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Validation/Exception429.php @@ -0,0 +1,23 @@ +status; + } +} diff --git a/workflow/engine/src/ProcessMaker/Validation/Rule.php b/workflow/engine/src/ProcessMaker/Validation/Rule.php new file mode 100644 index 000000000..7d2610b14 --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Validation/Rule.php @@ -0,0 +1,137 @@ +data; + } + + /** + * Get the Closure function. + * @return Closure + */ + function getCallback() + { + return $this->callback; + } + + /** + * Gets the Closure function that applies the validation rule. + * @return Closure + */ + function getCallbackLog() + { + return $this->callbackLog; + } + + /** + * Get the message to be saved in the log if the rule is not fulfilled. + * @return string + */ + function getMessage() + { + return $this->message; + } + + /** + * Get status code. + * @return int + */ + function getStatus() + { + return $this->status; + } + + /** + * Registers the data and the Closure function that contains the validation + * rule. + * @param array $data + * @param Closure $callback + * @return Rule + */ + public function validate($data, $callback = null) + { + $this->data = (object) $data; + if (is_callable($callback)) { + $this->callback = $callback; + } + return $this; + } + + /** + * Registers the customized message in case the validation rule is not met. + * @param string $message + * @return Rule + */ + public function message($message = "") + { + $this->message = $message; + return $this; + } + + /** + * Set status code. + * @param int $status + * @return $this + */ + function status($status = 0) + { + $this->status = $status; + return $this; + } + + /** + * Registers the Closure function in case the validation rule is not met. + * @param Closure $callback + * @return Rule + */ + public function log($callback = null) + { + if (is_callable($callback)) { + $this->callbackLog = $callback; + } + return $this; + } +} diff --git a/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php b/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php new file mode 100644 index 000000000..05146777b --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php @@ -0,0 +1,167 @@ +addRule() + ->validate($file, function($file) { + $filesystem = new Filesystem(); + $extension = $filesystem->extension($file->filename); + + return Bootstrap::getDisablePhpUploadExecution() === 1 && $extension === 'php'; + }) + ->status(550) + ->message(G::LoadTranslation('ID_THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED')) + ->log(function($rule) { + /** + * Levels supported by MonologProvider is: + * 100 "DEBUG" + * 200 "INFO" + * 250 "NOTICE" + * 300 "WARNING" + * 400 "ERROR" + * 500 "CRITICAL" + * 550 "ALERT" + * 600 "EMERGENCY" + */ + Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $rule->getMessage(), $rule->getData()->filename); + }); + + //rule: upload_attempts_limit_per_user + $validator->addRule() + ->validate($file, function($file) { + $systemConfiguration = System::getSystemConfiguration('', '', config("system.workspace")); + $filesWhiteList = explode(',', $systemConfiguration['upload_attempts_limit_per_user']); + $userId = Server::getUserId(); + $key = config("system.workspace") . '/' . $userId; + $attemps = (int) trim($filesWhiteList[0]); + $minutes = (int) trim($filesWhiteList[1]); + $pastAttemps = Cache::remember($key, $minutes, function() { + return 1; + }); + //We only increase when the file path exists, useful when pre-validation is done. + if (isset($file->path)) { + Cache::increment($key, 1); + } + if ($pastAttemps <= $attemps) { + return false; + } + return true; + }) + ->status(429) + ->message(G::LoadTranslation('ID_TOO_MANY_REQUESTS')) + ->log(function($rule) { + /** + * Levels supported by MonologProvider is: + * 100 "DEBUG" + * 200 "INFO" + * 250 "NOTICE" + * 300 "WARNING" + * 400 "ERROR" + * 500 "CRITICAL" + * 550 "ALERT" + * 600 "EMERGENCY" + */ + Bootstrap::registerMonologPhpUploadExecution('phpUpload', 250, $rule->getMessage(), $rule->getData()->filename); + }); + + return $validator->validate(); + } + + /** + * File upload validation. + * + * @return $this + */ + public function runRulesToAllUploadedFiles() + { + $files = $_FILES; + if (!is_array($files)) { + return; + } + $this->fails = []; + foreach ($files as $file) { + $data = (object) $file; + if (!is_array($data->name) || !is_array($data->tmp_name)) { + $data->name = [$data->name]; + $data->tmp_name = [$data->tmp_name]; + } + foreach ($data->name as $key => $value) { + if (empty($value)) { + continue; + } + $validator = $this->runRules(['filename' => $value, 'path' => $data->tmp_name[$key]]); + if ($validator->fails()) { + $this->fails[] = $validator; + } + } + } + return $this; + } + + /** + * Get the first error and call the argument function. + * + * @param function $callback + * @return $this + */ + public function dispach($callback) + { + if (!empty($this->fails[0])) { + if (!empty($callback) && is_callable($callback)) { + $callback($this->fails[0], $this->fails); + } + } + return $this; + } + + /** + * It obtains a single object to be used as a record of the whole environment. + * + * @return object + */ + public static function getValidationUploadedFiles() + { + if (self::$validationUploadedFiles === null) { + self::$validationUploadedFiles = new ValidationUploadedFiles(); + } + return self::$validationUploadedFiles; + } +} diff --git a/workflow/engine/src/ProcessMaker/Validation/Validator.php b/workflow/engine/src/ProcessMaker/Validation/Validator.php new file mode 100644 index 000000000..0fc1fe428 --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Validation/Validator.php @@ -0,0 +1,138 @@ +message; + } + + /** + * Get status code. + * @return int + */ + function getStatus() + { + return $this->status; + } + + /** + * Get the Closure function. + * @return Closure + */ + function getCallback() + { + return $this->callback; + } + + /** + * Add a validation rule. + * The rules are verified in the order in which they have been added. + * + * @param Rule $rule + * @return Rule + */ + public function addRule($rule = null) + { + if (!$rule instanceof Rule) { + $rule = new Rule(); + } + $this->rules[] = $rule; + return $rule; + } + + /** + * Process all added validation rules. + * @return Validator + */ + public function validate() + { + $this->message = ""; + $this->status = 0; + $this->fails = false; + foreach ($this->rules as $rule) { + $callback = $rule->getCallback(); + if (is_callable($callback)) { + if ($callback($rule->getData())) { + $this->message = $rule->getMessage(); + $this->status = $rule->getStatus(); + $this->fails = true; + $getCallbackLog = $rule->getCallbackLog(); + if (is_callable($getCallbackLog)) { + $getCallbackLog($rule); + } + break; + } + } + } + $callbackAfter = $this->getCallback(); + if (is_callable($callbackAfter)) { + $callbackAfter($this); + } + return $this; + } + + /** + * Get the current status of the validation, the value is true if there was a + * failure and false if all the validation rules have been passed. + * @return boolean + */ + public function fails() + { + return $this->fails; + } + + /** + * The Closure function is called when the validation process is finished. + * @param Closure $callback + * @return Validator + */ + public function after($callback) + { + if (is_callable($callback)) { + $this->callback = $callback; + } + return $this; + } +} diff --git a/workflow/engine/templates/admin/pmLogo.js b/workflow/engine/templates/admin/pmLogo.js index 1993cdf44..c4327afd1 100644 --- a/workflow/engine/templates/admin/pmLogo.js +++ b/workflow/engine/templates/admin/pmLogo.js @@ -283,7 +283,10 @@ Ext.onReady(function () { } else { var messageError = ""; - if (obj.failed == "1") { + if (obj.failed === true) { + messageError = obj.message; + } + else if (obj.failed == "1") { //| 1-> Fail in the type of the image messageError = _('ID_ERROR_UPLOADING_IMAGE_TYPE'); } diff --git a/workflow/public_html/sysGeneric.php b/workflow/public_html/sysGeneric.php index 773910f0e..ae4928652 100644 --- a/workflow/public_html/sysGeneric.php +++ b/workflow/public_html/sysGeneric.php @@ -6,6 +6,7 @@ use ProcessMaker\Core\AppEvent; use ProcessMaker\ChangeLog\ChangeLog; /*----------------------------------********---------------------------------*/ use ProcessMaker\Plugins\PluginRegistry; +use ProcessMaker\Validation\ValidationUploadedFiles; /** * bootstrap - ProcessMaker Bootstrap @@ -818,7 +819,7 @@ if (substr(SYS_COLLECTION, 0, 8) === 'gulliver') { $isWebEntry = \ProcessMaker\BusinessModel\WebEntry::isWebEntry(SYS_COLLECTION, $phpFile); if (\Bootstrap::getDisablePhpUploadExecution() === 1 && !$isWebEntry) { - $message = \G::LoadTranslation('THE_PHP_FILES_EXECUTION_WAS_DISABLED'); + $message = \G::LoadTranslation('ID_THE_PHP_FILES_EXECUTION_WAS_DISABLED'); \Bootstrap::registerMonologPhpUploadExecution('phpExecution', 550, $message, $phpFile); echo $message; die(); @@ -1085,6 +1086,8 @@ if (!defined('EXECUTE_BY_CRON')) { $oPluginRegistry->init(); if ($isControllerCall) { //Instance the Controller object and call the request method + ValidationUploadedFiles::getValidationUploadedFiles() + ->runRulesToAllUploadedFiles(); $controller = new $controllerClass(); $controller->setHttpRequestData($_REQUEST);//NewRelic Snippet - By JHL transactionLog($controllerAction); @@ -1105,6 +1108,8 @@ if (!defined('EXECUTE_BY_CRON')) { ->setLanguage(SYS_LANG) ->getUsrIdByUsrUid(empty($_SESSION['USER_LOGGED']) ? '' : $_SESSION['USER_LOGGED']); /*----------------------------------********---------------------------------*/ + ValidationUploadedFiles::getValidationUploadedFiles() + ->runRulesToAllUploadedFiles(); require_once $phpFile; } From ca3d7185784ae889dbf3b5806689aa735a928cb7 Mon Sep 17 00:00:00 2001 From: Roly Rudy Gutierrez Pinto Date: Tue, 2 Oct 2018 10:26:02 -0400 Subject: [PATCH 2/6] HOR-4435 --- config/customMimeTypes.php | 35 +++++++++++ framework/src/Maveriks/WebApplication.php | 4 +- .../translations/english/processmaker.en.po | 6 ++ workflow/engine/data/mysql/insert.sql | 1 + .../engine/src/ProcessMaker/Core/System.php | 3 +- .../Validation/ValidationUploadedFiles.php | 62 ++++++++++++++++++- 6 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 config/customMimeTypes.php diff --git a/config/customMimeTypes.php b/config/customMimeTypes.php new file mode 100644 index 000000000..2469474e3 --- /dev/null +++ b/config/customMimeTypes.php @@ -0,0 +1,35 @@ + 'text/plain', + 'doc' => ['application/msword', 'text/html'], + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'exe' => ['application/x-msdownload', 'application/x-dosexec'], + 'gif' => 'image/gif', + 'htm' => 'text/html', + 'html' => 'text/html', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'ppt' => 'application/vnd.ms-office', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pm' => 'text/plain', + 'pmt' => 'text/plain', + 'po' => 'text/x-po', + 'pdf' => 'application/pdf', + 'png' => 'image/png', + 'php' => 'text/x-php', + 'rar' => 'application/x-rar', + 'txt' => 'text/plain', + 'wmv' => ['video/x-ms-asf', 'video/x-ms-wmv'], + 'xls' => ['application/vnd.ms-excel', 'text/plain'], + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'zip' => 'application/zip', +]; diff --git a/framework/src/Maveriks/WebApplication.php b/framework/src/Maveriks/WebApplication.php index 9344be9dd..8b93a5a28 100644 --- a/framework/src/Maveriks/WebApplication.php +++ b/framework/src/Maveriks/WebApplication.php @@ -337,9 +337,7 @@ class WebApplication 'path' => $target['tmp_name'] ]); if ($validator->fails()) { - if ($validator->getStatus() === 429) { - throw new RestException($validator->getStatus()); - } + throw new RestException($validator->getStatus(), $validator->getMessage()); } return true; }; diff --git a/workflow/engine/content/translations/english/processmaker.en.po b/workflow/engine/content/translations/english/processmaker.en.po index 68431bc17..489aee6c0 100644 --- a/workflow/engine/content/translations/english/processmaker.en.po +++ b/workflow/engine/content/translations/english/processmaker.en.po @@ -24845,6 +24845,12 @@ msgstr "Error: The application {0} is not canceled." msgid "The default configuration was not defined" msgstr "The default configuration was not defined" +# TRANSLATION +# LABEL/ID_THE_MIMETYPE_EXTENSION_ERROR +#: LABEL/ID_THE_MIMETYPE_EXTENSION_ERROR +msgid "The mime type does not correspond to the permitted extension, please verify your file." +msgstr "The mime type does not correspond to the permitted extension, please verify your file." + # TRANSLATION # LABEL/ID_THE_NAME_CHANGE_MAY_CAUSE_DATA_LOSS #: LABEL/ID_THE_NAME_CHANGE_MAY_CAUSE_DATA_LOSS diff --git a/workflow/engine/data/mysql/insert.sql b/workflow/engine/data/mysql/insert.sql index 346bffb5d..70a0e836a 100644 --- a/workflow/engine/data/mysql/insert.sql +++ b/workflow/engine/data/mysql/insert.sql @@ -61034,6 +61034,7 @@ INSERT INTO TRANSLATION (TRN_CATEGORY,TRN_ID,TRN_LANG,TRN_VALUE,TRN_UPDATE_DATE ( 'LABEL','ID_THERE_PROBLEM_SENDING_EMAIL','en','There was a problem sending the email to','2016-04-08') , ( 'LABEL','ID_THE_APPLICATION_IS_NOT_CANCELED','en','Error: The application {0} is not canceled.','2016-06-15') , ( 'LABEL','ID_THE_DEFAULT_CONFIGURATION','en','The default configuration was not defined','2016-11-16') , +( 'LABEL','ID_THE_MIMETYPE_EXTENSION_ERROR','en','The mime type does not correspond to the permitted extension, please verify your file.','2018-10-2') , ( 'LABEL','ID_THE_NAME_CHANGE_MAY_CAUSE_DATA_LOSS','en','The change might cause data loss in the PM table. Do you want to continue?','2017-03-30') , ( 'LABEL','ID_THE_PHP_FILES_EXECUTION_WAS_DISABLED','en','The PHP files execution was disabled please contact the system administrator.','2018-04-20') , ( 'LABEL','ID_THE_REASON_REASSIGN_USER_EMPTY','en','Please complete the reassign reason.','2016-10-20') , diff --git a/workflow/engine/src/ProcessMaker/Core/System.php b/workflow/engine/src/ProcessMaker/Core/System.php index 5e5475ace..bcbdc0e8e 100644 --- a/workflow/engine/src/ProcessMaker/Core/System.php +++ b/workflow/engine/src/ProcessMaker/Core/System.php @@ -68,7 +68,8 @@ class System 'google_map_api_key' => '', 'google_map_signature' => '', 'logging_level' => 'INFO', - 'upload_attempts_limit_per_user' => '60,1' + 'upload_attempts_limit_per_user' => '60,1', + 'files_white_list' => '' ); /** diff --git a/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php b/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php index 05146777b..17f6442f0 100644 --- a/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php +++ b/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php @@ -8,7 +8,7 @@ use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Facades\Cache; use ProcessMaker\Core\System; use ProcessMaker\Services\OAuth2\Server; -use ProcessMaker\Validation\Validator; +use Symfony\Component\HttpFoundation\File\File; class ValidationUploadedFiles { @@ -102,6 +102,66 @@ class ValidationUploadedFiles Bootstrap::registerMonologPhpUploadExecution('phpUpload', 250, $rule->getMessage(), $rule->getData()->filename); }); + //rule: mimeType + $validator->addRule() + ->validate($file, function($file) { + $path = isset($file->path) ? $file->path : ""; + $filesystem = new Filesystem(); + if (!$filesystem->exists($path)) { + return false; + } + + $extension = $filesystem->extension($file->filename); + $mimeType = $filesystem->mimeType($path); + + $file = new File($path); + $guessExtension = $file->guessExtension(); + $mimeTypeFile = $file->getMimeType(); + + //mimeType known + if ($extension === $guessExtension && $mimeType === $mimeTypeFile) { + return false; + } + //mimeType custom + $customMimeTypes = config("customMimeTypes"); + $customMimeType = isset($customMimeTypes[$extension]) ? $customMimeTypes[$extension] : null; + if (is_string($customMimeType)) { + if ($customMimeType === $mimeType) { + return false; + } + } + if (is_array($customMimeType)) { + foreach ($customMimeType as $value) { + if ($value === $mimeType) { + return false; + } + } + } + //files_white_list + $systemConfiguration = System::getSystemConfiguration('', '', config("system.workspace")); + $filesWhiteList = explode(',', $systemConfiguration['files_white_list']); + if (in_array($extension, $filesWhiteList)) { + return false; + } + return true; + }) + ->status(415) + ->message(G::LoadTranslation('ID_THE_MIMETYPE_EXTENSION_ERROR')) + ->log(function($rule) { + /** + * Levels supported by MonologProvider is: + * 100 "DEBUG" + * 200 "INFO" + * 250 "NOTICE" + * 300 "WARNING" + * 400 "ERROR" + * 500 "CRITICAL" + * 550 "ALERT" + * 600 "EMERGENCY" + */ + Bootstrap::registerMonologPhpUploadExecution('phpUpload', 250, $rule->getMessage(), $rule->getData()->filename); + }); + return $validator->validate(); } From d0b7d7291bac53085dd7f3c85875ef77ccc19a13 Mon Sep 17 00:00:00 2001 From: Roly Rudy Gutierrez Pinto Date: Thu, 1 Nov 2018 13:24:47 -0400 Subject: [PATCH 3/6] HOR-4829 --- config/customMimeTypes.php | 1 + workflow/engine/controllers/pmTablesProxy.php | 6 +- .../methods/enterprise/addonsStoreAction.php | 6 +- .../src/ProcessMaker/BusinessModel/Light.php | 56 ++++++++++++++++++- .../src/ProcessMaker/Services/Api/Light.php | 12 ++-- .../Services/Api/Project/FilesManager.php | 10 ++-- .../ProcessMaker/Validation/Exception429.php | 23 -------- .../Validation/ExceptionRestApi.php | 10 ++++ 8 files changed, 83 insertions(+), 41 deletions(-) delete mode 100644 workflow/engine/src/ProcessMaker/Validation/Exception429.php create mode 100644 workflow/engine/src/ProcessMaker/Validation/ExceptionRestApi.php diff --git a/config/customMimeTypes.php b/config/customMimeTypes.php index 2469474e3..c78f3e0fa 100644 --- a/config/customMimeTypes.php +++ b/config/customMimeTypes.php @@ -22,6 +22,7 @@ return [ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'pm' => 'text/plain', 'pmt' => 'text/plain', + 'pmx' => 'application/xml', 'po' => 'text/x-po', 'pdf' => 'application/pdf', 'png' => 'image/png', diff --git a/workflow/engine/controllers/pmTablesProxy.php b/workflow/engine/controllers/pmTablesProxy.php index 4507ea2ff..798271ccd 100644 --- a/workflow/engine/controllers/pmTablesProxy.php +++ b/workflow/engine/controllers/pmTablesProxy.php @@ -8,7 +8,7 @@ */ use ProcessMaker\Core\System; -use ProcessMaker\Validation\Exception429; +use ProcessMaker\Validation\ExceptionRestApi; use ProcessMaker\Validation\ValidationUploadedFiles; header("Content-type: text/html;charset=utf-8"); @@ -726,7 +726,7 @@ class pmTablesProxy extends HttpProxyController try { ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { - throw new Exception429($validator->getMessage()); + throw new ExceptionRestApi($validator->getMessage()); }); $result = new stdClass(); $errors = ''; @@ -896,7 +896,7 @@ class pmTablesProxy extends HttpProxyController } $result->message = $msg; - } catch (Exception429 $e) { + } catch (ExceptionRestApi $e) { $result = new stdClass(); $result->success = false; $result->errorType = 'notice'; diff --git a/workflow/engine/methods/enterprise/addonsStoreAction.php b/workflow/engine/methods/enterprise/addonsStoreAction.php index 7197606cb..05ff152dd 100644 --- a/workflow/engine/methods/enterprise/addonsStoreAction.php +++ b/workflow/engine/methods/enterprise/addonsStoreAction.php @@ -2,7 +2,7 @@ use ProcessMaker\Core\System; use ProcessMaker\Plugins\PluginRegistry; -use ProcessMaker\Validation\Exception429; +use ProcessMaker\Validation\ExceptionRestApi; use ProcessMaker\Validation\ValidationUploadedFiles; function runBgProcessmaker($task, $log) @@ -19,7 +19,7 @@ function runBgProcessmaker($task, $log) try { ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { - throw new Exception429($validator->getMessage()); + throw new ExceptionRestApi($validator->getMessage()); }); if (isset($_REQUEST["action"])) { $action = $_REQUEST["action"]; @@ -317,7 +317,7 @@ try { $result["addons"] = array(); } G::outRes(G::json_encode($result)); -} catch (Exception429 $e) { +} catch (ExceptionRestApi $e) { $token = strtotime("now"); PMException::registerErrorLog($e, $token); G::outRes( diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/Light.php b/workflow/engine/src/ProcessMaker/BusinessModel/Light.php index fda4a306e..6013a824d 100644 --- a/workflow/engine/src/ProcessMaker/BusinessModel/Light.php +++ b/workflow/engine/src/ProcessMaker/BusinessModel/Light.php @@ -32,6 +32,8 @@ use ProcessMaker\Core\RoutingScreen; use ProcessMaker\Core\System; use ProcessMaker\Services\Api\Project\Activity\Step as ActivityStep; use ProcessMaker\Util\DateTime; +use ProcessMaker\Validation\ExceptionRestApi; +use ProcessMaker\Validation\Validator; use ProcessPeer; use Propel; use RBAC; @@ -1061,7 +1063,7 @@ class Light * * @throws Exception */ - public function documentUploadFiles($userUid, $app_uid, $app_doc_uid, $request_data) + public function documentUploadFiles($userUid, $app_uid, $app_doc_uid) { $response = array("status" => "fail"); if (isset($_FILES["form"]["name"]) && count($_FILES["form"]["name"]) > 0) { @@ -1097,6 +1099,58 @@ class Light } } if (count($arrayField) > 0) { + //rule validation + $appDocument = new AppDocument(); + $appDocument->load($app_doc_uid); + $inputDocument = new InputDocument(); + $ifInputExist = $inputDocument->InputExists($appDocument->getDocUid()); + if ($ifInputExist) { + $inputProperties = $inputDocument->load($appDocument->getDocUid()); + $inpDocTypeFile = $inputProperties['INP_DOC_TYPE_FILE']; + $inpDocMaxFilesize = (int) $inputProperties["INP_DOC_MAX_FILESIZE"]; + $inpDocMaxFilesizeUnit = $inputProperties["INP_DOC_MAX_FILESIZE_UNIT"]; + } + + for ($i = 0; $ifInputExist && $i < count($arrayField); $i++) { + $file = [ + 'filename' => $arrayFileName[$i], + 'path' => $arrayFileTmpName[$i] + ]; + $validator = new Validator(); + //rule: extension + $validator->addRule() + ->validate($file, function($file) use($inpDocTypeFile) { + $result = G::verifyInputDocExtension($inpDocTypeFile, $file->filename, $file->path); + return $result->status === false; + }) + ->status(415) + ->message(G::LoadTranslation('ID_UPLOAD_ERR_NOT_ALLOWED_EXTENSION')) + ->log(function($rule) { + Bootstrap::registerMonologPhpUploadExecution('phpUpload', 250, $rule->getMessage(), $rule->getData()->filename); + }); + + //rule: maximum file size + $validator->addRule() + ->validate($file, function($file) use($inpDocMaxFilesize, $inpDocMaxFilesizeUnit) { + if ($inpDocMaxFilesize > 0) { + $totalMaxFileSize = $inpDocMaxFilesize * ($inpDocMaxFilesizeUnit == "MB" ? 1024 * 1024 : 1024); + $fileSize = filesize($file->path); + if ($fileSize > $totalMaxFileSize) { + return true; + } + } + return false; + }) + ->status(413) + ->message(G::LoadTranslation("ID_SIZE_VERY_LARGE_PERMITTED")) + ->log(function($rule) { + Bootstrap::registerMonologPhpUploadExecution('phpUpload', 250, $rule->getMessage(), $rule->getData()->filename); + }); + $validator->validate(); + if ($validator->fails()) { + throw new ExceptionRestApi($validator->getMessage(), $validator->getStatus()); + } + } for ($i = 0; $i <= count($arrayField) - 1; $i++) { if ($arrayFileError[$i] == 0) { $indocUid = null; diff --git a/workflow/engine/src/ProcessMaker/Services/Api/Light.php b/workflow/engine/src/ProcessMaker/Services/Api/Light.php index 7dec46cf7..9f4233246 100644 --- a/workflow/engine/src/ProcessMaker/Services/Api/Light.php +++ b/workflow/engine/src/ProcessMaker/Services/Api/Light.php @@ -23,7 +23,7 @@ use ProcessMaker\Project\Adapter; use ProcessMaker\Services\Api; use ProcessMaker\Services\Api\Project\Activity\Step; use ProcessMaker\Util\DateTime; -use ProcessMaker\Validation\Exception429; +use ProcessMaker\Validation\ExceptionRestApi; use RBAC; use stdclass; use StepPeer; @@ -1467,8 +1467,8 @@ class Light extends Api $userUid = $this->getUserId(); $oMobile = new BusinessModelLight(); $filesUids = $oMobile->postUidUploadFiles($userUid, $app_uid, $request_data); - } catch (Exception429 $e) { - throw new RestException($e->getStatus()); + } catch (ExceptionRestApi $e) { + throw new RestException($e->getCode(), $e->getMessage()); } catch (Exception $e) { throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } @@ -1502,9 +1502,9 @@ class Light extends Api try { $userUid = $this->getUserId(); $oMobile = new BusinessModelLight(); - $response = $oMobile->documentUploadFiles($userUid, $app_uid, $app_doc_uid, $request_data); - } catch (Exception429 $e) { - throw new RestException($e->getStatus()); + $response = $oMobile->documentUploadFiles($userUid, $app_uid, $app_doc_uid); + } catch (ExceptionRestApi $e) { + throw new RestException($e->getCode(), $e->getMessage()); } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } diff --git a/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php b/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php index 876cd15cd..55c22fb9d 100644 --- a/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php +++ b/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php @@ -5,7 +5,7 @@ use Exception; use Luracast\Restler\RestException; use ProcessMaker\BusinessModel\FilesManager as FilesManagerBusinessModel; use ProcessMaker\Services\Api; -use ProcessMaker\Validation\Exception429; +use ProcessMaker\Validation\ExceptionRestApi; /** * Project\ProjectUsers Api Controller @@ -64,8 +64,8 @@ class FilesManager extends Api $arrayData = $filesManager->addProcessFilesManager($prj_uid, $userUid, $request_data); //Response $response = $arrayData; - } catch (Exception429 $e) { - throw new RestException($e->getStatus()); + } catch (ExceptionRestApi $e) { + throw new RestException($e->getCode(), $e->getMessage()); } catch (Exception $e) { //response throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); @@ -94,8 +94,8 @@ class FilesManager extends Api $sData = $filesManager->uploadProcessFilesManager($prj_uid, $prf_uid); //Response $response = $sData; - } catch (Exception429 $e) { - throw new RestException($e->getStatus()); + } catch (ExceptionRestApi $e) { + throw new RestException($e->getCode(), $e->getMessage()); } catch (Exception $e) { //response throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); diff --git a/workflow/engine/src/ProcessMaker/Validation/Exception429.php b/workflow/engine/src/ProcessMaker/Validation/Exception429.php deleted file mode 100644 index 200844d39..000000000 --- a/workflow/engine/src/ProcessMaker/Validation/Exception429.php +++ /dev/null @@ -1,23 +0,0 @@ -status; - } -} diff --git a/workflow/engine/src/ProcessMaker/Validation/ExceptionRestApi.php b/workflow/engine/src/ProcessMaker/Validation/ExceptionRestApi.php new file mode 100644 index 000000000..e781a81c6 --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Validation/ExceptionRestApi.php @@ -0,0 +1,10 @@ + Date: Mon, 5 Nov 2018 10:07:11 -0400 Subject: [PATCH 4/6] HOR-4432 --- .../engine/content/translations/english/processmaker.en.po | 6 ++++++ workflow/engine/data/mysql/insert.sql | 1 + workflow/engine/src/ProcessMaker/BusinessModel/Light.php | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/workflow/engine/content/translations/english/processmaker.en.po b/workflow/engine/content/translations/english/processmaker.en.po index 489aee6c0..52a774357 100644 --- a/workflow/engine/content/translations/english/processmaker.en.po +++ b/workflow/engine/content/translations/english/processmaker.en.po @@ -25931,6 +25931,12 @@ msgstr "The uploaded file exceeds the upload_max_filesize directive in php.ini" msgid "The file has not been attached because the extension is not allowed or because the content doesn't correspond." msgstr "The file has not been attached because the extension is not allowed or because the content doesn't correspond." +# TRANSLATION +# LABEL/ID_UPLOAD_INVALID_DOC_TYPE_FILE +#: LABEL/ID_UPLOAD_INVALID_DOC_TYPE_FILE +msgid "Invalid file format, please upload a file with one of the following formats {0}" +msgstr "Invalid file format, please upload a file with one of the following formats {0}" + # TRANSLATION # LABEL/ID_UPLOAD_ERR_NO_FILE #: LABEL/ID_UPLOAD_ERR_NO_FILE diff --git a/workflow/engine/data/mysql/insert.sql b/workflow/engine/data/mysql/insert.sql index 70a0e836a..679f209ff 100644 --- a/workflow/engine/data/mysql/insert.sql +++ b/workflow/engine/data/mysql/insert.sql @@ -61219,6 +61219,7 @@ INSERT INTO TRANSLATION (TRN_CATEGORY,TRN_ID,TRN_LANG,TRN_VALUE,TRN_UPDATE_DATE ( 'LABEL','ID_UPLOAD_ERR_FORM_SIZE','en','The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form','2014-01-15') , ( 'LABEL','ID_UPLOAD_ERR_INI_SIZE','en','The uploaded file exceeds the upload_max_filesize directive in php.ini','2014-01-15') , ( 'LABEL','ID_UPLOAD_ERR_NOT_ALLOWED_EXTENSION','en','The file has not been attached because the extension is not allowed or because the content doesn''t correspond.','2014-10-21') , +( 'LABEL','ID_UPLOAD_INVALID_DOC_TYPE_FILE','en','Invalid file format, please upload a file with one of the following formats {0}','2018-11-05') , ( 'LABEL','ID_UPLOAD_ERR_NO_FILE','en','No file was uploaded','2014-01-15') , ( 'LABEL','ID_UPLOAD_ERR_NO_TMP_DIR','en','Missing a temporary folder','2014-01-15') , ( 'LABEL','ID_UPLOAD_ERR_PARTIAL','en','The uploaded file was only partially uploaded','2014-01-15') , diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/Light.php b/workflow/engine/src/ProcessMaker/BusinessModel/Light.php index 6013a824d..5f52bcd3e 100644 --- a/workflow/engine/src/ProcessMaker/BusinessModel/Light.php +++ b/workflow/engine/src/ProcessMaker/BusinessModel/Light.php @@ -1124,7 +1124,7 @@ class Light return $result->status === false; }) ->status(415) - ->message(G::LoadTranslation('ID_UPLOAD_ERR_NOT_ALLOWED_EXTENSION')) + ->message(G::LoadTranslation('ID_UPLOAD_INVALID_DOC_TYPE_FILE', [$inpDocTypeFile])) ->log(function($rule) { Bootstrap::registerMonologPhpUploadExecution('phpUpload', 250, $rule->getMessage(), $rule->getData()->filename); }); From dd1392eb9072b2d4331ff242bc71bd5a39253807 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Tue, 6 Nov 2018 14:37:10 -0400 Subject: [PATCH 5/6] HOR-4944 --- .../engine/content/translations/english/processmaker.en.po | 6 ++++++ workflow/engine/data/mysql/insert.sql | 1 + workflow/engine/src/ProcessMaker/BusinessModel/Light.php | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/workflow/engine/content/translations/english/processmaker.en.po b/workflow/engine/content/translations/english/processmaker.en.po index 52a774357..526dc5812 100644 --- a/workflow/engine/content/translations/english/processmaker.en.po +++ b/workflow/engine/content/translations/english/processmaker.en.po @@ -25931,6 +25931,12 @@ msgstr "The uploaded file exceeds the upload_max_filesize directive in php.ini" msgid "The file has not been attached because the extension is not allowed or because the content doesn't correspond." msgstr "The file has not been attached because the extension is not allowed or because the content doesn't correspond." +# TRANSLATION +# LABEL/ID_UPLOAD_INVALID_DOC_MAX_FILESIZE +#: LABEL/ID_UPLOAD_INVALID_DOC_MAX_FILESIZE +msgid "File size exceeds the allowable limit of {0}" +msgstr "File size exceeds the allowable limit of {0}" + # TRANSLATION # LABEL/ID_UPLOAD_INVALID_DOC_TYPE_FILE #: LABEL/ID_UPLOAD_INVALID_DOC_TYPE_FILE diff --git a/workflow/engine/data/mysql/insert.sql b/workflow/engine/data/mysql/insert.sql index 679f209ff..b5854aefc 100644 --- a/workflow/engine/data/mysql/insert.sql +++ b/workflow/engine/data/mysql/insert.sql @@ -61219,6 +61219,7 @@ INSERT INTO TRANSLATION (TRN_CATEGORY,TRN_ID,TRN_LANG,TRN_VALUE,TRN_UPDATE_DATE ( 'LABEL','ID_UPLOAD_ERR_FORM_SIZE','en','The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form','2014-01-15') , ( 'LABEL','ID_UPLOAD_ERR_INI_SIZE','en','The uploaded file exceeds the upload_max_filesize directive in php.ini','2014-01-15') , ( 'LABEL','ID_UPLOAD_ERR_NOT_ALLOWED_EXTENSION','en','The file has not been attached because the extension is not allowed or because the content doesn''t correspond.','2014-10-21') , +( 'LABEL','ID_UPLOAD_INVALID_DOC_MAX_FILESIZE','en','File size exceeds the allowable limit of {0}','2018-11-06') , ( 'LABEL','ID_UPLOAD_INVALID_DOC_TYPE_FILE','en','Invalid file format, please upload a file with one of the following formats {0}','2018-11-05') , ( 'LABEL','ID_UPLOAD_ERR_NO_FILE','en','No file was uploaded','2014-01-15') , ( 'LABEL','ID_UPLOAD_ERR_NO_TMP_DIR','en','Missing a temporary folder','2014-01-15') , diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/Light.php b/workflow/engine/src/ProcessMaker/BusinessModel/Light.php index 5f52bcd3e..8ee6a3b2d 100644 --- a/workflow/engine/src/ProcessMaker/BusinessModel/Light.php +++ b/workflow/engine/src/ProcessMaker/BusinessModel/Light.php @@ -1142,7 +1142,7 @@ class Light return false; }) ->status(413) - ->message(G::LoadTranslation("ID_SIZE_VERY_LARGE_PERMITTED")) + ->message(G::LoadTranslation("ID_UPLOAD_INVALID_DOC_MAX_FILESIZE", [$inpDocMaxFilesize . $inpDocMaxFilesizeUnit])) ->log(function($rule) { Bootstrap::registerMonologPhpUploadExecution('phpUpload', 250, $rule->getMessage(), $rule->getData()->filename); }); From 8a3324aabc651cc8387a34f8f1e2f0bbce859c01 Mon Sep 17 00:00:00 2001 From: Roly Rudy Gutierrez Pinto Date: Fri, 9 Nov 2018 16:04:51 -0400 Subject: [PATCH 6/6] PMC-21 --- .../Validation/ValidationUploadedFiles.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php b/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php index 17f6442f0..42b11989b 100644 --- a/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php +++ b/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php @@ -187,6 +187,20 @@ class ValidationUploadedFiles if (empty($value)) { continue; } + if (is_array($value)) { + foreach ($value as $rowKey => $rowValue) { + foreach ($rowValue as $cellKey => $cellValue) { + if (empty($cellValue)) { + continue; + } + $validator = $this->runRules(['filename' => $cellValue, 'path' => $data->tmp_name[$key][$rowKey][$cellKey]]); + if ($validator->fails()) { + $this->fails[] = $validator; + } + } + } + continue; + } $validator = $this->runRules(['filename' => $value, 'path' => $data->tmp_name[$key]]); if ($validator->fails()) { $this->fails[] = $validator;