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/Db.php (8 issues)

1
<?php
2
3
namespace App;
4
5
/**
6
 * Database connection 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 Db extends \yii\db\Connection
16
{
17
	/**
18
	 * Sorting order flag.
19
	 */
20
	public const ASC = 'ASC';
21
22
	/**
23
	 * Sorting order flag.
24
	 */
25
	public const DESC = 'DESC';
26
27
	/**
28
	 * @var bool whether to turn on prepare emulation. Defaults to false, meaning PDO
29
	 *           will use the native prepare support if available. For some databases (such as MySQL),
30
	 *           this may need to be set true so that PDO can emulate the prepare support to bypass
31
	 *           the buggy native prepare support.
32
	 *           The default value is null, which means the PDO ATTR_EMULATE_PREPARES value will not be changed
33
	 */
34
	public $emulatePrepare = false;
35
36
	/**
37
	 * @var \App\Db Table of connections with database
38
	 */
39
	private static $cache = [];
40
41
	/**
42
	 * @var array Configuration with database
43
	 */
44
	private static $config = [];
45
46
	/**
47
	 * @var bool Enable caching database instance
48
	 */
49
	public static $connectCache = false;
50
51
	/**
52
	 * @var string Database Name
53
	 */
54
	public $dbName;
55
56
	/**
57
	 * @var string Database section
58
	 */
59
	public $dbType;
60
61
	/**
62
	 * @var string Host database server
63
	 */
64
	public $host;
65
66
	/**
67
	 * @var int Port database server
68
	 */
69
	public $port;
70
71
	/** {@inheritdoc} */
72
	public $schemaMap = [
73
		'pgsql' => 'App\Db\Drivers\Pgsql\Schema', // PostgreSQL
74
		'mysqli' => 'yii\db\mysql\Schema', // MySQL
75
		'mysql' => 'App\Db\Drivers\Mysql\Schema', // MySQL
76
		'sqlite' => 'yii\db\sqlite\Schema', // sqlite 3
77
		'sqlite2' => 'yii\db\sqlite\Schema', // sqlite 2
78
		'sqlsrv' => 'yii\db\mssql\Schema', // newer MSSQL driver on MS Windows hosts
79
		'oci' => 'yii\db\oci\Schema', // Oracle driver
80
		'mssql' => 'yii\db\mssql\Schema', // older MSSQL driver on MS Windows hosts
81
		'dblib' => 'yii\db\mssql\Schema', // dblib drivers on GNU/Linux (and maybe other OSes) hosts
82
		'cubrid' => 'yii\db\cubrid\Schema', // CUBRID
83
	];
84
85
	/**
86
	 * @var string the class used to create new database [[Command]] objects. If you want to extend the [[Command]] class,
87
	 *             you may configure this property to use your extended version of the class
88
	 */
89
	public $commandClass = '\App\Db\Command';
90
91
	/**
92
	 * @var Cache|string the cache object or the ID of the cache application component that
93
	 *                   is used to cache the table metadata
94
	 *
95 6356
	 * @see enableSchemaCache
96
	 */
97 6356
	public $schemaCache = false;
98 6356
99
	/**
100 3
	 * Creates the Db connection instance.
101 3
	 *
102 3
	 * @param string $type Name of database connection
103 3
	 *
104
	 * @return \App\Db
105
	 */
106
	public static function getInstance($type = 'base')
107
	{
108
		if (isset(self::$cache[$type])) {
109
			return self::$cache[$type];
110
		}
111
		$db = new self(self::getConfig($type));
112
		$db->dbType = $type;
113 4
		self::$cache[$type] = $db;
114
		return $db;
115 4
	}
116 3
117
	/**
118 4
	 * Load database connection configuration.
119
	 *
120
	 * @param string $type
121
	 *
122
	 * @return array with database configuration
123
	 */
124
	public static function getConfig(string $type)
