CVE-2023-49508: Insufficient Information (NVD-CWE-noinfo)
value: Directory Traversal vulnerability in YetiForceCompany YetiForceCRM versions 6.4.0 and before allows a remote authenticated attacker to obtain sensitive information via the license parameter in the LibraryLicense.php component.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-02-16T08:15Z
Repository: https://github.com/YetiForceCompany/YetiForceCRM
Language: PHP
Files changed: config/version.php, layouts/basic/modules/Settings/Dependencies/LibraryMoreInfo.tpl, modules/Settings/Dependencies/views/LibraryLicense.php, modules/Settings/Dependencies/views/LibraryMoreInfo.php
Commit date: 2023-07-17 09:10:15 +0200
Commit message: Improved loading of details and licenses of dependent libraries

Diff: diff --git a/config/version.php b/config/version.php
index 6501820192..56fa899c62 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,7 +1,7 @@
 <?php
 
 return [
-	'appVersion' => '6.4.270',
-	'patchVersion' => '2023.07.14',
+	'appVersion' => '6.4.271',
+	'patchVersion' => '2023.07.17',
 	'lib_roundcube' => '0.3.4',
 ];
diff --git a/layouts/basic/modules/Settings/Dependencies/LibraryMoreInfo.tpl b/layouts/basic/modules/Settings/Dependencies/LibraryMoreInfo.tpl
index d2355deb27..30a1a63e92 100644
--- a/layouts/basic/modules/Settings/Dependencies/LibraryMoreInfo.tpl
+++ b/layouts/basic/modules/Settings/Dependencies/LibraryMoreInfo.tpl
@@ -11,11 +11,11 @@
 		</button>
 	</div>
 	<div class="modal-body col-md-12">
 		{if $RESULT}
 			<code>
-				<pre>{$FILE_CONTENT}</pre>
+				<pre>{$FILE_CONTENT|escape}</pre>
 			</code>
 		{else}
 			<div class="alert alert-danger" role="alert">
 				{\App\Language::translate('LBL_MISSING_FILE', $QUALIFIED_MODULE)}
 			</div>
diff --git a/modules/Settings/Dependencies/views/LibraryLicense.php b/modules/Settings/Dependencies/views/LibraryLicense.php
index dded5e7247..a44542c42d 100644
--- a/modules/Settings/Dependencies/views/LibraryLicense.php
+++ b/modules/Settings/Dependencies/views/LibraryLicense.php
@@ -6,10 +6,11 @@
  * @package   Settings.View
  *
  * @copyright YetiForce S.A.
  * @license   YetiForce Public License 5.0 (licenses/LicenseEN.txt or yetiforce.com)
  * @author    Adrian Koń <a.kon@yetiforce.com>
