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/Parser/AbstractParser.php (5 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\Parser\AbstractParser
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\Parser;
22
23
use AppserverIo\Doppelgaenger\Config;
24
use AppserverIo\Doppelgaenger\Interfaces\ParserInterface;
25
use AppserverIo\Doppelgaenger\StructureMap;
26
use AppserverIo\Doppelgaenger\Entities\Definitions\StructureDefinitionHierarchy;
27
use AppserverIo\Doppelgaenger\Exceptions\ParserException;
28
use AppserverIo\Doppelgaenger\Interfaces\StructureDefinitionInterface;
29
30
/**
31
 * The abstract class AbstractParser which provides a basic implementation other parsers can inherit from
32
 *
33
 * @author    Bernhard Wick <[email protected]>
34
 * @copyright 2015 TechDivision GmbH - <[email protected]>
35
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
36
 * @link      https://github.com/appserver-io/doppelgaenger
37
 * @link      http://www.appserver.io/
38
 */
39
abstract class AbstractParser implements ParserInterface
40
{
41
42
    /**
43
     * The aspect of the configuration we need
44
     *
45
     * @var \AppserverIo\Doppelgaenger\Config $config
46
     */
47
    protected $config;
48
49
    /**
50
     * The path of the file we want to parse
51
     *
52
     * @var string $file
53
     */
54
    protected $file;
55
56
    /**
57
     * The token array representing the whole file
58
     *
59
     * @var array $tokens
60
     */
61
    protected $tokens = array();
62
63
    /**
64
     * The count of our main token array, so we do not have to calculate it over and over again
65
     *
66
     * @var integer $tokenCount
67
     */
68
    protected $tokenCount;
69
70
    /**
71
     * The current definition we are working on.
72
     * This should be filled during parsing and should be passed down to whatever parser we need so we know about
73
     * the current "parent" definition parts.
74
     *
75
     * @var \AppserverIo\Doppelgaenger\Interfaces\StructureDefinitionInterface $currentDefinition
76
     */
77
    protected $currentDefinition;
78
79
    /**
80
     * The list of structures (within this hierarchy) which we already parsed.
81
     *
82
     * @var \AppserverIo\Doppelgaenger\Entities\Definitions\StructureDefinitionHierarchy $structureDefinitionHierarchy
83
     */
84
    protected $structureDefinitionHierarchy;
85
86
    /**
87
     * Our structure map instance
88
     *
89
     * @var \AppserverIo\Doppelgaenger\StructureMap $structureMap
90
     */
91
    protected $structureMap;
92
93
    /**
94
     * Default constructor
95
     *
96
     * @param string                                       $file                         The path of the file we want to parse
97
     * @param \AppserverIo\Doppelgaenger\Config            $config                       Configuration
98
     * @param StructureDefinitionHierarchy                 $structureDefinitionHierarchy List of already parsed structures
99
     * @param \AppserverIo\Doppelgaenger\StructureMap|null $structureMap                 Our structure map instance
100
     * @param StructureDefinitionInterface|null            $currentDefinition            The current definition we are working on
101
     * @param array                                        $tokens                       The array of tokens taken from the file
102
     *
103
     * @throws \AppserverIo\Doppelgaenger\Exceptions\ParserException
104
     */
105
    public function __construct(
106
        $file,
107
        Config $config,
108
        StructureDefinitionHierarchy $structureDefinitionHierarchy = null,
109
        StructureMap $structureMap = null,
110
        StructureDefinitionInterface $currentDefinition = null,
111
        array $tokens = array()
112
    ) {
113
        $this->config = $config;
114
115
        if (empty($tokens)) {
116
            // Check if we can use the file
117
            if (!is_readable($file)) {
118
                throw new ParserException(sprintf('Could not read input file %s', $file));
119
            }
120
121
            // Get all the tokens and count them
122
            $this->tokens = token_get_all(file_get_contents($file));
123
0 ignored issues
show
Blank line found at end of control structure
Loading history...
124
        } else {
125
            $this->tokens = $tokens;
126
        }
127
128
        // We need the file saved
129
        $this->file = $file;
130
131
        // We also need the token count
132
        $this->tokenCount = count($this->tokens);
133
134
        $this->currentDefinition = $currentDefinition;
135
136
        $this->structureMap = is_null($structureMap) ? new StructureMap($config->getValue('autoloader/dirs'), $config->getValue('enforcement/dirs'), $config) : $structureMap;
137
        $this->structureDefinitionHierarchy = is_null($structureDefinitionHierarchy) ? new StructureDefinitionHierarchy() : $structureDefinitionHierarchy;
138
    }
139
140
    /**
141
     * Does a certain block of code contain a certain keyword
142
     *
143
     * @param string $docBlock The code block to search in
144
     * @param string $keyword  The keyword to search for
145
     *
146
     * @return boolean
147
     */
148
    protected function usesKeyword(
149
        $docBlock,
150
        $keyword
151
    ) {
152
        if (strpos($docBlock, $keyword) === false) {
153
            return false;
154
        } else {
155
            return true;
156
        }
157
    }
158
159
    /**
160
     * Get the starting line of the structure, FALSE if unknown
161
     *
162
     * @param array $tokens The token array
163
     *
164
     * @return integer|boolean
165
     */
166
    protected function getStartLine($tokens)
167
    {
168
        // Check the tokens
169
        $targetToken = $this->getToken();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class AppserverIo\Doppelgaenger\Parser\AbstractParser as the method getToken() does only exist in the following sub-classes of AppserverIo\Doppelgaenger\Parser\AbstractParser: AppserverIo\Doppelgaenge...AbstractStructureParser, AppserverIo\Doppelgaenger\Parser\AspectParser, AppserverIo\Doppelgaenger\Parser\ClassParser, AppserverIo\Doppelgaenger\Parser\FunctionParser, AppserverIo\Doppelgaenger\Parser\InterfaceParser, AppserverIo\Doppelgaenger\Parser\TraitParser. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
170
        $tokenCount = count($tokens);
171
        for ($i = 0; $i < $tokenCount; $i++) {
172
            // If we got the target token indicating the structure start
173
            if ($tokens[$i][0] === $targetToken && $tokens[$i - 1][0] !== T_PAAMAYIM_NEKUDOTAYIM) {
174
                return $tokens[$i][2];
175
            }
176
        }
177
178
        // Return that we found nothing
179
        return false;
180
    }
181
182
    /**
183
     * Get the ending line of the structure, FALSE if unknown
184
     *
185
     * @param array $tokens The token array
186
     *
187
     * @return integer|boolean
188
     */
189
    protected function getEndLine($tokens)
190
    {
191
        // Check the tokens for a line number
192
        $lastIndex = (count($tokens) - 1);
193
        for ($i = $lastIndex; $i >= 0; $i--) {
194
            // If we got a token we know about the line number of the last token
195
            if (is_array($tokens[$i])) {
196
                // we found something already
197
                $endLine = $tokens[$i][2];
198
                // might be a linebreak as well
199
                if ($tokens[$i][0] === T_WHITESPACE) {
200
                    $endLine += substr_count($tokens[$i][1], "\n");
201
                }
202
                return $endLine;
203
            }
204
        }
205
206
        // Return that we found nothing
207
        return false;
208
    }
209
210
    /**
211
     * Will search for a certain token in a certain entity.
212
     *
213
     * This method will search the signature of either a class or a function for a certain token e.g. final.
214
     * Will return true if the token is found, and false if not or an error occurred.
215
     *
216
     * @param array   $tokens        The token array to search in
217
     * @param integer $searchedToken The token we search for, use PHP tokens here
218
     * @param integer $parsedEntity  The type of entity we search in front of, use PHP tokens here
219
     *
220
     * @return boolean
221
     */
222
    protected function hasSignatureToken(
223
        $tokens,
224
        $searchedToken,
225
        $parsedEntity
226
    ) {
227
        // We have to check what kind of structure we will check. Class and function are the only valid ones.
228
        if ($parsedEntity !== T_FUNCTION && $parsedEntity !== T_CLASS && $parsedEntity !== T_INTERFACE) {
229
            return false;
230
        }
231
232
        // Check the tokens
233
        for ($i = 0; $i < count($tokens); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
234
            // If we got the function name we have to check if we have the final keyword in front of it.
235
            // I would say should be within 6 tokens in front of the function keyword.
236
            if ($tokens[$i][0] === $parsedEntity) {
237
                // Check if our $i is lower than 6, if so we have to avoid getting into a negative range
238
                if ($i < 6) {
239
                    $i = 6;
240
                }
241
242
                for ($j = $i - 1; $j >= $i - 6; $j--) {
243
                    if ($tokens[$j][0] === $searchedToken) {
244
                        return true;
245
                    }
246
                }
247
248
                // We passed the 6 token loop but did not find something. So report it.
249
                return false;
250
            }
251
        }
252
253
        // We are still here? That should not be.
254
        return false;
255
    }
256
257
    /**
258
     * Will return the DocBlock of a certain construct based on the token identifying it.
259
     * Will return an empty string if none is found
260
     *
261
     * @param array   $tokens         The token array to search in
262
     * @param integer $structureToken The type of entity we search in front of, use PHP tokens here e.g. T_CLASS
263
     *
264
     * @return string
265
     */
266
    protected function getDocBlock($tokens, $structureToken)
267
    {
268
        // we need tokens which woudl break the construct and the DocBlock apart
269
        $blockBreakers = array_flip(array(T_CLASS, T_TRAIT, T_INTERFACE, T_FUNCTION));
270
271
        // the general assumption is:
272
        // We go to the first occurance of the structure token and traverse back until
273
        // we find a DocBlock without passing any breaks in the flow. This should be the correct block
274
        $tokenCount = count($tokens);
275
        for ($i = 0; $i < $tokenCount; $i++) {
276
            // if we passed the structure token
277
            if ($tokens[$i][0] === $structureToken) {
278
                // traverse back until we find the first DocBlock
279
                for ($j = ($i - 1); $j >= 0; $j--) {
280
                    if ($tokens[$j][0] === T_DOC_COMMENT) {
281
                        return $tokens[$j][1];
282
0 ignored issues
show
Blank line found at end of control structure
Loading history...
283
                    } elseif (isset($blockBreakers[$tokens[$j][0]])) {
284
                        // if we pass any other construct tokens or breaks we will fail
285
                        break;
286
0 ignored issues
show
Blank line found at end of control structure
Loading history...
287
                    } elseif ($tokens[$j][0] === T_WHITESPACE && substr_count($tokens[$j][1], "\n") > 1) {
288
                        // if there is a bigger linebreak in between construct and block we will fail too
289
                        break;
290
                    }
291
                }
292
293
                // still here? We did not find anything then
294
                break;
295
            }
296
        }
297
298
        // still here? That does not sound right
299
        return '';
300
    }
301
}
302