Issues (1844)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  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.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  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.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  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.
  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.
  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.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  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.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  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.
  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.
  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.
  Header Injection
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.

class/MediaUploader.php (1 issue)

1
<?php declare(strict_types=1);
2
3
namespace XoopsModules\Xhelp;
4
5
/*
6
 * You may not change or alter any portion of this comment or credits
7
 * of supporting developers from this source code or any supporting source code
8
 * which is considered copyrighted (c) material of the original comment or credit authors.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
 */
14
15
/**
16
 * @copyright    XOOPS Project (https://xoops.org)
17
 * @license      GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
18
 * @author       XOOPS Development Team, Kazumi Ono (AKA onokazu)
19
 */
20
21
/**
22
 * !
23
 * Example
24
 *
25
 * require_once __DIR__ . '/uploader.php';
26
 * $allowed_mimetypes = array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/x-png');
27
 * $maxfilesize = 50000;
28
 * $maxfilewidth = 120;
29
 * $maxfileheight = 120;
30
 * $uploader = new Xhelp\MediaUploader('/home/xoops/uploads', $allowed_mimetypes, $maxfilesize, $maxfilewidth, $maxfileheight);
31
 * if ($uploader->fetchMedia($_POST['uploade_file_name'])) {
32
 * if (!$uploader->upload()) {
33
 * echo $uploader->getErrors();
34
 * } else {
35
 * echo '<h4>File uploaded successfully!</h4>'
36
 * echo 'Saved as: ' . $uploader->getSavedFileName() . '<br>';
37
 * echo 'Full path: ' . $uploader->getSavedDestination();
38
 * }
39
 * } else {
40
 * echo $uploader->getErrors();
41
 * }
42
 */
43
44
/**
45
 * Upload Media files
46
 *
47
 * Example of usage:
48
 * <code>
49
 * require_once __DIR__ . '/uploader.php';
50
 * $allowed_mimetypes = array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/x-png');
51
 * $maxfilesize = 50000;
52
 * $maxfilewidth = 120;
53
 * $maxfileheight = 120;
54
 * $uploader = new Xhelp\MediaUploader('/home/xoops/uploads', $allowed_mimetypes, $maxfilesize, $maxfilewidth, $maxfileheight);
55
 * if ($uploader->fetchMedia($_POST['uploade_file_name'])) {
56
 *            if (!$uploader->upload()) {
57
 *               echo $uploader->getErrors();
58
 *            } else {
59
 *               echo '<h4>File uploaded successfully!</h4>'
60
 *               echo 'Saved as: ' . $uploader->getSavedFileName() . '<br>';
61
 *               echo 'Full path: ' . $uploader->getSavedDestination();
62
 *            }
63
 * } else {
64
 *            echo $uploader->getErrors();
65
 * }
66
 * </code>
67
 *
68
 * @author        Kazumi Ono <[email protected]>
69
 * @copyright (c) 2000-2003 XOOPS Project (https://xoops.org)
70
 */
71
72
/**
73
 * class MediaUploader
74
 */
