diff --git a/.snyk b/.snyk new file mode 100644 index 000000000..3984df3ed --- /dev/null +++ b/.snyk @@ -0,0 +1,9 @@ +ignore: + - "vendor/**" + - "plugins/**" + - "includes/libraries/cryptojs/**" + - "includes/libraries/csrfp/**" + - "includes/libraries/ezimuel/**" + - "includes/libraries/plupload/**" + - "includes/libraries/yubico/**" + - "install/**" \ No newline at end of file diff --git a/api/Controller/Api/BaseController.php b/api/Controller/Api/BaseController.php index e75544a91..21dc48f06 100755 --- a/api/Controller/Api/BaseController.php +++ b/api/Controller/Api/BaseController.php @@ -80,8 +80,7 @@ public function sanitizeUrl(array $array) return dataSanitizer( $array, - $filters, - __DIR__.'/../../..' + $filters ); } diff --git a/api/Controller/Api/ItemController.php b/api/Controller/Api/ItemController.php index 593dc51eb..76261702f 100755 --- a/api/Controller/Api/ItemController.php +++ b/api/Controller/Api/ItemController.php @@ -185,29 +185,38 @@ public function createAction(array $userData) // get parameters $arrQueryStringParams = $this->getQueryStringParams(); - // check parameters - $arrCheck = $this->checkNewItemData($arrQueryStringParams, $userData); - if ($arrCheck['error'] === true) { - $strErrorDesc = $arrCheck['strErrorDesc']; - $strErrorHeader = $arrCheck['strErrorHeader']; + // Check that the parameters are indeed an array before using them + if (is_array($arrQueryStringParams)) { + // check parameters + $arrCheck = $this->checkNewItemData($arrQueryStringParams, $userData); + + if ($arrCheck['error'] === true) { + $strErrorDesc = $arrCheck['strErrorDesc']; + $strErrorHeader = $arrCheck['strErrorHeader']; + } else { + // launch + $itemModel = new ItemModel(); + $ret = $itemModel->addItem( + (int) $arrQueryStringParams['folder_id'], + (string) $arrQueryStringParams['label'], + (string) $arrQueryStringParams['password'], + (string) $arrQueryStringParams['description'], + (string) $arrQueryStringParams['login'], + (string) $arrQueryStringParams['email'], + (string) $arrQueryStringParams['url'], + (string) $arrQueryStringParams['tags'], + (string) $arrQueryStringParams['anyone_can_modify'], + (string) $arrQueryStringParams['icon'], + (int) $userData['id'], + (string) $userData['username'], + ); + $responseData = json_encode($ret); + } + } else { - // launch - $itemModel = new ItemModel(); - $ret = $itemModel->addItem( - $arrQueryStringParams['folder_id'], - $arrQueryStringParams['label'], - $arrQueryStringParams['password'], - $arrQueryStringParams['description'], - $arrQueryStringParams['login'], - $arrQueryStringParams['email'], - $arrQueryStringParams['url'], - $arrQueryStringParams['tags'], - $arrQueryStringParams['anyone_can_modify'], - $arrQueryStringParams['icon'], - $userData['id'], - $userData['username'], - ); - $responseData = json_encode($ret); + // Gérer le cas où les paramètres ne sont pas un tableau + $strErrorDesc = 'Data not consistent'; + $strErrorHeader = 'Expected array, received ' . gettype($arrQueryStringParams); } } } else { diff --git a/api/inc/jwt_utils.php b/api/inc/jwt_utils.php index 0df070d47..bfa33614b 100755 --- a/api/inc/jwt_utils.php +++ b/api/inc/jwt_utils.php @@ -88,7 +88,8 @@ function get_authorization_header() $authorizationHeader = $request->headers->get('Authorization'); $headers = null; - if (null !== $authorizationHeader) { + // Check if the authorization header is not empty + if (!empty($authorizationHeader)) { $headers = trim($authorizationHeader); } else if (function_exists('apache_request_headers') === true) { $requestHeaders = (array) apache_request_headers(); diff --git a/api/index.php b/api/index.php index c6c356a4a..4fea17350 100755 --- a/api/index.php +++ b/api/index.php @@ -23,7 +23,18 @@ * @see https://www.teampass.net */ -header("Access-Control-Allow-Origin: ".$_SERVER['HTTP_HOST']); +// Determine the protocol used +$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://'; + +// Validate and filter the host +$host = filter_var($_SERVER['HTTP_HOST'], FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME); + +// Allocate the correct CORS header +if ($host !== false) { + header("Access-Control-Allow-Origin: $protocol$host"); +} else { + header("Access-Control-Allow-Origin: 'null'"); +} header("Content-Type: application/json; charset=UTF-8"); header("Access-Control-Allow-Methods: POST, GET"); header("Access-Control-Max-Age: 3600"); @@ -33,7 +44,7 @@ // sanitize url segments $base = new BaseController(); $uri = $base->getUriSegments(); -if (is_array($uri) === false || is_string($uri) === true) { +if (!is_array($uri)) { $uri = [$uri]; // ensure $uril is table } diff --git a/includes/config/include.php b/includes/config/include.php index 7c30a8877..941ec9e71 100755 --- a/includes/config/include.php +++ b/includes/config/include.php @@ -28,7 +28,7 @@ define('TP_VERSION', '3.1.2'); define("UPGRADE_MIN_DATE", "1727110744"); -define('TP_VERSION_MINOR', '143'); +define('TP_VERSION_MINOR', '144'); define('TP_TOOL_NAME', 'Teampass'); define('TP_ONE_DAY_SECONDS', 86400); define('TP_ONE_WEEK_SECONDS', 604800); diff --git a/includes/core/login.php b/includes/core/login.php index 01bb265ca..4310d73ad 100755 --- a/includes/core/login.php +++ b/includes/core/login.php @@ -77,7 +77,7 @@ exit; } else { // Gérer les erreurs - echo 'Erreur lors de la récupération des informations utilisateur : ' . $userInfo['message']; + echo 'Erreur lors de la récupération des informations utilisateur : ' . htmlspecialchars($userInfo['message'], ENT_QUOTES, 'UTF-8'); }; } @@ -87,7 +87,6 @@ // Check if user exists in Teampass if (WIP === true) { error_log('---- CALLBACK LOGIN ----'); - //error_log('Info : ' . print_r($session->get('userOauth2Info'), true)); } $session->set('user-login', strstr($session->get('userOauth2Info')['userPrincipalName'], '@', true)); diff --git a/index.php b/index.php index 00a19dfc0..a99a7cc41 100755 --- a/index.php +++ b/index.php @@ -686,9 +686,9 @@
diff --git a/pages/tasks.php b/pages/tasks.php index bdbc464ea..84fc15c4d 100755 --- a/pages/tasks.php +++ b/pages/tasks.php @@ -151,7 +151,9 @@ } } catch (Exception $e) { - error_log('TEAMPASS Error - tasks page - '.$e->getMessage()); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('TEAMPASS Error - tasks page - '.$e->getMessage()); + } // deepcode ignore ServerLeak: no critical information is provided echo "An error occurred."; }?> diff --git a/pages/uploads.js.php b/pages/uploads.js.php index b734ed50f..20f7eb587 100755 --- a/pages/uploads.js.php +++ b/pages/uploads.js.php @@ -32,7 +32,7 @@ use TeampassClasses\PerformChecks\PerformChecks; use TeampassClasses\SessionManager\SessionManager; use TeampassClasses\ConfigManager\ConfigManager; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Request as RequestLocal; use TeampassClasses\Language\Language; // Load functions require_once __DIR__.'/../sources/main.functions.php'; @@ -40,7 +40,7 @@ // init loadClasses(); $session = SessionManager::getSession(); -$request = Request::createFromGlobals(); +$request = RequestLocal::createFromGlobals(); $lang = new Language(); if ($session->get('key') === null) { diff --git a/pages/uploads.php b/pages/uploads.php index ff0013a38..626d6f58c 100755 --- a/pages/uploads.php +++ b/pages/uploads.php @@ -28,7 +28,7 @@ */ use TeampassClasses\SessionManager\SessionManager; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Request as RequestLocal; use TeampassClasses\Language\Language; use TeampassClasses\NestedTree\NestedTree; use TeampassClasses\PerformChecks\PerformChecks; @@ -40,7 +40,7 @@ // init loadClasses('DB'); $session = SessionManager::getSession(); -$request = Request::createFromGlobals(); +$request = RequestLocal::createFromGlobals(); $lang = new Language(); // Load config if $SETTINGS not defined diff --git a/scripts/background_tasks___functions.php b/scripts/background_tasks___functions.php index aefb36af0..dac172797 100755 --- a/scripts/background_tasks___functions.php +++ b/scripts/background_tasks___functions.php @@ -101,7 +101,9 @@ function clearTasksLog() */ function provideLog(string $message, array $SETTINGS) { - error_log((string) date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], time()) . ' - '.$message); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log((string) date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], time()) . ' - '.$message); + } } function performVisibleFoldersHtmlUpdate (int $user_id) diff --git a/scripts/background_tasks___items_handler.php b/scripts/background_tasks___items_handler.php index 082fe6ae4..f189625f1 100755 --- a/scripts/background_tasks___items_handler.php +++ b/scripts/background_tasks___items_handler.php @@ -344,7 +344,7 @@ function handleTaskStep( 0, -1, (int) $ProcessArguments['item_id'], - (string) array_key_exists('pwd', $ProcessArguments) === true ? $ProcessArguments['pwd'] : (array_key_exists('object_key', $ProcessArguments) === true ? $ProcessArguments['object_key'] : ''), + (string) (array_key_exists('pwd', $ProcessArguments) === true ? $ProcessArguments['pwd'] : (array_key_exists('object_key', $ProcessArguments) === true ? $ProcessArguments['object_key'] : '')), false, false, [], diff --git a/scripts/background_tasks___items_handler_subtask.php b/scripts/background_tasks___items_handler_subtask.php index bd28b9841..89bc08cf6 100644 --- a/scripts/background_tasks___items_handler_subtask.php +++ b/scripts/background_tasks___items_handler_subtask.php @@ -109,7 +109,7 @@ 0, -1, (int) $ProcessArguments['item_id'], - (string) array_key_exists('pwd', $ProcessArguments) === true ? $ProcessArguments['pwd'] : (array_key_exists('object_key', $ProcessArguments) === true ? $ProcessArguments['object_key'] : ''), + (string) (array_key_exists('pwd', $ProcessArguments) === true ? $ProcessArguments['pwd'] : (array_key_exists('object_key', $ProcessArguments) === true ? $ProcessArguments['object_key'] : '')), false, false, [], diff --git a/scripts/background_tasks___userKeysCreation.php b/scripts/background_tasks___userKeysCreation.php index 6e9da1435..74cb6dd49 100755 --- a/scripts/background_tasks___userKeysCreation.php +++ b/scripts/background_tasks___userKeysCreation.php @@ -250,14 +250,12 @@ function getSubTasks($taskId) { */ function updateSubTask($subTaskId, $args) { if (empty($subTaskId) === true) { - error_log('Subtask ID is empty ... are we lost!?!'); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('Subtask ID is empty ... are we lost!?!'); + } return; } // Convertir les paramètres de la tâche en JSON - //$taskParamsJson = json_encode($taskParams); - - //error_log('Subtask update : '.$subTaskId." - ".print_r($taskParams,true)); - $query = [ 'updated_at' => time(), // Mettre à jour la date de dernière modification 'is_in_progress' => 1, @@ -293,7 +291,6 @@ function reloadSubTask($subTaskId) { } function updateTask($taskId) { - //error_log('Task update : '.$taskId); // Mettre à jour la tâche dans la base de données DB::update(prefixTable('background_tasks'), [ 'updated_at' => time(), // Mettre à jour la date de dernière modification diff --git a/scripts/background_tasks___userKeysCreation_subtaskHdl.php b/scripts/background_tasks___userKeysCreation_subtaskHdl.php index 2a46e7979..551f58173 100644 --- a/scripts/background_tasks___userKeysCreation_subtaskHdl.php +++ b/scripts/background_tasks___userKeysCreation_subtaskHdl.php @@ -58,9 +58,20 @@ // Once all items are processed, the subtask is marked as FINISHED $arguments = $_SERVER['argv']; -array_shift($arguments); +// Is there any arguments in array ? +if (!is_array($arguments)) { + // Stop the script + echo "Erreur : Les arguments ne sont pas disponibles ou ne sont pas un tableau.\n"; + + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('Error: Arguments are not available or not an array. (background_tasks___userKeysCreation_subtaskHdl.php)'); + } + exit(1); +} +array_shift($arguments); +// Get the arguments $inputData = [ 'subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], diff --git a/scripts/task_maintenance_purge_old_files.php b/scripts/task_maintenance_purge_old_files.php index 6d144c47f..027bf615a 100755 --- a/scripts/task_maintenance_purge_old_files.php +++ b/scripts/task_maintenance_purge_old_files.php @@ -73,42 +73,45 @@ function purgeTemporaryFiles(): void // Load expected files require_once __DIR__. '/../sources/main.functions.php'; - if (isset($SETTINGS) === true) { - //read folder - if (is_dir($SETTINGS['path_to_files_folder']) === true) { - $dir = opendir($SETTINGS['path_to_files_folder']); - if ($dir !== false) { - //delete file FILES - while (false !== ($f = readdir($dir))) { - if ($f !== '.' && $f !== '..' && $f !== '.htaccess') { - if (file_exists($dir . $f) && ((time() - filectime($dir . $f)) > 604800)) { - fileDelete($dir . '/' . $f, $SETTINGS); - } + // Verify if $SETTINGS is defined + if (isset($SETTINGS) === false) { + throw new \RuntimeException('Settings are not defined.'); + } + + // $SETTINGS is set then read folder + if (is_dir($SETTINGS['path_to_files_folder']) === true) { + $dir = opendir($SETTINGS['path_to_files_folder']); + if ($dir !== false) { + //delete file FILES + while (false !== ($f = readdir($dir))) { + if ($f !== '.' && $f !== '..' && $f !== '.htaccess') { + $filePath = $SETTINGS['path_to_files_folder'] . '/' . $f; + if (file_exists($filePath) && ((time() - filectime($filePath)) > 604800)) { + fileDelete($dir . '/' . $f, $SETTINGS); } } - - //Close dir - closedir($dir); } + + //Close dir + closedir($dir); } + } + + //read folder UPLOAD + if (is_dir($SETTINGS['path_to_upload_folder']) === true) { + $dir = opendir($SETTINGS['path_to_upload_folder']); - //read folder UPLOAD - if (is_dir($SETTINGS['path_to_upload_folder']) === true) { - $dir = opendir($SETTINGS['path_to_upload_folder']); - - if ($dir !== false) { - //delete file - while (false !== ($f = readdir($dir))) { - if ($f !== '.' && $f !== '..') { - if (strpos($f, '_delete.') > 0) { - fileDelete($SETTINGS['path_to_upload_folder'] . '/' . $f, $SETTINGS); - } + if ($dir !== false) { + //delete file + while (false !== ($f = readdir($dir))) { + if ($f !== '.' && $f !== '..') { + if (strpos($f, '_delete.') > 0) { + fileDelete($SETTINGS['path_to_upload_folder'] . '/' . $f, $SETTINGS); } } - //Close dir - closedir($dir); } + //Close dir + closedir($dir); } } - } \ No newline at end of file diff --git a/sources/admin.queries.php b/sources/admin.queries.php index 235a326c4..b348ccf39 100755 --- a/sources/admin.queries.php +++ b/sources/admin.queries.php @@ -1800,7 +1800,9 @@ $SETTINGS['cpassman_url'].'/'.DUO_CALLBACK ); } catch (DuoException $e) { - error_log('TEAMPASS Error - duo config - '.$e->getMessage()); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('TEAMPASS Error - duo config - '.$e->getMessage()); + } // deepcode ignore ServerLeak: Data is encrypted before being sent echo prepareExchangedData( array( @@ -1816,16 +1818,9 @@ try { $duo_client->healthCheck(); } catch (DuoException $e) { - /*if ($SETTINGS['duo_failmode'] == "OPEN") { - # If we're failing open, errors in 2FA still allow for success - $duo_error = $lang->get('duo_error_failopen'); - $data["duo_check"] = "open"; - } else { - # Duo has failed and is unavailable, redirect user to the login page - $duo_error = $lang->get('duo_error_secure'); - $data["duo_check"] = "failed"; - }*/ - error_log('TEAMPASS Error - duo config - '.$e->getMessage()); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('TEAMPASS Error - duo config - '.$e->getMessage()); + } // deepcode ignore ServerLeak: Data is encrypted before being sent echo prepareExchangedData( array( diff --git a/sources/backups.queries.php b/sources/backups.queries.php index dda4e20cc..12e118f16 100755 --- a/sources/backups.queries.php +++ b/sources/backups.queries.php @@ -352,7 +352,7 @@ $post_totalSize = filesize($post_backupFile); } - if ($handle) { + if ($handle !== false) { // Déplacer le pointeur de fichier à l'offset actuel fseek($handle, $post_offset); diff --git a/sources/core.php b/sources/core.php index df11f30f6..660d89d00 100755 --- a/sources/core.php +++ b/sources/core.php @@ -365,16 +365,14 @@ function() { $server_cert = openssl_x509_parse($server['ssl_server_cert']); $cert_name = $server_cert['name']; $cert_issuer = ''; - /** @var array $issuer */ - $issuer = $server_cert['issuer']; + $issuer = (array) $server_cert['issuer']; // $issuer is always an array (according to the structure of $server_cert) - if (is_array($issuer)) { - foreach ($issuer as $key => $value) { - if (is_array($value) === false) { - $cert_issuer .= "/{$key}={$value}"; - } + foreach ($issuer as $key => $value) { + if (is_array($value) === false) { + $cert_issuer .= "/{$key}={$value}"; } } + if (isset($cert_name) === true && empty($cert_name) === false && $cert_name !== $cert_issuer) { if (isset($server['https'])) { header('Strict-Transport-Security: max-age=500'); diff --git a/sources/downloadFile.php b/sources/downloadFile.php index 1ce536d9e..08dba32f9 100755 --- a/sources/downloadFile.php +++ b/sources/downloadFile.php @@ -92,15 +92,33 @@ // --------------------------------- // // Prepare GET variables -$get_filename = (string) $antiXss->xss_clean($request->query->get('name')); -$get_fileid = $antiXss->xss_clean($request->query->get('fileid')); -$get_pathIsFiles = (string) $antiXss->xss_clean($request->query->get('pathIsFiles')); +$getData = dataSanitizer( + [ + 'filename' => $request->query->get('name'), + 'fileid' => $request->query->get('fileid'), + 'pathIsFiles' => $request->query->get('pathIsFiles'), + ], + [ + 'filename' => 'trim|escape', + 'fileid' => 'cast:integer', + 'pathIsFiles' => 'trim|escape', + ] +); +$get_filename = (string) $antiXss->xss_clean($getData['filename']); +$get_fileid = (int) $antiXss->xss_clean($getData['fileid']); +$get_pathIsFiles = (string) $antiXss->xss_clean($getData['pathIsFiles']); // Remove newline characters from the filename $get_filename = str_replace(array("\r", "\n"), '', $get_filename); -// prepare Encryption class calls -header('Content-disposition: attachment; filename=' . rawurldecode(basename($get_filename))); +// Validate the filename to ensure it does not contain unwanted characters +$get_filename = preg_replace('/[^a-zA-Z0-9_\.-]/', '', basename($get_filename)); + +// Escape quotes to prevent header injection +$get_filename = str_replace('"', '\"', $get_filename); + +// Use Content-Disposition header with double quotes around filename +header('Content-Disposition: attachment; filename="' . rawurldecode($get_filename) . '"'); header('Content-Type: application/octet-stream'); header('Cache-Control: must-revalidate, no-cache, no-store'); header('Expires: 0'); @@ -164,8 +182,12 @@ if (empty($fileContent) === true) { // deepcode ignore PT: File and path are secured directly inside the function decryptFile() readfile($filePath); // Read the file from disk - } else { + } else if (is_string($fileContent)) { exit(base64_decode($fileContent)); + } else { + // $fileContent is not a string + echo 'ERROR_No_file_found'; + exit; } exit; } else { diff --git a/sources/folders.class.php b/sources/folders.class.php index 9dafd03fc..1dedbfe17 100644 --- a/sources/folders.class.php +++ b/sources/folders.class.php @@ -311,7 +311,6 @@ private function updateTimestamp() */ private function rebuildFolderTree($user_is_admin, $title, $parent_id, $isPersonal, $user_id, $newId) { - $session = SessionManager::getSession(); $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title'); $tree->rebuild(); diff --git a/sources/folders.queries.php b/sources/folders.queries.php index d94505490..633871f34 100755 --- a/sources/folders.queries.php +++ b/sources/folders.queries.php @@ -336,8 +336,7 @@ ]; $inputData = dataSanitizer( $data, - $filters, - $SETTINGS['cpassman_dir'] + $filters ); // Init @@ -589,8 +588,7 @@ ]; $inputData = dataSanitizer( $data, - $filters, - $SETTINGS['cpassman_dir'] + $filters ); // Check if parent folder is personal @@ -1271,6 +1269,17 @@ $SETTINGS['path_to_upload_folder'], decryptUserObjectKey($file['share_key'], $session->get('user-private_key')) ); + if (!is_string($fileContent)) { + // Case where $fileContent is not a string + echo prepareExchangedData( + array( + 'error' => true, + 'message' => 'Invalid file content', + ), + 'encode' + ); + break; + } // Step2 - create file // deepcode ignore InsecureHash: Is not a password, just a random string for a file name @@ -1289,7 +1298,7 @@ } fwrite( $outstream, - base64_decode($fileContent) + base64_decode((string) $fileContent) ); // Step3 - encrypt the file diff --git a/sources/identify.php b/sources/identify.php index 56a39e456..8e8165bce 100755 --- a/sources/identify.php +++ b/sources/identify.php @@ -2448,7 +2448,6 @@ function createOauth2User( int $userLdapHasBeenCreated ): array { - //error_log($SETTINGS['oauth2_enabled']." -- ".$username." -- ".$userInfo['oauth2_user_to_be_created']." -- ".$userLdapHasBeenCreated); // Prepare creating the new oauth2 user in Teampass if ((int) $SETTINGS['oauth2_enabled'] === 1 && $username !== 'admin' @@ -2487,7 +2486,7 @@ function createOauth2User( // Complete $userInfo $userInfo['has_been_created'] = 1; - error_log("--- USER CREATED ---"); + if (WIP === true) error_log("--- USER CREATED ---"); return [ 'error' => false, @@ -2511,7 +2510,7 @@ function createOauth2User( } // Oauth2 user already exists and authenticated - error_log("--- USER AUTHENTICATED ---"); + if (WIP === true) error_log("--- USER AUTHENTICATED ---"); $userInfo['has_been_created'] = 0; return [ 'error' => false, diff --git a/sources/import.queries.php b/sources/import.queries.php index 2b56f037b..460103b0b 100755 --- a/sources/import.queries.php +++ b/sources/import.queries.php @@ -342,9 +342,7 @@ } // Clean each array entry - if (is_array($post_items) === true) { - array_walk_recursive($post_items, 'cleanOutput'); - } + array_walk_recursive($post_items, 'cleanOutput'); // Loop on array foreach ($post_items as $item) { @@ -619,9 +617,6 @@ function handleGroups($groups, string $previousFolder, array $newItemsToAdd, int $groups = [$groups]; } - // Save the current folder ID to restore it after recursion - $currentFolderId = $previousFolder; - foreach ($groups as $group) { // Add the current group (folder) to the list $newItemsToAdd['folders'][] = [ diff --git a/sources/items.queries.php b/sources/items.queries.php index cf0d48397..88bd81caf 100755 --- a/sources/items.queries.php +++ b/sources/items.queries.php @@ -1089,7 +1089,9 @@ $session->get('user-id') ); if (DB::count() === 0) { - if (LOG_TO_SERVER === true) error_log('TEAMPASS | user '.$session->get('user-id').' has no sharekey for item '.$inputData['itemId']); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('TEAMPASS | user '.$session->get('user-id').' has no sharekey for item '.$inputData['itemId']); + } echo (string) prepareExchangedData( array( 'error' => true, @@ -2081,7 +2083,9 @@ // Remove the edition lock if no encryption steps are needed if ($encryptionTaskIsRequested === false) { - error_log('Remove the edition lock if no encryption steps are needed'); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('Remove the edition lock if no encryption steps are needed'); + } DB::delete( prefixTable('items_edition'), 'item_id = %i AND user_id = %i', @@ -2453,7 +2457,6 @@ // Create new task for the new item // If it is not a personnal one if ((int) $dataDestination['personal_folder'] !== 1) { - //error_log('item_copy' . print_r($itemDataArray, true)); storeTask( 'item_copy', $session->get('user-id'), @@ -2744,7 +2747,6 @@ } } else { $pwIsEmptyNormal == true; - //error_log('userKey: ' . print_r($userKey, true)); $decryptedObject = decryptUserObjectKey($userKey['share_key'], $session->get('user-private_key')); // if null then we have an error. // suspecting bad password @@ -6441,6 +6443,18 @@ decryptUserObjectKey($file_info['share_key'], $session->get('user-private_key')) ); + // Check error + if (isset($fileContent['error']) === true) { + echo (string) prepareExchangedData( + array( + 'error' => true, + 'message' => $fileContent['message'], + ), + 'encode' + ); + break; + } + // Encrypt data to return echo (string) prepareExchangedData( array( diff --git a/sources/ldap.queries.php b/sources/ldap.queries.php index 33f44a2cd..961060e5c 100755 --- a/sources/ldap.queries.php +++ b/sources/ldap.queries.php @@ -157,7 +157,9 @@ throw new Exception("Unsupported LDAP type: " . $SETTINGS['ldap_type']); } } catch (Exception $e) { - error_log('TEAMPASS Error - ldap - '.$e->getMessage()); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('TEAMPASS Error - ldap - '.$e->getMessage()); + } // deepcode ignore ServerLeak: No important data is sent and is encrypted before being sent echo prepareExchangedData( array( @@ -191,11 +193,9 @@ } catch (\LdapRecord\Query\ObjectNotFoundException $e) { $error = $e->getDetailedError(); - if ($error) { + if ($error && defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { error_log('TEAMPASS Error - LDAP - '.$error->getErrorCode()." - ".$error->getErrorMessage(). " - ".$error->getDiagnosticMessage()); - } else { - error_log('TEAMPASS Error - LDAP - Code: '.$e->getCode().' - Message: '.$e->getMessage()); - } + } // deepcode ignore ServerLeak: No important data is sent and is encrypted before being sent echo prepareExchangedData( array( @@ -231,10 +231,8 @@ } } catch (\LdapRecord\Query\ObjectNotFoundException $e) { $error = $e->getDetailedError(); - if ($error) { + if ($error && defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { error_log('TEAMPASS Error - LDAP - '.$error->getErrorCode()." - ".$error->getErrorMessage(). " - ".$error->getDiagnosticMessage()); - } else { - error_log('TEAMPASS Error - LDAP - Code: '.$e->getCode().' - Message: '.$e->getMessage()); } // deepcode ignore ServerLeak: No important data is sent and is encrypted before being sent echo prepareExchangedData( diff --git a/sources/logs.datatables.php b/sources/logs.datatables.php index 1ea6ed565..b437877bd 100755 --- a/sources/logs.datatables.php +++ b/sources/logs.datatables.php @@ -140,7 +140,7 @@ foreach ($aColumns as $column) { $sWhere .= $column . " LIKE '%" . $searchValue . "%' OR "; } - $sWhere = substr_replace($sWhere, '', -3) . ')'; + $sWhere = substr_replace((string) $sWhere, '', -3) . ')'; } } diff --git a/sources/main.functions.php b/sources/main.functions.php index 2d2d8850e..e676ad25c 100755 --- a/sources/main.functions.php +++ b/sources/main.functions.php @@ -1277,6 +1277,7 @@ function prepareExchangedData($data, string $type, ?string $key = null) return $data; } + if ($type === 'decode' && is_array($data) === false) { // Decrypt if needed if ($session->get('encryptClientServer') === 1) { @@ -1289,9 +1290,13 @@ function prepareExchangedData($data, string $type, ?string $key = null) $data = html_entity_decode(html_entity_decode($data)); } - // Return data array - return json_decode($data, true); + // Check if $data is a valid string before json_decode + if (is_string($data) && !empty($data)) { + // Return data array + return json_decode($data, true); + } } + return ''; } @@ -2404,7 +2409,7 @@ function encryptUserObjectKey(string $key, string $publicKey): string $rsa->loadKey($decodedPublicKey); // Encrypt $encrypted = $rsa->encrypt(base64_decode($key)); - if ($encrypted === false) { + if (empty($encrypted)) { // Check if key is empty or null throw new RuntimeException("Error while encrypting key."); } // Return @@ -2449,7 +2454,9 @@ function decryptUserObjectKey(string $key, string $privateKey): string return ''; } } catch (Exception $e) { - error_log('TEAMPASS Error - ldap - '.$e->getMessage()); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('TEAMPASS Error - ldap - '.$e->getMessage()); + } return 'Exception: could not decrypt object'; } } @@ -2503,9 +2510,9 @@ function encryptFile(string $fileInName, string $fileInPath): array * @param string $filePath Path to file * @param string $key Key to use * - * @return string + * @return string|array */ -function decryptFile(string $fileName, string $filePath, string $key): string +function decryptFile(string $fileName, string $filePath, string $key): string|array { if (! defined('FILE_BUFFER_SIZE')) { define('FILE_BUFFER_SIZE', 128 * 1024); @@ -2525,7 +2532,15 @@ function decryptFile(string $fileName, string $filePath, string $key): string $cipher->disablePadding(); // Get file content $safeFilePath = realpath($filePath . '/' . TP_FILE_PREFIX . $safeFileName); - $ciphertext = file_get_contents(filter_var($safeFilePath, FILTER_SANITIZE_URL)); + if ($safeFilePath !== false && file_exists($safeFilePath)) { + $ciphertext = file_get_contents(filter_var($safeFilePath, FILTER_SANITIZE_URL)); + } else { + // Handle the error: file doesn't exist or path is invalid + return [ + 'error' => true, + 'message' => 'This file has not been found.', + ]; + } if (WIP) error_log('DEBUG: File image url -> '.filter_var($safeFilePath, FILTER_SANITIZE_URL)); @@ -2644,7 +2659,6 @@ function storeUsersShareKey( } } else { // Create sharekey for each user - //error_log('Building QUERY - all_users_except_id: '. $all_users_except_id); //DB::debugmode(true); $users = DB::query( 'SELECT id, public_key @@ -2773,10 +2787,8 @@ function ldapCheckUserPassword(string $login, string $password, array $SETTINGS) $connection->connect(); } catch (\LdapRecord\Auth\BindException $e) { $error = $e->getDetailedError(); - if ($error) { + if ($error && defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { error_log('TEAMPASS Error - LDAP - '.$error->getErrorCode()." - ".$error->getErrorMessage(). " - ".$error->getDiagnosticMessage()); - } else { - error_log('TEAMPASS Error - LDAP - Code: '.$e->getCode().' - Message: '.$e->getMessage()); } // deepcode ignore ServerLeak: No important data is sent echo 'An error occurred.'; @@ -2792,10 +2804,8 @@ function ldapCheckUserPassword(string $login, string $password, array $SETTINGS) } } catch (\LdapRecord\Auth\BindException $e) { $error = $e->getDetailedError(); - if ($error) { + if ($error && defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { error_log('TEAMPASS Error - LDAP - '.$error->getErrorCode()." - ".$error->getErrorMessage(). " - ".$error->getDiagnosticMessage()); - } else { - error_log('TEAMPASS Error - LDAP - Code: '.$e->getCode().' - Message: '.$e->getMessage()); } // deepcode ignore ServerLeak: No important data is sent echo 'An error occurred.'; @@ -3526,16 +3536,12 @@ function upgradeRequired(): bool 'admin', 'upgrade_timestamp' ); - - // if not exists then error - if (is_null($val) === true || count($val) === 0 || defined('UPGRADE_MIN_DATE') === false) return true; - - // if empty or too old then error - if (empty($val['valeur']) === true || (int) $val['valeur'] < (int) UPGRADE_MIN_DATE) { - return true; - } - return false; + // Check if upgrade is required + return ( + is_null($val) || count($val) === 0 || !defined('UPGRADE_MIN_DATE') || + empty($val['valeur']) || (int) $val['valeur'] < (int) UPGRADE_MIN_DATE + ); } /** @@ -3624,8 +3630,9 @@ function handleUserKeys( } } catch (Exception $e) { // Show error message to user and log event - error_log('ERROR: User '.$userId.' - '.$e->getMessage()); - + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('ERROR: User '.$userId.' - '.$e->getMessage()); + } return prepareExchangedData([ 'error' => true, 'message' => $lang->get('pw_encryption_error'), @@ -4002,7 +4009,7 @@ function createTaskForItem( $taskName = [$taskName]; } foreach($taskName as $task) { - error_log('createTaskForItem - task: '.$task); + if (WIP === true) error_log('createTaskForItem - task: '.$task); switch ($task) { case 'item_password': @@ -4329,7 +4336,12 @@ function sendMailToUser( $emailSettings = new EmailSettings($SETTINGS); $emailService = new EmailService(); - if (count($post_replace) > 0 && is_null($post_replace) === false) { + // Sanitize inputs + $post_receipt = filter_var($post_receipt, FILTER_SANITIZE_EMAIL); + $post_subject = htmlspecialchars($post_subject, ENT_QUOTES, 'UTF-8'); + $post_body = htmlspecialchars($post_body, ENT_QUOTES, 'UTF-8'); + + if (count($post_replace) > 0) { $post_body = str_replace( array_keys($post_replace), array_values($post_replace), @@ -4337,8 +4349,11 @@ function sendMailToUser( ); } + // Remove newlines to prevent header injection + $post_body = str_replace(array("\r", "\n"), '', $post_body); + if ($immediate_email === true) { - + // Send email $ret = $emailService->sendMail( $post_subject, $post_body, diff --git a/sources/main.queries.php b/sources/main.queries.php index 636e97834..fad5905f6 100755 --- a/sources/main.queries.php +++ b/sources/main.queries.php @@ -136,55 +136,66 @@ function mainQuery(array $SETTINGS) // User's language loading $session = SessionManager::getSession(); $lang = new Language($session->get('user-language') ?? 'english'); + $request = SymfonyRequest::createFromGlobals(); - // Prepare post variables - $post_key = filter_input(INPUT_POST, 'key', FILTER_SANITIZE_FULL_SPECIAL_CHARS); - $post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS); - $post_type_category = filter_input(INPUT_POST, 'type_category', FILTER_SANITIZE_FULL_SPECIAL_CHARS); - $post_data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES); - + // Prepare POST variables + $inputData = dataSanitizer( + [ + 'type' => $request->request->filter('type', '', FILTER_SANITIZE_SPECIAL_CHARS), + 'data' => $request->request->filter('data', '', FILTER_SANITIZE_SPECIAL_CHARS), + 'key' => $request->request->filter('key', '', FILTER_SANITIZE_SPECIAL_CHARS), + 'type_category' => $request->request->filter('type_category', '', FILTER_SANITIZE_SPECIAL_CHARS), + ], + [ + 'type' => 'trim|escape', + 'data' => 'trim|escape', + 'key' => 'trim|escape', + 'type_category' => 'trim|escape', + ] + ); + // Check KEY - if (isValueSetNullEmpty($post_key) === true) { + if (isValueSetNullEmpty($inputData['key']) === true) { echo prepareExchangedData( array( 'error' => true, 'message' => $lang->get('key_is_not_correct'), ), 'encode', - $post_key + $inputData['key'] ); return false; } // decrypt and retreive data in JSON format - $dataReceived = empty($post_data) === false ? prepareExchangedData( - $post_data, + $dataReceived = empty($inputData['data']) === false ? prepareExchangedData( + $inputData['data'], 'decode' ) : ''; - switch ($post_type_category) { + switch ($inputData['type_category']) { case 'action_password': - echo passwordHandler($post_type, $dataReceived, $SETTINGS); + echo passwordHandler($inputData['type'], $dataReceived, $SETTINGS); break; case 'action_user': - echo userHandler($post_type, $dataReceived, $SETTINGS, $post_key); + echo userHandler($inputData['type'], $dataReceived, $SETTINGS, $inputData['key']); break; case 'action_mail': - echo mailHandler($post_type, $dataReceived, $SETTINGS); + echo mailHandler($inputData['type'], $dataReceived, $SETTINGS); break; case 'action_key': // deepcode ignore ServerLeak: All cases handled by keyHandler return an encrypted string that is sent back to the client - echo keyHandler($post_type, $dataReceived, $SETTINGS); + echo keyHandler($inputData['type'], $dataReceived, $SETTINGS); break; case 'action_system': - echo systemHandler($post_type, $dataReceived, $SETTINGS); + echo systemHandler($inputData['type'], $dataReceived, $SETTINGS); break; case 'action_utils': - echo utilsHandler($post_type, $dataReceived, $SETTINGS); + echo utilsHandler($inputData['type'], $dataReceived, $SETTINGS); break; } @@ -291,13 +302,12 @@ function userHandler(string $post_type, array|null|string $dataReceived, array $ (int) $session->get('user-can_manage_all_users') !== 1 && !in_array($post_type, $all_users_can_access)) { - echo prepareExchangedData( + return prepareExchangedData( array( 'error' => true, ), 'encode' ); - exit; } if (isset($dataReceived['user_id'])) { @@ -392,8 +402,7 @@ function userHandler(string $post_type, array|null|string $dataReceived, array $ (string) filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string) filter_var($dataReceived['pwd'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string) filter_var($dataReceived['token'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), - $SETTINGS, - (string) $post_key + $SETTINGS ); /* @@ -467,7 +476,6 @@ function mailHandler(string $post_type, /*php8 array|null|string */$dataReceived ), 'encode' ); - break; } // Only administrators and managers can send mails @@ -1802,7 +1810,9 @@ function changePrivateKeyEncryptionPassword( // Should fail here to avoid break user private key. if (strlen($privateKey) === 0 || strlen($hashedPrivateKey) < 30) { - error_log("Error reencrypt user private key. User ID: {$post_user_id}, Given OTP: '{$post_current_code}'"); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log("Error reencrypt user private key. User ID: {$post_user_id}, Given OTP: '{$post_current_code}'"); + } return prepareExchangedData( array( 'error' => true, diff --git a/sources/roles.queries.php b/sources/roles.queries.php index b19dcba7f..727689443 100755 --- a/sources/roles.queries.php +++ b/sources/roles.queries.php @@ -726,7 +726,9 @@ throw new Exception("Unsupported LDAP type: " . $SETTINGS['ldap_type']); } } catch (Exception $e) { - error_log('TEAMPASS Error - ldap - '.$e->getMessage()); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('TEAMPASS Error - ldap - '.$e->getMessage()); + } // deepcode ignore ServerLeak: No important data is sent and it is encrypted before sending echo prepareExchangedData(array( 'error' => true, @@ -741,7 +743,6 @@ } else { // Handle successful retrieval of groups // exists in Teampass - //error_log("Error: " . print_r($groupsData['userGroups'], true)); foreach($groupsData['userGroups'] as $key => $group) { $role_detail = DB::queryfirstrow( 'SELECT a.increment_id as increment_id, a.role_id as role_id, r.title as title diff --git a/sources/tasks.queries.php b/sources/tasks.queries.php index 8df2ef386..a707d6819 100755 --- a/sources/tasks.queries.php +++ b/sources/tasks.queries.php @@ -90,14 +90,24 @@ // --------------------------------- // // Prepare POST variables -$post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS); -$post_data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES); -$post_key = filter_input(INPUT_POST, 'key', FILTER_SANITIZE_FULL_SPECIAL_CHARS); -$post_task = filter_input(INPUT_POST, 'task', FILTER_SANITIZE_FULL_SPECIAL_CHARS); +$inputData = dataSanitizer( + [ + 'type' => $request->request->filter('type', '', FILTER_SANITIZE_SPECIAL_CHARS), + //'data' => $request->request->filter('data', '', FILTER_SANITIZE_SPECIAL_CHARS), + 'key' => $request->request->filter('key', '', FILTER_SANITIZE_SPECIAL_CHARS), + 'task' => $request->request->filter('task', '', FILTER_SANITIZE_SPECIAL_CHARS), + ], + [ + 'type' => 'trim|escape', + //'data' => 'trim|escape', + 'key' => 'trim|escape', + 'type_category' => 'trim|escape', + ] +); -if (null !== $post_type) { +if (null !== $inputData['type']) { // Do checks - if ($post_key !== $session->get('key')) { + if ($inputData['key'] !== $session->get('key')) { echo prepareExchangedData( array( 'error' => true, @@ -120,9 +130,9 @@ // Get PHP binary $phpBinaryPath = getPHPBinary(); - switch ($post_type) { + switch ($inputData['type']) { case 'perform_task': - echo performTask($post_task, $phpBinaryPath, $SETTINGS['date_format'].' '.$SETTINGS['time_format']); + echo performTask($inputData['task'], $phpBinaryPath, $SETTINGS['date_format'].' '.$SETTINGS['time_format']); break; @@ -316,7 +326,9 @@ function performTask(string $task, string $phpBinaryPath, string $datetimeFormat $error = false; } catch (ProcessFailedException $exception) { $error = true; - error_log('TEAMPASS Error - ldap - '.$exception->getMessage()); + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log('TEAMPASS Error - ldap - '.$exception->getMessage()); + } $output = 'An error occurred.'; } diff --git a/sources/tree.php b/sources/tree.php index 9e16ff1eb..11aafc2c8 100755 --- a/sources/tree.php +++ b/sources/tree.php @@ -421,7 +421,6 @@ function handleNode( (int) $nbItemsInSubfolders, (int) $nbSubfolders, $inputData['limitedFolders'], - (int) $SETTINGS['show_only_accessible_folders'], isset($SETTINGS['tree_counters']) === true && isset($SETTINGS['enable_tasks_manager']) === true && (int) $SETTINGS['enable_tasks_manager'] === 1 && (int) $SETTINGS['tree_counters'] === 1 ? 1 : 0, (bool) $inputData['userReadOnly'], $listFoldersLimitedKeys, @@ -575,7 +574,6 @@ function prepareNodeJson( * @param integer $nbItemsInSubfolders * @param integer $nbSubfolders * @param array $session_list_folders_limited - * @param integer $show_only_accessible_folders * @param integer $tree_counters * @param bool $session_user_read_only * @param array $listFoldersLimitedKeys @@ -594,7 +592,6 @@ function prepareNodeData( int $nbItemsInSubfolders, int $nbSubfolders, array $session_list_folders_limited, - int $show_only_accessible_folders, int $tree_counters, bool $session_user_read_only, array $listFoldersLimitedKeys, diff --git a/sources/upload.attachments.php b/sources/upload.attachments.php index 147c0cba3..82c0bb0f7 100755 --- a/sources/upload.attachments.php +++ b/sources/upload.attachments.php @@ -29,7 +29,7 @@ * @see https://www.teampass.net */ -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Request as RequestLocal; use TeampassClasses\SessionManager\SessionManager; use TeampassClasses\Language\Language; use TeampassClasses\PerformChecks\PerformChecks; @@ -39,7 +39,7 @@ // Load functions require_once 'main.functions.php'; $session = SessionManager::getSession(); -$request = Request::createFromGlobals(); +$request = RequestLocal::createFromGlobals(); // init loadClasses('DB'); $lang = new Language(); diff --git a/sources/upload.files.php b/sources/upload.files.php index 0a3c09856..f441761e0 100755 --- a/sources/upload.files.php +++ b/sources/upload.files.php @@ -29,13 +29,10 @@ * @see https://www.teampass.net */ - -use voku\helper\AntiXSS; -use TeampassClasses\NestedTree\NestedTree; use TeampassClasses\SessionManager\SessionManager; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Request as RequestLocal; +use Symfony\Component\HttpFoundation\File\Exception\FileException; use TeampassClasses\Language\Language; -use EZimuel\PHPSecureSession; use TeampassClasses\PerformChecks\PerformChecks; use TeampassClasses\ConfigManager\ConfigManager; @@ -45,7 +42,7 @@ // init loadClasses('DB'); $session = SessionManager::getSession(); -$request = Request::createFromGlobals(); +$request = RequestLocal::createFromGlobals(); $lang = new Language(); @@ -185,69 +182,79 @@ return false; } -// Validate the file size (Warning: the largest files supported by this code is 2GB) -$file_size = @filesize($_FILES['file']['tmp_name']); -if ($file_size === false || $file_size > $max_file_size_in_bytes) { - echo handleUploadError('File exceeds the maximum allowed size'); - return false; -} -if ($file_size <= 0) { - echo handleUploadError('File size outside allowed lower bound'); - return false; -} +// Validate file size (Warning: the largest files supported by this code is 2GB) +$file = $request->files->get('file'); -// 5 minutes execution time -set_time_limit(5 * 60); +if ($file) { + // Get the size of the uploaded file + $file_size = $file->getSize(); -// Validate the upload -if (isset($_FILES['file']) === false) { - echo handleUploadError('No upload found in $_FILES for Filedata'); - return false; -} elseif ( - isset($_FILES['file']['error']) === true - && $_FILES['file']['error'] != 0 -) { - echo handleUploadError($uploadErrors[$_FILES['Filedata']['error']]); - return false; -} elseif ( - isset($_FILES['file']['tmp_name']) === false - || @is_uploaded_file($_FILES['file']['tmp_name']) === false -) { - echo handleUploadError('Upload failed is_uploaded_file test.'); - return false; -} elseif (isset($_FILES['file']['name']) === false) { - echo handleUploadError('File has no name.'); - return false; -} + if ($file_size === false || $file_size > $max_file_size_in_bytes) { + echo handleUploadError('File exceeds the maximum allowed size'); + return false; + } + + if ($file_size <= 0) { + echo handleUploadError('File size outside allowed lower bound'); + return false; + } -// Validate file name (for our purposes we'll just remove invalid characters) -$file_name = preg_replace('/[^a-zA-Z0-9-_\.]/', '', strtolower(basename($_FILES['file']['name']))); -if (strlen($file_name) == 0 || strlen($file_name) > $MAX_FILENAME_LENGTH) { - echo handleUploadError('Invalid file name: ' . $file_name . '.'); - return false; -} + // 5 minutes execution time + set_time_limit(5 * 60); -// Validate file extension -$ext = strtolower( - getFileExtension( - filter_var($_FILES['file']['name'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) - ) -); -if ( - in_array( - $ext, - explode( - ',', - $SETTINGS['upload_docext'] . ',' . $SETTINGS['upload_imagesext'] . - ',' . $SETTINGS['upload_pkgext'] . ',' . $SETTINGS['upload_otherext'] - ) - ) === false - && $post_type_upload !== 'import_items_from_keepass' - && $post_type_upload !== 'import_items_from_csv' - && $post_type_upload !== 'restore_db' - && $post_type_upload !== 'upload_profile_photo' -) { - echo handleUploadError('Invalid file extension.'); + // Validate upload + if ($file->getError() !== UPLOAD_ERR_OK) { + echo handleUploadError($uploadErrors[$file->getError()]); + return false; + } + + // Validate file name (for our purposes we'll just remove invalid characters) + $file_name = preg_replace('/[^a-zA-Z0-9-_\.]/', '', strtolower(basename($file->getClientOriginalName()))); + + if (strlen($file_name) == 0 || strlen($file_name) > $MAX_FILENAME_LENGTH) { + error_log('Invalid file name: ' . $file_name . '.'); + echo handleUploadError('Invalid file name provided.'); + return false; + } + + // Check that file is a valid string + $originalName = $file->getClientOriginalName(); + if (is_string($originalName)) { + // Get file extension + $ext = pathinfo($originalName, PATHINFO_EXTENSION); + if (is_string($ext)) { + $ext = strtolower($ext); + } else { + // Case where the file extension is not a string + error_log('Invalid file name: ' . $file_name . '.'); + echo handleUploadError('Invalid file extension.'); + return false; + } + } else { + // Case where the file name is not a string + error_log('Invalid file name: ' . $file_name . '.'); + echo handleUploadError('Invalid file.'); + return false; + } + + // Validate against a list of allowed extensions + $allowed_extensions = explode( + ',', + $SETTINGS['upload_docext'] . ',' . $SETTINGS['upload_imagesext'] . + ',' . $SETTINGS['upload_pkgext'] . ',' . $SETTINGS['upload_otherext'] + ); + if ( + !in_array($ext, $allowed_extensions) + && $post_type_upload !== 'import_items_from_keepass' + && $post_type_upload !== 'import_items_from_csv' + && $post_type_upload !== 'restore_db' + && $post_type_upload !== 'upload_profile_photo' + ) { + echo handleUploadError('Invalid file extension.'); + return false; + } +} else { + echo handleUploadError('No upload found for Filedata'); return false; } @@ -257,13 +264,11 @@ return false; } -// Clean the fileName for security reasons -$fileName = preg_replace('/[^a-zA-Z0-9-_\.]/', '', strtolower(basename($fileName))); - // Make sure the fileName is unique but only if chunking is disabled if ($chunks < 2 && file_exists($targetDir . DIRECTORY_SEPARATOR . $fileName)) { - $fileNameA = substr($fileName, 0, strlen($ext)); - $fileNameB = substr($fileName, strlen($ext)); + // $ext is guaranteed to be a string due to prior checks + $fileNameA = substr($fileName, 0, strlen(/** @scrutinizer ignore-type */$ext)); + $fileNameB = substr($fileName, strlen(/** @scrutinizer ignore-type */$ext)); $count = 1; while (file_exists($targetDir . DIRECTORY_SEPARATOR . $fileNameA . '_' . $count . $fileNameB)) { @@ -286,12 +291,12 @@ // Remove old temp files if ($cleanupTargetDir && is_dir($targetDir) && ($dir = opendir($targetDir))) { - while (($file = readdir($dir)) !== false) { - $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file; + while (($fileClean = readdir($dir)) !== false) { + $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $fileClean; // Remove temp file if it is older than the max age and is not the current file if ( - preg_match('/\.part$/', $file) + preg_match('/\.part$/', $fileClean) && (filemtime($tmpfilePath) < time() - $maxFileAge) && ($tmpfilePath != "{$filePath}.part") ) { @@ -316,28 +321,69 @@ // Handle non multipart uploads older WebKit versions didn't support multipart in HTML5 if (strpos($contentType, 'multipart') !== false) { - if (isset($_FILES['file']['tmp_name']) && is_uploaded_file($_FILES['file']['tmp_name'])) { - // Open temp file - // deepcode ignore PT: $filePath is escaped and secured previously - $out = fopen("{$filePath}.part", $chunk == 0 ? 'wb' : 'ab'); - error_log($_FILES['file']['tmp_name']); - if ($out !== false) { - // Read binary input stream and append it to temp file - $in = fopen($_FILES['file']['tmp_name'], 'rb'); - - if ($in !== false) { - while ($buff = fread($in, 4096)) { - fwrite($out, $buff); + if ($file && $file->isValid()) { + // Path for the temporary file + $tempFilePath = "{$filePath}.part"; + + // Open the output file + try { + $out = fopen($tempFilePath, $chunk == 0 ? 'wb' : 'ab'); + + if ($out === false) { + throw new FileException('Failed to open output stream.'); + } + + // Open the uploaded temporary file + // But before we will move the file to a temporary location + // Temporary path of the uploaded file + $tmpFilePath = $file->getPathname(); + + // Has the file being uploaded via HPPT POST + if (is_uploaded_file($tmpFilePath)) { + // Clear file name + $fileName = basename($file->getClientOriginalName()); + $fileName = preg_replace('/[^a-zA-Z0-9_\.-]/', '_', $fileName); + + // Safe destination folder + $uploadDir = realpath($SETTINGS['path_to_upload_folder']); + $destinationPath = $uploadDir . DIRECTORY_SEPARATOR . $fileName; + + if (move_uploaded_file($tmpFilePath, $destinationPath)) { + // Open the moved file in read mode + $in = fopen($destinationPath, 'rb'); + } else { + // Do we have errors + echo handleUploadError('Error while moving the uploaded file.'); + exit; } } else { - echo handleUploadError('Failed to open input stream ' . $SETTINGS['path_to_files_folder'] . '.'); - return false; + // file has not being uploaded via HTTP POST + echo handleUploadError('FErreur : fichier non valide.'); + exit; + } + + if ($in === false) { + throw new FileException('Failed to open input stream.'); + } + + // Read and write to the output file + while ($buff = fread($in, 4096)) { + fwrite($out, $buff); } + + // Close the files fclose($in); fclose($out); - fileDelete($_FILES['file']['tmp_name'], $SETTINGS); - } else { - echo handleUploadError('Failed to open output stream ' . $SETTINGS['path_to_files_folder'] . '.'); + + // Delete temporary file if needed + fileDelete($destinationPath, $SETTINGS); + + } catch (FileException $e) { + // On error log + if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { + error_log($e->getMessage()); + } + echo handleUploadError('File processing failed.'); return false; } } else { diff --git a/sources/users.datatable.php b/sources/users.datatable.php index 6b5a8c91e..9b85b3ce8 100755 --- a/sources/users.datatable.php +++ b/sources/users.datatable.php @@ -99,7 +99,6 @@ // Build FUNCTIONS list $params = $request->query->all(); -//error_log(print_r($params, true)); $rolesList = []; $titles = DB::query('SELECT id,title FROM '.prefixTable('roles_title').' ORDER BY title ASC'); foreach ($titles as $title) { diff --git a/sources/users.queries.php b/sources/users.queries.php index 97d826d52..82461f9f8 100755 --- a/sources/users.queries.php +++ b/sources/users.queries.php @@ -2160,8 +2160,7 @@ $inputData = dataSanitizer( $data, - $filters, - $SETTINGS['cpassman_dir'] + $filters ); // Check send values @@ -2529,10 +2528,8 @@ } catch (\LdapRecord\Auth\BindException $e) { $error = $e->getDetailedError(); - if ($error) { + if ($error && defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { error_log('TEAMPASS Error - LDAP - '.$error->getErrorCode()." - ".$error->getErrorMessage(). " - ".$error->getDiagnosticMessage()); - } else { - error_log('TEAMPASS Error - LDAP - Code: '.$e->getCode().' - Message: '.$e->getMessage()); } // deepcode ignore ServerLeak: No important data is sent and it is encrypted before sending echo prepareExchangedData( @@ -2561,10 +2558,8 @@ ->paginate(100); } catch (\LdapRecord\Auth\BindException $e) { $error = $e->getDetailedError(); - if ($error) { + if ($error && defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) { error_log('TEAMPASS Error - LDAP - '.$error->getErrorCode()." - ".$error->getErrorMessage(). " - ".$error->getDiagnosticMessage()); - } else { - error_log('TEAMPASS Error - LDAP - Code: '.$e->getCode().' - Message: '.$e->getMessage()); } // deepcode ignore ServerLeak: No important data is sent and it is encrypted before sending echo prepareExchangedData(