+ * @author    Radosław Skrzypczak <r.skrzypczak@yetiforce.com>
  */
 class Settings_Dependencies_LibraryLicense_View extends Settings_Vtiger_BasicModal_View
 {
 	/**
 	 * Function get modal size.
@@ -34,12 +35,12 @@ class Settings_Dependencies_LibraryLicense_View extends Settings_Vtiger_BasicMod
 		$fileContent = '';
 		if ($request->isEmpty('license')) {
 			$result = false;
 		} else {
 			$dir = ROOT_DIRECTORY . DIRECTORY_SEPARATOR . 'licenses' . DIRECTORY_SEPARATOR;
-			$filePath = $dir . $request->getByType('license', 'Text') . '.txt';
-			if (file_exists($filePath)) {
+			$filePath = $dir . $request->getByType('license', \App\Purifier::PATH) . '.txt';
+			if (file_exists($filePath) && \App\Fields\File::isAllowedFileDirectory($filePath)) {
 				$result = true;
 				$fileContent = file_get_contents($filePath);
 			} else {
 				$result = false;
 			}
diff --git a/modules/Settings/Dependencies/views/LibraryMoreInfo.php b/modules/Settings/Dependencies/views/LibraryMoreInfo.php
index 02e02d9100..34e05eb3f0 100644
--- a/modules/Settings/Dependencies/views/LibraryMoreInfo.php
+++ b/modules/Settings/Dependencies/views/LibraryMoreInfo.php
@@ -6,10 +6,11 @@
  * @package   Settings.View
  *
  * @copyright YetiForce S.A.
  * @license   YetiForce Public License 5.0 (licenses/LicenseEN.txt or yetiforce.com)
  * @author    Adrian Koń <a.kon@yetiforce.com>
+ * @author    Radosław Skrzypczak <r.skrzypczak@yetiforce.com>
  */
 class Settings_Dependencies_LibraryMoreInfo_View extends Settings_Vtiger_BasicModal_View
 {
 	/**
 	 * Public libraries package files.
@@ -21,38 +22,29 @@ class Settings_Dependencies_LibraryMoreInfo_View extends Settings_Vtiger_BasicMo
 	/** {@inheritdoc} */
 	public function process(App\Request $request)
 	{
 		$result = false;
 		$fileContent = '';
-		if ($request->isEmpty('type') || $request->isEmpty('libraryName')) {
-			$result = false;
-		} else {
-			if ('public' === $request->getByType('type', 1)) {
+		if (!$request->isEmpty('type') && !$request->isEmpty('libraryName')) {
+			$type = $request->getByType('type', \App\Purifier::STANDARD);
+			if ('public' === $type) {
 				$dir = ROOT_DIRECTORY . DIRECTORY_SEPARATOR . 'public_html' . DIRECTORY_SEPARATOR . 'libraries' . DIRECTORY_SEPARATOR;
-				$libraryName = $request->getByType('libraryName', 'Text');
+				$libraryName = $request->getByType('libraryName', \App\Purifier::PATH);
 				foreach ($this->packageFiles as $file) {
 					$packageFile = $dir . $libraryName . DIRECTORY_SEPARATOR . $file;
-					if ($fileContent) {
-						continue;
-					}
-					if (file_exists($packageFile)) {
+					if (file_exists($packageFile) && \App\Fields\File::isAllowedFileDirectory($packageFile)) {
 						$fileContent = file_get_contents($packageFile);
 						$result = true;
-					} else {
-						$result = false;
+						break;
 					}
 				}
-			} elseif ('vendor' === $request->getByType('type', 1)) {
-				$filePath = 'vendor' . DIRECTORY_SEPARATOR . $request->getByType('libraryName', 'Text') . DIRECTORY_SEPARATOR . 'composer.json';
-				if (file_exists($filePath)) {
+			} elseif ('vendor' === $type) {
+				$filePath = ROOT_DIRECTORY . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $request->getByType('libraryName', \App\Purifier::PATH) . DIRECTORY_SEPARATOR . 'composer.json';
+				if (file_exists($filePath) && \App\Fields\File::isAllowedFileDirectory($filePath)) {
 					$fileContent = file_get_contents($filePath);
 					$result = true;
-				} else {
-					$result = false;
 				}
-			} else {
-				$result = false;
 			}
 		}
 		$this->preProcess($request);
 		$qualifiedModuleName = $request->getModule(false);
 		$viewer = $this->getViewer($request);

Vulnerable code: 	'appVersion' => '6.4.270',
	'patchVersion' => '2023.07.14',
				<pre>{$FILE_CONTENT}</pre>
			$filePath = $dir . $request->getByType('license', 'Text') . '.txt';
			if (file_exists($filePath)) {
		if ($request->isEmpty('type') || $request->isEmpty('libraryName')) {
			$result = false;
		} else {
			if ('public' === $request->getByType('type', 1)) {
				$libraryName = $request->getByType('libraryName', 'Text');
					if ($fileContent) {
						continue;
					}
					if (file_exists($packageFile)) {
					} else {
						$result = false;
			} elseif ('vendor' === $request->getByType('type', 1)) {
				$filePath = 'vendor' . DIRECTORY_SEPARATOR . $request->getByType('libraryName', 'Text') . DIRECTORY_SEPARATOR . 'composer.json';
				if (file_exists($filePath)) {
				} else {
					$result = false;
			} else {
				$result = false;
Fixed code: 	'appVersion' => '6.4.271',
	'patchVersion' => '2023.07.17',
				<pre>{$FILE_CONTENT|escape}</pre>
 * @author    Radosław Skrzypczak <r.skrzypczak@yetiforce.com>
			$filePath = $dir . $request->getByType('license', \App\Purifier::PATH) . '.txt';
			if (file_exists($filePath) && \App\Fields\File::isAllowedFileDirectory($filePath)) {
 * @author    Radosław Skrzypczak <r.skrzypczak@yetiforce.com>
		if (!$request->isEmpty('type') && !$request->isEmpty('libraryName')) {
			$type = $request->getByType('type', \App\Purifier::STANDARD);
			if ('public' === $type) {
				$libraryName = $request->getByType('libraryName', \App\Purifier::PATH);
					if (file_exists($packageFile) && \App\Fields\File::isAllowedFileDirectory($packageFile)) {
						break;
			} elseif ('vendor' === $type) {
				$filePath = ROOT_DIRECTORY . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $request->getByType('libraryName', \App\Purifier::PATH) . DIRECTORY_SEPARATOR . 'composer.json';
				if (file_exists($filePath) && \App\Fields\File::isAllowedFileDirectory($filePath)) {