This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved |
||
4 | * Copyright (c) Enalean, 2015. All Rights Reserved. |
||
5 | * |
||
6 | * This file is a part of Tuleap. |
||
7 | * |
||
8 | * Tuleap is free software; you can redistribute it and/or modify |
||
9 | * it under the terms of the GNU General Public License as published by |
||
10 | * the Free Software Foundation; either version 2 of the License, or |
||
11 | * (at your option) any later version. |
||
12 | * |
||
13 | * Tuleap is distributed in the hope that it will be useful, |
||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
16 | * GNU General Public License for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU General Public License |
||
19 | * along with Tuleap. If not, see <http://www.gnu.org/licenses/>. |
||
20 | */ |
||
21 | |||
22 | require_once('common/include/Error.class.php'); |
||
23 | require_once('FRSReleaseFactory.class.php'); |
||
24 | require_once('common/include/Codendi_HTTP_Download.php'); |
||
25 | |||
26 | class FRSFile extends Error { |
||
27 | |||
28 | const EVT_CREATE = 301; |
||
29 | const EVT_UPDATE = 302; |
||
30 | const EVT_DELETE = 303; |
||
31 | const EVT_RESTORE = 304; |
||
32 | |||
33 | /** |
||
34 | * This is an arbitrary chosen hard coded limit in order to avoid |
||
35 | * download of files that exceed the php memory_limit |
||
36 | * set to 196M using the standard download mechanism |
||
37 | * which places these files in the memory causing overflows. |
||
38 | * It will use php-pear-HTTP-download instead if it is installed |
||
39 | * on the system. |
||
40 | */ |
||
41 | const HARD_CODED_150MB_MEMORY_LIMIT = 157286400; |
||
42 | |||
43 | /** |
||
44 | * A 8 kb buffer size |
||
45 | */ |
||
46 | const PEAR_BUFFER_SIZE = 8192; |
||
47 | |||
48 | /** |
||
49 | * @var int $file_id the ID of this FRSFile |
||
50 | */ |
||
51 | var $file_id; |
||
52 | /** |
||
53 | * @var String $filename the name of this FRSFile |
||
54 | */ |
||
55 | var $filename; |
||
56 | /** |
||
57 | * @var String $filepath the full path where the file is created |
||
58 | */ |
||
59 | var $filepath; |
||
60 | /** |
||
61 | * @var int $release_id the ID of the release this FRSFile belong to |
||
62 | */ |
||
63 | var $release_id; |
||
64 | /** |
||
65 | * @var int $type_id the ID of the type of this FRSFile |
||
66 | */ |
||
67 | var $type_id; |
||
68 | /** |
||
69 | * @var int $processor_id the ID of the processor to use with this FRSFile |
||
70 | */ |
||
71 | var $processor_id; |
||
72 | /** |
||
73 | * @var int $release_time the ??? of this FRSFile |
||
74 | */ |
||
75 | var $release_time; |
||
76 | /** |
||
77 | * @var int $file_size the size of this FRSFile |
||
78 | */ |
||
79 | var $file_size; |
||
80 | /** |
||
81 | * @var int $post_date the ??? of this FRSFile |
||
82 | */ |
||
83 | var $post_date; |
||
84 | /** |
||
85 | * @var string $status the status of this FRSFile (A=>Active; D=>Deleted) |
||
86 | */ |
||
87 | var $status; |
||
88 | /** |
||
89 | * @var string $computed_md5 hash of the file computed in server side |
||
90 | */ |
||
91 | var $computed_md5; |
||
92 | /** |
||
93 | * @var string $reference_md5 hash of the file submited by user (calculated in client side) |
||
94 | */ |
||
95 | var $reference_md5; |
||
96 | /** |
||
97 | * @var integer $user_id id of user that created the file |
||
98 | */ |
||
99 | var $user_id; |
||
100 | /** |
||
101 | * @var string $file_location the full path of this FRSFile |
||
102 | */ |
||
103 | var $file_location; |
||
104 | |||
105 | /** |
||
106 | * |
||
107 | * @var string comment/description of the file |
||
108 | */ |
||
109 | private $comment; |
||
110 | |||
111 | /** |
||
112 | * @var FRSRelease $release The release the file belongs to |
||
113 | */ |
||
114 | protected $release; |
||
115 | |||
116 | function FRSFile($data_array = null) { |
||
117 | $this->file_id = null; |
||
118 | $this->filename = null; |
||
119 | $this->filepath = null; |
||
120 | $this->release_id = null; |
||
121 | $this->type_id = null; |
||
122 | $this->processor_id = null; |
||
123 | $this->release_time = null; |
||
124 | $this->file_size = null; |
||
125 | $this->post_date = null; |
||
126 | $this->status = null; |
||
127 | $this->computed_md5 = null; |
||
128 | $this->reference_md5 = null; |
||
129 | $this->user_id = null; |
||
130 | $this->file_location = null; |
||
131 | $this->comment = null; |
||
132 | |||
133 | if ($data_array) { |
||
134 | $this->initFromArray($data_array); |
||
135 | } |
||
136 | } |
||
137 | |||
138 | function getFileID() { |
||
139 | return $this->file_id; |
||
140 | } |
||
141 | |||
142 | function setFileID($file_id) { |
||
143 | $this->file_id = (int) $file_id; |
||
144 | } |
||
145 | |||
146 | function getFileName() { |
||
147 | return $this->filename; |
||
148 | } |
||
149 | |||
150 | function setFileName($filename) { |
||
151 | $this->filename = $filename; |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * Obtain the name of the file as stored in filesystem |
||
156 | * Old files are stored in the filesystem as uploaded by the user |
||
157 | * In that case filepath == NULL then the returned value is filename |
||
158 | * |
||
159 | * @return String |
||
160 | */ |
||
161 | function getFilePath() { |
||
162 | if ($this->filepath == null) { |
||
163 | return $this->filename; |
||
164 | } else { |
||
165 | return $this->filepath; |
||
166 | } |
||
167 | } |
||
168 | |||
169 | function setFilePath($filepath) { |
||
170 | $this->filepath = $filepath; |
||
171 | } |
||
172 | |||
173 | function getReleaseID() { |
||
174 | return $this->release_id; |
||
175 | } |
||
176 | |||
177 | function setReleaseID($release_id) { |
||
178 | $this->release_id = (int) $release_id; |
||
179 | } |
||
180 | |||
181 | function getTypeID() { |
||
182 | return $this->type_id; |
||
183 | } |
||
184 | |||
185 | function setTypeID($type_id) { |
||
186 | $this->type_id = (int) $type_id; |
||
187 | } |
||
188 | |||
189 | function getProcessorID() { |
||
190 | return $this->processor_id; |
||
191 | } |
||
192 | |||
193 | function setProcessorID($processor_id) { |
||
194 | $this->processor_id = (int) $processor_id; |
||
195 | } |
||
196 | |||
197 | function getReleaseTime() { |
||
198 | return $this->release_time; |
||
199 | } |
||
200 | |||
201 | function setReleaseTime($release_time) { |
||
202 | $this->release_time = (int) $release_time; |
||
203 | } |
||
204 | |||
205 | function setFileLocation($location) { |
||
206 | $this->file_location = $location; |
||
207 | } |
||
208 | |||
209 | /** |
||
210 | * Returns the location of the file on the server |
||
211 | * |
||
212 | * @global $GLOBALS['ftp_frs_dir_prefix'] |
||
213 | * @return string the location of this file on the server |
||
214 | */ |
||
215 | function getFileLocation() { |
||
216 | if ($this->file_location == null) { |
||
217 | $group = $this->getGroup(); |
||
218 | $group_unix_name = $group->getUnixName(false); |
||
219 | $basename = $this->getFilePath(); |
||
220 | $this->file_location = $GLOBALS['ftp_frs_dir_prefix'].'/'.$group_unix_name.'/'.$basename; |
||
221 | } |
||
222 | return $this->file_location; |
||
223 | } |
||
224 | |||
225 | function getFileSize() { |
||
226 | if ($this->file_size == null) { |
||
227 | $file_location = $this->getFileLocation(); |
||
228 | // Use file_utils to provide a workaround for the 2 GB limitation |
||
229 | $this->file_size = file_utils_get_size($file_location); |
||
230 | } |
||
231 | return $this->file_size; |
||
232 | } |
||
233 | |||
234 | function setFileSize($file_size) { |
||
235 | $this->file_size = $file_size; |
||
236 | } |
||
237 | |||
238 | static function convertBytesToKbytes($size_in_bytes, $decimals_precision = 0) { |
||
239 | $size_in_kbytes = $size_in_bytes / 1024; |
||
240 | |||
241 | $decimal_separator = $GLOBALS['Language']->getText('system','decimal_separator'); |
||
242 | $thousand_separator = $GLOBALS['Language']->getText('system','thousand_separator'); |
||
243 | // because I don't know how to specify a space in a .tab file |
||
244 | if ($thousand_separator == "' '") { |
||
245 | $thousand_separator = ' '; |
||
246 | } |
||
247 | return number_format($size_in_kbytes, $decimals_precision, $decimal_separator, $thousand_separator); |
||
248 | } |
||
249 | |||
250 | function getDisplayFileSize() { |
||
251 | $decimals_precision = 0; |
||
252 | if ($this->getFileSize() < 1024) { |
||
253 | $decimals_precision = 2; |
||
254 | } |
||
255 | return $this->convertBytesToKbytes($this->getFileSize(), $decimals_precision); |
||
256 | } |
||
257 | |||
258 | function getPostDate() { |
||
259 | return $this->post_date; |
||
260 | } |
||
261 | |||
262 | function setPostDate($post_date) { |
||
263 | $this->post_date = (int) $post_date; |
||
264 | } |
||
265 | |||
266 | function getStatus() { |
||
267 | return $this->status; |
||
268 | } |
||
269 | |||
270 | function setStatus($status) { |
||
271 | $this->status = $status; |
||
272 | } |
||
273 | |||
274 | function isActive() { |
||
275 | return ($this->status == 'A'); |
||
276 | } |
||
277 | |||
278 | function isDeleted() { |
||
279 | return ($this->status == 'D'); |
||
280 | } |
||
281 | |||
282 | function setComputedMd5($computedMd5) { |
||
283 | $this->computed_md5 = $computedMd5; |
||
284 | } |
||
285 | |||
286 | function getComputedMd5() { |
||
287 | return $this->computed_md5; |
||
288 | } |
||
289 | |||
290 | function setReferenceMd5($referenceMd5) { |
||
291 | $this->reference_md5 = $referenceMd5; |
||
292 | } |
||
293 | |||
294 | function getReferenceMd5() { |
||
295 | return $this->reference_md5; |
||
296 | } |
||
297 | |||
298 | function setUserID($userId) { |
||
299 | $this->user_id = $userId; |
||
300 | } |
||
301 | |||
302 | function getUserID() { |
||
303 | return $this->user_id; |
||
304 | } |
||
305 | |||
306 | function setRelease($release) { |
||
307 | $this->release = $release; |
||
308 | $this->release_id = $release->getReleaseID(); |
||
309 | } |
||
310 | |||
311 | function getRelease() { |
||
312 | return $this->release; |
||
313 | } |
||
314 | |||
315 | public function getComment() { |
||
316 | return $this->comment; |
||
317 | } |
||
318 | |||
319 | public function setComment($comment) { |
||
320 | $this->comment = $comment; |
||
321 | } |
||
322 | |||
323 | function initFromArray($array) { |
||
324 | if (isset($array['file_id'])) $this->setFileID($array['file_id']); |
||
325 | if (isset($array['filename'])) $this->setFileName($array['filename']); |
||
326 | if (isset($array['filepath'])) $this->setFilePath($array['filepath']); |
||
327 | if (isset($array['release_id'])) $this->setReleaseID($array['release_id']); |
||
328 | if (isset($array['type_id'])) $this->setTypeID($array['type_id']); |
||
329 | if (isset($array['processor_id'])) $this->setProcessorID($array['processor_id']); |
||
330 | if (isset($array['release_time'])) $this->setReleaseTime($array['release_time']); |
||
331 | if (isset($array['file_size'])) $this->setFileSize($array['file_size']); |
||
332 | if (isset($array['post_date'])) $this->setPostDate($array['post_date']); |
||
333 | if (isset($array['status'])) $this->setStatus($array['status']); |
||
334 | if (isset($array['computed_md5'])) $this->setComputedMd5($array['computed_md5']); |
||
335 | if (isset($array['reference_md5'])) $this->setReferenceMd5($array['reference_md5']); |
||
336 | if (isset($array['user_id'])) $this->setUserID($array['user_id']); |
||
337 | if (isset($array['comment'])) $this->setComment($array['comment']); |
||
338 | } |
||
339 | |||
340 | function toArray() { |
||
341 | $array = array(); |
||
342 | $array['file_id'] = $this->getFileID(); |
||
343 | $array['filename'] = $this->getFileName(); |
||
344 | $array['filepath'] = $this->getFilePath(); |
||
345 | $array['release_id'] = $this->getReleaseID(); |
||
346 | $array['type_id'] = $this->getTypeID(); |
||
347 | $array['processor_id'] = $this->getProcessorID(); |
||
348 | $array['release_time'] = $this->getReleaseTime(); |
||
349 | $array['file_location'] = $this->getFileLocation(); |
||
350 | $array['file_size'] = $this->getFileSize(); |
||
351 | $array['post_date'] = $this->getPostDate(); |
||
352 | $array['status'] = $this->getStatus(); |
||
353 | $array['computed_md5'] = $this->getComputedMd5(); |
||
354 | $array['reference_md5'] = $this->getReferenceMd5(); |
||
355 | $array['user_id'] = $this->getUserID(); |
||
356 | $array['comment'] = $this->getComment(); |
||
357 | |||
358 | return $array; |
||
359 | } |
||
360 | |||
361 | var $dao; |
||
362 | |||
363 | function &_getFRSFileDao() { |
||
364 | if (!$this->dao) { |
||
365 | $this->dao = new FRSFileDao(CodendiDataAccess::instance()); |
||
366 | } |
||
367 | return $this->dao; |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * Determine if the file exists really on the server or not |
||
372 | * |
||
373 | * @return boolean true if the file exists on the server, false otherwise |
||
374 | */ |
||
375 | function fileExists() { |
||
376 | return file_exists($this->getFileLocation()); |
||
377 | } |
||
378 | |||
379 | /** |
||
380 | * Get the Package ID of this File |
||
381 | * |
||
382 | * @return int the packahe ID of this file |
||
383 | */ |
||
384 | function getPackageID() { |
||
385 | // retrieve the release the file belongs to |
||
386 | $release_id = $this->getReleaseID(); |
||
387 | $release_fact = new FRSReleaseFactory(); |
||
388 | $release =& $release_fact->getFRSReleaseFromDb($release_id); |
||
389 | $package_id = $release->getPackageID(); |
||
390 | return $package_id; |
||
391 | } |
||
392 | |||
393 | |||
394 | /** |
||
395 | * Get the Group (the project) of this File |
||
396 | * |
||
397 | * @return Object{Group} the group the file belongs to |
||
0 ignored issues
–
show
|
|||
398 | */ |
||
399 | function getGroup() { |
||
400 | $pm = ProjectManager::instance(); |
||
401 | // retrieve the release the file belongs to |
||
402 | $release_id = $this->getReleaseID(); |
||
403 | $release_fact = new FRSReleaseFactory(); |
||
404 | $release = $release_fact->getFRSReleaseFromDb($release_id, null, null, FRSReleaseDao::INCLUDE_DELETED); |
||
405 | $group_id = $release->getGroupID(); |
||
406 | $group = $pm->getProject($group_id); |
||
407 | return $group; |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * Returns the content of the file, in a raw resource |
||
412 | * |
||
413 | * +2GB safe |
||
414 | * |
||
415 | * @return mixed the content of the file |
||
416 | */ |
||
417 | function getContent($offset=0, $size=-1) { |
||
418 | if ($size == -1) { |
||
419 | $size = $this->getFileSize(); |
||
420 | } |
||
421 | $path = PHP_BigFile::stream(realpath($this->getFileLocation())); |
||
422 | return file_get_contents($path, false, NULL, $offset, $size); |
||
423 | } |
||
424 | |||
425 | /** |
||
426 | * Log the download of the file in the log system |
||
427 | * |
||
428 | * Only log one download attempt per file/user/hour. Driven by SOAP:getFileChunk |
||
429 | * in order to reduce the amount of download attempt logged. |
||
430 | * |
||
431 | * @param int $user_id the user that download the file (if 0, the current user will be taken) |
||
432 | * @return boolean true if there is no error, false otherwise |
||
433 | */ |
||
434 | function LogDownload($user_id = 0) { |
||
435 | if ($user_id == 0) { |
||
436 | $user_id = UserManager::instance()->getCurrentUser()->getId(); |
||
437 | } |
||
438 | $time = $_SERVER['REQUEST_TIME'] - 3600; |
||
439 | $dao = $this->_getFrsFileDao(); |
||
440 | if (!$dao->existsDownloadLogSince($this->getFileID(), $user_id, $time)) { |
||
441 | return $dao->logDownload($this, $user_id); |
||
442 | } |
||
443 | return true; |
||
444 | } |
||
445 | |||
446 | /** |
||
447 | * userCanDownload : determine if the user can download the file or not |
||
448 | * |
||
449 | * WARNING : for the moment, user can download the file if the user can view the package and can view the release the file belongs to. |
||
450 | * |
||
451 | * @param int $user_id the ID of the user. If $user_id is 0, then we take the current user. |
||
452 | * @return boolean true if the user has permissions to download the file, false otherwise |
||
453 | */ |
||
454 | function userCanDownload($user_id = 0) { |
||
455 | if ($user_id == 0) { |
||
456 | $user_id = user_getid(); |
||
457 | } |
||
458 | |||
459 | $user = UserManager::instance()->getUserById($user_id); |
||
460 | if ($user) { |
||
461 | if ($user->isSuperUser()) { |
||
462 | return true; |
||
463 | } |
||
464 | } |
||
465 | |||
466 | $user_can_download = false; |
||
467 | if (! $this->isDeleted()) { |
||
468 | $group = $this->getGroup(); |
||
469 | $group_id = $group->getID(); |
||
470 | if (permission_exist('RELEASE_READ', $this->getReleaseID())) { |
||
471 | if (permission_is_authorized('RELEASE_READ',$this->getReleaseID(),$user_id,$group_id)) { |
||
472 | $user_can_download = true; |
||
473 | } |
||
474 | } else if (permission_is_authorized('PACKAGE_READ',$this->getPackageID(),$user_id,$group_id)) { |
||
475 | $user_can_download = true; |
||
476 | } |
||
477 | } |
||
478 | return $user_can_download; |
||
0 ignored issues
–
show
|
|||
479 | } |
||
480 | |||
481 | /** |
||
482 | * download : download the file, i.e. print it to stdout |
||
483 | * |
||
484 | * WARNING : this function does not check permissions, nor does it log the download |
||
485 | * |
||
486 | * @return boolean true if the user has permissions to download the file, false otherwise |
||
487 | */ |
||
488 | function download() { |
||
489 | $file_location = $this->getFileLocation(); |
||
490 | if ($this->fileSizeExceedsMemoryLimit() && $this->phpPearHttpDownloadExtensionIsInstalled()) { |
||
491 | return !PEAR::isError(Codendi_HTTP_Download::staticSend(array( |
||
492 | 'file' => $this->getFileLocation(), |
||
493 | 'cache' => false, |
||
494 | 'contentdisposition' => array(HTTP_DOWNLOAD_ATTACHMENT, basename($this->getFileName())), |
||
495 | 'buffersize' => self::PEAR_BUFFER_SIZE, |
||
496 | ) |
||
497 | )); |
||
498 | } else { //old school to be removed in 4.2 |
||
499 | $file_size = $this->getFileSize(); |
||
500 | |||
501 | // Make sure this URL is not cached anywhere otherwise download |
||
502 | // would be wrong |
||
503 | header('Expires: Mon, 26 Nov 1962 00:00:00 GMT'); |
||
504 | header('Pragma: private'); |
||
505 | header('Cache-control: private, must-revalidate'); |
||
506 | |||
507 | header("Content-Type: application/octet-stream"); |
||
508 | header('Content-Disposition: attachment; filename="'. basename($this->getFileName()) .'"'); |
||
509 | if ($file_size > 0){ |
||
510 | header("Content-Length: $file_size"); |
||
511 | } |
||
512 | header("Content-Transfer-Encoding: binary\n"); |
||
513 | |||
514 | //reset time limit for big files |
||
515 | set_time_limit(0); |
||
516 | flush(); |
||
517 | |||
518 | // Now transfer the file to the client |
||
519 | |||
520 | // Check the 2 GB limit (2^31 -1) |
||
521 | if ($file_size > 2147483647) { |
||
522 | if ($fp=popen('/bin/cat "' . escapeshellarg($file_location) . '"',"rb")) { |
||
523 | $blocksize = (2 << 20); //2M chunks |
||
524 | while(!feof($fp)) { |
||
525 | print(fread($fp, $blocksize)); |
||
526 | } |
||
527 | flush(); |
||
528 | pclose($fp); |
||
529 | } else return false; |
||
530 | } else if (readfile($file_location) == false) { |
||
0 ignored issues
–
show
|
|||
531 | return false; |
||
532 | } |
||
533 | |||
534 | return true; |
||
535 | } |
||
536 | } |
||
537 | |||
538 | private function fileSizeExceedsMemoryLimit() { |
||
539 | return $this->getFileSize() > self::HARD_CODED_150MB_MEMORY_LIMIT; |
||
540 | } |
||
541 | |||
542 | private function phpPearHttpDownloadExtensionIsInstalled() { |
||
543 | return class_exists('Codendi_HTTP_Download'); |
||
544 | } |
||
545 | |||
546 | /** |
||
547 | * Returns the HTML content for tooltip when hover a reference with the nature file |
||
548 | * @returns string HTML content for file tooltip |
||
549 | */ |
||
550 | function getReferenceTooltip() { |
||
551 | $tooltip = ''; |
||
552 | $rf = new FRSReleaseFactory(); |
||
553 | $pf = new FRSPackageFactory(); |
||
554 | $release_id = $this->getReleaseID(); |
||
555 | $release = $rf->getFRSReleaseFromDb($release_id); |
||
556 | $package_id = $release->getPackageID(); |
||
557 | $package = $pf->getFRSPackageFromDb($package_id); |
||
558 | $tooltip .= '<table>'; |
||
559 | $tooltip .= ' <tr>'; |
||
560 | $tooltip .= ' <td><strong>' . $GLOBALS['Language']->getText('file_admin_editreleases', 'filename') . ':</strong></td>'; |
||
561 | $tooltip .= ' <td>'.basename($this->getFileName()).'</td>'; |
||
562 | $tooltip .= ' </tr>'; |
||
563 | $tooltip .= ' <tr>'; |
||
564 | $tooltip .= ' <td><strong>' . $GLOBALS['Language']->getText('file_ref_tooltip', 'package_release') . ':</strong></td>'; |
||
565 | $tooltip .= ' <td>'.$package->getName().' / '.$release->getName().'</td>'; |
||
566 | $tooltip .= ' </tr>'; |
||
567 | $tooltip .= ' <tr>'; |
||
568 | $tooltip .= ' <td><strong>' . $GLOBALS['Language']->getText('file_showfiles', 'date') . ':</strong></td>'; |
||
569 | $tooltip .= ' <td>'.format_date($GLOBALS['Language']->getText('system', 'datefmt_short'), $release->getReleaseDate()).'</td>'; |
||
570 | $tooltip .= ' </tr>'; |
||
571 | $tooltip .= ' <tr>'; |
||
572 | $tooltip .= ' <td><strong>' . $GLOBALS['Language']->getText('file_showfiles', 'size') . ':</strong></td>'; |
||
573 | $tooltip .= ' <td>'.$this->getDisplayFileSize().'</td>'; |
||
574 | $tooltip .= ' </tr>'; |
||
575 | $tooltip .= '</table>'; |
||
576 | return $tooltip; |
||
577 | } |
||
578 | |||
579 | } |
||
580 | |||
581 | ?> |
||
582 |
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.