Issues (245)

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/Entities/Assertions/AssertionFactory.php (9 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
/**
4
 * \AppserverIo\Doppelgaenger\Entities\Assertions\AssertionFactory
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Bernhard Wick <[email protected]>
15
 * @copyright 2015 TechDivision GmbH - <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/appserver-io/doppelgaenger
18
 * @link      http://www.appserver.io/
19
 */
20
21
namespace AppserverIo\Doppelgaenger\Entities\Assertions;
22
23
use AppserverIo\Doppelgaenger\Dictionaries\ReservedKeywords;
24
use AppserverIo\Doppelgaenger\Entities\Lists\AssertionList;
25
use AppserverIo\Doppelgaenger\Interfaces\StructureDefinitionInterface;
26
use AppserverIo\Psr\MetaobjectProtocol\Dbc\Assertions\AssertionInterface;
27
use AppserverIo\Psr\MetaobjectProtocol\Dbc\Annotations\Ensures;
28
use AppserverIo\Psr\MetaobjectProtocol\Dbc\Annotations\Invariant;
29
use AppserverIo\Psr\MetaobjectProtocol\Dbc\Annotations\Requires;
30
31
/**
32
 * This class will help instantiating the right assertion class for any given assertion array
33
 *
34
 * @author    Bernhard Wick <[email protected]>
35
 * @copyright 2015 TechDivision GmbH - <[email protected]>
36
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
37
 * @link      https://github.com/appserver-io/doppelgaenger
38
 * @link      http://www.appserver.io/
39
 */
40
class AssertionFactory
41
{
42
43
    /**
44
     * All simple data types which are known but are aliased without an is_... function.
45
     *
46
     * @var string[] $scalarTypeMappings
47
     */
48
    protected $scalarTypeMappings = array(
49
        'boolean' => 'bool',
50
        'void' => 'null'
51
    );
52
53
    /**
54
     * All simple data types which are supported by PHP
55
     * and have a is_... function.
56
     *
57
     * @var string[] $validScalarTypes
58
     */
59
    protected $validScalarTypes = array(
60
        'array',
61
        'bool',
62
        'callable',
63
        'double',
64
        'float',
65
        'int',
66
        'integer',
67
        'long',
68
        'null',
69
        'numeric',
70
        'object',
71
        'real',
72
        'resource',
73
        'scalar',
74
        'string',
75
        'boolean',
76
        'void'
77
    );
78
79
    /**
80
     * The definition of the structure we are currently iterating through
81
     *
82
     * @var StructureDefinitionInterface $currentDefinition
83
     */
84
    protected $currentDefinition;
85
86
    /**
87
     * Parse assertions which are a collection of others
88
     *
89
     * @param string    $connective How are they combined? E.g. "||"
90
     * @param \stdClass $annotation The annotation to create chained assertions from
91
     *
92
     * @return \AppserverIo\Doppelgaenger\Entities\Assertions\ChainedAssertion
93
     */
94
    protected function createChainedAssertion($connective, \stdClass $annotation)
95
    {
96
        // Get all the parts of the string
97
        $assertionArray = explode(' ', $annotation->values['typeHint']);
98
99
        // Check all string parts for the | character
100
        $combinedPart = '';
101
        $combinedIndex = 0;
102
        foreach ($assertionArray as $key => $assertionPart) {
103
            // Check which part contains the | but does not only consist of it
104
            if ($this->filterOrCombinator($assertionPart) && trim($assertionPart) !== $connective) {
105
                $combinedPart = trim($assertionPart);
106
                $combinedIndex = $key;
107
                break;
108
            }
109
        }
110
111
112
        // Now we have to create all the separate assertions for each part of the $combinedPart string
113
        $assertionList = new AssertionList();
114
        foreach (explode($connective, $combinedPart) as $partString) {
115
            // Rebuild the assertion string with one partial string of the combined part
116
            $tmp = $assertionArray;
117
            $tmp[$combinedIndex] = $partString;
118
            $annotation->values['typeHint'] = $partString;
119
            $assertion = $this->getInstance($annotation);
120
121
            if (is_bool($assertion)) {
122
                continue;
123
0 ignored issues
show
Blank line found at end of control structure
Loading history...
124
            } else {
125
                $assertionList->add($assertion);
126
            }
127
        }
128
129
        // We got everything. Create a ChainedAssertion instance
130
        return new ChainedAssertion($assertionList, '||');
131
    }
132
133
    /**
134
     * Will parse assertions from a DocBlock comment piece. If $usedAnnotation is given we will concentrate on that
135
     * type of assertion only.
136
     * We might return false on error
137
     *
138
     * @param \stdClass $annotation The annotation to create simple assertions from
139
     *
140
     * @return boolean|\AppserverIo\Psr\MetaobjectProtocol\Dbc\Assertions\AssertionInterface
141
     *
142
     * @throws \AppserverIo\Doppelgaenger\Exceptions\ParserException
143
     */
144
    protected function createSimpleAssertion(\stdClass $annotation)
145
    {
146
        // Do we have an or connective aka "|"?
147
        if ($this->filterOrCombinator($annotation->values['typeHint'])) {
148
            // If we got invalid arguments then we will fail
149
            try {
150
                return $this->createChainedAssertion('|', $annotation);
151
0 ignored issues
show
Blank line found at end of control structure
Loading history...
152
            } catch (\InvalidArgumentException $e) {
153
                return false;
154
            }
155
        }
156
157
        // check what we have got
158
        $variable = $annotation->values['operand'];
159
        $type = $this->filterScalarType($annotation->values['typeHint']);
160
        $class = $this->filterType($annotation->values['typeHint']);
161
        $collectionType = $this->filterTypedCollection($annotation->values['typeHint']);
162
163
        // "mixed" is something we cannot work with, special case is a mixed typed collection which basically is an array
164
        if ($type === 'mixed' || $class === 'mixed') {
165
            return false;
166
0 ignored issues
show
Blank line found at end of control structure
Loading history...
167
        } elseif ($collectionType === 'mixed') {
168
            // we have a collection with mixed content, so basically an array
169
            $type = 'array';
170
            $collectionType = false;
171
        }
172
173
        if ($annotation->name === 'return') {
174
            $variable = ReservedKeywords::RESULT;
175
        }
176
177
        // Now we have to check what we got
178
        // First of all handle if we got a simple type
179
        if ($type !== false && !empty($type)) {
180
            return new TypeAssertion($variable, $type);
181
0 ignored issues
show
Blank line found at end of control structure
Loading history...
182
        } elseif ($class !== false && !empty($class)) {
183
            // seems we have an instance assertion here
184
185
            return new InstanceAssertion($variable, $class);
186
0 ignored issues
show
Blank line found at end of control structure
Loading history...
187
        } elseif ($collectionType !== false && !empty($collectionType)) {
188
            // seems we have a typed collection here
189
190
            return new TypedCollectionAssertion($variable, $collectionType);
191
        } else {
192
            return false;
193
        }
194
    }
195
196
    /**
197
     * Will filter for any referenced structure as a indicated type hinting of complex types
198
     *
199
     * @param string $string The string potentially containing a structure name
200
     *
201
     * @return boolean
202
     */
203
    protected function filterType($string)
204
    {
205
        // if the string is neither a scalar type, a typed collection and contains a namespace separator we assume a class
206
207
        // check if we know the simple type
208
        $validScalarTypes = array_flip($this->validScalarTypes);
209
        if (isset($validScalarTypes[strtolower($string)])) {
210
            return false;
211
        }
212
213
        // check if it might be a typed collection
214
        foreach (array('<', '>', '[', ']') as $needle) {
215
            if (strpos($string, $needle) !== false) {
216
                return false;
217
            }
218
        }
219
220
        // check if we have a namespace separator
221
        if (strpos($string, '\\') !== false) {
222
            return $string;
223
        }
224
225
        // check if we start with upper case and do only contain valid characters
226
        if (ctype_upper($string[0]) && preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $string)) {
227
            return $string;
228
        }
229
230
        // We found nothing; tell them.
231
        return false;
232
    }
233
234
    /**
235
     * Will filter any combinator defining a logical or-relation
236
     *
237
     * @param string $docString The DocBlock piece to search in
238
     *
239
     * @return boolean
240
     */
241
    protected function filterOrCombinator($docString)
242
    {
243
        if (strpos($docString, '|')) {
244
            return true;
245
        }
246
247
        // We found nothing; tell them.
248
        return false;
249
    }
250
251
    /**
252
     * Will filter for any simple type that may be used to indicate type hinting
253
     *
254
     * @param string $string The string potentially containing a scalar type hint
255
     *
256
     * @return boolean|string
257
     */
258
    protected function filterScalarType($string)
259
    {
260
        // check if we know the simple type
261
        $validScalarTypes = array_flip($this->validScalarTypes);
262
        if (!isset($validScalarTypes[$string])) {
263
            return false;
264
        }
265
266
        // if we have a mapping we have to return the mapped value instead
267
        if (isset($this->scalarTypeMappings[$string])) {
268
            $string = $this->scalarTypeMappings[$string];
269
        }
270
271
        // is it a scalar type we can check for?
272
        if (function_exists('is_' . $string)) {
273
            return $string;
274
        }
275
276
        // We found nothing; tell them.
277
        return false;
278
    }
279
280
    /**
281
     * Will filter for type safe collections of the form array<Type> or Type[]
282
     *
283
     * @param string $string The string potentially containing a type hint for a typed collection
284
     *
285
     * @return boolean|string
286
     */
287
    protected function filterTypedCollection($string)
288
    {
289
        $tmp = strpos($string, 'array<');
290
        if ($tmp !== false) {
291
            // we have a Java Generics like syntax
292
293
            if (strpos($string, '>') > $tmp) {
294
                $stringPiece = explode('array<', $string);
295
                $stringPiece = $stringPiece[1];
296
297
                return strstr($stringPiece, '>', true);
298
            }
299
0 ignored issues
show
Blank line found at end of control structure
Loading history...
300
        } elseif (strpos($string, '[]')) {
301
            // we have a common <TYPE>[] syntax
302
303
            return strstr($string, '[]', true);
304
        }
305
306
        // We found nothing; tell them.
307
        return false;
308
    }
309
310
    /**
311
     * Will return an instance of an assertion fitting the passed annotation object
312
     *
313
     * @param \stdClass $annotation Annotation object to generate assertion from
314
     *
315
     * @return \AppserverIo\Psr\MetaobjectProtocol\Dbc\Assertions\AssertionInterface
316
     * @throws \Exception
317
     */
318
    public function getInstance(\stdClass $annotation)
319
    {
320
        switch ($annotation->name) {
321
            case Ensures::ANNOTATION:
322
            case Invariant::ANNOTATION:
323
            case Requires::ANNOTATION:
324
                // complex annotations leave us with two possibilities: raw or custom assertions
325
                if (isset($annotation->values['type'], $annotation->values['constraint'])) {
326
                    // we need a custom assertion here
327
                    return $this->createAssertion($annotation->values['type'], $annotation->values['constraint']);
328
                }
329
                // RawAssertion is sufficient
330
                return new RawAssertion(array_pop($annotation->values));
331
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
332
            case 'param':
333
            case 'return':
334
                // simple assertions leave with a wide range of type assertions
335
                return $this->createSimpleAssertion($annotation);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->createSimpleAssertion($annotation); of type AppserverIo\Doppelgaenge...ypedCollectionAssertion adds false to the return on line 335 which is incompatible with the return type documented by AppserverIo\Doppelgaenge...ionFactory::getInstance of type AppserverIo\Psr\Metaobje...AssertionInterface|null. It seems like you forgot to handle an error condition.
Loading history...
336
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
337
            default:
338
                break;
339
        }
340
    }
341
342
    /**
343
     * Tries to create assertion of $assertionType
344
     *
345
     * @param string $assertionType the assertion type
346
     * @param string $constraint    the constraint to validate
347
     *
348
     * @return null|AssertionInterface
349
     * @throws \Exception
350
     */
351
    protected function createAssertion($assertionType, $constraint)
352
    {
353
        $assertionClassPath = $this->getAssertionClassPath($assertionType);
354
        if (null === $assertionClassPath) {
355
            throw new \Exception(
356
                sprintf(
357
                    'Cannot create complex assertion of type %s',
358
                    $assertionType
359
                )
360
            );
361
        }
362
363
        $potentialAssertion = new $assertionClassPath($constraint);
364
        if (!$potentialAssertion instanceof AssertionInterface) {
365
            throw new \Exception(
366
                sprintf(
367
                    'Specified assertion of type %s does not implement %s',
368
                    $potentialAssertion,
369
                    AssertionInterface::class
370
                )
371
            );
372
        }
373
374
        return $potentialAssertion;
375
    }
376
377
    /**
378
     * Resolves and returns the fully qualified namespace of $assertionType
379
     * or null if $assertionType cannot be resolved to an accessible class
380
     *
381
     * @param string $assertionType the assertion type
382
     *
383
     * @return null|string
384
     */
385
    protected function getAssertionClassPath($assertionType)
386
    {
387
        if (class_exists($assertionType)) {
388
            return $assertionType;
389
        }
390
391
        $potentialAssertion = $this->resolveUsedAssertionStructure($assertionType);
392
        if (class_exists($potentialAssertion)) {
393
            return $potentialAssertion;
394
        }
395
396
        $potentialAssertion = '\AppserverIo\Doppelgaenger\Entities\Assertions\\' . $assertionType;
397
        if (class_exists($potentialAssertion)) {
398
            return $potentialAssertion;
399
        }
400
401
        $potentialAssertion .= 'Assertion';
402
        if (class_exists($potentialAssertion)) {
403
            return $potentialAssertion;
404
        }
405
406
        return null;
407
    }
408
409
    /**
410
     * Iterates through the 'use' operators of the current structure and
411
     * returns the fully qualified namespace to the Assertion or null if none is found
412
     *
413
     * @param string $assertionType the assertion type
414
     *
415
     * @return null|string
416
     */
417
    protected function resolveUsedAssertionStructure($assertionType)
418
    {
419
        if (!$this->getCurrentDefinition()) {
420
            return null;
421
        }
422
423
        foreach ($this->getCurrentDefinition()->getUsedStructures() as $structure) {
424
            if (preg_match('/\w+$/', $structure, $matches) && $matches[0] === $assertionType) {
425
                return $structure;
426
            }
427
        }
428
429
        return null;
430
    }
431
432
    /**
433
     * Getter for all valid scalar types we can create assertions for
434
     *
435
     * @return string[]
436
     */
437
    public function getValidScalarTypes()
438
    {
439
        return $this->validScalarTypes;
440
    }
441
442
    /**
443
     * Sets the instance of the current definition
444
     *
445
     * @param StructureDefinitionInterface $currentDefinition the definition of the current structure
446
     *
447
     * @return void
448
     */
449
    public function setCurrentDefinition(StructureDefinitionInterface $currentDefinition)
450
    {
451
        $this->currentDefinition = $currentDefinition;
452
    }
453
454
    /**
455
     * Returns the instance of the current definition
456
     *
457
     * @return null|StructureDefinitionInterface
458
     */
459
    public function getCurrentDefinition()
460
    {
461
        return $this->currentDefinition;
462
    }
463
}
464