Issues (3882)

Security Analysis    39 potential vulnerabilities

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting (9)
Response Splitting can be used to send arbitrary responses.
  File Manipulation (2)
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure (7)
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection (13)
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting (8)
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

app/Module.php (3 issues)

1
<?php
2
3
namespace App;
4
5
/**
6
 * Modules basic class.
7
 *
8
 * @package App
9
 *
10
 * @copyright YetiForce S.A.
11
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
12
 * @author    Mariusz Krzaczkowski <[email protected]>
13
 * @author    RadosÅ‚aw Skrzypczak <[email protected]>
14
 */
15
class Module
16
{
17
	/**
18
	 * Cache for tabdata.php.
19
	 *
20
	 * @var array
21
	 */
22
	protected static $tabdataCache;
23
24
	/**
25
	 * Init tabdata from file.
26
	 */
27
	public static function init()
28
	{
29
		static::$tabdataCache = require ROOT_DIRECTORY . '/user_privileges/tabdata.php';
30
		static::$tabdataCache['tabName'] = array_flip(static::$tabdataCache['tabId']);
31
	}
32
33
	/**
34
	 * Init tabdata form db.
35 7
	 */
36
	public static function initFromDb()
37 7
	{
38 7
		static::$tabdataCache = static::getModuleMeta();
39 7
		static::$tabdataCache['tabName'] = array_flip(static::$tabdataCache['tabId']);
40
	}
41 5868
42
	/**
43 5868
	 * Gets entity info.
44 5868
	 *
45 5868
	 * @param string $moduleName
46
	 *
47
	 * @return array|null
48
	 */
49
	public static function getEntityInfo(string $moduleName = null): ?array
50 5868
	{
51 5844
		return self::getEntitiesInfo()[$moduleName] ?? null;
52
	}
53
54
	/**
55 62
	 * Gets all entities data.
56 62
	 *
57 62
	 * @param array
58 62
	 */
59 62
	public static function getEntitiesInfo(): array
60 62
	{
61 62
		$cacheName = 'ModuleEntityInfo';
62 62
		if (!Cache::has($cacheName, '')) {
63 62
			$entityInfos = [];
64
			$dataReader = (new \App\Db\Query())->from('vtiger_entityname')->createCommand()->query();
65 62
			while ($row = $dataReader->read()) {
66 62
				$row['fieldnameArr'] = $row['fieldname'] ? explode(',', $row['fieldname']) : [];
67
				$row['searchcolumnArr'] = $row['searchcolumn'] ? explode(',', $row['searchcolumn']) : [];
68
				$entityInfos[$row['modulename']] = $row;
69 62
			}
70
			return Cache::save($cacheName, '', $entityInfos);
0 ignored issues
show
Bug Best Practice introduced by
The expression return App\Cache::save($...Name, '', $entityInfos) returns the type boolean which is incompatible with the type-hinted return array.
Loading history...
71
		}
72
		return Cache::get($cacheName, '');
73
	}
74
75
	public static function getAllEntityModuleInfo($sort = false)
76
	{
77
		$entity = static::getEntitiesInfo();
78
		if ($sort) {
79
			usort($entity, function ($a, $b) {
80
				return $a['sequence'] < $b['sequence'] ? -1 : 1;
81
			});
82
		}
83
		return $entity;
84
	}
85
86
	protected static $isModuleActiveCache = [];
87
88
	/**
89
	 * Function to check whether the module is active.
90
	 *
91
	 * @param string $moduleName
92
	 *
93
	 * @return bool
94 15
	 */
95
	public static function isModuleActive(string $moduleName): bool
96 15
	{
97 15
		if (isset(static::$isModuleActiveCache[$moduleName])) {
98
			return static::$isModuleActiveCache[$moduleName];
99 3
		}
100 1
		if (\in_array($moduleName, ['CustomView', 'Users', 'Import', 'com_vtiger_workflow', 'PickList'])) {
101
			static::$isModuleActiveCache[$moduleName] = true;
102 1
			return true;
103
		}
104 2
		$moduleId = static::getModuleId($moduleName);
105 2
		$isActive = (isset(static::$tabdataCache['tabPresence'][$moduleId]) && 0 == static::$tabdataCache['tabPresence'][$moduleId]);
106 2
		static::$isModuleActiveCache[$moduleName] = $isActive;
107
		return $isActive;
108 2
	}
109
110
	/**
111
	 * Get module id by module name.
112
	 *
113
	 * @param string $moduleName
114
	 *
115
	 * @return bool|int
116
	 */
117
	public static function getModuleId($moduleName)
118 127
	{
119
		return static::$tabdataCache['tabId'][$moduleName] ?? false;
120 127
	}
121
122
	/**
123
	 * Get module nane by module id.
124
	 *
125
	 * @param int $tabId
126
	 *
127
	 * @return bool|string
128
	 */
129
	public static function getModuleName($tabId)
130 446
	{
131
		return static::$tabdataCache['tabName'][$tabId] ?? false;
132 446
	}
133
134
	/**
135
	 * Get module owner by module id.
136
	 *
137
	 * @param int $tabId
138
	 *
139
	 * @return int
140
	 */
141
	public static function getModuleOwner($tabId)
142
	{
143
		return static::$tabdataCache['tabOwnedby'][$tabId] ?? false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return static::tabdataCa...edby'][$tabId] ?? false could also return false which is incompatible with the documented return type integer. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
144
	}
145
146
	/**
147
	 * Get all module names.
148
	 *
149
	 * @return string[]
150
	 */
151
	public static function getAllModuleNames()
152
	{
153
		return static::$tabdataCache['tabName'];
154
	}
155
156
	/**
157
	 * Function to get the list of module for which the user defined sharing rules can be defined.
158
	 *
159
	 * @param array $eliminateModules
160
	 *
161
	 * @return array
162
	 */
163
	public static function getSharingModuleList($eliminateModules = false)
164 2
	{
165
		$modules = \vtlib\Functions::getAllModules(true, true, 0, false, 0);
166 2
		$sharingModules = [];
167 2
		foreach ($modules as $row) {
168 2
			if (!$eliminateModules || !\in_array($row['name'], $eliminateModules)) {
169 2
				$sharingModules[] = $row['name'];
170 2
			}
171
		}
172
		return $sharingModules;
173 2
	}
174
175
	/**
176
	 * Get sql for name in display format.
177
	 *
178
	 * @param string $moduleName
179
	 *
180
	 * @return string
181
	 */
182
	public static function getSqlForNameInDisplayFormat($moduleName)
183
	{
184
		$db = \App\Db::getInstance();
185
		$entityFieldInfo = static::getEntityInfo($moduleName);
186
		$fieldsName = $entityFieldInfo['fieldnameArr'];
187
		if (\count($fieldsName) > 1) {
188
			$sqlString = 'CONCAT(';
189
			foreach ($fieldsName as &$column) {
190
				$sqlString .= "{$db->quoteTableName($entityFieldInfo['tablename'])}.{$db->quoteColumnName($column)},' ',";
191
			}
192
			$formattedName = new \yii\db\Expression(rtrim($sqlString, ',\' \',') . ')');
193
		} else {
194
			$fieldsName = array_pop($fieldsName);
195
			$formattedName = "{$db->quoteTableName($entityFieldInfo['tablename'])}.{$db->quoteColumnName($fieldsName)}";
196
		}
197
		return $formattedName;
198
	}
199
200
	/**
201
	 * Function to get a action id for a given action name.
202
	 *
203
	 * @param string $action
204
	 *
205
	 * @return int|null
206
	 */
207
	public static function getActionId($action)
208 16
	{
209
		if (empty($action)) {
210 16
			return null;
211 6
		}
212
		if (Cache::has('getActionId', $action)) {
213 12
			return Cache::get('getActionId', $action);
214 6
		}
215
		$actionIds = static::$tabdataCache['actionId'];
216 7
		if (isset($actionIds[$action])) {
217 7
			$actionId = $actionIds[$action];
218 7
		}
219
		if (empty($actionId)) {
220 7
			$actionId = (new Db\Query())->select(['actionid'])->from('vtiger_actionmapping')->where(['actionname' => $action])->scalar();
221
		}
222
		if (is_numeric($actionId)) {
223 7
			$actionId = (int) $actionId;
224 7
		}
225
		Cache::save('getActionId', $action, $actionId, Cache::LONG);
226 7
		return $actionId;
227 7
	}
228
229
	/**
230
	 * Get module meta data.
231
	 *
232
	 * @return array
233
	 */
234
	public static function getModuleMeta()
235 7
	{
236
		$tabNames = $tabPresence = $tabOwned = [];
237 7
		$allModules = \vtlib\Functions::getAllModules(false, true);
238 7
		foreach ($allModules as $moduleInfo) {
239 7
			$tabNames[$moduleInfo['name']] = $tabId = (int) $moduleInfo['tabid'];
240 7
			$tabPresence[$tabId] = $moduleInfo['presence'];
241 7
			$tabOwned[$tabId] = $moduleInfo['ownedby'];
242 7
		}
243
		//Constructing the actionname=>actionid array
244
		$actionAll = [];
245 7
		$dataReader = (new Db\Query())->from(['vtiger_actionmapping'])->createCommand()->query();
246 7
		while ($row = $dataReader->read()) {
247 7
			$actionname = $row['actionname'];
248 7
			$actionAll[$actionname] = $actionid = (int) $row['actionid'];
249 7
			if (0 === (int) $row['securitycheck']) {
250 7
				$actionSecure[$actionid] = $actionname;
251 7
			}
252
		}
253
		return [
254
			'tabId' => $tabNames,
255 7
			'tabPresence' => $tabPresence,
256 7
			'tabOwnedby' => $tabOwned,
257 7
			'actionId' => $actionAll,
258 7
			'actionName' => $actionSecure,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $actionSecure does not seem to be defined for all execution paths leading up to this point.
Loading history...
259 7
		];
260
	}
261
262
	/**
263
	 * Function to create file about modules.
264
	 *
265
	 * @throws \App\Exceptions\NoPermitted
266
	 */
267
	public static function createModuleMetaFile()
268 7
	{
269
		Cache::delete('moduleTabs', 'all');
270 7
		Cache::delete('getTrackingModules', 'all');
271 7
		$filename = ROOT_DIRECTORY . '/user_privileges/tabdata.php';
272 7
		if (file_exists($filename)) {
273 7
			if (is_writable($filename)) {
274 7
				$moduleMeta = static::getModuleMeta();
275 7
				$content = '$tab_seq_array=' . Utils::varExport($moduleMeta['tabPresence']) . ";\n";
276 7
				$content .= 'return ' . Utils::varExport($moduleMeta) . ";\n";
277 7
				if (!Utils::saveToFile($filename, $content)) {
278
					throw new Exceptions\NoPermitted("Cannot write file ($filename)");
279
				}
280
			} else {
281
				Log::error("The file $filename is not writable");
282
			}
283
		} else {
284
			Log::error("The file $filename does not exist");
285
		}
286 7
		static::initFromDb();
287
	}
288
289 7
	/**
290 7
	 * Function changes the module type.
291
	 *
292
	 * @param string $moduleName
293
	 * @param int    $type
294
	 */
295
	public static function changeType(string $moduleName, int $type)
296
	{
297
		$moduleModel = \Vtiger_Module_Model::getInstance($moduleName);
298
		if ($moduleModel && $moduleModel->changeType($type) && PrivilegeUtil::modifyPermissions($moduleName, ['RecordPdfInventory'], \Vtiger_Module_Model::ADVANCED_TYPE === $type)) {
299
			UserPrivilegesFile::recalculateAll();
300
		}
301
	}
302
303
	/**
304
	 * Get all module names by filter.
305
	 *
306
	 * @param bool     $isEntityType
307
	 * @param bool     $showRestricted
308
	 * @param bool|int $presence
309
	 *
310
	 * @return string[]
311
	 */
312
	public static function getAllModuleNamesFilter($isEntityType = true, $showRestricted = false, $presence = false): array
313
	{
314
		$modules = [];
315
		foreach (\vtlib\Functions::getAllModules($isEntityType, $showRestricted, $presence) as $value) {
316
			$modules[$value['name']] = Language::translate($value['name'], $value['name']);
317
		}
318
		return $modules;
319
	}
320
321
	/**
322
	 * Function to get the list of all accessible modules for Quick Create.
323
	 *
324
	 * @param bool $restrictList
325
	 * @param bool $tree
326
	 *
327
	 * @return array List of Vtiger_Module_Model instances
328
	 */
329
	public static function getQuickCreateModules($restrictList = false, $tree = false): array
330
	{
331
		$restrictListString = $restrictList ? 1 : 0;
332
		if ($tree) {
333
			$userModel = \App\User::getCurrentUserModel();
334
			$quickCreateModulesTreeCache = \App\Cache::get('getQuickCreateModules', 'tree' . $restrictListString . $userModel->getDetail('roleid'));
335
			if (false !== $quickCreateModulesTreeCache) {
336
				return $quickCreateModulesTreeCache;
337
			}
338
		} else {
339
			$quickCreateModules = \App\Cache::get('getQuickCreateModules', $restrictListString);
340
			if (false !== $quickCreateModules) {
341
				return $quickCreateModules;
342
			}
343
		}
344
345
		$userPrivModel = \Users_Privileges_Model::getCurrentUserPrivilegesModel();
346
347
		$query = new \App\Db\Query();
348
		$query->select(['vtiger_tab.*'])->from('vtiger_field')
349
			->innerJoin('vtiger_tab', 'vtiger_tab.tabid = vtiger_field.tabid')
350
			->where(['<>', 'vtiger_tab.presence', 1]);
351
		if ($tree) {
352
			$query->andWhere(['<>', 'vtiger_tab.name', 'Users']);
353
		} else {
354
			$query->andWhere(['quickcreate' => [0, 2]])
355
				->andWhere(['<>', 'vtiger_tab.type', 1]);
356
		}
357
		if ($restrictList) {
358
			$query->andWhere(['not in', 'vtiger_tab.name', ['ModComments', 'PriceBooks', 'CallHistory', 'OSSMailView']]);
359
		}
360
		$quickCreateModules = [];
361
		$dataReader = $query->distinct()->createCommand()->query();
362
		while ($row = $dataReader->read()) {
363
			if ($userPrivModel->hasModuleActionPermission($row['tabid'], 'CreateView')) {
364
				$moduleModel = \Vtiger_Module_Model::getInstanceFromArray($row);
365
				$quickCreateModules[$row['name']] = $moduleModel;
366
			}
367
		}
368
		if ($tree) {
369
			$menu = \Vtiger_Menu_Model::getAll();
370
			$quickCreateModulesTree = [];
371
			foreach ($menu as $parent) {
372
				if (!empty($parent['childs'])) {
373
					$items = [];
374
					foreach ($parent['childs'] as $child) {
375
						if (isset($quickCreateModules[$child['mod']])) {
376
							$items[$quickCreateModules[$child['mod']]->name] = $quickCreateModules[$child['mod']];
377
							unset($quickCreateModules[$child['mod']]);
378
						}
379
					}
380
					if (!empty($items)) {
381
						$quickCreateModulesTree[] = ['name' => $parent['name'], 'icon' => $parent['icon'], 'modules' => $items];
382
					}
383
				}
384
			}
385
			if (!empty($quickCreateModules)) {
386
				$quickCreateModulesTree[] = ['name' => 'LBL_OTHER', 'icon' => 'yfm-Other', 'modules' => $quickCreateModules];
387
			}
388
			\App\Cache::save('getQuickCreateModules', 'tree' . $restrictListString . $userPrivModel->get('roleid'), $quickCreateModulesTree);
389
			return $quickCreateModulesTree;
390
		}
391
		\App\Cache::save('getQuickCreateModules', $restrictListString, $quickCreateModules);
392
		return $quickCreateModules;
393
	}
394
}
395
396
Module::init();
397