Passed
Branch development (176841)
by Elk
07:27
created

Bootstrap::_validRequestCheck()   B

Complexity

Conditions 10
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 8
nc 2
nop 0
dl 0
loc 13
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Initialize the ElkArte environment.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 *
15
 */
16
17
use ElkArte\Debug;
18
use ElkArte\Hooks;
19
use ElkArte\User;
20
use ElkArte\TokenHash;
21
use ElkArte\Errors\Errors;
22
use ElkArte\MembersList;
23
use ElkArte\Cache\Cache;
24
use ElkArte\Themes\ThemeLoader;
25
use ElkArte\Controller\Auth;
26
use ElkArte\EventManager;
27
use BBC\ParserWrapper;
28
29
/**
30
 * Class Bootstrap
31
 *
32
 * This takes care of the initial loading and feeding of Elkarte from
33
 * either SSI or Index
34
 */
35
class Bootstrap
36
{
37
	/**
38
	 * What is returned by the function getrusage.
39
	 *
40
	 * @var mixed[]
41
	 */
42
	protected $rusage_start = [];
43
44
	/**
45
	 * Bootstrap constructor.
46
	 *
47
	 * @param bool $standalone
48
	 *  - true to boot outside of elkarte
49
	 *  - false to bootstrap the main elkarte site.
50
	 * @throws \ElkArte\Exceptions\Exception
51
	 */
52
	public function __construct($standalone = true)
53
	{
54
		// Bootstrap only once.
55
		if (!defined('ELKBOOT'))
56
		{
57
			// We're going to set a few globals
58
			global $time_start, $ssi_error_reporting, $db_show_debug;
59
60
			// Your on the clock
61
			$time_start = microtime(true);
62
63
			// Unless settings.php tells us otherwise
64
			$db_show_debug = false;
65
66
			// Report errors but not depreciated ones
67
			$ssi_error_reporting = error_reporting(E_ALL & ~E_DEPRECATED);
68
69
			// Get the things needed for ALL modes
70
			$this->bringUpBasics();
71
72
			// Going to run from the side entrance and not directly from inside elkarte
73
			if ($standalone)
74
			{
75
				$this->ssi_main();
76
			}
77
		}
78
	}
79
80
	/**
81
	 * Calls the various initialization functions in the needed order
82
	 */
83
	public function bringUpBasics()
84
	{
85
		$this->setConstants();
86
		$this->setRusage();
87
		$this->clearGlobals();
88
		$this->loadSettingsFile();
89
		$this->validatePaths();
90
		$this->loadDependants();
91
		$this->loadAutoloader();
92
		$this->checkMaintance();
93
		$this->setDebug();
94
		$this->bringUp();
95
	}
96
97
	/**
98
	 * Set the core constants, you know the ones we often forget to
99
	 * update on new releases.
100
	 */
101
	private function setConstants()
102
	{
103
		// First things first, but not necessarily in that order.
104
		if (!defined('ELK'))
105
		{
106
			define('ELK', '1');
107
		}
108
		define('ELKBOOT', '1');
109
110
		// The software version
111
		define('FORUM_VERSION', 'ElkArte 2.0 dev');
112
113
		// Shortcut for the browser cache stale
114
		define('CACHE_STALE', '?20dev');
115
	}
116
117
	/**
118
	 * Get initial resource usage
119
	 */
120
	private function setRusage()
121
	{
122
		$this->rusage_start = getrusage();
123
	}
124
125
	/**
126
	 * If they glo, they need to be cleaned.
127
	 */
128
	private function clearGlobals()
129
	{
130
		// We don't need no globals. (a bug in "old" versions of PHP)
131
		foreach (array('db_character_set', 'cachedir') as $variable)
132
		{
133
			if (isset($GLOBALS[$variable]))
134
			{
135
				unset($GLOBALS[$variable], $GLOBALS[$variable]);
136
			}
137
		}
138
	}
139
140
	/**
141
	 * Loads the settings values into the global space
142
	 */
143
	private function loadSettingsFile()
144
	{
145
		// All those wonderful things found in settings
146
		global $maintenance, $mtitle, $msubject, $mmessage, $mbname, $language, $boardurl, $webmaster_email;
147
		global $cookiename, $db_type, $db_server, $db_port, $db_name, $db_user, $db_passwd;
148
		global $ssi_db_user, $ssi_db_passwd, $db_prefix, $db_persist, $db_error_send, $cache_accelerator;
149
		global $cache_uid, $cache_password, $cache_enable, $cache_memcached, $db_show_debug, $url_format;
150
		global $cachedir, $boarddir, $sourcedir, $extdir, $languagedir;
151
152
		// Where the Settings.php file is located
153
		$settings_loc = __DIR__ . '/Settings.php';
154
155
		// First thing: if the install dir exists, just send anybody there
156
		// The IGNORE_INSTALL_DIR constant is for developers only. Do not add it on production sites
157
		if (file_exists('install') && (file_exists('install/install.php') || file_exists('install/upgrade.php')))
158
		{
159
			if (file_exists($settings_loc))
160
			{
161
				require_once($settings_loc);
162
			}
163
164
			if (defined('IGNORE_INSTALL_DIR'))
165
			{
166
				if (file_exists($settings_loc) && empty($_SESSION['installing']))
167
				{
168
					$redirec_file = 'upgrade.php';
169
				}
170
				else
171
				{
172
					$redirec_file = 'install.php';
173
				}
174
175
				header('Location: http' . (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on' ? 's' : '') . '://' . (empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] === '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST']) . (strtr(dirname($_SERVER['PHP_SELF']), '\\', '/') == '/' ? '' : strtr(dirname($_SERVER['PHP_SELF']), '\\', '/')) . '/install/' . $redirec_file);
0 ignored issues
show
Security Response Splitting introduced by
'Location: http' . ! emp...stall/' . $redirec_file can contain request data and is used in response header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read tainted data from array
    in bootstrap.php on line 175

Response Splitting Attacks

Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
176
				die();
177
			}
178
		}
179
		else
180
		{
181
			require_once($settings_loc);
182
		}
183
	}
184
185
	/**
186
	 * Validate the paths set in Settings.php, correct as needed and move
187
	 * them to constants.
188
	 */
189
	private function validatePaths()
190
	{
191
		global $boarddir, $sourcedir, $cachedir, $extdir, $languagedir;
192
193
		// Make sure the paths are correct... at least try to fix them.
194
		if (!file_exists($boarddir) && file_exists(__DIR__ . '/agreement.txt'))
195
		{
196
			$boarddir = __DIR__;
197
		}
198
199
		if (!file_exists($sourcedir . '/SiteDispatcher.class.php') && file_exists($boarddir . '/sources'))
200
		{
201
			$sourcedir = $boarddir . '/sources';
202
		}
203
204
		// Check that directories which didn't exist in past releases are initialized.
205
		if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
206
		{
207
			$cachedir = $boarddir . '/cache';
208
		}
209
210
		if ((empty($extdir) || !file_exists($extdir)) && file_exists($sourcedir . '/ext'))
211
		{
212
			$extdir = $sourcedir . '/ext';
213
		}
214
215
		if ((empty($languagedir) || !file_exists($languagedir)) && file_exists($boarddir . '/themes/default/languages'))
216
		{
217
			$languagedir = $boarddir . '/themes/default/languages';
218
		}
219
220
		// Time to forget about variables and go with constants!
221
		define('BOARDDIR', $boarddir);
222
		define('CACHEDIR', $cachedir);
223
		define('EXTDIR', $extdir);
224
		define('LANGUAGEDIR', $languagedir);
225
		define('SOURCEDIR', $sourcedir);
226
		define('ADMINDIR', $sourcedir . '/ElkArte/AdminController');
227
		define('CONTROLLERDIR', $sourcedir . '/ElkArte/Controller');
228
		define('SUBSDIR', $sourcedir . '/subs');
229
		define('ADDONSDIR', $boarddir . '/addons');
230
		unset($boarddir, $cachedir, $sourcedir, $languagedir, $extdir);
231
	}
232
233
	/**
234
	 * We require access to several important files, so load them upfront
235
	 */
236
	private function loadDependants()
237
	{
238
		// Files we cannot live without.
239
		require_once(SOURCEDIR . '/QueryString.php');
240
		require_once(SOURCEDIR . '/Session.php');
241
		require_once(SOURCEDIR . '/Subs.php');
242
		require_once(SOURCEDIR . '/Logging.php');
243
		require_once(SOURCEDIR . '/Load.php');
244
		require_once(SOURCEDIR . '/Security.php');
245
		require_once(SUBSDIR . '/Cache.subs.php');
246
	}
247
248
	/**
249
	 * The autoloader will take care most requests for files
250
	 */
251
	private function loadAutoloader()
252
	{
253
		require_once(EXTDIR . '/ClassLoader.php');
254
255
		$loader = new \ElkArte\ext\Composer\Autoload\ClassLoader();
0 ignored issues
show
Bug introduced by
The type ElkArte\ext\Composer\Autoload\ClassLoader 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...
256
		$loader->setPsr4('ElkArte\\', SOURCEDIR . '/ElkArte');
257
		$loader->setPsr4('BBC\\', SOURCEDIR . '/ElkArte/BBC');
258
		$loader->register();
259
	}
260
261
	/**
262
	 * Check if we are in maintance mode, if so end here.
263
	 */
264
	private function checkMaintance()
265
	{
266
		global $maintenance, $ssi_maintenance_off;
267
268
		// Don't do john didley if the forum's been shut down completely.
269
		if (!empty($maintenance) && $maintenance == 2 && (!isset($ssi_maintenance_off) || $ssi_maintenance_off !== true))
270
		{
271
			Errors::instance()->display_maintenance_message();
272
		}
273
	}
274
275
	/**
276
	 * If you like lots of debug information in error messages and below the footer
277
	 * then set $db_show_debug to true in settings.  Don't do this on a production site.
278
	 */
279
	private function setDebug()
280
	{
281
		global $db_show_debug;
282
283
		// Show lots of debug information below the page, not for production sites
284
		if ($db_show_debug === true)
285
		{
286
			Debug::instance()->rusage('start', $this->rusage_start);
287
		}
288
	}
289
290
	/**
291
	 * Time to see what has been requested, by whom and dispatch it to the proper handler
292
	 */
293
	private function bringUp()
294
	{
295
		global $context;
296
297
		// Clean the request.
298
		cleanRequest();
299
300
		// Initiate the database connection and define some database functions to use.
301
		loadDatabase();
302
303
		// Let's set up our shiny new hooks handler.
304
		Hooks::init(database(), Debug::instance());
305
306
		// It's time for settings loaded from the database.
307
		reloadSettings();
308
309
		// Make sure we have ready the list of members for populating it
310
		MembersList::init(database(), Cache::instance(),  ParserWrapper::instance());
311
312
		// Our good ole' contextual array, which will hold everything
313
		if (empty($context))
314
		{
315
			$context = array();
316
		}
317
	}
318
319
	/**
320
	 * If you are running SSI standalone, you need to call this function after bootstrap is
321
	 * initialized.
322
	 *
323
	 * @throws \ElkArte\Exceptions\Exception
324
	 */
325
	public function ssi_main()
326
	{
327
		global $ssi_layers, $ssi_theme, $ssi_gzip, $ssi_ban, $ssi_guest_access;
328
		global $modSettings, $context, $board, $topic, $txt;
329
330
		// Check on any hacking attempts.
331
		$this->_validRequestCheck();
332
333
		// Gzip output? (because it must be boolean and true, this can't be hacked.)
334
		if (isset($ssi_gzip) && $ssi_gzip === true && detectServer()->outPutCompressionEnabled())
335
		{
336
			ob_start('ob_gzhandler');
337
		}
338
		else
339
		{
340
			$modSettings['enableCompressedOutput'] = '0';
341
		}
342
343
		// Primarily, this is to fix the URLs...
344
		ob_start('ob_sessrewrite');
345
346
		// Start the session... known to scramble SSI includes in cases...
347
		if (!headers_sent())
348
		{
349
			loadSession();
350
		}
351
		else
352
		{
353
			if (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))
354
			{
355
				// Make a stab at it, but ignore the E_WARNINGs generated because we can't send headers.
356
				$temp = error_reporting(error_reporting() & !E_WARNING);
357
				loadSession();
358
				error_reporting($temp);
359
			}
360
361
			if (!isset($_SESSION['session_value']))
362
			{
363
				$tokenizer = new TokenHash();
364
				$_SESSION['session_value'] = $tokenizer->generate_hash(32, session_id());
365
				$_SESSION['session_var'] = substr(preg_replace('~^\d+~', '', $tokenizer->generate_hash(16, session_id())), 0, rand(7, 12));
366
			}
367
368
			// This is here only to avoid session errors in PHP7
369
			// microtime effectively forces the replacing of the session in the db each
370
			// time the page is loaded
371
			$_SESSION['mictrotime'] = microtime();
372
		}
373
374
		// Get rid of $board and $topic... do stuff loadBoard would do.
375
		unset($board, $topic);
376
		$context['user']['is_mod'] = User::$info->is_mod = false;
377
		$context['linktree'] = array();
378
379
		// Load the user and their cookie, as well as their settings.
380
		loadUserSettings();
0 ignored issues
show
Deprecated Code introduced by
The function loadUserSettings() has been deprecated: kept until any trace of $user_info has been completely removed ( Ignorable by Annotation )

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

380
		/** @scrutinizer ignore-deprecated */ loadUserSettings();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
381
382
		// Load the current user's permissions....
383
		loadPermissions();
384
385
		// Load the current or SSI theme. (just use $ssi_theme = id_theme;)
386
		new ThemeLoader(isset($ssi_theme) ? (int) $ssi_theme : 0);
387
388
		// Load BadBehavior functions
389
		loadBadBehavior();
390
391
		// @todo: probably not the best place, but somewhere it should be set...
392
		if (!headers_sent())
393
		{
394
			header('Content-Type: text/html; charset=UTF-8');
395
		}
396
397
		// Take care of any banning that needs to be done.
398
		if (isset($_REQUEST['ssi_ban']) || (isset($ssi_ban) && $ssi_ban === true))
399
		{
400
			is_not_banned();
401
		}
402
403
		// Do we allow guests in here?
404
		if (empty($ssi_guest_access) && empty($modSettings['allow_guestAccess']) && User::$info->is_guest && basename($_SERVER['PHP_SELF']) !== 'SSI.php')
405
		{
406
			$controller = new Auth(new EventManager());
407
			$controller->setUser(\ElkArte\User::$info);
408
			$controller->action_kickguest();
409
			obExit(null, true);
410
		}
411
412
		if (!empty($modSettings['front_page']) && is_callable(array($modSettings['front_page'], 'frontPageHook')))
413
		{
414
			$modSettings['default_forum_action'] = ['action' => 'forum'];
415
		}
416
		else
417
		{
418
			$modSettings['default_forum_action'] = [];
419
		}
420
421
		// Load the stuff like the menu bar, etc.
422
		if (isset($ssi_layers))
423
		{
424
			$template_layers = theme()->getLayers();
425
			$template_layers->removeAll();
426
			foreach ($ssi_layers as $layer)
427
			{
428
				$template_layers->addBegin($layer);
429
			}
430
			template_header();
431
		}
432
		else
433
		{
434
			setupThemeContext();
435
		}
436
437
		// We need to set up user agent, and make more checks on the request
438
		$req = request();
439
440
		// Make sure they didn't muss around with the settings... but only if it's not cli.
441
		if (isset($_SERVER['REMOTE_ADDR']) && session_id() === '')
442
		{
443
			trigger_error($txt['ssi_session_broken'], E_USER_NOTICE);
444
		}
445
446
		// Without visiting the forum this session variable might not be set on submit.
447
		if (!isset($_SESSION['USER_AGENT']) && (!isset($_GET['ssi_function']) || $_GET['ssi_function'] !== 'pollVote'))
448
		{
449
			$_SESSION['USER_AGENT'] = $req->user_agent();
450
		}
451
	}
452
453
	/**
454
	 * Used to ensure SSI requests are valid and not a probing attempt
455
	 */
456
	private function _validRequestCheck()
457
	{
458
		global $ssi_theme, $ssi_layers;
459
460
		// Check on any hacking attempts.
461
		if (
462
			isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS'])
463
			|| isset($_REQUEST['ssi_theme']) && (int) $_REQUEST['ssi_theme'] == (int) $ssi_theme
464
			|| isset($_COOKIE['ssi_theme']) && (int) $_COOKIE['ssi_theme'] == (int) $ssi_theme
465
			|| isset($_REQUEST['ssi_layers'], $ssi_layers) && $_REQUEST['ssi_layers'] == $ssi_layers
466
			|| isset($_REQUEST['context']))
467
		{
468
			die('No access...');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
469
		}
470
	}
471
}
472