Issues (3882)

Security Analysis    39 potential vulnerabilities

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

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

app/Request.php (7 issues)

1
<?php
2
/**
3
 * Request basic class.
4
 *
5
 * @package App
6
 *
7
 * @copyright YetiForce S.A.
8
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
9
 * @author    Mariusz Krzaczkowski <[email protected]>
10
 * @author    Radosław Skrzypczak <[email protected]>
11
 */
12
13
namespace App;
14
15
/**
16
 * Request basic class.
17
 */
18
class Request
19
{
20
	/**
21
	 * Raw request data.
22
	 *
23
	 * @var array
24
	 */
25
	protected $rawValues = [];
26
27
	/**
28
	 * Headers request.
29
	 *
30
	 * @var array
31
	 */
32
	protected $headers;
33
34
	/**
35
	 * Self instance.
36
	 *
37
	 * @var Request
38
	 */
39
	protected static $request;
40
41
	/**
42
	 * Purified request values for get.
43
	 *
44
	 * @var array
45
	 */
46
	protected $purifiedValuesByGet = [];
47
48
	/**
49
	 * Purified request values for type.
50
	 *
51
	 * @var array
52
	 */
53
	protected $purifiedValuesByType = [];
54
55
	/**
56
	 * Purified request values for integer.
57
	 *
58
	 * @var array
59
	 */
60
	protected $purifiedValuesByInteger = [];
61
62
	/**
63
	 * Purified request values for array.
64
	 *
65
	 * @var array
66
	 */
67
	protected $purifiedValuesByArray = [];
68
69
	/**
70
	 * Purified request values for exploded.
71
	 *
72
	 * @var array
73
	 */
74
	protected $purifiedValuesByExploded = [];
75
76
	/**
77
	 * Purified request values for multi dimension array.
78
	 *
79
	 * @var array
80
	 */
81
	protected $purifiedValuesByMultiDimension = [];
82
83
	/**
84
	 * Purified request values for date range.
85
	 *
86
	 * @var array
87
	 */
88
	protected $purifiedValuesByDateRange = [];
89
90
	/**
91
	 * Purified request values for date html.
92
	 *
93
	 * @var array
94
	 */
95
	protected $purifiedValuesByHtml = [];
96
	/**
97
	 * List of headings and sanitization methods.
98
	 *
99
	 * @var array
100 16
	 */
101
	public $headersPurifierMap = [
102 16
	];
103 16
104
	/**
105
	 * Constructor.
106 16
	 *
107
	 * @param array $rawValues
108
	 * @param bool  $overwrite
109
	 */
110
	public function __construct($rawValues, $overwrite = true)
111
	{
112
		$this->rawValues = $rawValues;
113
		if ($overwrite) {
114
			static::$request = $this;
115
		}
116 38
	}
117
118 38
	/**
119
	 * Function to get the value for a given key.
120
	 *
121 38
	 * @param string $key
122
	 * @param mixed  $value Default value
123
	 *
124 38
	 * @return mixed
125
	 */
126
	public function get($key, $value = '')
127
	{
128
		if (isset($this->purifiedValuesByGet[$key])) {
129
			return $this->purifiedValuesByGet[$key];
130
		}
131
		if (isset($this->rawValues[$key])) {
132
			$value = $this->rawValues[$key];
133
		} else {
134
			return $value;
135
		}
136
		if (\is_string($value) && (0 === strpos($value, '[') || 0 === strpos($value, '{'))) {
137
			$decodeValue = Json::decode($value);
138
			if (isset($decodeValue)) {
139
				$value = $decodeValue;
140
			}
141
		}
142
		if ($value) {
143
			$value = Purifier::purify($value);
144
		}
145
146
		return $this->purifiedValuesByGet[$key] = $value;
147
	}
148
149
	/**
150
	 * Purify by data type.
151
	 *
152
	 * Type list:
153
	 * Standard - only words
154 15
	 * 1 - only words
155
	 * Alnum - word and int
156 15
	 * 2 - word and int
157
	 *
158
	 * @param string     $key     Key name
159 15
	 * @param int|string $type    Data type that is only acceptable, default only words 'Standard'
160 15
	 * @param mixed      $convert
161
	 *
162
	 * @return bool|mixed
163
	 */
164
	public function getByType($key, $type = 'Standard', $convert = false)
165
	{
166
		if (isset($this->purifiedValuesByType[$key][$type])) {
167
			return $this->purifiedValuesByType[$key][$type];
168
		}
169
		if (isset($this->rawValues[$key])) {
170
			return $this->purifiedValuesByType[$key][$type] = Purifier::purifyByType($this->rawValues[$key], $type, $convert);
171
		}
172
		return false;
173
	}
174
175
	/**
176
	 * Function to get the boolean value for a given key.
177
	 *
178
	 * @param string $key
179
	 * @param bool   $defaultValue Default value
180
	 *
181
	 * @return bool
182
	 */
183
	public function getBoolean(string $key, bool $defaultValue = null)
184
	{
185
		$value = $this->get($key, $defaultValue);
186
		if (\is_bool($value)) {
187
			return $value;
188
		}
189
		return 0 === strcasecmp('true', (string) $value) || '1' === (string) $value;
190
	}
191
192
	/**
193
	 * Function to get the integer value for a given key.
194
	 *
195
	 * @param string $key
196
	 * @param int    $value
197
	 *
198
	 * @return int
199
	 */
200
	public function getInteger($key, $value = 0)
201
	{
202
		if (isset($this->purifiedValuesByInteger[$key])) {
203
			return $this->purifiedValuesByInteger[$key];
204
		}
205
		if (!isset($this->rawValues[$key])) {
206
			return $value;
207
		}
208
		if (false !== ($value = filter_var($this->rawValues[$key], FILTER_VALIDATE_INT))) {
209
			return $this->purifiedValuesByInteger[$key] = $value;
210
		}
211
212
		throw new \App\Exceptions\IllegalValue("ERR_NOT_ALLOWED_VALUE||$key||{$this->rawValues[$key]}", 406);
213
	}
214
215
	/**
216
	 * Function to get the array values for a given key.
217
	 *
218
	 * @param string      $key
219
	 * @param mixed       $type
220
	 * @param array       $value
221
	 * @param string|null $keyType
222
	 *
223
	 * @return array
224
	 */
225
	public function getArray($key, $type = false, $value = [], ?string $keyType = null)
226
	{
227
		if (isset($this->purifiedValuesByArray[$key])) {
228
			return $this->purifiedValuesByArray[$key];
229
		}
230
		if (isset($this->rawValues[$key])) {
231
			$value = $this->rawValues[$key];
232
			if (!$value) {
233
				return [];
234
			}
235
			if (\is_string($value) && (0 === strpos($value, '[') || 0 === strpos($value, '{'))) {
236
				$decodeValue = Json::decode($value);
237
				if (isset($decodeValue)) {
238
					$value = $decodeValue;
239
				} else {
240
					\App\Log::warning('Invalid data format, problem encountered while decoding JSON. Data should be in JSON format. Data: ' . $value);
241
				}
242
			}
243
			if ($value) {
244
				if (\is_array($value)) {
245
					$input = [];
246
					foreach ($value as $k => $v) {
247
						if (!\is_int($k)) {
248
							$k = $keyType ? Purifier::purifyByType($k, $keyType) : Purifier::purify($k);
249
						}
250
						$input[$k] = $type ? Purifier::purifyByType($v, $type) : Purifier::purify($v);
251
					}
252
					$value = $input;
253
				} else {
254
					$value = $type ? Purifier::purifyByType($value, $type) : Purifier::purify($value);
255
				}
256
			}
257
258
			return $this->purifiedValuesByArray[$key] = (array) $value;
259
		}
260
		return $value;
261
	}
262
263
	/**
264
	 * Function to get the exploded values for a given key.
265
	 *
266
	 * @param string      $key
267
	 * @param string      $delimiter
268
	 * @param bool|string $type
269
	 *
270
	 * @return array
271
	 */
272
	public function getExploded($key, $delimiter = ',', $type = false)
273
	{
274
		if (isset($this->purifiedValuesByExploded[$key])) {
275
			return $this->purifiedValuesByExploded[$key];
276
		}
277
		$value = [];
278
		if (isset($this->rawValues[$key])) {
279
			if ('' === $this->rawValues[$key]) {
280
				return $value;
281
			}
282 1
			$value = explode($delimiter, $this->rawValues[$key]);
283
			if ($value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
284 1
				$value = $type ? Purifier::purifyByType($value, $type) : Purifier::purify($value);
0 ignored issues
show
$value of type string[] is incompatible with the type string expected by parameter $input of App\Purifier::purify(). ( Ignorable by Annotation )

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

284
				$value = $type ? Purifier::purifyByType($value, $type) : Purifier::purify(/** @scrutinizer ignore-type */ $value);
Loading history...
285 1
			}
286 1
287 1
			return $this->purifiedValuesByExploded[$key] = $value;
288
		}
289
290 1
		return $value;
291 1
	}
292 1
293
	/**
294
	 * Purify multi dimension array.
295 1
	 *
296
	 * @param mixed        $values
297 1
	 * @param array|string $template
298
	 *
299
	 * @throws \App\Exceptions\IllegalValue
300 1
	 *
301
	 * @return mixed
302 1
	 */
303
	private function purifyMultiDimensionArray($values, $template)
304
	{
305
		if (\is_array($template)) {
306
			foreach ($values as $firstKey => $value) {
307
				if (\is_array($value)) {
308
					if (1 === \count($template)) {
309
						$template = current($template);
310
					}
311
					foreach ($value as $secondKey => $val) {
312
						$tempTemplate = $template;
313
						if (isset($template[$firstKey])) {
314
							$tempTemplate = $template[$firstKey];
315 1
						}
316
						if (1 === \count($tempTemplate)) {
317
							$tempTemplate = current($tempTemplate);
318 1
						} elseif (!isset($tempTemplate[$secondKey])) {
319
							throw new Exceptions\IllegalValue("ERR_NOT_ALLOWED_VALUE||{$secondKey}", 406);
320
						} else {
321
							$tempTemplate = $tempTemplate[$secondKey];
322
						}
323
						$values[$firstKey][$secondKey] = $this->purifyMultiDimensionArray($val, $tempTemplate);
324
					}
325
				} else {
326
					if (\is_array($template) && 1 === \count($template)) {
327
						$values[$firstKey] = $this->purifyMultiDimensionArray($value, current($template));
328
					} elseif (isset($template[$firstKey])) {
329 1
						$values[$firstKey] = $this->purifyMultiDimensionArray($value, $template[$firstKey]);
330
					} else {
331 1
						throw new Exceptions\IllegalValue("ERR_NOT_ALLOWED_VALUE||{$firstKey}||" . print_r($template, true), 406);
0 ignored issues
show
Are you sure print_r($template, true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

331
						throw new Exceptions\IllegalValue("ERR_NOT_ALLOWED_VALUE||{$firstKey}||" . /** @scrutinizer ignore-type */ print_r($template, true), 406);
Loading history...
332 1
					}
333
				}
334 1
			}
335 1
		} else {
336
			$values = empty($values) ? $values : ($template ? Purifier::purifyByType($values, $template) : Purifier::purify($values));
337
		}
338
		return $values;
339
	}
340
341
	/**
342
	 * Function to get multi dimension array.
343 1
	 *
344 1
	 * @param string $key
345
	 * @param array  $template
346
	 *
347 1
	 * @return array
348
	 */
349
	public function getMultiDimensionArray(string $key, array $template): array
350
	{
351
		$return = [];
352
		if (isset($this->purifiedValuesByMultiDimension[$key])) {
353
			$return = $this->purifiedValuesByMultiDimension[$key];
354
		} elseif (isset($this->rawValues[$key]) && ($value = $this->rawValues[$key])) {
355
			if (\is_string($value) && (0 === strpos($value, '[') || 0 === strpos($value, '{'))) {
356
				$decodeValue = Json::decode($value);
357
				if (null !== $decodeValue) {
358
					$value = $decodeValue;
359
				} else {
360
					Log::warning('Invalid data format, problem encountered while decoding JSON. Data should be in JSON format. Data: ' . $value);
361
				}
362
			}
363
			$value = (array) $this->purifyMultiDimensionArray($value, $template);
364
			$return = $this->purifiedValuesByMultiDimension[$key] = $value;
365
		}
366
367
		return $return;
368
	}
369
370
	/**
371
	 * Function to get the date range values for a given key.
372
	 *
373
	 * @param string $key request param like 'createdtime'
374
	 *
375
	 * @return array
376
	 */
377
	public function getDateRange($key)
378
	{
379
		return $this->getByType($key, 'DateRangeUserFormat');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getByType(... 'DateRangeUserFormat') also could return the type boolean which is incompatible with the documented return type array.
Loading history...
380
	}
381
382
	/**
383
	 * Function to get html the value for a given key.
384
	 *
385
	 * @param string $key
386
	 * @param mixed  $value
387
	 *
388
	 * @return mixed
389
	 */
390
	public function getForHtml($key, $value = '')
391
	{
392
		if (isset($this->purifiedValuesByHtml[$key])) {
393
			return $this->purifiedValuesByHtml[$key];
394
		}
395
		if (isset($this->rawValues[$key])) {
396
			$value = $this->rawValues[$key];
397
		}
398
		if ($value) {
399
			$value = \App\Purifier::purifyHtml($value);
400
		}
401
402
		return $this->purifiedValuesByHtml[$key] = $value;
403
	}
404
405
	/**
406
	 * Function to get the value if its safe to use for SQL Query (column).
407
	 *
408
	 * @param string $key
409
	 * @param bool   $skipEmtpy
410
	 *
411
	 * @return string
412
	 */
413 1
	public function getForSql($key, $skipEmtpy = true)
414
	{
415 1
		return Purifier::purifySql($this->get($key), $skipEmtpy);
0 ignored issues
show
Bug Best Practice introduced by
The expression return App\Purifier::pur...>get($key), $skipEmtpy) also could return the type boolean which is incompatible with the documented return type string.
Loading history...
416
	}
417
418
	/**
419 1
	 * Function to get the request mode.
420
	 *
421
	 * @return string
422
	 */
423
	public function getMode()
424
	{
425
		return '' !== $this->getRaw('mode') ? $this->getByType('mode', 'Alnum') : '';
0 ignored issues
show
Bug Best Practice introduced by
The expression return '' !== $this->get...e('mode', 'Alnum') : '' also could return the type boolean which is incompatible with the documented return type string.
Loading history...
426
	}
427 1
428
	/**
429 1
	 * Get all data.
430
	 *
431
	 * @return array
432
	 */
433
	public function getAll()
434
	{
435
		foreach ($this->rawValues as $key => $value) {
436
			$this->get($key);
437
		}
438
439
		return $this->purifiedValuesByGet;
440
	}
441
442
	/**
443
	 * Get all raw data.
444
	 *
445
	 * @return array
446
	 */
447
	public function getAllRaw()
448
	{
449
		return $this->rawValues;
450
	}
451
452
	/**
453
	 * Get raw value.
454 1
	 *
455
	 * @param string $key
456 1
	 * @param mixed  $defaultValue
457
	 *
458
	 * @return mixed
459 1
	 */
460 1
	public function getRaw($key, $defaultValue = '')
461 1
	{
462 1
		if (isset($this->rawValues[$key])) {
463
			return $this->rawValues[$key];
464
		}
465
466
		return $defaultValue;
467
	}
468
469
	/**
470
	 * Get all headers.
471
	 *
472
	 * @return string[]
473
	 */
474 1
	public function getHeaders()
475
	{
476
		if (isset($this->headers)) {
477
			return $this->headers;
478
		}
479
		$data = array_change_key_case(getallheaders(), CASE_LOWER);
480
		foreach ($data as $key => &$value) {
481
			if ('' !== $value) {
482
				$value = isset($this->headersPurifierMap[$key]) ? Purifier::purifyByType($value, $this->headersPurifierMap[$key]) : Purifier::purify($value);
483
			}
484
		}
485
		return $this->headers = $data;
486
	}
487
488
	/**
489
	 * Get header for a given key.
490
	 *
491
	 * @param string $key
492
	 *
493
	 * @return string
494
	 */
495
	public function getHeader($key)
496
	{
497
		if (!isset($this->headers)) {
498
			$this->getHeaders();
499
		}
500
		return $this->headers[$key] ?? null;
501
	}
502
503
	/**
504
	 * Get request method.
505
	 *
506
	 * @throws \App\Exceptions\AppException
507
	 *
508
	 * @return string
509
	 */
510
	public static function getRequestMethod()
511
	{
512
		$method = $_SERVER['REQUEST_METHOD'];
513
		if ('POST' === $method && isset($_SERVER['HTTP_X_HTTP_METHOD'])) {
514
			if ('DELETE' === $_SERVER['HTTP_X_HTTP_METHOD']) {
515
				$method = 'DELETE';
516
			} elseif ('PUT' === $_SERVER['HTTP_X_HTTP_METHOD']) {
517
				$method = 'PUT';
518
			} else {
519
				throw new \App\Exceptions\AppException('Unexpected Header');
520
			}
521
		}
522
		return strtoupper($method);
523
	}
524 2
525
	/**
526 2
	 * Get server and execution environment information.
527 2
	 *
528
	 * @param string $key
529
	 * @param mixed  $default
530
	 *
531
	 * @return bool
532
	 */
533
	public function getServer($key, $default = false)
534
	{
535
		if (!isset($_SERVER[$key])) {
536
			return $default;
537
		}
538
		return Purifier::purifyByType($_SERVER[$key], 'Text');
539
	}
540
541
	/**
542
	 * Get module name.
543
	 *
544
	 * @param bool $raw
545
	 *
546
	 * @return string
547
	 */
548
	public function getModule($raw = true)
549
	{
550
		$moduleName = $this->getByType('module', \App\Purifier::ALNUM);
551
		if (!$raw && !$this->isEmpty('parent', true) && 'Settings' === ($parentModule = $this->getByType('parent', \App\Purifier::ALNUM))) {
552
			$moduleName = "$parentModule:$moduleName";
553
		}
554
		return $moduleName;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $moduleName also could return the type boolean which is incompatible with the documented return type string.
Loading history...
555
	}
556
557 1
	/**
558
	 * Check for existence of key.
559 1
	 *
560
	 * @param string $key
561
	 *
562
	 * @return bool
563
	 */
564
	public function has($key)
565
	{
566
		return isset($this->rawValues[$key]);
567
	}
568
569
	/**
570 1
	 * Function to check if the key is empty.
571
	 *
572 1
	 * @param string $key
573
	 * @param bool   $emptyFunction
574
	 *
575
	 * @return bool
576 1
	 */
577
	public function isEmpty($key, $emptyFunction = false)
578
	{
579
		if ($emptyFunction) {
580
			return empty($this->rawValues[$key]);
581
		}
582
		return !isset($this->rawValues[$key]) || '' === $this->rawValues[$key];
583
	}
584
585
	/**
586
	 * Function to set the value for a given key.
587 15
	 *
588
	 * @param string $key
589 15
	 * @param mixed  $value
590 15
	 * @param bool   $onlyRaw
591
	 *
592 15
	 * @return $this
593
	 */
594
	public function set($key, $value, bool $onlyRaw = false): self
595
	{
596
		if ($onlyRaw) {
597
			$this->rawValues[$key] = $value;
598
		} else {
599
			$this->rawValues[$key] = $this->purifiedValuesByGet[$key] = $this->purifiedValuesByInteger[$key] = $this->purifiedValuesByHtml[$key] = $value;
600
			$this->purifiedValuesByType[$key] = [];
601
		}
602
		return $this;
603
	}
604
605
	/**
606
	 * Function to remove the value for a given key.
607
	 *
608
	 * @param string $key
609
	 */
610
	public function delete($key)
611
	{
612
		if (isset($this->purifiedValuesByGet[$key])) {
613
			unset($this->purifiedValuesByGet[$key]);
614
		}
615
		if (isset($this->purifiedValuesByInteger[$key])) {
616
			unset($this->purifiedValuesByInteger[$key]);
617
		}
618
		if (isset($this->purifiedValuesByType[$key])) {
619
			unset($this->purifiedValuesByType[$key]);
620
		}
621
		if (isset($this->purifiedValuesByHtml[$key])) {
622
			unset($this->purifiedValuesByHtml[$key]);
623
		}
624
		if (isset($this->purifiedValuesByArray[$key])) {
625
			unset($this->purifiedValuesByArray[$key]);
626
		}
627
		if (isset($this->purifiedValuesByDateRange[$key])) {
628
			unset($this->purifiedValuesByDateRange[$key]);
629
		}
630
		if (isset($this->purifiedValuesByExploded[$key])) {
631
			unset($this->purifiedValuesByExploded[$key]);
632
		}
633
		if (isset($this->purifiedValuesByMultiDimension[$key])) {
634
			unset($this->purifiedValuesByMultiDimension[$key]);
635
		}
636
		if (isset($this->rawValues[$key])) {
637
			unset($this->rawValues[$key]);
638
		}
639
	}
640
641
	/**
642
	 * Get all request keys.
643
	 *
644
	 * @return array
645
	 */
646
	public function getKeys()
647
	{
648
		return array_keys($this->rawValues);
649
	}
650
651
	/**
652
	 * Function to check if the ajax request.
653
	 *
654
	 * @return bool
655
	 */
656
	public function isAjax()
657
	{
658
		if (!empty($_SERVER['HTTP_X_PJAX']) && true === $_SERVER['HTTP_X_PJAX']) {
659
			return true;
660
		}
661
		if (!empty($_SERVER['HTTP_X_REQUESTED_WITH'])) {
662
			return true;
663
		}
664
		return false;
665
	}
666
667
	/**
668
	 * Is json.
669
	 *
670
	 * @return bool
671
	 */
672
	public function isJSON()
673
	{
674
		return false !== strpos($this->getHeader('accept'), 'application/json');
675
	}
676
677
	/**
678
	 * Validating read access request.
679
	 *
680
	 * @throws \App\Exceptions\Csrf
681
	 */
682
	public function validateReadAccess()
683
	{
684
		// Referer check if present - to over come && Check for user post authentication.
685
		if (\Config\Security::$verifyRefererHeader && isset($_SERVER['HTTP_REFERER']) && \App\User::getCurrentUserId() && 'Install' !== $this->get('module')) {
686
			$allowed = array_merge(\Config\Security::$allowedFrameDomains, \Config\Security::$allowedFormDomains);
687
			$allowed[] = \App\Config::main('site_URL');
688
			$throw = true;
689
			foreach ($allowed as $value) {
690
				if (0 === stripos($_SERVER['HTTP_REFERER'], $value)) {
691
					$throw = false;
692
				}
693
			}
694
			if ($throw) {
695
				throw new \App\Exceptions\Csrf('Illegal request');
696
			}
697
		}
698
	}
699
700
	/**
701
	 * Validating write access request.
702
	 *
703
	 * @param bool $skipRequestTypeCheck
704
	 *
705
	 * @throws \App\Exceptions\Csrf
706 3
	 */
707
	public function validateWriteAccess($skipRequestTypeCheck = false)
708 3
	{
709
		if (!$skipRequestTypeCheck && 'POST' !== $_SERVER['REQUEST_METHOD']) {
710
			throw new \App\Exceptions\Csrf('Invalid request - validate Write Access', 403);
711
		}
712 3
		$this->validateReadAccess();
713
		if (\App\Config::security('csrfActive')) {
714
			\CsrfMagic\Csrf::check();
715
		}
716
	}
717
718
	/**
719
	 * Static instance initialization.
720
	 *
721
	 * @param array|bool $request
722
	 *
723
	 * @return Request
724
	 */
725 41
	public static function init($request = false)
726
	{
727 41
		if (!static::$request) {
728
			static::$request = new self($request ?: $_REQUEST);
729
		}
730 41
		return static::$request;
731 41
	}
732
733
	/**
734 41
	 * Support static methods, all functions must start with "_".
735
	 *
736
	 * @param string     $name
737 41
	 * @param array|null $arguments
738 41
	 *
739 41
	 * @throws \App\Exceptions\AppException
740
	 *
741
	 * @return mixed
742
	 */
743
	public static function __callStatic($name, $arguments = null)
744
	{
745
		if (!static::$request) {
746
			static::init();
747
		}
748
		$function = ltrim($name, '_');
749
		if (!method_exists(static::$request, $function)) {
750
			throw new \App\Exceptions\AppException('Method not found');
751
		}
752
		if (empty($arguments)) {
753
			return static::$request->{$function}();
754
		}
755
		$first = array_shift($arguments);
756
		if (empty($arguments)) {
757
			return static::$request->{$function}($first);
758
		}
759
		return static::$request->{$function}($first, $arguments[0]);
760
	}
761
}
762