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/objectcache/ObjectCache.php (2 issues)

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
 * Functions to get cache objects.
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 Cache
22
 */
23
24
use MediaWiki\Logger\LoggerFactory;
25
use MediaWiki\MediaWikiServices;
26
27
/**
28
 * Functions to get cache objects
29
 *
30
 * The word "cache" has two main dictionary meanings, and both
31
 * are used in this factory class. They are:
32
 *
33
 *   - a) Cache (the computer science definition).
34
 *        A place to store copies or computations on existing data for
35
 *        higher access speeds.
36
 *   - b) Storage.
37
 *        A place to store lightweight data that is not canonically
38
 *        stored anywhere else (e.g. a "hoard" of objects).
39
 *
40
 * The former should always use strongly consistent stores, so callers don't
41
 * have to deal with stale reads. The latter may be eventually consistent, but
42
 * callers can use BagOStuff:READ_LATEST to see the latest available data.
43
 *
44
 * Primary entry points:
45
 *
46
 * - ObjectCache::getMainWANInstance()
47
 *   Purpose: Memory cache.
48
 *   Stored in the local data-center's main cache (keyspace different from local-cluster cache).
49
 *   Delete events are broadcasted to other DCs main cache. See WANObjectCache for details.
50
 *
51
 * - ObjectCache::getLocalServerInstance( $fallbackType )
52
 *   Purpose: Memory cache for very hot keys.
53
 *   Stored only on the individual web server (typically APC or APCu for web requests,
54
 *   and EmptyBagOStuff in CLI mode).
55
 *   Not replicated to the other servers.
56
 *
57
 * - ObjectCache::getLocalClusterInstance()
58
 *   Purpose: Memory storage for per-cluster coordination and tracking.
59
 *   A typical use case would be a rate limit counter or cache regeneration mutex.
60
 *   Stored centrally within the local data-center. Not replicated to other DCs.
61
 *   Configured by $wgMainCacheType.
62
 *
63
 * - ObjectCache::getMainStashInstance()
64
 *   Purpose: Ephemeral global storage.
65
 *   Stored centrally within the primary data-center.
66
 *   Changes are applied there first and replicated to other DCs (best-effort).
67
 *   To retrieve the latest value (e.g. not from a replica DB), use BagOStuff::READ_LATEST.
68
 *   This store may be subject to LRU style evictions.
69
 *
70
 * - ObjectCache::getInstance( $cacheType )
71
 *   Purpose: Special cases (like tiered memory/disk caches).
72
 *   Get a specific cache type by key in $wgObjectCaches.
73
 *
74
 * All the above cache instances (BagOStuff and WANObjectCache) have their makeKey()
75
 * method scoped to the *current* wiki ID. Use makeGlobalKey() to avoid this scoping
76
 * when using keys that need to be shared amongst wikis.
77
 *
78
 * @ingroup Cache
79
 */
