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/installer/LocalSettingsGenerator.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
 * Generator for LocalSettings.php file.
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 Deployment
22
 */
23
24
/**
25
 * Class for generating LocalSettings.php file.
26
 *
27
 * @ingroup Deployment
28
 * @since 1.17
29
 */
30
class LocalSettingsGenerator {
31
32
	protected $extensions = [];
33
	protected $values = [];
34
	protected $groupPermissions = [];
35
	protected $dbSettings = '';
36
	protected $IP;
37
38
	/**
39
	 * @var Installer
40
	 */
41
	protected $installer;
42
43
	/**
44
	 * Constructor.
45
	 *
46
	 * @param Installer $installer
47
	 */
48
	public function __construct( Installer $installer ) {
49
		$this->installer = $installer;
50
51
		$this->extensions = $installer->getVar( '_Extensions' );
0 ignored issues
show
Documentation Bug introduced by
It seems like $installer->getVar('_Extensions') of type * is incompatible with the declared type array of property $extensions.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
52
		$this->skins = $installer->getVar( '_Skins' );
0 ignored issues
show
The property skins does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
53
		$this->IP = $installer->getVar( 'IP' );
54
55
		$db = $installer->getDBInstaller( $installer->getVar( 'wgDBtype' ) );
56
57
		$confItems = array_merge(
58
			[
59
				'wgServer', 'wgScriptPath',
60
				'wgPasswordSender', 'wgImageMagickConvertCommand', 'wgShellLocale',
61
				'wgLanguageCode', 'wgEnableEmail', 'wgEnableUserEmail', 'wgDiff3',
62
				'wgEnotifUserTalk', 'wgEnotifWatchlist', 'wgEmailAuthentication',
63
				'wgDBtype', 'wgSecretKey', 'wgRightsUrl', 'wgSitename', 'wgRightsIcon',
64
				'wgRightsText', '_MainCacheType', 'wgEnableUploads',
65
				'_MemCachedServers', 'wgDBserver', 'wgDBuser',
66
				'wgDBpassword', 'wgUseInstantCommons', 'wgUpgradeKey', 'wgDefaultSkin',
67
				'wgMetaNamespace', 'wgLogo', 'wgAuthenticationTokenVersion', 'wgPingback',
68
			],
69
			$db->getGlobalNames()
70
		);
71
72
		$unescaped = [ 'wgRightsIcon', 'wgLogo' ];
73
		$boolItems = [
74
			'wgEnableEmail', 'wgEnableUserEmail', 'wgEnotifUserTalk',
75
			'wgEnotifWatchlist', 'wgEmailAuthentication', 'wgEnableUploads', 'wgUseInstantCommons',
76
			'wgPingback',
77
		];
78
79
		foreach ( $confItems as $c ) {
80
			$val = $installer->getVar( $c );
81
82
			if ( in_array( $c, $boolItems ) ) {
83
				$val = wfBoolToStr( $val );
84
			}
85
86
			if ( !in_array( $c, $unescaped ) && $val !== null ) {
87
				$val = self::escapePhpString( $val );
88
			}
89
90
			$this->values[$c] = $val;
91
		}
92
93
		$this->dbSettings = $db->getLocalSettings();
94
		$this->values['wgEmergencyContact'] = $this->values['wgPasswordSender'];
95
	}
96
97
	/**
98
	 * For $wgGroupPermissions, set a given ['group']['permission'] value.
99
	 * @param string $group Group name
100
	 * @param array $rightsArr An array of permissions, in the form of:
101
	 *   [ 'right' => true, 'right2' => false ]
102
	 */
103
	public function setGroupRights( $group, $rightsArr ) {
104
		$this->groupPermissions[$group] = $rightsArr;
105
	}
106
107
	/**
108
	 * Returns the escaped version of a string of php code.
109
	 *
110
	 * @param string $string
111
	 *
112
	 * @return string
113
	 */
114 View Code Duplication
	public static function escapePhpString( $string ) {
115
		if ( is_array( $string ) || is_object( $string ) ) {
116
			return false;
117
		}
118
119
		return strtr(
120
			$string,
121
			[
122
				"\n" => "\\n",
123
				"\r" => "\\r",
124
				"\t" => "\\t",
125
				"\\" => "\\\\",
126
				"\$" => "\\\$",
127
				"\"" => "\\\""
128
			]
129
		);
130
	}
131
132
	/**
133
	 * Return the full text of the generated LocalSettings.php file,
134
	 * including the extensions and skins.
135
	 *
136
	 * @return string
137
	 */
138
	public function getText() {
139
		$localSettings = $this->getDefaultText();
140
141 View Code Duplication
		if ( count( $this->skins ) ) {
142
			$localSettings .= "
143
# Enabled skins.
144
# The following skins were automatically enabled:\n";
145
146
			foreach ( $this->skins as $skinName ) {
147
				$localSettings .= $this->generateExtEnableLine( 'skins', $skinName );
148
			}
149
150
			$localSettings .= "\n";
151
		}
152
153 View Code Duplication
		if ( count( $this->extensions ) ) {
154
			$localSettings .= "
155
# Enabled extensions. Most of the extensions are enabled by adding
156
# wfLoadExtensions('ExtensionName');
157
# to LocalSettings.php. Check specific extension documentation for more details.
158
# The following extensions were automatically enabled:\n";
159
160
			foreach ( $this->extensions as $extName ) {
161
				$localSettings .= $this->generateExtEnableLine( 'extensions', $extName );
162
			}
163
164
			$localSettings .= "\n";
165
		}
166
167
		$localSettings .= "
168
# End of automatically generated settings.
169
# Add more configuration options below.\n\n";
170
171
		return $localSettings;
172
	}
173
174
	/**
175
	 * Generate the appropriate line to enable the given extension or skin
176
	 *
177
	 * @param string $dir Either "extensions" or "skins"
178
	 * @param string $name Name of extension/skin
179
	 * @throws InvalidArgumentException
180
	 * @return string
181
	 */
182
	private function generateExtEnableLine( $dir, $name ) {
183
		if ( $dir === 'extensions' ) {
184
			$jsonFile = 'extension.json';
185
			$function = 'wfLoadExtension';
186
		} elseif ( $dir === 'skins' ) {
187
			$jsonFile = 'skin.json';
188
			$function = 'wfLoadSkin';
189
		} else {
190
			throw new InvalidArgumentException( '$dir was not "extensions" or "skins' );
191
		}
192
193
		$encName = self::escapePhpString( $name );
194
195
		if ( file_exists( "{$this->IP}/$dir/$encName/$jsonFile" ) ) {
196
			return "$function( '$encName' );\n";
197
		} else {
198
			return "require_once \"\$IP/$dir/$encName/$encName.php\";\n";
199
		}
200
	}
201
202
	/**
203
	 * Write the generated LocalSettings to a file
204
	 *
205
	 * @param string $fileName Full path to filename to write to
206
	 */
207
	public function writeFile( $fileName ) {
208
		file_put_contents( $fileName, $this->getText() );
209
	}
210
211
	/**
212
	 * @return string
213
	 */
214
	protected function buildMemcachedServerList() {
215
		$servers = $this->values['_MemCachedServers'];
216
217
		if ( !$servers ) {
218
			return '[]';
219
		} else {
220
			$ret = '[ ';
221
			$servers = explode( ',', $servers );
222
223
			foreach ( $servers as $srv ) {
224
				$srv = trim( $srv );
225
				$ret .= "'$srv', ";
226
			}
227
228
			return rtrim( $ret, ', ' ) . ' ]';
229
		}
230
	}
231
232
	/**
233
	 * @return string
234
	 */
235
	protected function getDefaultText() {
0 ignored issues
show
getDefaultText 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...
236
		if ( !$this->values['wgImageMagickConvertCommand'] ) {
237
			$this->values['wgImageMagickConvertCommand'] = '/usr/bin/convert';
238
			$magic = '#';
239
		} else {
240
			$magic = '';
241
		}
242
243
		if ( !$this->values['wgShellLocale'] ) {
244
			$this->values['wgShellLocale'] = 'en_US.UTF-8';
245
			$locale = '#';
246
		} else {
247
			$locale = '';
248
		}
249
250
		$metaNamespace = '';
251
		if ( $this->values['wgMetaNamespace'] !== $this->values['wgSitename'] ) {
252
			$metaNamespace = "\$wgMetaNamespace = \"{$this->values['wgMetaNamespace']}\";\n";
253
		}
254
255
		$groupRights = '';
256
		$noFollow = '';
257
		if ( $this->groupPermissions ) {
258
			$groupRights .= "# The following permissions were set based on your choice in the installer\n";
259
			foreach ( $this->groupPermissions as $group => $rightArr ) {
260
				$group = self::escapePhpString( $group );
261
				foreach ( $rightArr as $right => $perm ) {
262
					$right = self::escapePhpString( $right );
263
					$groupRights .= "\$wgGroupPermissions['$group']['$right'] = " .
264
						wfBoolToStr( $perm ) . ";\n";
265
				}
266
			}
267
			$groupRights .= "\n";
268
269
			if ( ( isset( $this->groupPermissions['*']['edit'] ) &&
270
					$this->groupPermissions['*']['edit'] === false )
271
				&& ( isset( $this->groupPermissions['*']['createaccount'] ) &&
272
					$this->groupPermissions['*']['createaccount'] === false )
273
				&& ( isset( $this->groupPermissions['*']['read'] ) &&
274
					$this->groupPermissions['*']['read'] !== false )
275
			) {
276
				$noFollow = "# Set \$wgNoFollowLinks to true if you open up your wiki to editing by\n"
277
					. "# the general public and wish to apply nofollow to external links as a\n"
278
					. "# deterrent to spammers. Nofollow is not a comprehensive anti-spam solution\n"
279
					. "# and open wikis will generally require other anti-spam measures; for more\n"
280
					. "# information, see https://www.mediawiki.org/wiki/Manual:Combating_spam\n"
281
					. "\$wgNoFollowLinks = false;\n\n";
282
			}
283
		}
284
285
		$serverSetting = "";
286
		if ( array_key_exists( 'wgServer', $this->values ) && $this->values['wgServer'] !== null ) {
287
			$serverSetting = "\n## The protocol and server name to use in fully-qualified URLs\n";
288
			$serverSetting .= "\$wgServer = \"{$this->values['wgServer']}\";";
289
		}
290
291
		switch ( $this->values['_MainCacheType'] ) {
292
			case 'anything':
293
			case 'db':
294
			case 'memcached':
295
			case 'accel':
296
				$cacheType = 'CACHE_' . strtoupper( $this->values['_MainCacheType'] );
297
				break;
298
			case 'none':
299
			default:
300
				$cacheType = 'CACHE_NONE';
301
		}
302
303
		$mcservers = $this->buildMemcachedServerList();
304
305
		return "<?php
306
# This file was automatically generated by the MediaWiki {$GLOBALS['wgVersion']}
307
# installer. If you make manual changes, please keep track in case you
308
# need to recreate them later.
309
#
310
# See includes/DefaultSettings.php for all configurable settings
311
# and their default values, but don't forget to make changes in _this_
312
# file, not there.
313
#
314
# Further documentation for configuration settings may be found at:
315
# https://www.mediawiki.org/wiki/Manual:Configuration_settings
316
317
# Protect against web entry
318
if ( !defined( 'MEDIAWIKI' ) ) {
319
	exit;
320
}
321
322
## Uncomment this to disable output compression
323
# \$wgDisableOutputCompression = true;
324
325
\$wgSitename = \"{$this->values['wgSitename']}\";
326
{$metaNamespace}
327
## The URL base path to the directory containing the wiki;
328
## defaults for all runtime URL paths are based off of this.
329
## For more information on customizing the URLs
330
## (like /w/index.php/Page_title to /wiki/Page_title) please see:
331
## https://www.mediawiki.org/wiki/Manual:Short_URL
332
\$wgScriptPath = \"{$this->values['wgScriptPath']}\";
333
${serverSetting}
334
335
## The URL path to static resources (images, scripts, etc.)
336
\$wgResourceBasePath = \$wgScriptPath;
337
338
## The URL path to the logo.  Make sure you change this from the default,
339
## or else you'll overwrite your logo when you upgrade!
340
\$wgLogo = \"{$this->values['wgLogo']}\";
341
342
## UPO means: this is also a user preference option
343
344
\$wgEnableEmail = {$this->values['wgEnableEmail']};
345
\$wgEnableUserEmail = {$this->values['wgEnableUserEmail']}; # UPO
346
347
\$wgEmergencyContact = \"{$this->values['wgEmergencyContact']}\";
348
\$wgPasswordSender = \"{$this->values['wgPasswordSender']}\";
349
350
\$wgEnotifUserTalk = {$this->values['wgEnotifUserTalk']}; # UPO
351
\$wgEnotifWatchlist = {$this->values['wgEnotifWatchlist']}; # UPO
352
\$wgEmailAuthentication = {$this->values['wgEmailAuthentication']};
353
354
## Database settings
355
\$wgDBtype = \"{$this->values['wgDBtype']}\";
356
\$wgDBserver = \"{$this->values['wgDBserver']}\";
357
\$wgDBname = \"{$this->values['wgDBname']}\";
358
\$wgDBuser = \"{$this->values['wgDBuser']}\";
359
\$wgDBpassword = \"{$this->values['wgDBpassword']}\";
360
361
{$this->dbSettings}
362
363
## Shared memory settings
364
\$wgMainCacheType = $cacheType;
365
\$wgMemCachedServers = $mcservers;
366
367
## To enable image uploads, make sure the 'images' directory
368
## is writable, then set this to true:
369
\$wgEnableUploads = {$this->values['wgEnableUploads']};
370
{$magic}\$wgUseImageMagick = true;
371
{$magic}\$wgImageMagickConvertCommand = \"{$this->values['wgImageMagickConvertCommand']}\";
372
373
# InstantCommons allows wiki to use images from https://commons.wikimedia.org
374
\$wgUseInstantCommons = {$this->values['wgUseInstantCommons']};
375
376
# Periodically send a pingback to https://www.mediawiki.org/ with basic data
377
# about this MediaWiki instance. The Wikimedia Foundation shares this data
378
# with MediaWiki developers to help guide future development efforts.
379
\$wgPingback = {$this->values['wgPingback']};
380
381
## If you use ImageMagick (or any other shell command) on a
382
## Linux server, this will need to be set to the name of an
383
## available UTF-8 locale
384
{$locale}\$wgShellLocale = \"{$this->values['wgShellLocale']}\";
385
386
## Set \$wgCacheDirectory to a writable directory on the web server
387
## to make your wiki go slightly faster. The directory should not
388
## be publically accessible from the web.
389
#\$wgCacheDirectory = \"\$IP/cache\";
390
391
# Site language code, should be one of the list in ./languages/data/Names.php
392
\$wgLanguageCode = \"{$this->values['wgLanguageCode']}\";
393
394
\$wgSecretKey = \"{$this->values['wgSecretKey']}\";
395
396
# Changing this will log out all existing sessions.
397
\$wgAuthenticationTokenVersion = \"{$this->values['wgAuthenticationTokenVersion']}\";
398
399
# Site upgrade key. Must be set to a string (default provided) to turn on the
400
# web installer while LocalSettings.php is in place
401
\$wgUpgradeKey = \"{$this->values['wgUpgradeKey']}\";
402
403
## For attaching licensing metadata to pages, and displaying an
404
## appropriate copyright notice / icon. GNU Free Documentation
405
## License and Creative Commons licenses are supported so far.
406
\$wgRightsPage = \"\"; # Set to the title of a wiki page that describes your license/copyright
407
\$wgRightsUrl = \"{$this->values['wgRightsUrl']}\";
408
\$wgRightsText = \"{$this->values['wgRightsText']}\";
409
\$wgRightsIcon = \"{$this->values['wgRightsIcon']}\";
410
411
# Path to the GNU diff3 utility. Used for conflict resolution.
412
\$wgDiff3 = \"{$this->values['wgDiff3']}\";
413
414
{$groupRights}{$noFollow}## Default skin: you can change the default skin. Use the internal symbolic
415
## names, ie 'vector', 'monobook':
416
\$wgDefaultSkin = \"{$this->values['wgDefaultSkin']}\";
417
";
418
	}
419
}
420