125
	{
126
		if (!isset(self::$config[$type])) {
127
			self::$config[$type] = Config::db($type) ?? Config::db('base');
128
		}
129
		return self::$config[$type];
130
	}
131
132
	/**
133
	 * Set database connection configuration.
134
	 *
135
	 * @param array  $config
136
	 * @param string $type
137 2
	 */
138
	public static function setConfig($config, $type = 'base')
139 2
	{
140 2
		self::$config[$type] = $config;
141 2
	}
142 2
143 2
	/**
144 2
	 * Get info database server.
145 2
	 *
146
	 * @return array
147
	 */
148 2
	public function getInfo()
149
	{
150
		$pdo = $this->getSlavePdo();
151 2
		$statement = $pdo->prepare('SHOW VARIABLES');
152 2
		$statement->execute();
153 2
		$conf = $statement->fetchAll(\PDO::FETCH_KEY_PAIR);
154 2
		$statement = $pdo->prepare('SHOW STATUS');
155 2
		$statement->execute();
156 2
		$conf = array_merge($conf, $statement->fetchAll(\PDO::FETCH_KEY_PAIR));
157 2
		$statement = $pdo->prepare('SELECT VERSION()');
158
		$statement->execute();
159
		$fullVersion = $statement->fetch(\PDO::FETCH_COLUMN);
160
		[$version] = explode('-', $conf['version']);
161
		$conf['version_comment'] = $conf['version_comment'] . '|' . $fullVersion;
162
		$typeDb = 'MySQL';
163
		if (false !== stripos($conf['version_comment'], 'MariaDb')) {
164
			$typeDb = 'MariaDb';
165
		}
166
		$memory = $conf['key_buffer_size'] + ($conf['query_cache_size'] ?? 0) + $conf['tmp_table_size'] + $conf['innodb_buffer_pool_size'] +
167
		($conf['innodb_additional_mem_pool_size'] ?? 0) + $conf['innodb_log_buffer_size'] + ($conf['max_connections'] * ($conf['sort_buffer_size']
168
				+ $conf['read_buffer_size'] + $conf['read_rnd_buffer_size'] + $conf['join_buffer_size'] + $conf['thread_stack'] + $conf['binlog_cache_size']));
169
		return \array_merge($conf, [
170
			'driver' => $this->getDriverName(),
171
			'typeDb' => $typeDb,
172 6352
			'serverVersion' => $version,
173
			'maximumMemorySize' => $memory,
174 6352
			'clientVersion' => $pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION),
175
			'connectionStatus' => $pdo->getAttribute(\PDO::ATTR_CONNECTION_STATUS),
176
			'serverInfo' => $pdo->getAttribute(\PDO::ATTR_SERVER_INFO),
177
		]);
178
	}
179
180
	/**
181
	 * Get database info.
182
	 *
183
	 * @return array
184
	 */
185
	public function getDbInfo(): array
186 75
	{
187
		$return = [
188 75
			'isFileSize' => false,
189
			'size' => 0,
190
			'dataSize' => 0,
191
			'indexSize' => 0,
192
			'filesSize' => 0,
193
			'tables' => [],
194
		];
195
		$statement = $this->getSlavePdo()->prepare("SHOW TABLE STATUS FROM `{$this->dbName}`");
196
		$statement->execute();
197
		while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
198
			$return['tables'][$row['Name']] = [
199 3
				'rows' => $row['Rows'],
200
				'format' => $row['Row_format'],
201 3
				'engine' => $row['Engine'],
202
				'dataSize' => $row['Data_length'],
203
				'indexSize' => $row['Index_length'],
204
				'collation' => $row['Collation'],
205
			];
206
			$return['dataSize'] += $row['Data_length'];
207 3
			$return['indexSize'] += $row['Index_length'];
208
			$return['size'] += $row['Data_length'] += $row['Index_length'];
209
		}
