Issues (4122)

Security Analysis    not enabled

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

  Cross-Site Scripting
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.
  File Exposure
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.
  File Manipulation
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.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  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.
  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.
  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.
  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.
  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.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  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.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
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.

includes/libs/lockmanager/DBLockManager.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Version of LockManager based on using DB table locks.
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 * @ingroup LockManager
22
 */
23
24
/**
25
 * Version of LockManager based on using named/row DB locks.
26
 *
27
 * This is meant for multi-wiki systems that may share files.
28
 *
29
 * All lock requests for a resource, identified by a hash string, will map to one bucket.
30
 * Each bucket maps to one or several peer DBs, each on their own server.
31
 * A majority of peer DBs must agree for a lock to be acquired.
32
 *
33
 * Caching is used to avoid hitting servers that are down.
34
 *
35
 * @ingroup LockManager
36
 * @since 1.19
37
 */
38
abstract class DBLockManager extends QuorumLockManager {
39
	/** @var array[]|IDatabase[] Map of (DB names => server config or IDatabase) */
40
	protected $dbServers; // (DB name => server config array)
41
	/** @var BagOStuff */
42
	protected $statusCache;
43
44
	protected $lockExpiry; // integer number of seconds
45
	protected $safeDelay; // integer number of seconds
46
	/** @var IDatabase[] Map Database connections (DB name => Database) */
47
	protected $conns = [];
48
49
	/**
50
	 * Construct a new instance from configuration.
51
	 *
52
	 * @param array $config Parameters include:
53
	 *   - dbServers   : Associative array of DB names to server configuration.
54
	 *                   Configuration is an associative array that includes:
55
	 *                     - host        : DB server name
56
	 *                     - dbname      : DB name
57
	 *                     - type        : DB type (mysql,postgres,...)
58
	 *                     - user        : DB user
59
	 *                     - password    : DB user password
60
	 *                     - tablePrefix : DB table prefix
61
	 *                     - flags       : DB flags; bitfield of IDatabase::DBO_* constants
62
	 *   - dbsByBucket : Array of 1-16 consecutive integer keys, starting from 0,
63
	 *                   each having an odd-numbered list of DB names (peers) as values.
64
	 *   - lockExpiry  : Lock timeout (seconds) for dropped connections. [optional]
65
	 *                   This tells the DB server how long to wait before assuming
66
	 *                   connection failure and releasing all the locks for a session.
67
	 *   - srvCache    : A BagOStuff instance using APC or the like.
68
	 */
69
	public function __construct( array $config ) {
70
		parent::__construct( $config );
71
72
		$this->dbServers = $config['dbServers'];
73
		// Sanitize srvsByBucket config to prevent PHP errors
74
		$this->srvsByBucket = array_filter( $config['dbsByBucket'], 'is_array' );
75
		$this->srvsByBucket = array_values( $this->srvsByBucket ); // consecutive
76
77
		if ( isset( $config['lockExpiry'] ) ) {
78
			$this->lockExpiry = $config['lockExpiry'];
79
		} else {
80
			$met = ini_get( 'max_execution_time' );
81
			$this->lockExpiry = $met ? $met : 60; // use some sane amount if 0
82
		}
83
		$this->safeDelay = ( $this->lockExpiry <= 0 )
84
			? 60 // pick a safe-ish number to match DB timeout default
85
			: $this->lockExpiry; // cover worst case
86
87
		// Tracks peers that couldn't be queried recently to avoid lengthy
88
		// connection timeouts. This is useless if each bucket has one peer.
89
		$this->statusCache = isset( $config['srvCache'] )
90
			? $config['srvCache']
91
			: new HashBagOStuff();
92
	}
93
94
	/**
95
	 * @TODO change this code to work in one batch
96
	 * @param string $lockSrv
97
	 * @param array $pathsByType
98
	 * @return StatusValue
99
	 */
100 View Code Duplication
	protected function getLocksOnServer( $lockSrv, array $pathsByType ) {
101
		$status = StatusValue::newGood();
102
		foreach ( $pathsByType as $type => $paths ) {
103
			$status->merge( $this->doGetLocksOnServer( $lockSrv, $paths, $type ) );
104
		}
105
106
		return $status;
107
	}
108
109
	abstract protected function doGetLocksOnServer( $lockSrv, array $paths, $type );
110
111
	protected function freeLocksOnServer( $lockSrv, array $pathsByType ) {
112
		return StatusValue::newGood();
113
	}
114
115
	/**
116
	 * @see QuorumLockManager::isServerUp()
117
	 * @param string $lockSrv
118
	 * @return bool
119
	 */
120
	protected function isServerUp( $lockSrv ) {
121
		if ( !$this->cacheCheckFailures( $lockSrv ) ) {
122
			return false; // recent failure to connect
123
		}
124
		try {
125
			$this->getConnection( $lockSrv );
126
		} catch ( DBError $e ) {
127
			$this->cacheRecordFailure( $lockSrv );
128
129
			return false; // failed to connect
130
		}
131
132
		return true;
133
	}
134
135
	/**
136
	 * Get (or reuse) a connection to a lock DB
137
	 *
138
	 * @param string $lockDb
139
	 * @return IDatabase
140
	 * @throws DBError
141
	 * @throws UnexpectedValueException
142
	 */
143
	protected function getConnection( $lockDb ) {
144
		if ( !isset( $this->conns[$lockDb] ) ) {
145
			if ( $this->dbServers[$lockDb] instanceof IDatabase ) {
146
				// Direct injected connection hande for $lockDB
147
				$db = $this->dbServers[$lockDb];
148
			} elseif ( is_array( $this->dbServers[$lockDb] ) ) {
149
				// Parameters to construct a new database connection
150
				$config = $this->dbServers[$lockDb];
151
				$db = Database::factory( $config['type'], $config );
0 ignored issues
show
It seems like $config defined by $this->dbServers[$lockDb] on line 150 can also be of type object<IDatabase>; however, Database::factory() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
152
			} else {
153
				throw new UnexpectedValueException( "No server called '$lockDb'." );
154
			}
155
156
			$db->clearFlag( DBO_TRX );
157
			# If the connection drops, try to avoid letting the DB rollback
158
			# and release the locks before the file operations are finished.
159
			# This won't handle the case of DB server restarts however.
160
			$options = [];
161
			if ( $this->lockExpiry > 0 ) {
162
				$options['connTimeout'] = $this->lockExpiry;
163
			}
164
			$db->setSessionOptions( $options );
165
			$this->initConnection( $lockDb, $db );
166
167
			$this->conns[$lockDb] = $db;
168
		}
169
170
		return $this->conns[$lockDb];
171
	}
172
173
	/**
174
	 * Do additional initialization for new lock DB connection
175
	 *
176
	 * @param string $lockDb
177
	 * @param IDatabase $db
178
	 * @throws DBError
179
	 */
180
	protected function initConnection( $lockDb, IDatabase $db ) {
181
	}
182
183
	/**
184
	 * Checks if the DB has not recently had connection/query errors.
185
	 * This just avoids wasting time on doomed connection attempts.
186
	 *
187
	 * @param string $lockDb
188
	 * @return bool
189
	 */
190
	protected function cacheCheckFailures( $lockDb ) {
191
		return ( $this->safeDelay > 0 )
192
			? !$this->statusCache->get( $this->getMissKey( $lockDb ) )
193
			: true;
194
	}
195
196
	/**
197
	 * Log a lock request failure to the cache
198
	 *
199
	 * @param string $lockDb
200
	 * @return bool Success
201
	 */
202
	protected function cacheRecordFailure( $lockDb ) {
203
		return ( $this->safeDelay > 0 )
204
			? $this->statusCache->set( $this->getMissKey( $lockDb ), 1, $this->safeDelay )
205
			: true;
206
	}
207
208
	/**
209
	 * Get a cache key for recent query misses for a DB
210
	 *
211
	 * @param string $lockDb
212
	 * @return string
213
	 */
214
	protected function getMissKey( $lockDb ) {
215
		return 'dblockmanager:downservers:' . str_replace( ' ', '_', $lockDb );
216
	}
217
218
	/**
219
	 * Make sure remaining locks get cleared for sanity
220
	 */
221
	function __destruct() {
222
		$this->releaseAllLocks();
223
		foreach ( $this->conns as $db ) {
224
			$db->close();
225
		}
226
	}
227
}
228