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/resourceloader/ResourceLoaderContext.php (3 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
 * Context for ResourceLoader modules.
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
 * @author Trevor Parscal
22
 * @author Roan Kattouw
23
 */
24
25
use MediaWiki\Logger\LoggerFactory;
26
27
/**
28
 * Object passed around to modules which contains information about the state
29
 * of a specific loader request.
30
 */
31
class ResourceLoaderContext {
32
	protected $resourceLoader;
33
	protected $request;
34
	protected $logger;
35
36
	// Module content vary
37
	protected $skin;
38
	protected $language;
39
	protected $debug;
40
	protected $user;
41
42
	// Request vary (in addition to cache vary)
43
	protected $modules;
44
	protected $only;
45
	protected $version;
46
	protected $raw;
47
	protected $image;
48
	protected $variant;
49
	protected $format;
50
51
	protected $direction;
52
	protected $hash;
53
	protected $userObj;
54
	protected $imageObj;
55
56
	/**
57
	 * @param ResourceLoader $resourceLoader
58
	 * @param WebRequest $request
59
	 */
60
	public function __construct( ResourceLoader $resourceLoader, WebRequest $request ) {
61
		$this->resourceLoader = $resourceLoader;
62
		$this->request = $request;
63
		$this->logger = $resourceLoader->getLogger();
64
65
		// Future developers: Avoid use of getVal() in this class, which performs
66
		// expensive UTF normalisation by default. Use getRawVal() instead.
67
		// Values here are either one of a finite number of internal IDs,
68
		// or previously-stored user input (e.g. titles, user names) that were passed
69
		// to this endpoint by ResourceLoader itself from the canonical value.
70
		// Values do not come directly from user input and need not match.
71
72
		// List of modules
73
		$modules = $request->getRawVal( 'modules' );
74
		$this->modules = $modules ? self::expandModuleNames( $modules ) : [];
75
76
		// Various parameters
77
		$this->user = $request->getRawVal( 'user' );
78
		$this->debug = $request->getFuzzyBool(
79
			'debug',
80
			$resourceLoader->getConfig()->get( 'ResourceLoaderDebug' )
81
		);
82
		$this->only = $request->getRawVal( 'only', null );
83
		$this->version = $request->getRawVal( 'version', null );
84
		$this->raw = $request->getFuzzyBool( 'raw' );
85
86
		// Image requests
87
		$this->image = $request->getRawVal( 'image' );
88
		$this->variant = $request->getRawVal( 'variant' );
89
		$this->format = $request->getRawVal( 'format' );
90
91
		$this->skin = $request->getRawVal( 'skin' );
92
		$skinnames = Skin::getSkinNames();
93
		// If no skin is specified, or we don't recognize the skin, use the default skin
94
		if ( !$this->skin || !isset( $skinnames[$this->skin] ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->skin of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
95
			$this->skin = $resourceLoader->getConfig()->get( 'DefaultSkin' );
96
		}
97
	}
98
99
	/**
100
	 * Expand a string of the form jquery.foo,bar|jquery.ui.baz,quux to
101
	 * an array of module names like [ 'jquery.foo', 'jquery.bar',
102
	 * 'jquery.ui.baz', 'jquery.ui.quux' ]
103
	 * @param string $modules Packed module name list
104
	 * @return array Array of module names
105
	 */
106
	public static function expandModuleNames( $modules ) {
107
		$retval = [];
108
		$exploded = explode( '|', $modules );
109
		foreach ( $exploded as $group ) {
110
			if ( strpos( $group, ',' ) === false ) {
111
				// This is not a set of modules in foo.bar,baz notation
112
				// but a single module
113
				$retval[] = $group;
114
			} else {
115
				// This is a set of modules in foo.bar,baz notation
116
				$pos = strrpos( $group, '.' );
117
				if ( $pos === false ) {
118
					// Prefixless modules, i.e. without dots
119
					$retval = array_merge( $retval, explode( ',', $group ) );
120
				} else {
121
					// We have a prefix and a bunch of suffixes
122
					$prefix = substr( $group, 0, $pos ); // 'foo'
123
					$suffixes = explode( ',', substr( $group, $pos + 1 ) ); // [ 'bar', 'baz' ]
124
					foreach ( $suffixes as $suffix ) {
125
						$retval[] = "$prefix.$suffix";
126
					}
127
				}
128
			}
129
		}
130
		return $retval;
131
	}
132
133
	/**
134
	 * Return a dummy ResourceLoaderContext object suitable for passing into
135
	 * things that don't "really" need a context.
136
	 * @return ResourceLoaderContext
137
	 */
138
	public static function newDummyContext() {
139
		return new self( new ResourceLoader(
140
			ConfigFactory::getDefaultInstance()->makeConfig( 'main' ),
141
			LoggerFactory::getInstance( 'resourceloader' )
142
		), new FauxRequest( [] ) );
143
	}
144
145
	/**
146
	 * @return ResourceLoader
147
	 */
148
	public function getResourceLoader() {
149
		return $this->resourceLoader;
150
	}
151
152
	/**
153
	 * @return WebRequest
154
	 */
155
	public function getRequest() {
156
		return $this->request;
157
	}
158
159
	/**
160
	 * @since 1.27
161
	 * @return \Psr\Log\LoggerInterface
162
	 */
163
	public function getLogger() {
164
		return $this->logger;
165
	}
166
167
	/**
168
	 * @return array
169
	 */
170
	public function getModules() {
171
		return $this->modules;
172
	}
173
174
	/**
175
	 * @return string
176
	 */
177
	public function getLanguage() {
178
		if ( $this->language === null ) {
179
			// Must be a valid language code after this point (T64849)
180
			// Only support uselang values that follow built-in conventions (T102058)
181
			$lang = $this->getRequest()->getRawVal( 'lang', '' );
182
			// Stricter version of RequestContext::sanitizeLangCode()
183
			if ( !Language::isValidBuiltInCode( $lang ) ) {
184
				wfDebug( "Invalid user language code\n" );
185
				$lang = $this->getResourceLoader()->getConfig()->get( 'LanguageCode' );
186
			}
187
			$this->language = $lang;
188
		}
189
		return $this->language;
190
	}
191
192
	/**
193
	 * @return string
194
	 */
195 View Code Duplication
	public function getDirection() {
196
		if ( $this->direction === null ) {
197
			$this->direction = $this->getRequest()->getRawVal( 'dir' );
198
			if ( !$this->direction ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->direction of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
199
				// Determine directionality based on user language (bug 6100)
200
				$this->direction = Language::factory( $this->getLanguage() )->getDir();
201
			}
202
		}
203
		return $this->direction;
204
	}
205
206
	/**
207
	 * @return string
208
	 */
209
	public function getSkin() {
210
		return $this->skin;
211
	}
212
213
	/**
214
	 * @return string|null
215
	 */
216
	public function getUser() {
217
		return $this->user;
218
	}
219
220
	/**
221
	 * Get a Message object with context set.  See wfMessage for parameters.
222
	 *
223
	 * @since 1.27
224
	 * @param mixed ...
225
	 * @return Message
226
	 */
227
	public function msg() {
228
		return call_user_func_array( 'wfMessage', func_get_args() )
229
			->inLanguage( $this->getLanguage() )
230
			// Use a dummy title because there is no real title
231
			// for this endpoint, and the cache won't vary on it
232
			// anyways.
233
			->title( Title::newFromText( 'Dwimmerlaik' ) );
234
	}
235
236
	/**
237
	 * Get the possibly-cached User object for the specified username
238
	 *
239
	 * @since 1.25
240
	 * @return User
241
	 */
242
	public function getUserObj() {
243
		if ( $this->userObj === null ) {
244
			$username = $this->getUser();
245
			if ( $username ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $username of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
246
				// Use provided username if valid, fallback to anonymous user
247
				$this->userObj = User::newFromName( $username ) ?: new User;
248
			} else {
249
				// Anonymous user
250
				$this->userObj = new User;
251
			}
252
		}
253
254
		return $this->userObj;
255
	}
256
257
	/**
258
	 * @return bool
259
	 */
260
	public function getDebug() {
261
		return $this->debug;
262
	}
263
264
	/**
265
	 * @return string|null
266
	 */
267
	public function getOnly() {
268
		return $this->only;
269
	}
270
271
	/**
272
	 * @see ResourceLoaderModule::getVersionHash
273
	 * @see ResourceLoaderClientHtml::makeLoad
274
	 * @return string|null
275
	 */
276
	public function getVersion() {
277
		return $this->version;
278
	}
279
280
	/**
281
	 * @return bool
282
	 */
283
	public function getRaw() {
284
		return $this->raw;
285
	}
286
287
	/**
288
	 * @return string|null
289
	 */
290
	public function getImage() {
291
		return $this->image;
292
	}
293
294
	/**
295
	 * @return string|null
296
	 */
297
	public function getVariant() {
298
		return $this->variant;
299
	}
300
301
	/**
302
	 * @return string|null
303
	 */
304
	public function getFormat() {
305
		return $this->format;
306
	}
307
308
	/**
309
	 * If this is a request for an image, get the ResourceLoaderImage object.
310
	 *
311
	 * @since 1.25
312
	 * @return ResourceLoaderImage|bool false if a valid object cannot be created
313
	 */
314
	public function getImageObj() {
315
		if ( $this->imageObj === null ) {
316
			$this->imageObj = false;
317
318
			if ( !$this->image ) {
319
				return $this->imageObj;
320
			}
321
322
			$modules = $this->getModules();
323
			if ( count( $modules ) !== 1 ) {
324
				return $this->imageObj;
325
			}
326
327
			$module = $this->getResourceLoader()->getModule( $modules[0] );
328
			if ( !$module || !$module instanceof ResourceLoaderImageModule ) {
329
				return $this->imageObj;
330
			}
331
332
			$image = $module->getImage( $this->image, $this );
333
			if ( !$image ) {
334
				return $this->imageObj;
335
			}
336
337
			$this->imageObj = $image;
338
		}
339
340
		return $this->imageObj;
341
	}
342
343
	/**
344
	 * @return bool
345
	 */
346
	public function shouldIncludeScripts() {
347
		return $this->getOnly() === null || $this->getOnly() === 'scripts';
348
	}
349
350
	/**
351
	 * @return bool
352
	 */
353
	public function shouldIncludeStyles() {
354
		return $this->getOnly() === null || $this->getOnly() === 'styles';
355
	}
356
357
	/**
358
	 * @return bool
359
	 */
360
	public function shouldIncludeMessages() {
361
		return $this->getOnly() === null;
362
	}
363
364
	/**
365
	 * All factors that uniquely identify this request, except 'modules'.
366
	 *
367
	 * The list of modules is excluded here for legacy reasons as most callers already
368
	 * split up handling of individual modules. Including it here would massively fragment
369
	 * the cache and decrease its usefulness.
370
	 *
371
	 * E.g. Used by RequestFileCache to form a cache key for storing the reponse output.
372
	 *
373
	 * @return string
374
	 */
375
	public function getHash() {
376
		if ( !isset( $this->hash ) ) {
377
			$this->hash = implode( '|', [
378
				// Module content vary
379
				$this->getLanguage(),
380
				$this->getSkin(),
381
				$this->getDebug(),
382
				$this->getUser(),
383
				// Request vary
384
				$this->getOnly(),
385
				$this->getVersion(),
386
				$this->getRaw(),
387
				$this->getImage(),
388
				$this->getVariant(),
389
				$this->getFormat(),
390
			] );
391
		}
392
		return $this->hash;
393
	}
394
}
395