Issues (388)

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/Uploader.php (2 issues)

1
<?php declare(strict_types=1);
2
3
namespace XoopsModules\Songlist;
4
5
use function mb_substr;
6
7
use const DS;
8
9
/**
10
 * Class SonglistMediaUploader
11
 */
12
class Uploader
13
{
14
    public $mediaName;
15
    public $mediaType;
16
    public $mediaSize;
17
    public $mediaTmpName;
18
    public $mediaError;
19
    public $uploadDir         = '';
20
    public $allowedMimeTypes  = [];
21
    public $allowedExtensions = [];
22
    public $maxFileSize       = 3299999999;
23
    public $maxWidth;
24
    public $maxHeight;
25
    public $targetFileName;
26
    public $prefix;
27
    public $errors            = [];
28
    public $savedDestination;
29
    public $savedFileName;
30
31
    /**
32
     * Constructor
33
     *
34
     * @param string $uploadDir
35
     * @param array  $allowedMimeTypes
36
     * @param int    $maxFileSize
37
     * @param int    $maxWidth
38
     * @param int    $maxHeight
39
     * @param array  $allowedExtensions
40
     * @internal param int $cmodvalue
41
     */
42
    public function __construct($uploadDir, $allowedMimeTypes, $maxFileSize, $maxWidth = null, $maxHeight = null, $allowedExtensions = null)
43
    {
44
        if (\is_array($allowedMimeTypes)) {
0 ignored issues
show
The condition is_array($allowedMimeTypes) is always true.
Loading history...
45
            $this->allowedMimeTypes = &$allowedMimeTypes;
46
        }
47
//        $this->uploadDir = $uploadDir . (DS != mb_substr($uploadDir, mb_strlen($uploadDir) - 1, 1) ? DS : '');
48
//        if (\is_dir($uploadDir)) {
49
//            foreach (\explode(DS, $uploadDir) as $folder) {
50
//                $path .= DS . $folder;
51
//                if (!\mkdir($path, 0777) && !\is_dir($path)) {
52
//                    throw new \RuntimeException(\sprintf('Directory "%s" was not created', $path));
53
//                }
54
//            }
55
//        }
56
        $this->uploadDir = $uploadDir;
57
        $this->maxFileSize = (int)$maxFileSize;
58
        if (isset($maxWidth)) {
59
            $this->maxWidth = (int)$maxWidth;
60
        }
61
        if (isset($maxHeight)) {
62
            $this->maxHeight = (int)$maxHeight;
63
        }
64
        if (isset($allowedExtensions) && \is_array($allowedExtensions)) {
65
            $this->allowedExtensions = &$allowedExtensions;
66
        }
67
    }
68
69
    /**
70
     * Fetch the uploaded file
71
     *
72
     * @param       $index_name
73
     * @param int   $index Index of the file (if more than one uploaded under that name)
74
     * @return bool
75
     * @internal param string $media_name Name of the file field
76
     */
77
    public function fetchMedia($index_name, $index = null): bool
78
    {
79
        if (!isset($_FILES[$index_name])) {
80
            $this->setErrors('File not found');
81
82
            return false;
83
        }
84
85
        if (\is_array($_FILES[$index_name]['name'][$index]) && isset($index)) {
86
            $this->mediaName    = $_FILES[$index_name]['name'][$index];
87
            $this->mediaType    = $_FILES[$index_name]['type'][$index];
88
            $this->mediaSize    = $_FILES[$index_name]['size'][$index];
89
            $this->mediaTmpName = $_FILES[$index_name]['tmp_name'][$index];
90
            $this->mediaError   = !empty($_FILES[$index_name]['error'][$index]) ? $_FILES[$index_name]['errir'][$index] : 0;
91
        } else {
92
            $this->mediaName    = $_FILES[$index_name]['name'];
93
            $this->mediaType    = $_FILES[$index_name]['type'];
94
            $this->mediaSize    = $_FILES[$index_name]['size'];
95
            $this->mediaTmpName = $_FILES[$index_name]['tmp_name'];
96
            $this->mediaError   = !empty($_FILES[$index_name]['error']) ? $_FILES[$index_name]['error'] : 0;
97
        }
98
        $this->errors = [];
99
        if ((int)$this->mediaSize < 0) {
100
            $this->setErrors('Invalid File Size');
101
102
            return false;
103
        }
104
        if ('' == $this->mediaName) {
105
            $this->setErrors('Filename Is Empty');
106
107
            return false;
108
        }
109
        if ('none' === $this->mediaTmpName || !\is_uploaded_file($this->mediaTmpName) || 0 == $this->mediaSize) {
110
            $this->setErrors('No file uploaded');
111
112
            return false;
113
        }
114
        if ($this->mediaError > 0) {
115
            $this->setErrors('Error occurred: Error #' . $this->mediaError);
116
117
            return false;
118
        }
119
120
        return true;
121
    }
122
123
    /**
124
     * Set the target filename
125
     *
126
     * @param string $value
127
     **/
128
    public function setTargetFileName(string $value): void
129
    {
130
        $this->targetFileName = \trim($value);
131
    }
132
133
    /**
134
     * Set the prefix
135
     *
136
     * @param string $value
137
     **/
138
    public function setPrefix($value): void
139
    {
140
        $this->prefix = \trim($value);
141
    }
142
143
    /**
144
     * Get the uploaded filename
145
     *
146
     * @return  string
147
     **/
148
    public function getMediaName(): string
149
    {
150
        return $this->mediaName;
151
    }
152
153
    /**
154
     * Get the type of the uploaded file
155
     *
156
     * @return  string
157
     **/
158
    public function getMediaType(): string
159
    {
160
        return $this->mediaType;
161
    }
162
163
    /**
164
     * Get the size of the uploaded file
165
     *
166
     * @return  int
167
     **/
168
    public function getMediaSize(): int
169
    {
170
        return $this->mediaSize;
171
    }
172
173
    /**
174
     * Get the temporary name that the uploaded file was stored under
175
     *
176
     * @return  string
177
     **/
178
    public function getMediaTmpName(): string
179
    {
180
        return $this->mediaTmpName;
181
    }
182
183
    /**
184
     * Get the saved filename
185
     *
186
     * @return  string
187
     **/
188
    public function getSavedFileName(): string
189
    {
190
        return $this->savedFileName;
191
    }
192
193
    /**
194
     * Get the destination the file is saved to
195
     *
196
     * @return  string
197
     **/
198
    public function getSavedDestination(): string
199
    {
200
        return $this->savedDestination;
201
    }
202
203
    /**
204
     * Check the file and copy it to the destination
205
     *
206
     * @param int $chmod
207
     * @return bool
208
     */
209
    public function upload($chmod = 0644): bool
210
    {
211
        if ('' == $this->uploadDir) {
212
            $this->setErrors('Upload directory not set');
213
214
            return false;
215
        }
216
        if (!\is_dir($this->uploadDir)) {
217
            $this->setErrors('Failed opening directory: ' . $this->uploadDir);
218
219
            return false;
220
        }
221
        if (!\is_writable($this->uploadDir)) {
222
            $this->setErrors('Failed opening directory with write permission: ' . $this->uploadDir);
223
224
            return false;
225
        }
226
        if (!$this->checkMimeType()) {
227
            $this->setErrors('MIME type not allowed: ' . $this->mediaType);
228
229
            return false;
230
        }
231
        if (!$this->checkExtension()) {
232
            $this->setErrors('Extension not allowed');
233
234
            return false;
235
        }
236
        if (!$this->checkMaxFileSize()) {
237
            $this->setErrors('File size too large: ' . $this->mediaSize);
238
        }
239
        if (!$this->checkMaxWidth()) {
240
            $this->setErrors(\sprintf('File width must be smaller than %u', $this->maxWidth));
241
        }
242
        if (!$this->checkMaxHeight()) {
243
            $this->setErrors(\sprintf('File height must be smaller than %u', $this->maxHeight));
244
        }
245
        if (\count($this->errors) > 0) {
246
            return false;
247
        }
248
        if (!$this->_copyFile($chmod)) {
249
            $this->setErrors('Failed uploading file: ' . $this->mediaName);
250
251
            return false;
252
        }
253
254
        return true;
255
    }
256
257
    /**
258
     * Copy the file to its destination
259
     *
260
     * @param $chmod
261
     * @return bool
262
     */
263
    public function _copyFile($chmod): bool
264
    {
265
        $matched = [];
266
        if (!\preg_match('/\.([a-zA-Z0-9]+)$/', $this->mediaName, $matched)) {
267
            return false;
268
        }
269
        if (isset($this->targetFileName)) {
270
            $this->savedFileName = $this->targetFileName;
271
        } elseif (isset($this->prefix)) {
272
            $this->savedFileName = \uniqid($this->prefix, true) . '.' . \mb_strtolower($matched[1]);
273
        } else {
274
            $this->savedFileName = \mb_strtolower($this->mediaName);
275
        }
276
        $this->savedDestination = $this->uploadDir . '/' . $this->savedFileName;
277
        if (!\move_uploaded_file($this->mediaTmpName, $this->savedDestination)) {
278
            return false;
279
        }
280
        @\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

280
        /** @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...
281
282
        return true;
283
    }
284
285
    /**
286
     * Is the file the right size?
287
     *
288
     * @return  bool
289
     **/
290
    public function checkMaxFileSize(): bool
291
    {
292
        if ($this->mediaSize > $this->maxFileSize) {
293
            return false;
294
        }
295
296
        return true;
297
    }
298
299
    /**
300
     * Is the picture the right width?
301
     *
302
     * @return  bool
303
     **/
304
    public function checkMaxWidth(): bool
305
    {
306
        if (!isset($this->maxWidth) || $this->maxWidth < 1) {
307
            return true;
308
        }
309
        if (false !== $dimension = \getimagesize($this->mediaTmpName)) {
310
            if ($dimension[0] > $this->maxWidth) {
311
                return false;
312
            }
313
        } else {
314
            \trigger_error(\sprintf('Failed fetching image size of %s, skipping max width check..', $this->mediaTmpName), \E_USER_WARNING);
315
        }
316
317
        return true;
318
    }
319
320
    /**
321
     * Is the picture the right height?
322
     *
323
     * @return  bool
324
     **/
325
    public function checkMaxHeight(): bool
326
    {
327
        if (!isset($this->maxHeight) || $this->maxHeight < 1) {
328
            return true;
329
        }
330
        if (false !== $dimension = \getimagesize($this->mediaTmpName)) {
331
            if ($dimension[1] > $this->maxHeight) {
332
                return false;
333
            }
334
        } else {
335
            \trigger_error(\sprintf('Failed fetching image size of %s, skipping max height check..', $this->mediaTmpName), \E_USER_WARNING);
336
        }
337
338
        return true;
339
    }
340
341
    /**
342
     * Is the file the right Mime type
343
     *
344
     * (is there a right type of mime? ;-)
345
     *
346
     * @return  bool
347
     **/
348
    public function checkMimeType(): bool
349
    {
350
        if (\count($this->allowedMimeTypes) > 0 && !\in_array($this->mediaType, $this->allowedMimeTypes, true)) {
351
            return false;
352
        }
353
354
        return true;
355
    }
356
357
    /**
358
     * Is the file the right extension
359
     *
360
     * @return  bool
361
     **/
362
    public function checkExtension(): bool
363
    {
364
        $ext = mb_substr(mb_strrchr($this->mediaName, '.'), 1);
365
        if (!empty($this->allowedExtensions) && !\in_array(mb_strtolower($ext), $this->allowedExtensions, true)) {
366
            return false;
367
        }
368
369
        return true;
370
    }
371
372
    /**
373
     * Add an error
374
     *
375
     * @param string $error
376
     **/
377
    public function setErrors($error): void
378
    {
379
        $this->errors[] = \trim($error);
380
    }
381
382
    /**
383
     * Get generated errors
384
     *
385
     * @param bool $ashtml Format using HTML?
386
     *
387
     * @return  array|string    Array of array messages OR HTML string
388
     */
389
    public function &getErrors($ashtml = true)
390
    {
391
        if (!$ashtml) {
392
            return $this->errors;
393
        }
394
        $ret = '';
395
        if (\count($this->errors) > 0) {
396
            $ret = '<h4>Errors Returned While Uploading</h4>';
397
            foreach ($this->errors as $error) {
398
                $ret .= $error . '<br>';
399
            }
400
        }
401
402
        return $ret;
403
    }
404
}
405