Issues (16)

Security Analysis    no request data  

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.

src/Validoo/Validator.php (14 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
namespace Validoo;
4
5
/**
6
 * Validator Class
7
 * @author Alessandro Manno <[email protected]>
8
 * @author Chiara Ferrazza <[email protected]>
9
 * @copyright (c) 2016, Facile.it
10
 * @license https://github.com/facile-it/validoo/blob/master/LICENSE MIT Licence
11
 * @link https://github.com/facile-it/validoo
12
 */
13
14
/**
15
 * TODO: Exception handling for rules with parameters
16
 * TODO: unit tests for numeric, float, alpha_numeric, max_length, min_length, exact_length
17
 * TODO: add protection filters for several input vulnerabilities.
18
 */
19
class Validator
20
{
21
22
    /** @var array */
23
    private $errors = [];
24
    /** @var array */
25
    private $namings = [];
26
    /** @var array */
27
    private $customErrorsWithInputName = [];
28
    /** @var array */
29
    private $customErrors = [];
30
31
    /**
32
     * Constructor is not allowed because Validoo uses its own
33
     * static method to instantiate the validation
34
     * @param $errors
35
     * @param $namings
36
     */
37 48
    private function __construct($errors, $namings)
38
    {
39 48
        $this->errors = $errors;
40 48
        $this->namings = $namings;
41 48
    }
42
43
    /**
44
     * @param $inputs
45
     * @param array $rules
46
     * @param array|null $naming
47
     * @return Validator
48
     * @throws ValidooException
49
     */
50 48
    public static function validate($inputs, array $rules, array $naming = null): self
51
    {
52 48
        $errors = null;
53 48
        foreach ($rules as $input => $input_rules) {
0 ignored issues
show
Blank line found at start of control structure
Loading history...
54
55 48
            if (is_string($input_rules))
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
56 2
                $input_rules = explode("|", $input_rules);
57
58 48
            if (!is_array($input_rules))
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
59
                throw new ValidooException(ValidooException::ARRAY_EXPECTED, $input);
60
61 48
            if (in_array("onlyifset", $input_rules) && !isset($inputs[$input]))
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
62 1
                continue;
63
64 48
            foreach ($input_rules as $rule => $closure) {
65 48
                if (!isset($inputs[$input])) {
66 7
                    $input_value = null;
67
                } else {
68 41
                    $input_value = $inputs[$input];
69
                }
70 48
                if (is_numeric($rule)) {
71 46
                    $rule = $closure;
72
                }
73 48
                if ('onlyifset' == $rule)
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
74 1
                    continue;
75
76 48
                $rule_and_params = self::getParams($rule);
77 48
                $params = $real_params = $rule_and_params['params'];
78 48
                $rule = $rule_and_params['rule'];
79 48
                $params = self::getParamValues($params, $inputs);
80 48
                array_unshift($params, $input_value);
81
82 48
                if (false == self::doValidation($closure, $params, $rule)) {
83 27
                    $errors[$input][$rule]['result'] = false;
84 48
                    $errors[$input][$rule]['params'] = $real_params;
85
                }
86
            }
87
0 ignored issues
show
Blank line found at end of control structure
Loading history...
88
        }
89 48
        return new self($errors, $naming);
90
    }
91
92
    /**
93
     * @param $closure
94
     * @param $params
95
     * @param $rule
96
     * @return mixed
97
     * @throws ValidooException
98
     */
99 48
    private static function doValidation($closure, $params, $rule)
100
    {
101 48
        if (@get_class($closure) === 'Closure') {
102 2
            $refl_func = new \ReflectionFunction($closure);
103 2
            $validation = $refl_func->invokeArgs($params);
104 46
        } else if (@method_exists(get_called_class(), $rule)) {
105 46
            $refl = new \ReflectionMethod(get_called_class(), $rule);
106 46
            if ($refl->isStatic()) {
107 46
                $refl->setAccessible(true);
108 46
                $validation = $refl->invokeArgs(null, $params);
109
            } else {
110 46
                throw new ValidooException(ValidooException::STATIC_METHOD, $rule);
111
            }
112
        } else {
113
            throw new ValidooException(ValidooException::UNKNOWN_RULE, $rule);
114
        }
115 48
        return $validation;
116
    }
117
118
    /**
119
     * Gets the parameter names of a rule
120
     * @param $rule
121
     * @return mixed
122
     */
123 48
    private static function getParams($rule)
124
    {
125 48
        if (preg_match("#^([\w]+)\((.+?)\)$#", $rule, $matches)) {
126
            return [
127 6
                'rule' => $matches[1],
128 6
                'params' => explode(',', $matches[2])
129
            ];
130
        }
131
        return [
132 42
            'rule' => $rule,
133
            'params' => []
134
        ];
135
    }
136
137
    /**
138
     * Handle parameter with input name
139
     * eg: equals(:name)
140
     * @param mixed $params
141
     * @param $inputs
142
     * @return mixed
143
     */
144 48
    private static function getParamValues($params, $inputs)
145
    {
146 48
        foreach ($params as $key => $param) {
147 6
            if (preg_match("#^:([\w]+)$#", $param, $param_type)) {
148 6
                $params[$key] = @$inputs[(string)$param_type[1]];
149
            }
150
        }
151 48
        return $params;
152
    }
153
154
    /**
155
     * @param null $input
156
     * @return bool
157
     */
158 9
    protected static function required($input = null): bool
159
    {
160 9
        if (is_string($input))
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
161 3
            $input = trim($input);
162 9
        if (is_numeric($input))
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
163 2
            return true;
164
165 7
        return (null !== $input && !empty($input));
166
    }
167
168
    /**
169
     * @param $input
170
     * @return bool
171
     */
172
    protected static function numeric($input): bool
173
    {
174
        return is_numeric($input);
175
    }
176
177
    /**
178
     * @param $input
179
     * @return bool
180
     */
181 4
    protected static function email($input): bool
182
    {
183 4
        return filter_var($input, FILTER_VALIDATE_EMAIL);
184
    }
185
186
    /**
187
     * @param $input
188
     * @return bool
189
     */
190 2
    protected static function isdir($input): bool
191
    {
192 2
        return is_dir($input);
193
    }
194
195
    /**
196
     * @param $input
197
     * @return bool
198
     */
199
    protected static function isarray($input): bool
200
    {
201
        return is_array($input);
202
    }
203
204
    /**
205
     * @param $input
206
     * @return bool
207
     */
208 11
    protected static function integer($input): bool
209
    {
210 11
        return is_int($input) || ($input == (string)(int)$input);
211
    }
212
213
    /**
214
     * @param $input
215
     * @return bool
216
     */
217
    protected static function float($input): bool
218
    {
219
        return is_float($input) || ($input == (string)(float)$input);
220
    }
221
222
    /**
223
     * @param $input
224
     * @return bool
225
     */
226 4
    protected static function alpha($input): bool
227
    {
228 4
        return (preg_match("#^[a-zA-ZÀ-ÿ]+$#", $input) == 1);
229
    }
230
231
    /**
232
     * @param $input
233
     * @return bool
234
     */
235
    protected static function alpha_numeric($input): bool
0 ignored issues
show
Method name "Validator::alpha_numeric" is not in camel caps format
Loading history...
236
    {
237
        return (preg_match("#^[a-zA-ZÀ-ÿ0-9]+$#", $input) == 1);
238
    }
239
240
    /**
241
     * @param $input
242
     * @return bool
243
     */
244 4
    protected static function ip($input): bool
245
    {
246 4
        return filter_var($input, FILTER_VALIDATE_IP);
247
    }
248
249
    /**
250
     * @param $input
251
     * @return bool
252
     */
253 9
    protected static function url($input): bool
254
    {
255 9
        return filter_var($input, FILTER_VALIDATE_URL);
256
    }
257
258
    /**
259
     * @param $input
260
     * @param $length
261
     * @return bool
262
     */
263
    protected static function max_length($input, $length): bool
0 ignored issues
show
Method name "Validator::max_length" is not in camel caps format
Loading history...
264
    {
265
        return (strlen($input) <= $length);
266
    }
267
268
    /**
269
     * @param $input
270
     * @param $length
271
     * @return bool
272
     */
273
    protected static function min_length($input, $length): bool
0 ignored issues
show
Method name "Validator::min_length" is not in camel caps format
Loading history...
274
    {
275
        return (strlen($input) >= $length);
276
    }
277
278
    /**
279
     * @param $input
280
     * @param $length
281
     * @return bool
282
     */
283
    protected static function exact_length($input, $length): bool
0 ignored issues
show
Method name "Validator::exact_length" is not in camel caps format
Loading history...
284
    {
285
        return (strlen($input) == $length);
286
    }
287
288
    /**
289
     * @param $input
290
     * @param $param
291
     * @return bool
292
     */
293 4
    protected static function equals($input, $param): bool
294
    {
295 4
        return ($input == $param);
296
    }
297
298
    /**
299
     * @param $input
300
     * @return bool
301
     */
302
    protected static function is_filename($input): bool
0 ignored issues
show
Method name "Validator::is_filename" is not in camel caps format
Loading history...
303
    {
304
        return preg_match('/^[A-Za-z0-9-_]+[.]{1}[A-Za-z0-9]+$/', $input);
305
    }
306
307
    protected static function exists_file($input): bool
0 ignored issues
show
Method name "Validator::exists_file" is not in camel caps format
Loading history...
308
    {
309
        return file_exists($input);
310
    }
311
312
    /**
313
     * @return bool
314
     */
315 46
    public function isSuccess()
316
    {
317 46
        return empty($this->errors);
318
    }
319
320
    /**
321
     * @param array $errors_array
322
     */
323 1
    public function customErrors(array $errors_array)
324
    {
325 1
        foreach ($errors_array as $key => $value) {
326
            // handle input.rule eg (name.required)
327 1
            if (preg_match("#^(.+?)\.(.+?)$#", $key, $matches)) {
328
                // $this->customErrorsWithInputName[name][required] = error message
329
                $this->customErrorsWithInputName[(string)$matches[1]][(string)$matches[2]] = $value;
330
            } else {
331 1
                $this->customErrors[(string)$key] = $value;
332
            }
333
        }
334 1
    }
335
336
    /**
337
     * @param string|null $lang
338
     * @return array
339
     * @throws ValidooException
340
     */
341 3
    public function getErrors(string $lang = null): array
342
    {
343 3
        if (null === $lang) {
344 3
            $lang = $this->getDefaultLang();
345
        }
346
347 3
        $error_results = [];
348 3
        $default_error_texts = $this->getDefaultErrorTexts($lang);
349
350 3
        foreach ($this->errors as $input_name => $results) {
351 3
            foreach ($results as $rule => $result) {
352 3
                $named_input = $this->handleNaming($input_name);
353
                /**
354
                 * if parameters are input name they should be named as well
355
                 */
356 3
                $result['params'] = $this->handleParameterNaming($result['params']);
357
                // if there is a custom message with input name, apply it
358 3
                if (isset($this->customErrorsWithInputName[(string)$input_name][(string)$rule])) {
359
                    $error_message = $this->customErrorsWithInputName[(string)$input_name][(string)$rule];
360
                } // if there is a custom message for the rule, apply it
361 3
                else if (isset($this->customErrors[(string)$rule])) {
362 1
                    $error_message = $this->customErrors[(string)$rule];
363
                } // if there is a custom validator try to fetch from its error file
364 2
                else if (isset($custom_error_texts[(string)$rule])) {
365
                    $error_message = $custom_error_texts[(string)$rule];
366
                } // if none try to fetch from default error file
367 2
                else if (isset($default_error_texts[(string)$rule])) {
368 2
                    $error_message = $default_error_texts[(string)$rule];
369
                } else {
370
                    throw new ValidooException(ValidooException::NO_ERROR_TEXT, $rule);
371
                }
372
                /**
373
                 * handle :params(..)
374
                 */
375 3
                if (preg_match_all("#:params\((.+?)\)#", $error_message, $param_indexes)) {
376 1
                    foreach ($param_indexes[1] as $param_index) {
377 1
                        $error_message = str_replace(':params(' . $param_index . ')', $result['params'][$param_index], $error_message);
378
                    }
379
                }
380 3
                $error_results[] = str_replace(':attribute', $named_input, $error_message);
381
            }
382
        }
383
384 3
        return $error_results;
385
    }
386
387
    /**
388
     * @return string
389
     */
390 3
    protected function getDefaultLang(): string
391
    {
392 3
        return 'en';
393
    }
394
395
    /*
396
     * TODO: need improvements for tel and urn urls.
397
     * check out url.test.php for the test result
398
     * urn syntax: http://www.faqs.org/rfcs/rfc2141.html
399
     *
400
     */
401
402
    /**
403
     * @param string|null $lang
404
     * @return array|mixed
405
     */
406 3
    protected function getDefaultErrorTexts(string $lang = null)
407
    {
408
        /* handle default error text file */
409 3
        $default_error_texts = [];
410 3
        if (file_exists(__DIR__ . '/errors/' . $lang . '.php')) {
411
            /** @noinspection PhpIncludeInspection */
412 3
            $default_error_texts = include __DIR__ . '/errors/' . $lang . '.php';
413
        }
414 3
        if (file_exists(__DIR__ . '/errors/' . $lang . '.json')) {
415
            $default_error_texts = json_decode(file_get_contents(__DIR__ . '/errors/' . $lang . '.json'), true);
416
        }
417
418
419 3
        return $default_error_texts;
420
    }
421
422
    /**
423
     * @param string $input_name
424
     * @return mixed|string
425
     */
426 3
    protected function handleNaming(string $input_name)
427
    {
428 3
        if (isset($this->namings[$input_name])) {
429
            $named_input = $this->namings[$input_name];
430
        } else {
431 3
            $named_input = $input_name;
432
        }
433
434 3
        return $named_input;
435
    }
436
437
    /**
438
     * @param array $params
439
     * @return array
440
     */
441 3
    protected function handleParameterNaming(array $params)
442
    {
443 3
        foreach ($params as $key => $param) {
444 1
            if (preg_match("#^:([\w]+)$#", $param, $param_type)) {
445 1
                if (isset($this->namings[(string)$param_type[1]])) {
446 1
                    $params[$key] = $this->namings[(string)$param_type[1]];
447
                } else {
448 1
                    $params[$key] = $param_type[1];
449
                }
450
            }
451
        }
452
453 3
        return $params;
454
    }
455
456
    /**
457
     * @param string $input_name
458
     * @param string|null $rule_name
459
     * @return bool
460
     */
461
    public function has(string $input_name, string $rule_name = null): bool
462
    {
463
        if (null !== $rule_name) {
464
            return isset($this->errors[$input_name][$rule_name]);
465
        }
466
467
        return isset($this->errors[$input_name]);
468
    }
469
470
    /**
471
     * @return array
472
     */
473
    final public function getResults(): array
474
    {
475
        return $this->errors;
476
    }
477
}
478