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/Purifier.php (12 issues)

1
<?php
2
/**
3
 * Purifier file.
4
 *
5
 * @package App
6
 *
7
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
8
 * @copyright YetiForce S.A.
9
 * @author    Mariusz Krzaczkowski <[email protected]>
10
 * @author    Radosław Skrzypczak <[email protected]>
11
 */
12
13
namespace App;
14
15
/**
16
 * Purifier basic class.
17
 */
18
class Purifier
19
{
20
	/** @var string Purify type date in user format. */
21
	public const DATE_USER_FORMAT = 'DateInUserFormat';
22
23
	/** @var string Purify type integer. */
24
	public const INTEGER = 'Integer';
25
26
	/** @var string Purify type standard. */
27
	public const STANDARD = 'Standard';
28
29
	/** @var string Purify type sql. */
30
	public const SQL = 'Sql';
31
32
	/** @var string Purify type text. */
33
	public const TEXT = 'Text';
34
35
	/** @var string Purify type number. */
36
	public const NUMBER = 'Number';
37
38
	/** @var string Purify type html. */
39
	public const HTML = 'Html';
40
41
	/** @var string Purify type boolean. */
42
	public const BOOL = 'Bool';
43
44
	/** @var string Purify type url. */
45
	public const URL = 'Url';
46
47
	/** @var string Purify type Alnum. */
48
	public const ALNUM = 'Alnum';
49
50
	/** @var string Purify type Alnum 2 (A-Za-z0-9\/\+\-). */
51
	public const ALNUM2 = 'AlnumType2';
52
53
	/** @var string Purify type AlnumExtended. */
54
	public const ALNUM_EXTENDED = 'AlnumExtended';
55
56
	/** @var string Purify type Digits. */
57
	public const DIGITS = 'Digits';
58
59
	/** @var string Purify type HTML text parser */
60
	public const HTML_TEXT_PARSER = 'HtmlTextParser';
61
62
	/** @var string Purify type Path. */
63
	public const PATH = 'Path';
64
65
	/** @var string Purify type email. */
66
	public const EMAIL = 'Email';
67
68
	/**
69
	 * Default charset.
70
	 *
71
	 * @var string
72
	 */
73
	public static $defaultCharset;
74
75
	/**
76
	 * Cache for purify instance.
77
	 *
78
	 * @var bool|\HTMLPurifier
79
	 */
80
	private static $purifyInstanceCache = false;
81
82
	/**
83
	 * Cache for Html purify instance.
84
	 *
85
	 * @var bool|\HTMLPurifier
86
	 */
87
	private static $purifyHtmlInstanceCache = false;
88
89
	/** @var bool|\HTMLPurifier Cache for Html template purify instance. */
90
	private static $purifyTextParserInstanceCache = false;
91 52
92
	/**
93 52
	 * Html events attributes.
94 3
	 *
95
	 * @var string
96 51
	 */
97 51
	private static $htmlEventAttributes = 'onerror|onblur|onchange|oncontextmenu|onfocus|oninput|oninvalid|onreset|onsearch|onselect|onsubmit|onkeydown|onkeypress|onkeyup|' .
98 51
	'onclick|ondblclick|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onmousedown|onmousemove|onmouseout|onmouseover|onbeforepaste|onresizestart|onactivate|' .
99 51
	'onmouseup|onmousewheel|onscroll|onwheel|oncopy|oncut|onpaste|onload|onselectionchange|onabort|onselectstart|ondragdrop|onmouseleave|onmouseenter|onunload|onresize|onmessage|' .
100 9
	'onpropertychange|onfilterchange|onstart|onfinish|onbounce|onrowsinserted|onrowsdelete|onrowexit|onrowenter|ondatasetcomplete|ondatasetchanged|ondataavailable|oncellchange|' .
101
	'onbeforeupdate|onafterupdate|onerrorupdate|onhelp|onbeforeprint|onafterprint|oncontrolselect|onfocusout|onfocusin|ondeactivate|onbeforeeditfocus|onbeforedeactivate|onbeforeactivate|' .
102
	'onresizeend|onmovestart|onmoveend|onmove|onbeforecopy|onbeforecut|onbeforeunload|onhashchange|onoffline|ononline|onreadystatechange|onstop|onlosecapture';
103
104 46
	/**
105
	 * Remove unnecessary code list.
106
	 *
107
	 * @var string[]
108
	 */
109
	private static $removeUnnecessaryCode = [
110
		'href="javascript:window.history.back();"',
111
		'href="javascript:void(0);"',
112 46
	];
113
114 46
	/**
115 2
	 * Purify (Cleanup) malicious snippets of code from the input.
116 2
	 *
117 2
	 * @param string $input
118
	 * @param bool   $loop  Purify values in the loop
119 46
	 *
120 45
	 * @return string
121 43
	 */
122 43
	public static function purify($input, $loop = true)
123 43
	{
124 43
		if (empty($input)) {
125 43
			return $input;
126 43
		}
127
		$value = $input;
128
		if (!\is_array($input)) {
0 ignored issues
show
The condition is_array($input) is always false.
Loading history...
129 43
			$cacheKey = md5($input);
130
			if (Cache::has('purify', $cacheKey)) {
131
				return Cache::get('purify', $cacheKey);
132 44
			}
133
		}
134
		// Initialize the instance if it has not yet done
135
		if (!static::$purifyInstanceCache) {
0 ignored issues
show
Since $purifyInstanceCache is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $purifyInstanceCache to at least protected.
Loading history...
136
			$config = \HTMLPurifier_Config::createDefault();
137
			$config->set('Core.Encoding', static::$defaultCharset);
138
			$config->set('Cache.SerializerPermissions', 0775);
139
			$config->set('Cache.SerializerPath', ROOT_DIRECTORY . \DIRECTORY_SEPARATOR . 'cache' . \DIRECTORY_SEPARATOR . 'vtlib');
140
			$config->set('HTML.Allowed', '');
141
			static::$purifyInstanceCache = new \HTMLPurifier($config);
142
		}
143 22
		if (static::$purifyInstanceCache) {
144
			// Composite type
145 22
			if (\is_array($input)) {
0 ignored issues
show
The condition is_array($input) is always false.
Loading history...
146 1
				$value = [];
147
				foreach ($input as $k => $v) {
148 21
					$value[$k] = static::purify($v);
149 21
				}
150 21
			} elseif (\is_string($input)) {
0 ignored issues
show
The condition is_string($input) is always true.
Loading history...
151 8
				$input = str_replace(["\r\n"], "\n", $input);
152
				static::purifyHtmlEventAttributes($input);
153
				$value = static::$purifyInstanceCache->purify(static::decodeHtml($input));
154 19
				if ($loop) {
155 1
					$last = '';
156 1
					while ($last !== $value) {
157
						$last = $value;
158 19
						$value = static::purify($value, false);
159 19
					}
160 19
				}
161 16
				Cache::save('purify', $cacheKey, $value, Cache::SHORT);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cacheKey does not seem to be defined for all execution paths leading up to this point.
Loading history...
162 16
			}
163 16
		}
164 14
		return $value;
165 14
	}
166
167
	/**
168 16
	 * Purify HTML (Cleanup) malicious snippets of code from the input.
169 16
	 *
170
	 * @param string $input
171 16
	 * @param bool   $loop  Purify values in the loop
172
	 *
173
	 * @return string
174
	 */
175
	public static function purifyHtml(string $input, $loop = true): string
176
	{
177
		if (empty($input)) {
178
			return $input;
179 61
		}
180
		$value = $input;
181 61
		$cacheKey = md5($input);
182 5
		if (Cache::has('purifyHtml', $cacheKey)) {
183 5
			return Cache::get('purifyHtml', $cacheKey);
184
		}
185 56
186
		// Initialize the instance if it has not yet done
187
		if (!static::$purifyHtmlInstanceCache) {
0 ignored issues
show
Since $purifyHtmlInstanceCache is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $purifyHtmlInstanceCache to at least protected.
Loading history...
188
			$config = static::getHtmlConfig();
189
			static::$purifyHtmlInstanceCache = new \HTMLPurifier($config);
190
		}
191
		if (static::$purifyHtmlInstanceCache) {
192 1
			$input = str_replace(["\r\n"], "\n", $input);
193
			$value = static::$purifyHtmlInstanceCache->purify($input);
194 1
			$value = static::removeUnnecessaryCode($value);
195 1
			if ($loop) {
196 1
				$last = '';
197 1
				while ($last !== $value) {
198 1
					$last = $value;
199 1
					$value = static::purifyHtml($value, false);
200 1
				}
201 1
			}
202 1
			static::purifyHtmlEventAttributes(static::decodeHtml($value));
203 1
			$value = preg_replace("/(^[\r\n]*|[\r\n]+)[\\s\t]*[\r\n]+/", "\n", $value);
204 1
			Cache::save('purifyHtml', $cacheKey, $value, Cache::SHORT);
205 1
		}
206 1
		return $value;
207 1
	}
208 1
209
	/**
210
	 * Purify HTML (Cleanup) malicious snippets of code from text parser.
211
	 *
212
	 * @param string $input
213
	 * @param bool   $loop  Purify values in the loop
214
	 *
215
	 * @return string
216
	 */
217 1
	public static function purifyTextParser($input, $loop = true): string
218 1
	{
219 1
		if (empty($input)) {
220 1
			return $input;
221 1
		}
222 1
		$cacheKey = md5($input);
223 1
		if (Cache::has('purifyTextParser', $cacheKey)) {
224 1
			return Cache::get('purifyTextParser', $cacheKey);
225 1
		}
226 1
		if (!static::$purifyTextParserInstanceCache) {
0 ignored issues
show
Since $purifyTextParserInstanceCache is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $purifyTextParserInstanceCache to at least protected.
Loading history...
227 1
			$config = static::getHtmlConfig(['directives' => ['HTML.AllowedCommentsRegexp' => '/^(\s+{% |{% )[\s\S]+( %}| %}\s+)$/u']]);
228 1
			static::$purifyTextParserInstanceCache = new \HTMLPurifier($config);
229 1
		}
230
		$value = static::$purifyTextParserInstanceCache->purify($input);
231
		$value = static::removeUnnecessaryCode($value);
232
		static::purifyHtmlEventAttributes($value);
233
		if ($loop) {
234
			$last = '';
235
			while ($last !== $value) {
236
				$last = $value;
237 1
				$value = static::purifyTextParser($value, false);
238 1
			}
239
		}
240
		$value = preg_replace("/(^[\r\n]*|[\r\n]+)[\\s\t]*[\r\n]+/", "\n", $value);
241
		Cache::save('purifyTextParser', $cacheKey, $value, Cache::SHORT);
242
		return $value;
243 1
	}
244 1
245
	/**
246
	 * To purify malicious html event attributes.
247 1
	 *
248 1
	 * @param string $value
249 1
	 */
250 1
	public static function purifyHtmlEventAttributes(string $value): void
251 1
	{
252 1
		if (preg_match('#(<[^><]+?[\x00-\x20"\'])([^a-z_\\-]on\\w*|xmlns)(\\s*=\\s*[^><]*)([><]*)#i', $value, $matches)) {
253 1
			\App\Log::error('purifyHtmlEventAttributes: ' . $value, 'IllegalValue');
254 1
			throw new Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE|1|' . print_r($matches, true) . "||$value", 406);
0 ignored issues
show
Are you sure print_r($matches, 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

254
			throw new Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE|1|' . /** @scrutinizer ignore-type */ print_r($matches, true) . "||$value", 406);
Loading history...
255
		}
256 1
		if (preg_match('#<([^><]+?)(' . static::$htmlEventAttributes . ')(\\s*=\\s*[^><]*)([>]*)#i', $value, $matches)) {
0 ignored issues
show
Since $htmlEventAttributes is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $htmlEventAttributes to at least protected.
Loading history...
257 1
			\App\Log::error('purifyHtmlEventAttributes: ' . $value, 'IllegalValue');
258
			throw new Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE|2|' . print_r($matches, true) . "||$value", 406);
259 1
		}
260 1
		if (preg_match('#<([^><]+?)javascript:[\w\.]+\(([>]*)#i', $value, $matches)) {
261 1
			\App\Log::error('purifyHtmlEventAttributes: ' . $value, 'IllegalValue');
262 1
			throw new Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE|3|' . print_r($matches, true) . "||$value", 406);
263 1
		}
264 1
	}
265 1
266 1
	/**
267 1
	 * Remove unnecessary code.
268
	 *
269 1
	 * @param string $value
270 1
	 *
271 1
	 * @return string
272
	 */
273
	public static function removeUnnecessaryCode(string $value): string
274
	{
275
		foreach (self::$removeUnnecessaryCode as $code) {
276
			if (false !== stripos($value, $code)) {
277
				$value = str_ireplace($code, '', $value);
278
			}
279
		}
280
		return $value;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $value could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
281
	}
282 3
283
	/**
284 3
	 * Get html config.
285 3
	 *
286
	 * @param array $options
287 1
	 *
288 1
	 * @return \HTMLPurifier_Config
289
	 */
290
	public static function getHtmlConfig(array $options = [])
291
	{
292
		$config = \HTMLPurifier_Config::createDefault();
293
		$config->set('Core.Encoding', static::$defaultCharset);
294
		$config->set('Cache.SerializerPermissions', 0775);
295
		$config->set('Cache.SerializerPath', ROOT_DIRECTORY . \DIRECTORY_SEPARATOR . 'cache' . \DIRECTORY_SEPARATOR . 'vtlib');
296
		$config->set('HTML.Doctype', 'HTML 4.01 Transitional');
297
		$config->set('CSS.AllowTricky', true);
298
		$config->set('CSS.Proprietary', true);
299
		$config->set('CSS.Trusted', true);
300
		$config->set('Core.RemoveInvalidImg', true);
301
		$config->set('HTML.SafeIframe', true);
302
		$config->set('HTML.SafeEmbed', true);
303
		$config->set('URI.SafeIframeRegexp', '%^(http:|https:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/)%');
304
		$config->set('HTML.DefinitionRev', 1);
305
		$config->set('HTML.TargetBlank', true);
306 48
		$config->set('Attr.EnableID', true);
307
		$config->set('CSS.MaxImgLength', null);
308 48
		$config->set('URI.AllowedSchemes', [
309 1
			'http' => true,
310 1
			'https' => true,
311 1
			'mailto' => true,
312
			'ftp' => true,
313
			'nntp' => true,
314 48
			'news' => true,
315
			'tel' => true,
316 48
			'data' => true,
317 45
		]);
318 3
		foreach ($options['directives'] ?? [] as $key => $value) {
319 3
			$config->set($key, $value);
320 45
		}
321 43
		if ($def = $config->getHTMLDefinition(true)) {
322 4
			$def->addElement('section', 'Block', 'Flow', 'Common');
323 4
			$def->addElement('nav', 'Block', 'Flow', 'Common');
324 41
			$def->addElement('article', 'Block', 'Flow', 'Common');
325 2
			$def->addElement('aside', 'Block', 'Flow', 'Common');
326
			$def->addElement('header', 'Block', 'Flow', 'Common');
327
			$def->addElement('footer', 'Block', 'Flow', 'Common');
328 2
			$def->addElement('address', 'Block', 'Flow', 'Common');
329 2
			$def->addElement('hgroup', 'Block', 'Required: h1 | h2 | h3 | h4 | h5 | h6', 'Common');
330 39
			$def->addElement('figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common');
331 2
			$def->addElement('figcaption', 'Inline', 'Flow', 'Common');
332 2
			$def->addElement('video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', [
333 37
				'src' => 'URI',
334
				'type' => 'Text',
335
				'width' => 'Length',
336 37
				'height' => 'Length',
337 1
				'poster' => 'URI',
338 1
				'preload' => 'Enum#auto,metadata,none',
339 36
				'controls' => 'Bool',
340 2
			]);
341 2
			$def->addElement('audio', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', [
342 2
				'src' => 'URI',
343 2
				'type' => 'Text',
344 2
				'preload' => 'Enum#auto,metadata,none',
345 1
				'controls' => 'Bool',
346
			]);
347
			$def->addElement('source', 'Block', 'Flow', 'Common', [
348 2
				'src' => 'URI',
349 1
				'type' => 'Text',
350
			]);
351 2
			$def->addElement('link', 'Block', 'Flow', 'Common', [
352 34
				'href' => 'URI',
353 2
				'type' => 'Text',
354 2
				'rel' => 'Text',
355 32
			]);
356
			$def->addElement('yetiforce', 'Inline', 'Inline', 'Common', [
357
				'type' => 'Text',
358 32
				'crm-id' => 'Length',
359
				'attachment-id' => 'Length',
360
				'height' => 'Length',
361 32
				'width' => 'Length',
362 2
			]);
363 2
			$def->addElement('s', 'Inline', 'Inline', 'Common');
364 30
			$def->addElement('var', 'Inline', 'Inline', 'Common');
365 2
			$def->addElement('sub', 'Inline', 'Inline', 'Common');
366 2
			$def->addElement('sup', 'Inline', 'Inline', 'Common');
367 1
			$def->addElement('mark', 'Inline', 'Inline', 'Common');
368
			$def->addElement('wbr', 'Inline', 'Empty', 'Core');
369 2
			$def->addElement('ins', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']);
370 28
			$def->addElement('del', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']);
371 1
			// TinyMCE
372 1
			$def->addAttribute('img', 'data-mce-src', 'Text');
373 1
			$def->addAttribute('img', 'data-mce-json', 'Text');
374
			// Others
375 1
			$def->addAttribute('iframe', 'allowfullscreen', 'Bool');
376 28
			$def->addAttribute('table', 'height', 'Text');
377
			$def->addAttribute('td', 'border', 'Text');
378
			$def->addAttribute('th', 'border', 'Text');
379
			$def->addAttribute('tr', 'width', 'Text');
380
			$def->addAttribute('tr', 'height', 'Text');
381 28
			$def->addAttribute('tr', 'border', 'Text');
382
			$def->addAttribute('a', 'data-id', 'Text');
383
			$def->addAttribute('a', 'data-module', 'Text');
384 28
		}
385 1
		if ($uriDef = $config->getURIDefinition()) {
386 1
			$uriDef->addFilter(new Extension\HTMLPurifier\Domain(), $config);
387 28
		}
388 3
		return $config;
389 2
	}
390
391 3
	/**
392 26
	 * Function to return the valid SQl input.
393 2
	 *
394 1
	 * @param string $input
395
	 * @param bool   $skipEmpty Skip the check if string is empty
396 2
	 *
397 24
	 * @return bool|string
398 2
	 */
399 2
	public static function purifySql($input, $skipEmpty = true)
400 22
	{
401 2
		if ((empty($input) && $skipEmpty) || Validator::sql($input)) {
402 1
			return $input;
403
		}
404 2
		\App\Log::error('purifySql: ' . $input, 'IllegalValue');
405 20
		throw new \App\Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE||' . $input, 406);
406
	}
407
408 20
	/**
409
	 * Purify by data type.
410
	 *
411 20
	 * Type list:
412
	 * Standard - only words
413 20
	 * 1 - only words
414 18
	 * Alnum - word and int
415
	 * 2 - word and int
416 46
	 *
417 13
	 * @param mixed  $input
418 13
	 * @param string $type    Data type that is only acceptable
419
	 * @param mixed  $convert
420
	 *
421 33
	 * @return mixed
422
	 */
423
	public static function purifyByType($input, $type, $convert = false)
424
	{
425
		if (\is_array($input)) {
426
			$value = [];
427
			foreach ($input as $k => $v) {
428
				$value[$k] = static::purifyByType($v, $type);
429
			}
430
		} else {
431 3
			$value = null;
432
			switch ($type) {
433 3
				case 'Standard': // only word
434
				case 1:
435
					$value = Validator::standard($input) ? $input : null;
436
					break;
437
				case 'Alnum': // word and int
438
				case 2:
439
					$value = Validator::alnum($input) ? $input : null;
440
					break;
441
				case 'AlnumExtended':
442
					$value = preg_match('/^[\sA-Za-z0-9\,\_\.\=\-]+$/', $input) ? $input : null;
443
					break;
444 5872
				case 'AlnumType2':
445
					$value = preg_match('/^[\sA-Za-z0-9\/\+\-]+$/', $input) ? $input : null;
446 5872
					break;
447
				case 'DateInUserFormat': // date in user format
448
					if (!$input) {
449
						return '';
450
					}
451
					$value = Validator::dateInUserFormat($input) ? ($convert ? Fields\Date::formatToDB($input) : $input) : null;
452
					break;
453
				case 'TimeInUserFormat':
454
					$value = Validator::timeInUserFormat($input) ? ($convert ? Fields\Time::formatToDB($input) : $input) : null;
455
					break;
456 84
				case 'DateRangeUserFormat': // date range user format
457
					$dateFormat = User::getCurrentUserModel()->getDetail('date_format');
458 84
					$v = [];
459
					foreach (explode(',', $input) as $i) {
460
						if (!Validator::dateInUserFormat($i)) {
461
							$v = [];
462
							break;
463
						}
464
						[$y, $m, $d] = Fields\Date::explode($i, $dateFormat);
465
						if (checkdate((int) $m, (int) $d, (int) $y) && is_numeric($y) && is_numeric($m) && is_numeric($d)) {
466
							$v[] = \DateTimeField::convertToDBFormat($i);
467
						}
468
					}
469
					if ($v) {
470
						$value = $v;
471
					}
472
					break;
473
				case 'DateTimeInIsoFormat': // date in base format yyyy-mm-dd
474
					$value = Validator::dateTimeInIsoFormat($input) ? date('Y-m-d H:i:s', strtotime($input)) : null;
475
					break;
476
				case 'Bool':
477
					$value = self::bool($input);
478
					break;
479
				case 'NumberInUserFormat': // number in user format
480
					$input = Fields\Double::formatToDb($rawInput = $input);
481
					if (is_numeric($input) && Fields\Double::formatToDisplay($input, false) === Fields\Double::truncateZeros($rawInput)) {
482
						$value = $input;
483
					}
484
					break;
485
				case 'Number':
486
					$dbFormat = Fields\Double::formatToDb($input);
487
					if (is_numeric($dbFormat) && Fields\Double::formatToDisplay($dbFormat, false) === Fields\Double::truncateZeros($input)) {
488
						$value = $input;
489
					}
490
					break;
491
				case 'Double':
492
					if (false !== ($input = filter_var($input, FILTER_VALIDATE_FLOAT))) {
493
						$value = $input;
494
					}
495
					break;
496
				case 'Phone':
497
					$value = preg_match('/^[\s0-9+\-()]+$/', $input) ? $input : null;
498
					break;
499
				case 'Email':
500
					if (!$input) {
501
						return '';
502
					}
503
					$value = Validator::email($input) ? $input : null;
504
					break;
505
				case 'Html':
506
					$value = self::purifyHtml($input);
507
					break;
508
				case 'Integer': // Integer
509
					if (false !== ($input = filter_var($input, FILTER_VALIDATE_INT))) {
510
						$value = $input;
511
					}
512
					break;
513
				case 'Digits': // Digits - eg. 000523
514
					if (false !== ($input = filter_var($input, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^[0-9]+$/']]))) {
515
						$value = $input;
516
					}
517
					break;
518
				case 'Color': // colors
519
					$value = preg_match('/^(#[0-9a-fA-F]{6})$/', $input) ? $input : null;
520
					break;
521
				case 'Year': // 2018 etc
522
					if (is_numeric($input) && (int) $input >= 0 && (int) $input <= 3000 && 4 === \strlen((string) $input)) {
523
						$value = (string) $input;
524
					}
525
					break;
526
				case 'Version':
527
					$value = preg_match('/^[\.0-9]+$/', $input) ? $input : null;
528
					break;
529
				case self::PATH:
530
					$value = Validator::path($input) && Validator::path(static::purify($input)) ? $input : null;
531
					break;
532
				case 'Url':
533
					if (!$input) {
534
						return '';
535
					}
536
					$value = Validator::url($input) ? $input : null;
537
					break;
538
				case 'MailId':
539
					$input = ltrim(rtrim(trim($input), '>'), '<');
540
					$value = $input === strip_tags($input) ? $input : null;
541
					break;
542
				case 'ClassName':
543
					$value = preg_match('/^[a-z\\\_]+$/i', $input) ? $input : null;
544
					break;
545
				case self::SQL:
546
					$value = $input && Validator::sql($input) ? $input : null;
547
					break;
548
				case self::HTML_TEXT_PARSER:
549
					$value = self::purifyTextParser($input);
550
					break;
551
				case 'Text':
552
					$value = self::purify($input);
553
					break;
554
				default:
555
					if (method_exists('App\Validator', $type)) {
556
						if (Validator::{$type}($input)) {
557
							$value = $input;
558
						}
559
					} else {
560
						$value = self::purify($input);
561
					}
562
					break;
563
			}
564
			if (null === $value) {
565
				\App\Log::error('purifyByType: ' . $input, 'IllegalValue');
566
				throw new \App\Exceptions\IllegalValue('ERR_NOT_ALLOWED_VALUE||' . $input, 406);
567
			}
568
		}
569
		return $value;
570
	}
571
572
	/**
573
	 * Function to convert the given value to bool.
574
	 *
575
	 * @param int|string $value
576
	 *
577
	 * @return bool|null
578
	 */
579
	public static function bool($value)
580
	{
581
		return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
582
	}
583
584
	/**
585
	 * Function to convert the given string to html.
586
	 *
587
	 * @param string $string
588
	 * @param bool   $encode
589
	 *
590
	 * @return string
591
	 */
592
	public static function encodeHtml($string)
593
	{
594
		return $string !== null ? htmlspecialchars($string, ENT_QUOTES, static::$defaultCharset) : '';
0 ignored issues
show
The condition $string !== null is always true.
Loading history...
595
	}
596
597
	/**
598
	 * Function to decode html.
599
	 *
600
	 * @param string $string
601
	 *
602
	 * @return string
603
	 */
604
	public static function decodeHtml($string)
605
	{
606
		return $string !== null ? html_entity_decode($string, ENT_QUOTES, static::$defaultCharset) : '';
0 ignored issues
show
The condition $string !== null is always true.
Loading history...
607
	}
608
}
609
610
Purifier::$defaultCharset = (string) \App\Config::main('default_charset', 'UTF-8');
611