diff --git a/htdocs/api/class/api_documents.class.php b/htdocs/api/class/api_documents.class.php index 7372ea88798..6147b87b56b 100644 --- a/htdocs/api/class/api_documents.class.php +++ b/htdocs/api/class/api_documents.class.php @@ -821,7 +821,50 @@ class Documents extends DolibarrApi throw new RestException(500, "Failed to open file '".$destfiletmp."' for write"); } - $result = dol_move($destfiletmp, $destfile, 0, $overwriteifexists, 1, 1); + $disablevirusscan = 0; + $src_file = $destfiletmp; + $dest_file = $destfile; + + // Security: + // If we need to make a virus scan + if (empty($disablevirusscan) && file_exists($src_file)) { + $checkvirusarray = dolCheckVirus($src_file); + if (count($checkvirusarray)) { + dol_syslog('Files.lib::dol_move_uploaded_file File "'.$src_file.'" (target name "'.$dest_file.'") KO with antivirus: errors='.join(',', $checkvirusarray), LOG_WARNING); + throw new RestException(500, 'ErrorFileIsInfectedWithAVirus: '.join(',', $checkvirusarray)); + } + } + + // Security: + // Disallow file with some extensions. We rename them. + // Because if we put the documents directory into a directory inside web root (very bad), this allows to execute on demand arbitrary code. + if (isAFileWithExecutableContent($dest_file) && empty($conf->global->MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED)) { + // $upload_dir ends with a slash, so be must be sure the medias dir to compare to ends with slash too. + $publicmediasdirwithslash = $conf->medias->multidir_output[$conf->entity]; + if (!preg_match('/\/$/', $publicmediasdirwithslash)) { + $publicmediasdirwithslash .= '/'; + } + + if (strpos($upload_dir, $publicmediasdirwithslash) !== 0 || !getDolGlobalInt("MAIN_DOCUMENT_DISABLE_NOEXE_IN_MEDIAS_DIR")) { // We never add .noexe on files into media directory + $dest_file .= '.noexe'; + } + } + + // Security: + // We refuse cache files/dirs, upload using .. and pipes into filenames. + if (preg_match('/^\./', basename($src_file)) || preg_match('/\.\./', $src_file) || preg_match('/[<>|]/', $src_file)) { + dol_syslog("Refused to deliver file ".$src_file, LOG_WARNING); + throw new RestException(500, "Refused to deliver file ".$src_file); + } + + // Security: + // We refuse cache files/dirs, upload using .. and pipes into filenames. + if (preg_match('/^\./', basename($dest_file)) || preg_match('/\.\./', $dest_file) || preg_match('/[<>|]/', $dest_file)) { + dol_syslog("Refused to deliver file ".$dest_file, LOG_WARNING); + throw new RestException(500, "Refused to deliver file ".$dest_file); + } + + $result = dol_move($destfiletmp, $dest_file, 0, $overwriteifexists, 1, 1); if (!$result) { throw new RestException(500, "Failed to move file into '".$destfile."'"); } diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index 55b5f1f3c4c..a2cf39ddfb3 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -1250,7 +1250,7 @@ function dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disable $publicmediasdirwithslash .= '/'; } - if (strpos($upload_dir, $publicmediasdirwithslash) !== 0) { // We never add .noexe on files into media directory + if (strpos($upload_dir, $publicmediasdirwithslash) !== 0 || !getDolGlobalInt("MAIN_DOCUMENT_DISABLE_NOEXE_IN_MEDIAS_DIR")) { // We never add .noexe on files into media directory $file_name .= '.noexe'; $successcode = 2; }