Issues (19)

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.

Validator.php (8 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
 * This file is part of the Cubiche package.
4
 *
5
 * Copyright (c) Cubiche
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Cubiche\Core\Validator;
12
13
use Cubiche\Core\Validator\Exception\ValidationException;
14
use Cubiche\Core\Validator\Mapping\ClassMetadata;
15
use Cubiche\Core\Validator\Mapping\Driver\StaticDriver;
16
use Metadata\Driver\DriverChain;
17
use Metadata\MetadataFactory;
18
use Metadata\MetadataFactoryInterface;
19
use Respect\Validation\Exceptions\NestedValidationException;
20
21
/**
22
 * Validator class.
23
 *
24
 * @author Ivannis Suárez Jerez <[email protected]>
25
 */
26
class Validator implements ValidatorInterface
27
{
28
    /**
29
     * @var array
30
     */
31
    protected $constraints = array();
32
33
    /**
34
     * @var string
35
     */
36
    protected $defaultGroup;
37
38
    /**
39
     * @var MetadataFactoryInterface
40
     */
41
    protected $metadataFactory;
42
43
    /**
44
     * @var Validator
45
     */
46
    private static $instance = null;
47
48
    /**
49
     * Validator constructor.
50
     *
51
     * @param MetadataFactoryInterface $metadataFactory
52
     * @param string                   $defaultGroup
53
     */
54
    private function __construct(MetadataFactoryInterface $metadataFactory, $defaultGroup = Assert::DEFAULT_GROUP)
55
    {
56
        $this->metadataFactory = $metadataFactory;
57
        $this->defaultGroup = $defaultGroup;
58
    }
59
60
    /**
61
     * @param MetadataFactoryInterface $metadataFactory
62
     */
63
    public static function setMetadataFactory(MetadataFactoryInterface $metadataFactory)
64
    {
65
        static::create()->metadataFactory = $metadataFactory;
66
    }
67
68
    /**
69
     * @param $defaultGroup
70
     */
71
    public static function setDefaultGroup($defaultGroup)
72
    {
73
        static::create()->defaultGroup = $defaultGroup;
74
    }
75
76
    /**
77
     * @return Validator
78
     */
79
    public static function create()
80
    {
81
        if (static::$instance === null) {
0 ignored issues
show
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
82
            static::$instance = new static(
0 ignored issues
show
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
83
                new MetadataFactory(new DriverChain(array(new StaticDriver())))
84
            );
85
        }
86
87
        return static::$instance;
0 ignored issues
show
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
88
    }
89
90
    /**
91
     * @param Assert $assert
92
     * @param string $className
93
     * @param string $group
94
     *
95
     * @return $this
96
     */
97
    protected function addConstraint(Assert $assert, $className = null, $group = null)
98
    {
99
        $className = $this->normalizeClassName($className);
100
        $group = $this->normalizeGroup($group);
101
102
        if (!isset($this->constraints[$className])) {
103
            $this->constraints[$className] = array();
104
        }
105
106
        if (!isset($this->constraints[$className][$group])) {
107
            $this->constraints[$className][$group] = Assert::create();
108
        }
109
110
        $this->constraints[$className][$group]->addRules($assert->getRules());
111
112
        return $this;
113
    }
114
115
    /**
116
     * @param string $className
117
     * @param string $group
118
     *
119
     * @return Assert
120
     */
121
    protected function getConstraintsByGroup($className = null, $group = null)
122
    {
123
        $className = $this->normalizeClassName($className);
124
        $group = $this->normalizeGroup($group);
125
126
        if (!isset($this->constraints[$className])) {
127
            return Assert::create()->alwaysValid();
128
        }
129
130
        if (!isset($this->constraints[$className][$group])) {
131
            return Assert::create()->alwaysValid();
132
        }
133
134
        return $this->constraints[$className][$group];
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public static function assert($value, $constraints = null, $group = null)
141
    {
142
        return static::create()->assertConstraints($value, $constraints, $group);
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148
    protected function assertConstraints($value, $constraints = null, $group = null)
149
    {
150
        $group = $this->normalizeGroup($group);
151
152
        // If explicit constraints are passed, validate the value against
153
        // those constraints
154
        if (null !== $constraints) {
155
            if (!is_array($constraints)) {
156
                $constraints = array($constraints);
157
            }
158
159
            foreach ($constraints as $constraint) {
160
                $this->addConstraint($constraint, null, $group);
161
            }
162
163
            $constraints = $this->getConstraintsByGroup(null, $group);
164
165
            try {
166
                $returnValue = $constraints->assert($value);
167
            } catch (NestedValidationException $e) {
168
                throw new ValidationException(
169
                    implode(', ', $e->getMessages()),
170
                    $e->getMessages(),
171
                    $e->getCode(),
172
                    $e->getPrevious()
173
                );
174
            }
175
176
            return $returnValue;
177
        }
178
179
        // If an object is passed without explicit constraints, validate that
180
        // object against the constraints defined for the object's class
181
        if (is_object($value)) {
182
            $this->addObjectConstraints($value);
183
184
            $constraints = $this->getConstraintsByGroup(get_class($value), $group);
185
186
            try {
187
                $returnValue = $constraints->assert($value);
188
            } catch (NestedValidationException $e) {
189
                throw new ValidationException(
190
                    implode(', ', $e->getMessages()),
191
                    $e->getMessages(),
192
                    $e->getCode(),
193
                    $e->getPrevious()
194
                );
195
            }
196
197
            return $returnValue;
198
        }
199
200
        // If an array is passed without explicit constraints, validate each
201
        // object in the array
202
        if (is_array($value)) {
203
            $this->addArrayConstraints($value);
204
205
            $returnValue = true;
206
            foreach ($value as $item) {
207
                $constraints = $this->getConstraintsByGroup(is_object($item) ? get_class($item) : null, $group);
208
209
                try {
210
                    $returnValue = $returnValue && $constraints->assert($item);
211
                } catch (NestedValidationException $e) {
212
                    throw new ValidationException(
213
                        implode(', ', $e->getMessages()),
214
                        $e->getMessages(),
215
                        $e->getCode(),
216
                        $e->getPrevious()
217
                    );
218
                }
219
            }
220
221
            return $returnValue;
222
        }
223
224
        throw new \RuntimeException(sprintf(
225
            'Cannot validate values of type "%s" automatically. Please '.
226
            'provide a constraint.',
227
            gettype($value)
228
        ));
229
    }
230
231
    /**
232
     * {@inheritdoc}
233
     */
234
    public static function validate($value, $constraints = null, $group = null)
235
    {
236
        return static::create()->validateConstraints($value, $constraints, $group);
237
    }
238
239
    /**
240
     * {@inheritdoc}
241
     */
242
    protected function validateConstraints($value, $constraints = null, $group = null)
243
    {
244
        $group = $this->normalizeGroup($group);
245
246
        // If explicit constraints are passed, validate the value against
247
        // those constraints
248
        if (null !== $constraints) {
249
            if (!is_array($constraints)) {
250
                $constraints = array($constraints);
251
            }
252
253
            foreach ($constraints as $constraint) {
254
                $this->addConstraint($constraint, null, $group);
255
            }
256
257
            return $this->getConstraintsByGroup(null, $group)->validate($value);
258
        }
259
260
        // If an object is passed without explicit constraints, validate that
261
        // object against the constraints defined for the object's class
262
        if (is_object($value)) {
263
            $this->addObjectConstraints($value);
264
265
            return $this->getConstraintsByGroup(get_class($value), $group)->validate($value);
266
        }
267
268
        // If an array is passed without explicit constraints, validate each
269
        // object in the array
270
        if (is_array($value)) {
271
            $this->addArrayConstraints($value);
272
273
            $returnValue = true;
274
            foreach ($value as $item) {
275
                $constraints = $this->getConstraintsByGroup(is_object($item) ? get_class($item) : null, $group);
276
                $returnValue = $returnValue && $constraints->validate($item);
277
            }
278
279
            return $returnValue;
280
        }
281
282
        throw new \RuntimeException(sprintf(
283
            'Cannot validate values of type "%s" automatically. Please '.
284
            'provide a constraint.',
285
            gettype($value)
286
        ));
287
    }
288
289
    /**
290
     * @param object $object
291
     */
292
    protected function addObjectConstraints($object)
293
    {
294
        $metadata = $this->metadataFactory->getMetadataForClass(get_class($object));
295
        if ($metadata !== null) {
296
            /** @var ClassMetadata $classMetadata */
297
            $classMetadata = $metadata->getRootClassMetadata();
298
299 View Code Duplication
            foreach ($classMetadata->getPropertiesMetadata() as $propertyMetadata) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
300
                foreach ($propertyMetadata->getConstraints() as $group => $constraints) {
301
                    $allOf = Assert::create();
302
                    foreach ($constraints as $constraint) {
303
                        $allOf->addRules($constraint->getRules());
304
                    }
305
306
                    $this->addConstraint(
307
                        Assert::create()->attribute($propertyMetadata->getPropertyName(), $allOf),
0 ignored issues
show
\Cubiche\Core\Validator\...PropertyName(), $allOf) of type object<Respect\Validation\Validator> is not a sub-type of object<Cubiche\Core\Validator\Assert>. It seems like you assume a child class of the class Respect\Validation\Validator to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
308
                        get_class($object),
309
                        $group
310
                    );
311
                }
312
            }
313
314 View Code Duplication
            foreach ($classMetadata->getMethodsMetadata() as $methodMetadata) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
315
                foreach ($methodMetadata->getConstraints() as $group => $constraints) {
316
                    $allOf = Assert::create();
317
                    foreach ($constraints as $constraint) {
318
                        $allOf->addRules($constraint->getRules());
319
                    }
320
321
                    $this->addConstraint(
322
                        Assert::create()->call([$object, $methodMetadata->getMethodName()], $allOf),
0 ignored issues
show
The call to Assert::call() has too many arguments starting with array($object, $methodMetadata->getMethodName()).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
\Cubiche\Core\Validator\...tMethodName()), $allOf) of type object<Respect\Validation\Validator> is not a sub-type of object<Cubiche\Core\Validator\Assert>. It seems like you assume a child class of the class Respect\Validation\Validator to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
323
                        get_class($object),
324
                        $group
325
                    );
326
                }
327
            }
328
        }
