Issues (1236)

Security Analysis    not enabled

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/Psalm/CodeLocation.php (4 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
namespace Psalm;
3
4
use function explode;
5
use function max;
6
use function min;
7
use PhpParser;
8
use function preg_match;
9
use const PREG_OFFSET_CAPTURE;
10
use function preg_quote;
11
use function preg_replace;
12
use Psalm\Internal\Analyzer\CommentAnalyzer;
13
use function str_replace;
14
use function strlen;
15
use function strpos;
16
use function strrpos;
17
use function substr;
18
use function substr_count;
19
use function trim;
20
21
class CodeLocation
22
{
23
    /** @var string */
24
    public $file_path;
25
26
    /** @var string */
27
    public $file_name;
28
29
    /** @var int */
30
    public $raw_line_number;
31
32
    /** @var int */
33
    private $end_line_number = -1;
34
35
    /** @var int */
36
    public $raw_file_start;
37
38
    /** @var int */
39
    public $raw_file_end;
40
41
    /** @var int */
42
    protected $file_start;
43
44
    /** @var int */
45
    protected $file_end;
46
47
    /** @var bool */
48
    protected $single_line;
49
50
    /** @var int */
51
    protected $preview_start;
52
53
    /** @var int */
54
    private $preview_end = -1;
55
56
    /** @var int */
57
    private $selection_start = -1;
58
59
    /** @var int */
60
    private $selection_end = -1;
61
62
    /** @var int */
63
    private $column_from = -1;
64
65
    /** @var int */
66
    private $column_to = -1;
67
68
    /** @var string */
69
    private $snippet = '';
70
71
    /** @var null|string */
72
    private $text;
73
74
    /** @var int|null */
75
    public $docblock_start;
76
77
    /** @var int|null */
78
    public $docblock_end;
79
80
    /** @var int|null */
81
    private $docblock_start_line_number;
82
83
    /** @var int|null */
84
    private $docblock_line_number;
85
86
    /** @var null|int */
87
    private $regex_type;
88
89
    /** @var bool */
90
    private $have_recalculated = false;
91
92
    /** @var null|CodeLocation */
93
    public $previous_location;
94
95
    const VAR_TYPE = 0;
96
    const FUNCTION_RETURN_TYPE = 1;
97
    const FUNCTION_PARAM_TYPE = 2;
98
    const FUNCTION_PHPDOC_RETURN_TYPE = 3;
99
    const FUNCTION_PHPDOC_PARAM_TYPE = 4;
100
    const FUNCTION_PARAM_VAR = 5;
101
    const CATCH_VAR = 6;
102
    const FUNCTION_PHPDOC_METHOD = 7;
103
104
    /**
105
     * @param bool                 $single_line
106
     * @param null|CodeLocation    $previous_location
107
     * @param null|int             $regex_type
108
     * @param null|string          $selected_text
109
     */
110
    public function __construct(
111
        FileSource $file_source,
112
        PhpParser\Node $stmt,
113
        CodeLocation $previous_location = null,
114
        $single_line = false,
115
        $regex_type = null,
116
        $selected_text = null
117
    ) {
118
        $this->file_start = (int)$stmt->getAttribute('startFilePos');
119
        $this->file_end = (int)$stmt->getAttribute('endFilePos');
120
        $this->raw_file_start = $this->file_start;
121
        $this->raw_file_end = $this->file_end;
122
        $this->file_path = $file_source->getFilePath();
123
        $this->file_name = $file_source->getFileName();
124
        $this->single_line = $single_line;
125
        $this->regex_type = $regex_type;
126
        $this->previous_location = $previous_location;
127
        $this->text = $selected_text;
128
129
        $doc_comment = $stmt->getDocComment();
130
131
        $this->docblock_start = $doc_comment ? $doc_comment->getFilePos() : null;
0 ignored issues
show
Deprecated Code introduced by
The method PhpParser\Comment::getFilePos() has been deprecated with message: Use getStartFilePos() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
132
        $this->docblock_end = $doc_comment ? $this->file_start : null;
133
        $this->docblock_start_line_number = $doc_comment ? $doc_comment->getLine() : null;
0 ignored issues
show
Deprecated Code introduced by
The method PhpParser\Comment::getLine() has been deprecated with message: Use getStartLine() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
134
135
        $this->preview_start = $this->docblock_start ?: $this->file_start;
136
137
        $this->raw_line_number = $stmt->getLine();
138
    }
139
140
    /**
141
     * @param int $line
142
     *
143
     * @return void
144
     */
145
    public function setCommentLine($line)
146
    {
147
        $this->docblock_line_number = $line;
148
    }
149
150
    /**
151
     * @psalm-suppress MixedArrayAccess
152
     *
153
     * @return void
154
     */
155
    private function calculateRealLocation()
156
    {
157
        if ($this->have_recalculated) {
158
            return;
159
        }
160
161
        $this->have_recalculated = true;
162
163
        $this->selection_start = $this->file_start;
164
        $this->selection_end = $this->file_end + 1;
165
166
        $project_analyzer = Internal\Analyzer\ProjectAnalyzer::getInstance();
167
168
        $codebase = $project_analyzer->getCodebase();
169
170
        $file_contents = $codebase->getFileContents($this->file_path);
171
172
        $file_length = strlen($file_contents);
173
174
        $search_limit = $this->single_line ? $this->selection_start : $this->selection_end;
175
176
        if ($search_limit <= $file_length) {
177
            $preview_end = strpos(
178
                $file_contents,
179
                "\n",
180
                $search_limit
181
            );
182
        } else {
183
            $preview_end = false;
184
        }
185
186
        // if the string didn't contain a newline
187
        if ($preview_end === false) {
188
            $preview_end = $this->selection_end;
189
        }
190
191
        $this->preview_end = $preview_end;
192
193
        if ($this->docblock_line_number &&
194
            $this->docblock_start_line_number &&
195
            $this->preview_start < $this->selection_start
196
        ) {
197
            $preview_lines = explode(
198
                "\n",
199
                substr(
200
                    $file_contents,
201
                    $this->preview_start,
202
                    $this->selection_start - $this->preview_start - 1
203
                )
204
            );
205
206
            $preview_offset = 0;
207
208
            $comment_line_offset = $this->docblock_line_number - $this->docblock_start_line_number;
209
210
            for ($i = 0; $i < $comment_line_offset; ++$i) {
211
                $preview_offset += strlen($preview_lines[$i]) + 1;
212
            }
213
214
            if (!isset($preview_lines[$i])) {
215
                throw new \Exception('Should have offset');
216
            }
217
218
            $key_line = $preview_lines[$i];
219
220
            $indentation = (int)strpos($key_line, '@');
221
222
            $key_line = trim(preg_replace('@\**/\s*@', '', substr($key_line, $indentation)));
223
224
            $this->selection_start = $preview_offset + $indentation + $this->preview_start;
225
            $this->selection_end = $this->selection_start + strlen($key_line);
226
        }
227
228
        if ($this->regex_type !== null) {
229
            switch ($this->regex_type) {
230
                case self::VAR_TYPE:
231
                    $regex = '/@(psalm-)?var[ \t]+' . CommentAnalyzer::TYPE_REGEX . '/';
232
                    $match_offset = 2;
233
                    break;
234
235
                case self::FUNCTION_RETURN_TYPE:
236
                    $regex = '/\\:\s+(\\??\s*[A-Za-z0-9_\\\\\[\]]+)/';
237
                    $match_offset = 1;
238
                    break;
239
240
                case self::FUNCTION_PARAM_TYPE:
241
                    $regex = '/^(\\??\s*[A-Za-z0-9_\\\\\[\]]+)\s/';
242
                    $match_offset = 1;
243
                    break;
244
245
                case self::FUNCTION_PHPDOC_RETURN_TYPE:
246
                    $regex = '/@(psalm-)?return[ \t]+' . CommentAnalyzer::TYPE_REGEX . '/';
247
                    $match_offset = 2;
248
                    break;
249
250
                case self::FUNCTION_PHPDOC_METHOD:
251
                    $regex = '/@(psalm-)method[ \t]+.*/';
252
                    $match_offset = 2;
253
                    break;
254
255
                case self::FUNCTION_PHPDOC_PARAM_TYPE:
256
                    $regex = '/@(psalm-)?param[ \t]+' . CommentAnalyzer::TYPE_REGEX . '/';
257
                    $match_offset = 2;
258
                    break;
259
260
                case self::FUNCTION_PARAM_VAR:
261
                    $regex = '/(\$[^ ]*)/';
262
                    $match_offset = 1;
263
                    break;
264
265
                case self::CATCH_VAR:
266
                    $regex = '/(\$[^ ^\)]*)/';
267
                    $match_offset = 1;
268
                    break;
269
270
                default:
271
                    throw new \UnexpectedValueException('Unrecognised regex type ' . $this->regex_type);
272
            }
273
274
            $preview_snippet = substr(
275
                $file_contents,
276
                $this->selection_start,
277
                $this->selection_end - $this->selection_start
278
            );
279
280
            if ($this->text) {
281
                $regex = '/(' . str_replace(',', ',[ ]*', preg_quote($this->text, '/')) . ')/';
282
                $match_offset = 1;
283
            }
284
285
            if (preg_match($regex, $preview_snippet, $matches, PREG_OFFSET_CAPTURE)) {
286
                $this->selection_start = $this->selection_start + (int)$matches[$match_offset][1];
287
                $this->selection_end = $this->selection_start + strlen((string)$matches[$match_offset][0]);
288
            }
289
        }
290
291
        // reset preview start to beginning of line
292
        $this->preview_start = (int)strrpos(
293
            $file_contents,
294
            "\n",
295
            min($this->preview_start, $this->selection_start) - strlen($file_contents)
296
        ) + 1;
297
298
        $this->selection_start = max($this->preview_start, $this->selection_start);
299
        $this->selection_end = min($this->preview_end, $this->selection_end);
300
301
        if ($this->preview_end - $this->selection_end > 200) {
302
            $this->preview_end = (int)strrpos(
303
                $file_contents,
304
                "\n",
305
                $this->selection_end + 200 - strlen($file_contents)
306
            );
307
308
            // if the line is over 200 characters long
309
            if ($this->preview_end < $this->selection_end) {
310
                $this->preview_end = $this->selection_end + 50;
311
            }
312
        }
313
314
        $this->snippet = substr($file_contents, $this->preview_start, $this->preview_end - $this->preview_start);
315
        $this->text = substr($file_contents, $this->selection_start, $this->selection_end - $this->selection_start);
316
317
        // reset preview start to beginning of line
318
        $this->column_from = $this->selection_start -
319
            (int)strrpos($file_contents, "\n", $this->selection_start - strlen($file_contents));
320
321
        $newlines = substr_count($this->text, "\n");
322
323
        if ($newlines) {
324
            $this->column_to = $this->selection_end -
325
                (int)strrpos($file_contents, "\n", $this->selection_end - strlen($file_contents));
326
        } else {
327
            $this->column_to = $this->column_from + strlen($this->text);
328
        }
329
330
        $this->end_line_number = $this->getLineNumber() + $newlines;
331
    }
332
333
    /**
334
     * @return int
335
     */
336
    public function getLineNumber()
337
    {
338
        return $this->docblock_line_number ?: $this->raw_line_number;
339
    }
340
341
    /**
342
     * @return int
343
     */
344
    public function getEndLineNumber()
345
    {
346
        $this->calculateRealLocation();
347
348
        return $this->end_line_number;
349
    }
350
351
    /**
352
     * @return string
353
     */
354
    public function getSnippet()
355
    {
356
        $this->calculateRealLocation();
357
358
        return $this->snippet;
359
    }
360
361
    /**
362
     * @return string
363
     */
364
    public function getSelectedText()
365
    {
366
        $this->calculateRealLocation();
367
368
        return (string)$this->text;
369
    }
370
371
    /**
372
     * @return int
373
     */
374
    public function getColumn()
375
    {
376
        $this->calculateRealLocation();
377
378
        return $this->column_from;
379
    }
380
381
    /**
382
     * @return int
383
     */
384
    public function getEndColumn()
385
    {
386
        $this->calculateRealLocation();
387
388
        return $this->column_to;
389
    }
390
391
    /**
392
     * @return array{0: int, 1: int}
0 ignored issues
show
The doc-type array{0: could not be parsed: Unknown type name "array{0:" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
393
     */
394
    public function getSelectionBounds()
395
    {
396
        $this->calculateRealLocation();
397
398
        return [$this->selection_start, $this->selection_end];
399
    }
400
401
    /**
402
     * @return array{0: int, 1: int}
0 ignored issues
show
The doc-type array{0: could not be parsed: Unknown type name "array{0:" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
403
     */
404
    public function getSnippetBounds()
405
    {
406
        $this->calculateRealLocation();
407
408
        return [$this->preview_start, $this->preview_end];
409
    }
410
411
    /**
412
     * @return string
413
     */
414
    public function getHash()
415
    {
416
        return (string) $this->file_start;
417
    }
418
419
    public function getShortSummary() : string
420
    {
421
        return $this->file_name . ':' . $this->getLineNumber() . ':' . $this->getColumn();
422
    }
423
}
424