75
class MediaUploader
76
{
77
    public $mediaName;
78
    public $mediaType;
79
    public $mediaSize;
80
    public $mediaTmpName;
81
    public $mediaError;
82
    public $uploadDir        = '';
83
    public $allowedMimeTypes = [];
84
    public $maxFileSize      = 0;
85
    public $maxWidth;
86
    public $maxHeight;
87
    public $targetFileName;
88
    public $prefix;
89
    public $ext;
90
    public $dimension;
91
    public $errors           = [];
92
    public $savedDestination;
93
    public $savedFileName;
94
    /**
95
     * No admin check for uploads
96
     */
97
    public $noadmin_sizecheck;
98
99
    /**
100
     * Constructor
101
     *
102
     * @param string    $uploadDir
103
     * @param array|int $allowedMimeTypes
104
     * @param int       $maxFileSize
105
     * @param int       $maxWidth
106
     * @param int       $maxHeight
107
     * @internal param int $cmodvalue
108
     */
109
    public function __construct(string $uploadDir, $allowedMimeTypes, int $maxFileSize, int $maxWidth = 0, int $maxHeight = 0)
110
    {
111
        if (\is_array($allowedMimeTypes)) {
112
            $this->allowedMimeTypes = &$allowedMimeTypes;
113
        }
114
        $this->uploadDir   = $uploadDir;
115
        $this->maxFileSize = $maxFileSize;
116
        if (null !== $maxWidth) {
117
            $this->maxWidth = $maxWidth;
118
        }
119
        if (null !== $maxHeight) {
120
            $this->maxHeight = $maxHeight;
121
        }
122
    }
123
124
    /**
125
     * @param string $value
126
     */
127
    public function noAdminSizeCheck(string $value): void
128
    {
129
        $this->noadmin_sizecheck = $value;
130
    }
131
132
    /**
133
     * Fetch the uploaded file
134
     *
135
     * @param string $media_name Name of the file field
136
     * @param null   $index      Index of the file (if more than one uploaded under that name)
137
     * @return bool
138
     */
139
    public function fetchMedia(string $media_name, $index = null): bool
140
    {
141
        global $_FILES;
142
143
        if (!isset($_FILES[$media_name])) {
144
            $this->setErrors('You either did not choose a file to upload or the server has insufficient read/writes to upload this file.!');
145
146
            return false;
147
        }
148
149
        if (\is_array($_FILES[$media_name]['name']) && null !== $index) {
150
            $index              = (int)$index;
151
            $this->mediaName    = @get_magic_quotes_gpc() ? \stripslashes($_FILES[$media_name]['name'][$index]) : $_FILES[$media_name]['name'][$index];
152
            $this->mediaType    = $_FILES[$media_name]['type'][$index];
153
            $this->mediaSize    = $_FILES[$media_name]['size'][$index];
154
            $this->mediaTmpName = $_FILES[$media_name]['tmp_name'][$index];
155
            $this->mediaError   = !empty($_FILES[$media_name]['error'][$index]) ? $_FILES[$media_name]['errir'][$index] : 0;
156
        } else {
157
            $media_name         = @$_FILES[$media_name];
158
            $this->mediaName    = @get_magic_quotes_gpc() ? \stripslashes($media_name['name']) : $media_name['name'];
159
            $this->mediaName    = $media_name['name'];
160
            $this->mediaType    = $media_name['type'];
161
            $this->mediaSize    = $media_name['size'];
162
            $this->mediaTmpName = $media_name['tmp_name'];
163
            $this->mediaError   = !empty($media_name['error']) ? $media_name['error'] : 0;
164
        }
165
        $this->dimension = \getimagesize($this->mediaTmpName);
166
167
        $this->errors = [];
168
169
        if ((int)$this->mediaSize < 0) {
170
            $this->setErrors('Invalid File Size');
171
172
            return false;
173
        }
174
        if ('' === $this->mediaName) {
175
            $this->setErrors('Filename Is Empty');
176
177
            return false;
178
        }
179
180
        if ('none' === $this->mediaTmpName) {
181
            $this->setErrors('No file uploaded, this is a error');
182
183
            return false;
184
        }
185
186
        if (!$this->checkMaxFileSize()) {
187
            $this->setErrors(\sprintf('File Size: %u. Maximum Size Allowed: %u', $this->mediaSize, $this->maxFileSize));
188
        }
189
190
        if (\is_array($this->dimension)) {
191
            if (!$this->checkMaxWidth($this->dimension[0])) {
192
                $this->setErrors(\sprintf('File width: %u. Maximum width allowed: %u', $this->dimension[0], $this->maxWidth));
193
            }
194
            if (!$this->checkMaxHeight($this->dimension[1])) {
195
                $this->setErrors(\sprintf('File height: %u. Maximum height allowed: %u', $this->dimension[1], $this->maxHeight));
196
            }
197
        }
198
199
        if (\count($this->errors) > 0) {
200
            return false;
201
        }
202
203
        if (!$this->checkMimeType()) {
204
            $this->setErrors('MIME type not allowed: ' . $this->mediaType);
205
        }
206
207
        if (!\is_uploaded_file($this->mediaTmpName)) {
208
            switch ($this->mediaError) {
209
                case 0: // no error; possible file attack!
210
                    $this->setErrors('There was a problem with your upload. Error: 0');
211
                    break;
212
                case 1: // uploaded file exceeds the upload_max_filesize directive in php.ini
213
                    //if ($this->noAdminSizeCheck)
214
                    //{
215
                    //    return true;
216
                    //}
217
                    $this->setErrors('The file you are trying to upload is too big. Error: 1');
218
                    break;
219
                case 2: // uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form
220
                    $this->setErrors('The file you are trying to upload is too big. Error: 2');
221
                    break;
222
                case 3: // uploaded file was only partially uploaded
223
                    $this->setErrors('The file you are trying upload was only partially uploaded. Error: 3');
224
                    break;
225
                case 4: // no file was uploaded
226
                    $this->setErrors('No file selected for upload. Error: 4');
227
                    break;
228
                default: // a default error, just in case!  :)
229
                    $this->setErrors('No file selected for upload. Error: 5');
230
                    break;
231
            }
232
233
            return false;
234
        }
235
236
        return true;
237
    }
238
239
    /**
240
     * Set the target filename
241
     *
242
     * @param string $value
243
     */
244
    public function setTargetFileName(string $value): void
245
    {
246
        $this->targetFileName = \trim($value);
247
    }
248
249
    /**
250
     * Set the prefix
251
     *
252
     * @param string $value
253
     */
254
    public function setPrefix(string $value): void
255
    {
256
        $this->prefix = \trim($value);
257
    }
258
259
    /**
260
     * Get the uploaded filename
261
     *
262
     * @return string
263
     */
264
    public function getMediaName(): string
265
    {
266
        return $this->mediaName;
267
    }
268
269
    /**
270
     * Get the type of the uploaded file
271
     *
272
     * @return string
273
     */
274
    public function getMediaType(): string
275
    {
276
        return $this->mediaType;
277
    }
278
279
    /**
280
     * Get the size of the uploaded file
281
     *
282
     * @return int
283
     */
284
    public function getMediaSize(): int
285
    {
286
        return $this->mediaSize;
287
    }
288
289
    /**
290
     * Get the temporary name that the uploaded file was stored under
291
     *
292
     * @return string
293
     */
294
    public function getMediaTmpName(): string
295
    {
296
        return $this->mediaTmpName;
297
    }
298
299
    /**
300
     * Get the saved filename
301
     *
302
     * @return string
303
     */
304
    public function getSavedFileName(): string
305
    {
306
        return $this->savedFileName;
307
    }
308
309
    /**
310
     * Get the destination the file is saved to
311
     *
312
     * @return string
313
     */
314
    public function getSavedDestination(): string
315
    {
316
        return $this->savedDestination;
317
    }
318
319
    /**
320
     * Check the file and copy it to the destination
321
     *
322
     * @param int $chmod
323
     * @return bool
324
     */
325
    public function upload(int $chmod = 0644): bool
326
    {
327
        if ('' === $this->uploadDir) {
328
            $this->setErrors('Upload directory not set');
329
330
            return false;
331
        }
332
333
        if (!\is_dir($this->uploadDir)) {
334
            $this->setErrors('Failed opening directory: ' . $this->uploadDir);
335
        }
336
337
        if (!\is_writable($this->uploadDir)) {
338
            $this->setErrors('Failed opening directory with write permission: ' . $this->uploadDir);
339
        }
340
341
        if (!$this->checkMaxFileSize()) {
342
            $this->setErrors(\sprintf('File Size: %u. Maximum Size Allowed: %u', $this->mediaSize, $this->maxFileSize));
343
        }
344
345
        if (\is_array($this->dimension)) {
346
            if (!$this->checkMaxWidth($this->dimension[0])) {
347
                $this->setErrors(\sprintf('File width: %u. Maximum width allowed: %u', $this->dimension[0], $this->maxWidth));
348
            }
349
            if (!$this->checkMaxHeight($this->dimension[1])) {
350
                $this->setErrors(\sprintf('File height: %u. Maximum height allowed: %u', $this->dimension[1], $this->maxHeight));
351
            }
352
        }
353
354
        if (!$this->checkMimeType()) {
355
            $this->setErrors('MIME type not allowed: ' . $this->mediaType);
356
        }
357
358
        if (!$this->_copyFile($chmod)) {
359
            $this->setErrors('Failed uploading file: ' . $this->mediaName);
360
        }
361
362
        if (\count($this->errors) > 0) {
363
            return false;
364
        }
365
366
        return true;
367
    }
368
369
    /**
370
     * Copy the file to its destination
371
     *
372
     * @param int $chmod
373
     * @return bool
374
     */
375
    public function _copyFile(int $chmod): bool
376
    {
377
        $matched = [];
378
        if (!\preg_match('/\.([a-zA-Z0-9]+)$/', $this->mediaName, $matched)) {
379
            return false;
380
        }
381
        if (null !== $this->targetFileName) {
382
            $this->savedFileName = $this->targetFileName;
383
        } elseif (null !== $this->prefix) {
384
            $this->savedFileName = \uniqid($this->prefix, true) . '.' . \mb_strtolower($matched[1]);
385
        } else {
386
            $this->savedFileName = \mb_strtolower($this->mediaName);
387
        }
388
        $this->savedFileName    = \preg_replace('!\s+!', '_', $this->savedFileName);
389
        $this->savedDestination = $this->uploadDir . $this->savedFileName;
390
        if (\is_file($this->savedDestination) && !!\is_dir($this->savedDestination)) {
391
            $this->setErrors('File ' . $this->mediaName . ' already exists on the server. Please rename this file and try again.<br>');
392
393
            return false;
394
        }
395
        if (!\move_uploaded_file($this->mediaTmpName, $this->savedDestination)) {
396
            return false;
397
        }
398
        @\chmod($this->savedDestination, $chmod);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

398
        /** @scrutinizer ignore-unhandled */ @\chmod($this->savedDestination, $chmod);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
399
400
        return true;
401
    }
402
403
    /**
404
     * Is the file the right size?
405
     *
406
     * @return bool
407
     */
408
    public function checkMaxFileSize(): bool
409
    {
410
        if ($this->noadmin_sizecheck) {
411
            return true;
412
        }
413
        if ($this->mediaSize > $this->maxFileSize) {
414
            return false;
415
        }
416
417
        return true;
418
    }
419
420
    /**
421
     * Is the picture the right width?
422
     *
423
     * @param int $dimension
424
     * @return bool
425
     */
426
    public function checkMaxWidth(int $dimension): bool
427
    {
428
        if (null === $this->maxWidth) {
429
            return true;
430
        }
431
        if ($dimension > $this->maxWidth) {
432
            return false;
433
        }
434
435
        return true;
436
    }
437
438
    /**
439
     * Is the picture the right height?
440
     *
441
     * @param int $dimension
442
     * @return bool
443
     */
444
    public function checkMaxHeight(int $dimension): bool
445
    {
446
        if (null === $this->maxHeight) {
447
            return true;
448
        }
449
        if ($dimension > $this->maxWidth) {
450
            return false;
451
        }
452
453
        return true;
454
    }
455
456
    /**
457
     * Is the file the right Mime type
458
     *
459
     * (is there a right type of mime? ;-)
460
     *
461
     * @return bool
462
     */
463
    public function checkMimeType(): bool
464
    {
465
        if (\count($this->allowedMimeTypes) > 0 && !\in_array($this->mediaType, $this->allowedMimeTypes)) {
466
            return false;
467
        }
468
469
        return true;
470
    }
471
472
    /**
473
     * Add an error
474
     *
475
     * @param string $error
476
     */
477
    public function setErrors(string $error): void
478
    {
479
        $this->errors[] = \trim($error);
480
    }
481
482
    /**
483
     * Get generated errors
484
     *
485
     * @param bool $ashtml Format using HTML?
486
     * @return array |string    Array of array messages OR HTML string
487
     */
488
    public function &getErrors(bool $ashtml = true)
489
    {
490
        if (!$ashtml) {
491
            return $this->errors;
492
        }
493
494
        $ret = '';
495
        if (\count($this->errors) > 0) {
496
            $ret = '<h4>Errors Returned While Uploading</h4>';
497
            foreach ($this->errors as $error) {
498
                $ret .= $error . '<br>';
499
            }
500
        }
501
502
        return $ret;
503
    }
504
}
505