CVE-2021-4092 (published 2021-12-11T14:15Z)

yetiforcecrm is vulnerable to Cross-Site Request Forgery (CSRF)

Severity: MEDIUM
CVSS v2 base score: 4.3 (AV:N/AC:M/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 4.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N)

Weakness information:
cwe_id: CWE-352
cwe_name: Cross-Site Request Forgery (CSRF)
description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
extended_description: When a web server is designed to receive a request from a client without any mechanism for verifying that it was intentionally sent, then it might be possible for an attacker to trick a client into making an unintentional request to the web server which will be treated as an authentic request. This can be done via a URL, image load, XMLHttpRequest, etc. and can result in exposure of data or unintended code execution.
         

Fix information:
repo_url: https://github.com/yetiforcecompany/yetiforcecrm
commit_details: repo_url: https://github.com/yetiforcecompany/yetiforcecrm
author: Mariusz Krzaczkowski
author_date: 2021-12-10 10:29:46+01:00
msg: Added minor improvements
repository: repo_url: https://github.com/yetiforcecompany/yetiforcecrm
repo_name: YetiForceCompany/YetiForceCRM
description: Our team created for you one of the most innovative CRM systems that supports mainly business processes and allows for customization according to your needs. Be ahead of your competition and implement YetiForce!
repo_language: PHP
owner: yetiforcecompany
file_changes: filename: Page.php
change_type: ModificationType.MODIFY
diff: @@ -175,7 +175,7 @@ abstract class Page extends Base
 			'linklabel' => 'LBL_SIGN_OUT',
 			'linkurl' => 'index.php?module=Users&parent=Settings&action=Logout',
 			'icon' => 'fas fa-power-off fa-fw',
-			'linkclass' => 'btn-danger d-md-none',
+			'linkclass' => 'btn-danger d-md-none js-post-action',
 		];
 		$headerLinkInstances = [];
 		foreach ($headerLinks as $headerLink) {
@@ -254,7 +254,7 @@ abstract class Page extends Base
 			'linklabel' => 'LBL_SIGN_OUT',
 			'linkurl' => 'index.php?module=Users&parent=Settings&action=Logout',
 			'icon' => 'fas fa-power-off fa-fw',
-			'linkclass' => 'd-none d-sm-none d-md-block',
+			'linkclass' => 'd-none d-sm-none d-md-block js-post-action',
 		];
 		$headerLinkInstances = [];
 		foreach ($headerLinks as $headerLink) {

programming_language: PHP
filename: version.php
change_type: ModificationType.MODIFY
diff: @@ -1,7 +1,7 @@
 <?php
 
 return [
-	'appVersion' => '6.2.262',
-	'patchVersion' => '2021.12.09',
+	'appVersion' => '6.2.263',
+	'patchVersion' => '2021.12.10',
 	'lib_roundcube' => '0.2.3',
 ];

programming_language: PHP
filename: PasswordModalFooter.tpl
change_type: ModificationType.MODIFY
diff: @@ -1,29 +1,29 @@
 {*<!-- {[The file is published on the basis of YetiForce Public License 4.0 that can be found in the following directory: licenses/LicenseEN.txt or yetiforce.com]} -->*}
 {strip}
-<!-- tpl-Users-Modals-PasswordModalFooter -->
-<div class="modal-footer">
-	{if ($MODE === 'massReset' || $MODE === 'reset') &&  $ACTIVE_SMTP}
-		<button class="btn btn-success" type="submit" name="saveButton"
-				{if App\Config::main('systemMode') === 'demo'}disabled="disabled"{/if}>
-			<span class="fas fa-redo-alt mr-2"></span><strong>{\App\Language::translate('BTN_RESET_PASSWORD', $MODULE_NAME)}</strong>
-		</button>
-	{/if}
-	{if $MODE === 'change'}
-		<button class="btn btn-success" type="submit" name="saveButton"
-				{if App\Config::main('systemMode') === 'demo'}disabled="disabled"{/if}>
-			<span class="fas fa-redo-alt mr-2"></span><strong>{\App\Language::translate('LBL_CHANGE_PASSWORD', $MODULE_NAME)}</strong>
-		</button>
-	{/if}
-	{if $LOCK_EXIT}
-		<a class="btn btn-danger" role="button" href="index.php?module=Users&amp;parent=Settings&amp;action=Logout">
-			<span class="fas fa-power-off mr-2"></span><strong>{\App\Language::translate('LBL_SIGN_OUT', $MODULE_NAME)}</strong>
-		</a>
-	{else}
-		<button class="btn btn-danger" type="reset" data-dismiss="modal">
-			<span class="fas fa-times mr-2"></span><strong>{\App\Language::translate('LBL_CANCEL', $MODULE_NAME)}</strong>
-		</button>
-	{/if}
-</div>
-</form>
-<!-- /tpl-Users-Modals-PasswordModalFooter -->
+	<!-- tpl-Users-Modals-PasswordModalFooter -->
+	<div class="modal-footer">
+		{if ($MODE === 'massReset' || $MODE === 'reset') &&  $ACTIVE_SMTP}
+			<button class="btn btn-success" type="submit" name="saveButton"
+				{if App\Config::main('systemMode') === 'demo'}disabled="disabled" {/if}>
+				<span class="fas fa-redo-alt mr-2"></span><strong>{\App\Language::translate('BTN_RESET_PASSWORD', $MODULE_NAME)}</strong>
+			</button>
+		{/if}
+		{if $MODE === 'change'}
+			<button class="btn btn-success" type="submit" name="saveButton"
+				{if App\Config::main('systemMode') === 'demo'}disabled="disabled" {/if}>
+				<span class="fas fa-redo-alt mr-2"></span><strong>{\App\Language::translate('LBL_CHANGE_PASSWORD', $MODULE_NAME)}</strong>
+			</button>
+		{/if}
+		{if $LOCK_EXIT}
+			<a class="btn btn-danger js-post-action" role="button" href="index.php?module=Users&amp;parent=Settings&amp;action=Logout">
+				<span class="fas fa-power-off mr-2"></span><strong>{\App\Language::translate('LBL_SIGN_OUT', $MODULE_NAME)}</strong>
+			</a>
+		{else}
+			<button class="btn btn-danger" type="reset" data-dismiss="modal">
+				<span class="fas fa-times mr-2"></span><strong>{\App\Language::translate('LBL_CANCEL', $MODULE_NAME)}</strong>
+			</button>
+		{/if}
+	</div>
+	</form>
+	<!-- /tpl-Users-Modals-PasswordModalFooter -->
 {/strip}

programming_language: PHP
filename: Index.tpl
change_type: ModificationType.MODIFY
diff: @@ -20,10 +20,10 @@
 						<a class="btn btn-success mr-2" role="button" href="javascript:window.history.back();">
 							<span class="fas fa-chevron-left mr-2"></span>{\App\Language::translate('LBL_GO_BACK')}
 						</a>
-						<a class="btn btn-warning mr-2" role="button" href="?module=Users&action=Logout">
+						<a class="btn btn-warning mr-2 js-post-action" role="button" href="index.php?module=Users&action=Logout">
 							<span class="fas fa-power-off fa-fw mr-2"></span>{\App\Language::translate('LBL_SIGN_OUT')}
 						</a>
-						<a class="btn btn-primary" role="button" href="?module=Users&action=Logout">
+						<a class="btn btn-primary" role="button" href="index.php">
 							<i class="fas fa-home mr-2"></i>{\App\Language::translate('LBL_MAIN_PAGE')}
 						</a>
 					</p>

programming_language: PHP
filename: Logout.php
change_type: ModificationType.MODIFY
diff: @@ -38,12 +38,4 @@ class Users_Logout_Action extends \App\Controller\Action
 		//End
 		header('location: index.php');
 	}
