CVE-2021-4092 (JavaScript, published 2021-12-11T14:15Z)
Description: value: yetiforcecrm is vulnerable to Cross-Site Request Forgery (CSRF)
Severity: MEDIUM (CVSS v3 base score: 4.3)
Weakness: CWE-352 \u2014 Cross-Site Request Forgery (CSRF)
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.
Repository: https://github.com/yetiforcecompany/yetiforcecrm
Files changed:
app/Controller/View/Page.php
config/version.php
layouts/basic/modules/Users/Modals/PasswordModalFooter.tpl
layouts/basic/modules/Vtiger/Index.tpl
modules/Users/actions/Logout.php
public_html/layouts/basic/modules/Vtiger/resources/List.js
public_html/layouts/basic/modules/Vtiger/resources/RelatedList.js
public_html/layouts/basic/modules/Vtiger/resources/Vtiger.js
public_html/layouts/resources/Connector.js
public_html/layouts/resources/app.js
Commit message: Added minor improvements


Vulnerable code:
			'linkclass' => 'btn-danger d-md-none',
			'linkclass' => 'd-none d-sm-none d-md-block',
	'appVersion' => '6.2.262',
	'patchVersion' => '2021.12.09',
<!-- 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 -->
						<a class="btn btn-warning mr-2" role="button" href="?module=Users&action=Logout">
						<a class="btn btn-primary" role="button" href="?module=Users&action=Logout">

	/**
	 * {@inheritdoc}
	 */
	public function validateRequest(App\Request $request)
	{
		$request->validateReadAccess();
	}
					app.openUrlMethodPost('index.php', formData);
			app.openUrlMethodPost(exportActionUrl, params, formAttr);
						app.openUrlMethodPost(thisInstance.getSelectOptionFromChosenOption(liElement).data('deleteurl'));
					app.openUrlMethodPost(thisInstance.getSelectOptionFromChosenOption(liElement).data('approveurl'));
					app.openUrlMethodPost(thisInstance.getSelectOptionFromChosenOption(liElement).data('denyurl'));
					app.openUrlMethodPost(massActionUrl, postData);
			form.submit();

	requestForm: function (url, params = {}) {
		app.openUrlMethodPost(url, params);
				form.submit();
				form.submit();
	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();
	},

Fixed code:
			'linkclass' => 'btn-danger d-md-none js-post-action',
			'linkclass' => 'd-none d-sm-none d-md-block js-post-action',
	'appVersion' => '6.2.263',
	'patchVersion' => '2021.12.10',
	<!-- 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 -->
						<a class="btn btn-warning mr-2 js-post-action" role="button" href="index.php?module=Users&action=Logout">
						<a class="btn btn-primary" role="button" href="index.php">
					AppConnector.requestForm('index.php', formData);
			AppConnector.requestForm(exportActionUrl, params, formAttr);
						AppConnector.requestForm(thisInstance.getSelectOptionFromChosenOption(liElement).data('deleteurl'));
					AppConnector.requestForm(thisInstance.getSelectOptionFromChosenOption(liElement).data('approveurl'));
					AppConnector.requestForm(thisInstance.getSelectOptionFromChosenOption(liElement).data('denyurl'));
					AppConnector.requestForm(massActionUrl, postData);
			form.trigger('submit');
	/**
	 * 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();
				form.trigger('submit');
				form.trigger('submit');
	},
	/**
	 * 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'));
			}
		});
	app.registerPostActionEvent(document);