210
		try {
211
			$statement = $this->getSlavePdo()->prepare("SELECT * FROM `information_schema`.`INNODB_SYS_TABLESPACES` WHERE `NAME` LIKE '{$this->dbName}/%'");
212
			$statement->execute();
213
			while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
214
				$tableName = str_replace($this->dbName . '/', '', $row['NAME']);
215
				if (!empty($row['ALLOCATED_SIZE'])) {
216
					if (isset($return['tables'][$tableName])) {
217
						$return['tables'][$tableName]['fileSize'] = $row['ALLOCATED_SIZE'];
218
						$return['isFileSize'] = true;
219 11
					}
220
					$return['filesSize'] += $row['ALLOCATED_SIZE'];
221 11
				}
222 7
			}
223 7
		} catch (\Throwable $th) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
224 7
		}
225 7
		return $return;
226 7
	}
227 7
228
	/**
229 4
	 * Processes a SQL statement by quoting table and column names that are enclosed within double brackets.
230 4
	 * Tokens enclosed within double curly brackets are treated as table names, while
231 4
	 * tokens enclosed within double square brackets are column names. They will be quoted accordingly.
232 4
	 * Also, the percentage character "%" at the beginning or ending of a table name will be replaced
233
	 * with [[tablePrefix]].
234 11
	 *
235
	 * @param string $sql the SQL to be quoted
236
	 *
237
	 * @return string the quoted SQL
238
	 */
239
	public function quoteSql($sql)
240
	{
241
		return str_replace('#__', $this->tablePrefix, $sql);
242
	}
243
244 10
	/**
245
	 * Returns the ID of the last inserted row or sequence value.
246 10
	 *
247
	 * @param string $sequenceName name of the sequence object (required by some DBMS) ex. table vtiger_picklist >>> vtiger_picklist_picklistid_seq
248
	 *
249
	 * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
250
	 *
251
	 * @see https://www.php.net/manual/en/function.PDO-lastInsertId.php
252
	 */
253
	public function getLastInsertID($sequenceName = '')
254
	{
255
		return parent::getLastInsertID(str_replace('#__', $this->tablePrefix, $sequenceName));
256
	}
257 4
258
	/**
259 4
	 * Creates the PDO instance.
260 4
	 * This method is called by [[open]] to establish a DB connection.
261 4
	 * The default implementation will create a PHP PDO instance.
262
	 * You may override this method if the default PDO needs to be adapted for certain DBMS.
263 4
	 *
264 4
	 * @return PDO the pdo instance
0 ignored issues
show
The type App\PDO was not found. Did you mean PDO? If so, make sure to prefix the type with \.
Loading history...
265
	 */
266
	protected function createPdoInstance()
267
	{
268
		if (Debuger::isDebugBar()) {
269
			$bebugBar = Debuger::getDebugBar();
270
			$pdo = new Debug\DebugBar\TraceablePDO(parent::createPdoInstance());
271
			if ($bebugBar->hasCollector('pdo')) {
272
				$pdoCollector = $bebugBar->getCollector('pdo');
273
				$pdoCollector->addConnection($pdo, $this->dbType);
0 ignored issues
show
The method addConnection() does not exist on DebugBar\DataCollector\DataCollectorInterface. It seems like you code against a sub-type of DebugBar\DataCollector\DataCollectorInterface such as DebugBar\DataCollector\PDO\PDOCollector. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

273
				$pdoCollector->/** @scrutinizer ignore-call */ 
274
                   addConnection($pdo, $this->dbType);
Loading history...
274
			} else {
275
				$pdoCollector = new \DebugBar\DataCollector\PDO\PDOCollector();
276
				$pdoCollector->addConnection($pdo, $this->dbType);
277
				$bebugBar->addCollector($pdoCollector);
278
			}
279
			return $pdo;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $pdo returns the type App\Debug\DebugBar\TraceablePDO which is incompatible with the documented return type App\PDO.
Loading history...
280
		}
281
		return parent::createPdoInstance();
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::createPdoInstance() returns the type PDO which is incompatible with the documented return type App\PDO.
Loading history...
282
	}