80
class ObjectCache {
81
	/** @var BagOStuff[] Map of (id => BagOStuff) */
82
	public static $instances = [];
83
	/** @var WANObjectCache[] Map of (id => WANObjectCache) */
84
	public static $wanInstances = [];
85
86
	/**
87
	 * Get a cached instance of the specified type of cache object.
88
	 *
89
	 * @param string $id A key in $wgObjectCaches.
90
	 * @return BagOStuff
91
	 */
92
	public static function getInstance( $id ) {
93
		if ( !isset( self::$instances[$id] ) ) {
94
			self::$instances[$id] = self::newFromId( $id );
95
		}
96
97
		return self::$instances[$id];
98
	}
99
100
	/**
101
	 * Get a cached instance of the specified type of WAN cache object.
102
	 *
103
	 * @since 1.26
104
	 * @param string $id A key in $wgWANObjectCaches.
105
	 * @return WANObjectCache
106
	 */
107
	public static function getWANInstance( $id ) {
108
		if ( !isset( self::$wanInstances[$id] ) ) {
109
			self::$wanInstances[$id] = self::newWANCacheFromId( $id );
110
		}
111
112
		return self::$wanInstances[$id];
113
	}
114
115
	/**
116
	 * Create a new cache object of the specified type.
117
	 *
118
	 * @param string $id A key in $wgObjectCaches.
119
	 * @return BagOStuff
120
	 * @throws InvalidArgumentException
121
	 */
122
	public static function newFromId( $id ) {
123
		global $wgObjectCaches;
124
125
		if ( !isset( $wgObjectCaches[$id] ) ) {
126
			// Always recognize these ones
127
			if ( $id === CACHE_NONE ) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $id (string) and CACHE_NONE (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
128
				return new EmptyBagOStuff();
129
			} elseif ( $id === 'hash' ) {
130
				return new HashBagOStuff();
131
			}
132
133
			throw new InvalidArgumentException( "Invalid object cache type \"$id\" requested. " .
134
				"It is not present in \$wgObjectCaches." );
135
		}
136
137
		return self::newFromParams( $wgObjectCaches[$id] );
138
	}
139
140
	/**
141
	 * Get the default keyspace for this wiki.
142
	 *
143
	 * This is either the value of the `CachePrefix` configuration variable,
144
	 * or (if the former is unset) the `DBname` configuration variable, with
145
	 * `DBprefix` (if defined).
146
	 *
147
	 * @return string
148
	 */
149
	public static function getDefaultKeyspace() {
150
		global $wgCachePrefix;
151
152
		$keyspace = $wgCachePrefix;
153
		if ( is_string( $keyspace ) && $keyspace !== '' ) {
154
			return $keyspace;
155
		}
156
157
		return wfWikiID();
158
	}
159
160
	/**
161
	 * Create a new cache object from parameters.
162
	 *
163
	 * @param array $params Must have 'factory' or 'class' property.
164
	 *  - factory: Callback passed $params that returns BagOStuff.
165
	 *  - class: BagOStuff subclass constructed with $params.
166
	 *  - loggroup: Alias to set 'logger' key with LoggerFactory group.
167
	 *  - .. Other parameters passed to factory or class.
168
	 * @return BagOStuff
169
	 * @throws InvalidArgumentException
170
	 */
171
	public static function newFromParams( $params ) {
0 ignored issues
show
newFromParams uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
172 View Code Duplication
		if ( isset( $params['loggroup'] ) ) {
173
			$params['logger'] = LoggerFactory::getInstance( $params['loggroup'] );
174
		} else {
175
			$params['logger'] = LoggerFactory::getInstance( 'objectcache' );
176
		}
177
		if ( !isset( $params['keyspace'] ) ) {
178
			$params['keyspace'] = self::getDefaultKeyspace();
179
		}
180
		if ( isset( $params['factory'] ) ) {
181
			return call_user_func( $params['factory'], $params );
182
		} elseif ( isset( $params['class'] ) ) {
183
			$class = $params['class'];
184
			// Automatically set the 'async' update handler
185
			$params['asyncHandler'] = isset( $params['asyncHandler'] )
186
				? $params['asyncHandler']
187
				: 'DeferredUpdates::addCallableUpdate';
188
			// Enable reportDupes by default
189
			$params['reportDupes'] = isset( $params['reportDupes'] )
190
				? $params['reportDupes']
191
				: true;
192
			// Do b/c logic for SqlBagOStuff
193
			if ( is_a( $class, SqlBagOStuff::class, true ) ) {
194
				if ( isset( $params['server'] ) && !isset( $params['servers'] ) ) {
195
					$params['servers'] = [ $params['server'] ];
196
					unset( $params['server'] );
197
				}
198
				// In the past it was not required to set 'dbDirectory' in $wgObjectCaches
199
				if ( isset( $params['servers'] ) ) {
200
					foreach ( $params['servers'] as &$server ) {
201
						if ( $server['type'] === 'sqlite' && !isset( $server['dbDirectory'] ) ) {
202
							$server['dbDirectory'] = MediaWikiServices::getInstance()
203
								->getMainConfig()->get( 'SQLiteDataDir' );
204
						}
205
					}
206
				}
207
			}
208
209
			// Do b/c logic for MemcachedBagOStuff
210
			if ( is_subclass_of( $class, MemcachedBagOStuff::class ) ) {
211
				if ( !isset( $params['servers'] ) ) {
212
					$params['servers'] = $GLOBALS['wgMemCachedServers'];
213
				}
214
				if ( !isset( $params['debug'] ) ) {
215
					$params['debug'] = $GLOBALS['wgMemCachedDebug'];
216
				}
217
				if ( !isset( $params['persistent'] ) ) {
218
					$params['persistent'] = $GLOBALS['wgMemCachedPersistent'];
219
				}
220
				if ( !isset( $params['timeout'] ) ) {
221
					$params['timeout'] = $GLOBALS['wgMemCachedTimeout'];
222
				}
223
			}
224
			return new $class( $params );
225
		} else {
226
			throw new InvalidArgumentException( "The definition of cache type \""
227
				. print_r( $params, true ) . "\" lacks both "
228
				. "factory and class parameters." );
229
		}
230
	}
231
232
	/**
233
	 * Factory function for CACHE_ANYTHING (referenced from DefaultSettings.php)
234
	 *
235
	 * CACHE_ANYTHING means that stuff has to be cached, not caching is not an option.
236
	 * If a caching method is configured for any of the main caches ($wgMainCacheType,
237
	 * $wgMessageCacheType, $wgParserCacheType), then CACHE_ANYTHING will effectively
238
	 * be an alias to the configured cache choice for that.
239
	 * If no cache choice is configured (by default $wgMainCacheType is CACHE_NONE),
240
	 * then CACHE_ANYTHING will forward to CACHE_DB.
241
	 *
242
	 * @param array $params
243
	 * @return BagOStuff
244
	 */
245
	public static function newAnything( $params ) {
246
		global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType;
247
		$candidates = [ $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType ];
248
		foreach ( $candidates as $candidate ) {
249
			if ( $candidate !== CACHE_NONE && $candidate !== CACHE_ANYTHING ) {
250
				return self::getInstance( $candidate );
251
			}
252
		}
253
254
		if ( MediaWikiServices::getInstance()->isServiceDisabled( 'DBLoadBalancer' ) ) {
255
			// The LoadBalancer is disabled, probably because
256
			// MediaWikiServices::disableStorageBackend was called.
257
			$candidate = CACHE_NONE;
258
		} else {
259
			$candidate = CACHE_DB;
260
		}
261
262
		return self::getInstance( $candidate );
263
	}
264
265
	/**
266
	 * Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
267
	 *
268
	 * This will look for any APC or APCu style server-local cache.
269
	 * A fallback cache can be specified if none is found.
270
	 *
271
	 *     // Direct calls
272
	 *     ObjectCache::getLocalServerInstance( $fallbackType );
273
	 *
274
	 *     // From $wgObjectCaches via newFromParams()
275
	 *     ObjectCache::getLocalServerInstance( [ 'fallback' => $fallbackType ] );
276
	 *
277
	 * @param int|string|array $fallback Fallback cache or parameter map with 'fallback'
278
	 * @return BagOStuff
279
	 * @throws InvalidArgumentException
280
	 * @since 1.27
281
	 */
282
	public static function getLocalServerInstance( $fallback = CACHE_NONE ) {
283
		$cache = MediaWikiServices::getInstance()->getLocalServerObjectCache();
284
		if ( $cache instanceof EmptyBagOStuff ) {
285
			if ( is_array( $fallback ) ) {
286
				$fallback = isset( $fallback['fallback'] ) ? $fallback['fallback'] : CACHE_NONE;
287
			}
288
			$cache = self::getInstance( $fallback );
289
		}
290
291
		return $cache;
292
	}
293
294
	/**
295
	 * Create a new cache object of the specified type.
296
	 *
297
	 * @since 1.26
298
	 * @param string $id A key in $wgWANObjectCaches.
299
	 * @return WANObjectCache
300
	 * @throws UnexpectedValueException
301
	 */
302
	public static function newWANCacheFromId( $id ) {
303
		global $wgWANObjectCaches, $wgObjectCaches;
304
305
		if ( !isset( $wgWANObjectCaches[$id] ) ) {
306
			throw new UnexpectedValueException(
307
				"Cache type \"$id\" requested is not present in \$wgWANObjectCaches." );
308
		}
309
310
		$params = $wgWANObjectCaches[$id];
311
		if ( !isset( $wgObjectCaches[$params['cacheId']] ) ) {
312
			throw new UnexpectedValueException(
313
				"Cache type \"{$params['cacheId']}\" is not present in \$wgObjectCaches." );
314
		}
315
		$params['store'] = $wgObjectCaches[$params['cacheId']];
316
317
		return self::newWANCacheFromParams( $params );
318
	}
319
320
	/**
321
	 * Create a new cache object of the specified type.
322
	 *
323
	 * @since 1.28
324
	 * @param array $params
325
	 * @return WANObjectCache
326
	 * @throws UnexpectedValueException
327
	 */
328
	public static function newWANCacheFromParams( array $params ) {
329
		$erGroup = MediaWikiServices::getInstance()->getEventRelayerGroup();
330
		foreach ( $params['channels'] as $action => $channel ) {
331
			$params['relayers'][$action] = $erGroup->getRelayer( $channel );
332
			$params['channels'][$action] = $channel;
333
		}
334
		$params['cache'] = self::newFromParams( $params['store'] );
335 View Code Duplication
		if ( isset( $params['loggroup'] ) ) {
336
			$params['logger'] = LoggerFactory::getInstance( $params['loggroup'] );
337
		} else {
338
			$params['logger'] = LoggerFactory::getInstance( 'objectcache' );
339
		}
340
		$class = $params['class'];
341
342
		return new $class( $params );
343
	}
344
345
	/**
346
	 * Get the main cluster-local cache object.
347
	 *
348
	 * @since 1.27
349
	 * @return BagOStuff
350
	 */
351
	public static function getLocalClusterInstance() {
352
		global $wgMainCacheType;
353
354
		return self::getInstance( $wgMainCacheType );
355
	}
356
357
	/**
358
	 * Get the main WAN cache object.
359
	 *
360
	 * @since 1.26
361
	 * @return WANObjectCache
362
	 * @deprecated Since 1.28 Use MediaWikiServices::getMainWANCache()
363
	 */
364
	public static function getMainWANInstance() {
365
		return MediaWikiServices::getInstance()->getMainWANObjectCache();
366
	}
367
368
	/**
369
	 * Get the cache object for the main stash.
370
	 *
371
	 * Stash objects are BagOStuff instances suitable for storing light
372
	 * weight data that is not canonically stored elsewhere (such as RDBMS).
373
	 * Stashes should be configured to propagate changes to all data-centers.
374
	 *
375
	 * Callers should be prepared for:
376
	 *   - a) Writes to be slower in non-"primary" (e.g. HTTP GET/HEAD only) DCs
377
	 *   - b) Reads to be eventually consistent, e.g. for get()/getMulti()
378
	 * In general, this means avoiding updates on idempotent HTTP requests and
379
	 * avoiding an assumption of perfect serializability (or accepting anomalies).
380
	 * Reads may be eventually consistent or data might rollback as nodes flap.
381
	 * Callers can use BagOStuff:READ_LATEST to see the latest available data.
382
	 *
383
	 * @return BagOStuff
384
	 * @since 1.26
385
	 * @deprecated Since 1.28 Use MediaWikiServices::getMainObjectStash
386
	 */
387
	public static function getMainStashInstance() {
388
		return MediaWikiServices::getInstance()->getMainObjectStash();
389
	}
390
391
	/**
392
	 * Clear all the cached instances.
393
	 */
394
	public static function clear() {
395
		self::$instances = [];
396
		self::$wanInstances = [];
397
	}
398
}
399