-
-	/**
-	 * {@inheritdoc}
-	 */
-	public function validateRequest(App\Request $request)
-	{
-		$request->validateReadAccess();
-	}
 }

programming_language: PHP
filename: List.js
change_type: ModificationType.MODIFY
diff: @@ -195,7 +195,7 @@ $.Class(
 					let formData = container.find('.js-modal-form').serializeFormData();
 					const listInstance = Vtiger_List_Js.getInstance();
 					$.extend(formData, listInstance.getSearchParams());
-					app.openUrlMethodPost('index.php', formData);
+					AppConnector.requestForm('index.php', formData);
 					Vtiger_Helper_Js.showMessage({
 						text: app.vtranslate('JS_STARTED_GENERATING_FILE'),
 						type: 'info'
@@ -355,7 +355,7 @@ $.Class(
 			}
 			let params = Vtiger_List_Js.getInstance().getSearchParams();
 			delete params.view;
-			app.openUrlMethodPost(exportActionUrl, params, formAttr);
+			AppConnector.requestForm(exportActionUrl, params, formAttr);
 		},
 		/**
 		 * Function to reload list
@@ -1488,7 +1488,7 @@ $.Class(
 					Vtiger_Helper_Js.showConfirmationBox({
 						message: app.vtranslate('JS_LBL_ARE_YOU_SURE_YOU_WANT_TO_DELETE_FILTER')
 					}).done((e) => {
-						app.openUrlMethodPost(thisInstance.getSelectOptionFromChosenOption(liElement).data('deleteurl'));
+						AppConnector.requestForm(thisInstance.getSelectOptionFromChosenOption(liElement).data('deleteurl'));
 					});
 					event.stopPropagation();
 				});
@@ -1505,7 +1505,7 @@ $.Class(
 					//to close the dropdown
 					thisInstance.getFilterSelectElement().data('select2').close();
 					const liElement = $(event.currentTarget).closest('.select2-results__option');
-					app.openUrlMethodPost(thisInstance.getSelectOptionFromChosenOption(liElement).data('approveurl'));
+					AppConnector.requestForm(thisInstance.getSelectOptionFromChosenOption(liElement).data('approveurl'));
 					event.stopPropagation();
 				});
 			}
@@ -1521,7 +1521,7 @@ $.Class(
 					//to close the dropdown
 					thisInstance.getFilterSelectElement().data('select2').close();
 					const liElement = $(event.currentTarget).closest('.select2-results__option');
-					app.openUrlMethodPost(thisInstance.getSelectOptionFromChosenOption(liElement).data('denyurl'));
+					AppConnector.requestForm(thisInstance.getSelectOptionFromChosenOption(liElement).data('denyurl'));
 					event.stopPropagation();
 				});
 			}

programming_language: JavaScript
filename: RelatedList.js
change_type: ModificationType.MODIFY
diff: @@ -86,7 +86,7 @@ jQuery.Class(
 					data: postData
 				};
 				if (type === 'sendByForm') {
-					app.openUrlMethodPost(massActionUrl, postData);
+					AppConnector.requestForm(massActionUrl, postData);
 					progressIndicatorElement.progressIndicator({ mode: 'hide' });
 				} else {
 					AppConnector.request(actionParams)

programming_language: TypeScript
filename: Vtiger.js
change_type: ModificationType.MODIFY
diff: @@ -171,7 +171,7 @@ var Vtiger_Index_Js = {
 				form.append($('<input>', { name: i, value: JSON.stringify(postData[i]) }));
 			}
 			$('body').append(form);
-			form.submit();
+			form.trigger('submit');
 		} else {
 			window.location.href = url;
 		}

programming_language: JavaScript
filename: Connector.js
change_type: ModificationType.MODIFY
diff: @@ -146,8 +146,31 @@ window.AppConnector = {
 		}
 		return aDeferred.promise();
 	},
-
-	requestForm: function (url, params = {}) {
-		app.openUrlMethodPost(url, params);
+	/**
+	 * Send form data
+	 * @param {string} url
+	 * @param {object} postData
+	 * @param {object} formAttr
+	 */
+	requestForm: function (url, postData = {}, formAttr = {}) {
+		$.extend(formAttr, {
+			method: 'post',
+			action: url,
+			style: 'display:none;'
+		});
+		let form = $('<form></form>', formAttr);
+		if (typeof csrfMagicName !== 'undefined') {
+			postData[csrfMagicName] = csrfMagicToken;
+		}
+		$.each(postData, (index, value) => {
+			let input = $(document.createElement('input'));
+			input.attr('type', 'hidden');
+			input.attr('name', index);
+			input.val(value);
+			form.append(input);
+		});
+		$('body').append(form);
+		form.trigger('submit');
+		form.remove();
 	}
 };

programming_language: JavaScript
filename: app.js
change_type: ModificationType.MODIFY
diff: @@ -306,7 +306,7 @@ var App = (window.App = {
 					$(data).removeAttr('data-validation-engine');
 				});
 				form.addClass('not_validation');
-				form.submit();
+				form.trigger('submit');
 			},
 			/**
 			 * Register tab events
@@ -484,7 +484,7 @@ var App = (window.App = {
 					$(data).removeAttr('data-validation-engine');
 				});
 				form.addClass('not_validation');
-				form.submit();
+				form.trigger('submit');
 			},
 			/**
 			 * Save quick create form
@@ -2627,27 +2627,6 @@ var app = (window.app = {
 			window.location.href = url;
 		}
 	},
-	openUrlMethodPost(url, postData = {}, formAttr = {}) {
-		$.extend(formAttr, {
-			method: 'post',
-			action: url,
-			style: 'display:none;'
-		});
-		let form = $('<form></form>', formAttr);
-		if (typeof csrfMagicName !== 'undefined') {
-			postData[csrfMagicName] = csrfMagicToken;
-		}
-		$.each(postData, (index, value) => {
-			let input = $(document.createElement('input'));
-			input.attr('type', 'hidden');
-			input.attr('name', index);
-			input.val(value);
-			form.append(input);
-		});
-		$('body').append(form);
-		form.submit();
-		form.remove();
-	},
 	/**
 	 * Convert url string to object
 	 *
@@ -3177,6 +3156,19 @@ var app = (window.app = {
 				}
 			});
 		}
+	},
+	/**
+	 * Register POST action
+	 * @param {jQuery} container
+	 */
+	registerPostActionEvent: function (container) {
+		container.on('click', '.js-post-action', function (e) {
+			e.preventDefault();
+			let element = $(this);
+			if (element.attr('href')) {
+				AppConnector.requestForm(element.attr('href'));
+			}
+		});
 	}
 });
 CKEDITOR.disableAutoInline = true;
@@ -3203,6 +3195,7 @@ $(function () {
 	app.registerFormsEvents(document);
 	app.registerRecordActionsEvents(document);
 	app.registerKeyboardShortcutsEvent(document);
+	app.registerPostActionEvent(document);
 	App.Components.QuickCreate.register(document);
 	App.Components.Scrollbar.initPage();
 	App.Clipboard.register(document);

programming_language: JavaScript