283
284
	/**
285
	 * Get table unique ID. Temporary function.
286
	 *
287
	 * @param string       $tableName
288
	 * @param false|string $columnName
289
	 * @param bool         $seq
290
	 *
291
	 * @return int
292
	 */
293
	public function getUniqueID($tableName, $columnName = false, $seq = true)
294
	{
295
		if ($seq) {
296
			$tableName .= '_seq';
297
			$id = (new \App\Db\Query())->from($tableName)->scalar($this);
298
			++$id;
299
			$this->createCommand()->update($tableName, [
300
				'id' => $id,
301
			])->execute();
302
		} else {
303
			$id = (new \App\Db\Query())
304
				->from($tableName)
305
				->max($columnName, $this);
306
			++$id;
307
		}
308
		return $id;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $id also could return the type false|string which is incompatible with the documented return type integer.
Loading history...
309
	}
310
311
	/**
312
	 * Check if table is present in database.
313
	 *
314
	 * @param string $tableName
315
	 *
316
	 * @return bool
317
	 */
318
	public function isTableExists($tableName)
319
	{
320
		return \in_array(str_replace('#__', $this->tablePrefix, $tableName), $this->getSchema()->getTableNames());
321
	}
322
323
	/**
324
	 * Creating a new DB table.
325
	 *
326
	 * @param string $tableName
327
	 * @param mixed  $columns
328
	 *
329
	 * @return bool
330
	 */
331
	public function createTable($tableName, $columns)
332
	{
333
		$tableOptions = null;
334
		if ('mysql' === $this->getDriverName()) {
335
			$tableOptions = 'CHARACTER SET utf8 ENGINE=InnoDB';
336
		}
337
		$this->createCommand()->createTable($tableName, $columns, $tableOptions)->execute();
338
	}
339
340
	/**
341
	 * Get table keys.
342
	 *
343
	 * @param string $tableName
344
	 *
345
	 * @return array
346
	 */
347
	public function getTableKeys($tableName)
348
	{
349
		if (Cache::has('getTableKeys', $tableName)) {
350
			return Cache::get('getTableKeys', $tableName);
351
		}
352
		if (!$this->isTableExists($tableName)) {
353
			return [];
354
		}
355
		$tableName = $this->quoteTableName(str_replace('#__', $this->tablePrefix, $tableName));
356
		$keys = [];
357
		if ('mysql' === $this->getDriverName()) {
358
			$dataReader = $this->createCommand()->setSql('SHOW KEYS FROM ' . $tableName)->query();
359
			while ($row = $dataReader->read()) {
360
				$keys[$row['Key_name']][$row['Column_name']] = ['columnName' => $row['Column_name'], 'unique' => empty($row['Non_unique'])];
361
			}
362
		}
363
		Cache::save('getTableKeys', $tableName, $keys, Cache::LONG);
364
		return $keys;
365
	}
366
367
	/**
368
	 * Get table primary keys.
369
	 *
370
	 * @param type $tableName
0 ignored issues
show
The type App\type was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
371
	 *
372
	 * @return type
373
	 */
374
	public function getPrimaryKey($tableName)
375
	{
376
		if (Cache::has('getPrimaryKey', $tableName)) {
377
			return Cache::get('getPrimaryKey', $tableName);
378
		}
379
		$key = [];
380
		if ('mysql' === $this->getDriverName()) {
381
			$tableKeys = $this->getTableKeys($tableName);
382
			$key = isset($tableKeys['PRIMARY']) ? ['PRIMARY' => array_keys($tableKeys['PRIMARY'])] : [];
383
		}
384
		Cache::save('getPrimaryKey', $tableName, $key, Cache::LONG);
385
		return $key;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $key returns the type array|array<string,array> which is incompatible with the documented return type App\type.
Loading history...
386
	}
387
}
388