addRule() ->validate($file, function($file) { $filesystem = new Filesystem(); $extension = strtolower($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) { $message = $rule->getMessage(); $context = [ 'filename' => $rule->getData()->filename, 'url' => $_SERVER["REQUEST_URI"] ?? '' ]; Log::channel(':phpUpload')->alert($message, Bootstrap::context($context)); }); //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 = !empty($_SESSION['USER_LOGGED']) ? $_SESSION['USER_LOGGED'] : Server::getUserId(); $key = config("system.workspace") . '/' . $userId; $attempts = (int) trim($filesWhiteList[0]); $minutes = (int) trim($filesWhiteList[1]); $pastAttempts = 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 ($pastAttempts <= $attempts) { return false; } return true; }) ->status(429) ->message(G::LoadTranslation('ID_TOO_MANY_REQUESTS')) ->log(function($rule) { $message = $rule->getMessage(); $context = [ 'filename' => $rule->getData()->filename, 'url' => $_SERVER["REQUEST_URI"] ?? '' ]; Log::channel(':phpUpload')->notice($message, Bootstrap::context($context)); }); //rule: mimeType $validator->addRule() ->validate($file, function($file) { $path = isset($file->path) ? $file->path : ""; $filesystem = new Filesystem(); if (!$filesystem->exists($path)) { return false; } $extension = strtolower($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) { $message = $rule->getMessage(); $context = [ 'filename' => $rule->getData()->filename, 'url' => $_SERVER["REQUEST_URI"] ?? '' ]; Log::channel(':phpUpload')->notice($message, Bootstrap::context($context)); }); return $validator->validate(); } /** * File upload validation. * * @return $this * * @see workflow/public_html/sysGeneric.php */ public function runRulesToAllUploadedFiles() { $files = $_FILES; if (!is_array($files)) { return; } $this->fails = []; $validator = $this->runRulesForFileEmpty(); if ($validator->fails()) { $this->fails[] = $validator; } 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; } 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; } } } return $this; } /** * Run rules if files is empty. * * @see ProcessMaker\Validation\ValidationUploadedFiles->runRulesToAllUploadedFiles() * @see Luracast\Restler\Format\UploadFormat->decode() */ public function runRulesForFileEmpty() { $validator = new Validator(); //rule: validate $_SERVER['CONTENT_LENGTH'] $rule = $validator->addRule(); $rule->validate(null, function($file) use ($rule) { //according to the acceptance criteria the information is always shown in MBytes $phpShorthandByte = new PhpShorthandByte(); $postMaxSize = ini_get("post_max_size"); $postMaxSizeBytes = $phpShorthandByte->valueToBytes($postMaxSize); $uploadMaxFileSize = ini_get("upload_max_filesize"); $uploadMaxFileSizeBytes = $phpShorthandByte->valueToBytes($uploadMaxFileSize); if ($postMaxSizeBytes < $uploadMaxFileSizeBytes) { $uploadMaxFileSize = $postMaxSize; $uploadMaxFileSizeBytes = $postMaxSizeBytes; } //according to the acceptance criteria the information is always shown in MBytes $uploadMaxFileSizeMBytes = $uploadMaxFileSizeBytes / (1024 ** 2); //conversion constant $message = G::LoadTranslation('ID_THE_FILE_SIZE_IS_BIGGER_THAN_THE_MAXIMUM_ALLOWED', [$uploadMaxFileSizeMBytes]); $rule->message($message); /** * If you can, you may want to set post_max_size to a low value (say 1M) to make * testing easier. First test to see how your script behaves. Try uploading a file * that is larger than post_max_size. If you do you will get a message like this * in your error log: * * [09-Jun-2010 19:28:01] PHP Warning: POST Content-Length of 30980857 bytes exceeds * the limit of 2097152 bytes in Unknown on line 0 * * This makes the script is not completed. * * Solving the problem: * The PHP documentation http://php.net/manual/en/ini.core.php#ini.post-max-size * provides a hack to solve this problem: * * If the size of post data is greater than post_max_size, the $_POST and $_FILES * superglobals are empty. */ if ($_SERVER['REQUEST_METHOD'] === 'POST' && empty($_POST) && empty($_FILES) && $_SERVER['CONTENT_LENGTH'] > 0) { return true; } return false; }) ->status(400) ->log(function($rule) { $message = $rule->getMessage(); $context = [ 'filename' => "", 'url' => $_SERVER["REQUEST_URI"] ?? '' ]; Log::channel(':phpUpload')->error($message, Bootstrap::context($context)); }); return $validator->validate(); } /** * Check if the loaded files comply with the validation rules, add here if you * want more validation rules. * Accept per argument an array or object that contains a "filename" and "path" values. * The rules are verified in the order in which they have been added. * * @param array|object $file * @return Validator */ public function runRulesForPostFilesOfNote($file) { $validator = $this->runRules($file); //rule: file exists $rule = $validator->addRule(); $rule->validate($file, function($file) use($rule) { $path = isset($file->path) ? $file->path : ""; $filesystem = new Filesystem(); if (!$filesystem->exists($path)) { $rule->message(G::LoadTranslation('ID_NOT_EXISTS_FILE')); return self::INVALID; } return self::VALID; }) ->status(400) ->log(function($rule) { $message = $rule->getMessage(); $context = [ 'filename' => $rule->getData()->filename, 'url' => $_SERVER["REQUEST_URI"] ?? '' ]; Log::channel(':phpUpload')->error($message, Bootstrap::context($context)); }); //rule: extensions $rule = $validator->addRule(); $rule->validate($file, function($file) use($rule) { $filesystem = new Filesystem(); $extension = strtolower($filesystem->extension($file->filename)); $extensions = [ 'pdf', 'gif', 'jpg', 'png', 'doc', 'docx', 'xls', 'xlsx', 'txt', 'mp4', 'mpv', 'mpeg', 'mpg', 'mov' ]; if (!in_array($extension, $extensions)) { $rule->message(G::LoadTranslation('ID_YOU_UPLOADED_AN_UNSUPPORTED_FILE_EXTENSION')); return self::INVALID; } return self::VALID; }) ->status(400) ->log(function($rule) { $message = $rule->getMessage(); $context = [ 'filename' => $rule->getData()->filename, 'url' => $_SERVER["REQUEST_URI"] ?? '' ]; Log::channel(':phpUpload')->error($message, Bootstrap::context($context)); }); //rule: file size $rule = $validator->addRule(); $rule->validate($file, function($file) use($rule) { $path = isset($file->path) ? $file->path : ""; $filesystem = new Filesystem(); $limitSize = '10M'; $size = $filesystem->size($path); $phpShorthandByte = new PhpShorthandByte(); $postMaxSizeBytes = $phpShorthandByte->valueToBytes($limitSize); if ($size > $postMaxSizeBytes) { $rule->message(G::LoadTranslation('ID_YOUR_FILE_HAS_EXCEEDED', [$limitSize])); return self::INVALID; } return self::VALID; }) ->status(400) ->log(function($rule) { $message = $rule->getMessage(); $context = [ 'filename' => $rule->getData()->filename, 'url' => $_SERVER["REQUEST_URI"] ?? '' ]; Log::channel(':phpUpload')->error($message, Bootstrap::context($context)); }); return $validator->validate(); } /** * Get the first error and call the argument function. * * @param function $callback * @return $this */ public function dispatch($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; } }