329
    }
330
331
    /**
332
     * @param string $className
333
     *
334
     * @return ClassMetadata|null
335
     */
336
    public static function getMetadataForClass($className)
337
    {
338
        return self::create()->metadataFactory->getMetadataForClass($className);
339
    }
340
341
    /**
342
     * @param string $namespace
343
     * @param bool   $prepend
344
     */
345
    public static function registerValidator($namespace, $prepend = false)
346
    {
347
        Assert::registerValidator($namespace, $prepend);
348
    }
349
350
    /**
351
     * @param mixed $array
352
     */
353
    protected function addArrayConstraints($array)
354
    {
355
        foreach ($array as $key => $value) {
356
            if (is_array($value)) {
357
                $this->addArrayConstraints($value);
358
359
                continue;
360
            }
361
362
            // Scalar and null values in the collection are ignored
363
            if (is_object($value)) {
364
                $this->addObjectConstraints($value);
365
            } else {
366
                throw new \RuntimeException(sprintf(
367
                    'Cannot validate values of type "%s" automatically. Please '.
368
                    'provide a constraint.',
369
                    gettype($value)
370
                ));
371
            }
372
        }
373
    }
374
375
    /**
376
     * Normalizes the given group.
377
     *
378
     * @param string $group
379
     *
380
     * @return string
381
     */
382
    protected function normalizeGroup($group = null)
383
    {
384
        return $group === null || empty($group)  ? $this->defaultGroup : $group;
385
    }
386
387
    /**
388
     * Normalizes the given className.
389
     *
390
     * @param string $className
391
     *
392
     * @return string
393
     */
394
    protected function normalizeClassName($className = null)
395
    {
396
        return $className !== null ? $className : self::class;
397
    }
398
}
399